OpenCV 4.10.0
开源计算机视觉
正在加载...
正在搜索...
无匹配项
samples/cpp/fitellipse.cpp

使用 fitEllipse 技术的示例

/********************************************************************************
*
*
* 此程序演示了椭圆拟合。程序找到
* 轮廓并使用三种方法用椭圆近似它。
* 1: OpenCV 的原始方法 fitEllipse,它实现了 Fitzgibbon 1995 方法。
* 2: Taubin 1991 提出的近似均方误差 (AMS) 方法 fitEllipseAMS
* 3: Fitzgibbon1999 提出的直接最小二乘 (Direct) 方法 fitEllipseDirect。
*
* 跟踪栏指定阈值参数。
*
* 白线是轮廓/输入点,以及用于生成数据的真实椭圆。
* 1: 蓝线是使用 OpenCV 的原始方法拟合的椭圆。
* 2: 绿线是使用 AMS 方法拟合的椭圆。
* 3: 红线是使用 Direct 方法拟合的椭圆。
*
*
* 原作者:Denis Burenkov
* AMS 和 Direct 方法作者:Jasper Shemilt
*
*
********************************************************************************/
#include <iostream>
using namespace cv;
using namespace std;
class canvas{
public:
bool setupQ;
cv::Point origin;
cv::Point corner;
int minDims,maxDims;
double scale;
int rows, cols;
cv::Mat img;
void init(int minD, int maxD){
// 使用最小和最大行和列大小初始化画布。
minDims = minD; maxDims = maxD;
origin = cv::Point(0,0);
corner = cv::Point(0,0);
scale = 1.0;
rows = 0;
cols = 0;
setupQ = false;
}
void stretch(cv::Point2f min, cv::Point2f max){
// 拉伸画布以包含点 min 和 max。
if(setupQ){
if(corner.x < max.x){corner.x = (int)(max.x + 1.0);};
if(corner.y < max.y){corner.y = (int)(max.y + 1.0);};
if(origin.x > min.x){origin.x = (int) min.x;};
if(origin.y > min.y){origin.y = (int) min.y;};
} else {
origin = cv::Point((int)min.x, (int)min.y);
corner = cv::Point((int)(max.x + 1.0), (int)(max.y + 1.0));
}
int c = (int)(scale*((corner.x + 1.0) - origin.x));
if(c<minDims){
scale = scale * (double)minDims/(double)c;
} else {
if(c>maxDims){
scale = scale * (double)maxDims/(double)c;
}
}
int r = (int)(scale*((corner.y + 1.0) - origin.y));
if(r<minDims){
scale = scale * (double)minDims/(double)r;
} else {
if(r>maxDims){
scale = scale * (double)maxDims/(double)r;
}
}
cols = (int)(scale*((corner.x + 1.0) - origin.x));
rows = (int)(scale*((corner.y + 1.0) - origin.y));
setupQ = true;
}
void stretch(vector<Point2f> pts)
{ // 拉伸画布,使所有点 pts 都在画布上。
cv::Point2f min = pts[0];
cv::Point2f max = pts[0];
for(size_t i=1; i < pts.size(); i++){
Point2f pnt = pts[i];
if(max.x < pnt.x){max.x = pnt.x;};
if(max.y < pnt.y){max.y = pnt.y;};
if(min.x > pnt.x){min.x = pnt.x;};
if(min.y > pnt.y){min.y = pnt.y;};
};
stretch(min, max);
}
void stretch(cv::RotatedRect box)
{ // 拉伸画布,使矩形框位于画布上。
cv::Point2f min = box.center;
cv::Point2f max = box.center;
cv::Point2f vtx[4];
box.points(vtx);
for( int i = 0; i < 4; i++ ){
cv::Point2f pnt = vtx[i];
if(max.x < pnt.x){max.x = pnt.x;};
if(max.y < pnt.y){max.y = pnt.y;};
if(min.x > pnt.x){min.x = pnt.x;};
if(min.y > pnt.y){min.y = pnt.y;};
}
stretch(min, max);
}
void drawEllipseWithBox(cv::RotatedRect box, cv::Scalar color, int lineThickness)
{
if(img.empty()){
stretch(box);
img = cv::Mat::zeros(rows,cols,CV_8UC3);
}
box.center = scale * cv::Point2f(box.center.x - origin.x, box.center.y - origin.y);
box.size.width = (float)(scale * box.size.width);
box.size.height = (float)(scale * box.size.height);
ellipse(img, box, color, lineThickness, LINE_AA);
Point2f vtx[4];
box.points(vtx);
for( int j = 0; j < 4; j++ ){
line(img, vtx[j], vtx[(j+1)%4], color, lineThickness, LINE_AA);
}
}
void drawPoints(vector<Point2f> pts, cv::Scalar color)
{
if(img.empty()){
stretch(pts);
img = cv::Mat::zeros(rows,cols,CV_8UC3);
}
for(size_t i=0; i < pts.size(); i++){
Point2f pnt = scale * cv::Point2f(pts[i].x - origin.x, pts[i].y - origin.y);
img.at<cv::Vec3b>(int(pnt.y), int(pnt.x))[0] = (uchar)color[0];
img.at<cv::Vec3b>(int(pnt.y), int(pnt.x))[1] = (uchar)color[1];
img.at<cv::Vec3b>(int(pnt.y), int(pnt.x))[2] = (uchar)color[2];
};
}
void drawLabels( std::vector<std::string> text, std::vector<cv::Scalar> colors)
{
if(img.empty()){
img = cv::Mat::zeros(rows,cols,CV_8UC3);
}
int vPos = 0;
for (size_t i=0; i < text.size(); i++) {
cv::Scalar color = colors[i];
std::string txt = text[i];
Size textsize = getTextSize(txt, FONT_HERSHEY_COMPLEX, 1, 1, 0);
vPos += (int)(1.3 * textsize.height);
Point org((img.cols - textsize.width), vPos);
cv::putText(img, txt, org, FONT_HERSHEY_COMPLEX, 1, color, 1, LINE_8);
}
}
};
static void help(char** argv)
{
cout << "\n此程序演示了椭圆拟合。该程序找到\n"
"轮廓并用椭圆对其进行近似。使用三种方法来找到\n"
"椭圆拟合:fitEllipse、fitEllipseAMS 和 fitEllipseDirect。\n"
"调用:\n"
<< argv[0] << " [image_name -- 默认 ellipses.jpg]\n" << endl;
}
int sliderPos = 70;
Mat image;
bool fitEllipseQ, fitEllipseAMSQ, fitEllipseDirectQ;
cv::Scalar fitEllipseColor = Scalar(255, 0, 0);
cv::Scalar fitEllipseAMSColor = Scalar( 0,255, 0);
cv::Scalar fitEllipseDirectColor = Scalar( 0, 0,255);
cv::Scalar fitEllipseTrueColor = Scalar(255,255,255);
void processImage(int, void*);
int main( int argc, char** argv )
{
fitEllipseQ = true;
fitEllipseAMSQ = true;
fitEllipseDirectQ = true;
cv::CommandLineParser parser(argc, argv,"{help h||}{@image|ellipses.jpg|}");
if (parser.has("help"))
{
help(argv);
return 0;
}
string filename = parser.get<string>("@image");
image = imread(samples::findFile(filename), 0);
if( image.empty() )
{
cout << "无法打开图像 " << filename << "\n";
return 0;
}
imshow("source", image);
namedWindow("result", WINDOW_NORMAL );
// 创建工具栏。HighGUI 使用。
createTrackbar( "threshold", "result", &sliderPos, 255, processImage );
processImage(0, 0);
// 等待按键;相同的功能安排事件处理
return 0;
}
inline static bool isGoodBox(const RotatedRect& box) {
//size.height >= size.width 总是,只有当点在一条线上或在同一点时,size.width=0
return (box.size.height <= box.size.width * 30) && (box.size.width > 0);
}
// 定义轨迹条回调函数。此函数查找轮廓,
// 绘制它们,并用椭圆近似。
void processImage(int /*h*/, void*)
{
RotatedRect box, boxAMS, boxDirect;
vector<vector<Point> > contours;
Mat bimage = image >= sliderPos;
findContours(bimage, contours, RETR_LIST, CHAIN_APPROX_NONE);
canvas paper;
paper.init(int(0.8*MIN(bimage.rows, bimage.cols)), int(1.2*MAX(bimage.rows, bimage.cols)));
paper.stretch(cv::Point2f(0.0f, 0.0f), cv::Point2f((float)(bimage.cols+2.0), (float)(bimage.rows+2.0)));
std::vector<std::string> text;
std::vector<cv::Scalar> color;
if (fitEllipseQ) {
text.push_back("OpenCV");
color.push_back(fitEllipseColor);
}
if (fitEllipseAMSQ) {
text.push_back("AMS");
color.push_back(fitEllipseAMSColor);
}
if (fitEllipseDirectQ) {
text.push_back("Direct");
color.push_back(fitEllipseDirectColor);
}
paper.drawLabels(text, color);
int margin = 2;
vector< vector<Point2f> > points;
for(size_t i = 0; i < contours.size(); i++)
{
size_t count = contours[i].size();
if( count < 6 )
continue;
Mat pointsf;
Mat(contours[i]).convertTo(pointsf, CV_32F);
vector<Point2f>pts;
for (int j = 0; j < pointsf.rows; j++) {
Point2f pnt = Point2f(pointsf.at<float>(j,0), pointsf.at<float>(j,1));
if ((pnt.x > margin && pnt.y > margin && pnt.x < bimage.cols-margin && pnt.y < bimage.rows-margin)) {
if(j%20==0){
pts.push_back(pnt);
}
}
}
points.push_back(pts);
}
for(size_t i = 0; i < points.size(); i++)
{
vector<Point2f> pts = points[i];
// 至少 5 个点可以拟合椭圆
if (pts.size()<5) {
continue;
}
if (fitEllipseQ) {
box = fitEllipse(pts);
if (isGoodBox(box)) {
paper.drawEllipseWithBox(box, fitEllipseColor, 3);
}
}
if (fitEllipseAMSQ) {
boxAMS = fitEllipseAMS(pts);
if (isGoodBox(boxAMS)) {
paper.drawEllipseWithBox(boxAMS, fitEllipseAMSColor, 2);
}
}
if (fitEllipseDirectQ) {
boxDirect = fitEllipseDirect(pts);
if (isGoodBox(boxDirect)){
paper.drawEllipseWithBox(boxDirect, fitEllipseDirectColor, 1);
}
}
paper.drawPoints(pts, fitEllipseTrueColor);
}
imshow("result", paper.img);
}
用于命令行解析。
定义 utility.hpp:820
n 维密集数组类
定义 mat.hpp:812
static CV_NODISCARD_STD MatExpr zeros(int rows, int cols, int type)
返回指定大小和类型的零数组。
_Tp & at(int i0=0)
返回对指定数组元素的引用。
int cols
定义 mat.hpp:2138
bool empty() const
如果数组没有元素,则返回 true。
int rows
行和列的数量或 (-1, -1)(当矩阵具有超过 2 维时)
定义 mat.hpp:2138
void convertTo(OutputArray m, int rtype, double alpha=1, double beta=0) const
将数组转换为另一种数据类型,并进行可选缩放。
_Tp y
点的 y 坐标
定义 types.hpp:202
_Tp x
点的 x 坐标
定义 types.hpp:201
该类表示平面上的旋转(即非直立)矩形。
定义 types.hpp:531
Point2f center
返回矩形的质量中心
定义 types.hpp:563
Size2f size
返回矩形的宽度和高度
定义 types.hpp:565
void points(Point2f pts[]) const
用于指定图像或矩形大小的模板类。
定义 types.hpp:335
_Tp height
高度
定义 types.hpp:363
_Tp width
宽度
定义 types.hpp:362
用于短数值向量的模板类,是 Matx 的特例。
定义 matx.hpp:369
Point2i Point
定义 types.hpp:209
Point_< float > Point2f
定义 types.hpp:207
#define CV_32F
定义 interface.h:78
unsigned char uchar
定义 interface.h:51
#define CV_8UC3
定义 interface.h:90
#define MIN(a, b)
定义 cvdef.h:513
#define MAX(a, b)
定义 cvdef.h:517
void imshow(const String &winname, InputArray mat)
在指定窗口中显示图像。
int waitKey(int delay=0)
等待按键。
void namedWindow(const String &winname, int flags=WINDOW_AUTOSIZE)
创建窗口。
int createTrackbar(const String &trackbarname, const String &winname, int *value, int count, TrackbarCallback onChange=0, void *userdata=0)
创建一个滑动条并将其附加到指定的窗口。
CV_EXPORTS_W Mat imread(const String &filename, int flags=IMREAD_COLOR)
从文件加载图像。
void putText(InputOutputArray img, const String &text, Point org, int fontFace, double fontScale, Scalar color, int thickness=1, int lineType=LINE_8, bool bottomLeftOrigin=false)
绘制文本字符串。
RotatedRect fitEllipseDirect(InputArray points)
将椭圆拟合到一组二维点。
RotatedRect fitEllipseAMS(InputArray points)
将椭圆拟合到一组二维点。
void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method, Point offset=Point())
在二值图像中查找轮廓。
RotatedRect fitEllipse(InputArray points)
将椭圆拟合到一组二维点。
int main(int argc, char *argv[])
定义 highgui_qt.cpp:3
与磁盘上文件关联的文件存储的“黑盒”表示。
定义 core.hpp:102
STL 命名空间。