还有,图片找不到了,也很典型,圆弧,或者类圆弧的目标边缘,中间被其他物体穿插过去,往往这种圆弧边缘还是多条靠的比较近,放大局部还是凹凸不平
问题描述没看明白,是要用opencv做圆弧边缘检测?还是什么?
仅供参考:
static double distanceBtwPoints(const Point &a, const Point &b) {
double xDiff = a.x - b.x;
double yDiff = a.y - b.y;
return std::sqrt((xDiff * xDiff) + (yDiff * yDiff));
}
/*static int sign(const int v) {
if (v>2) return 1;
if (v<2) return -1;
return 0;
}*/
static int sign(const int v) {//太小的值当0处理
if (v>0) return 1;
if (v<0) return -1;
return 0;
}
double angle3Points(Point pt1, Point pt2, Point pt0) {// finds a angle between vectors from pt0->pt1 and from pt0->pt2
double dx1 = pt1.x - pt0.x;
double dy1 = pt1.y - pt0.y;
double dx2 = pt2.x - pt0.x;
double dy2 = pt2.y - pt0.y;
double angle=acos((dx1*dx2 + dy1*dy2) / sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10))*180.0 / 3.14159265;
// double cross=dx1*dy2-dx2*dy1;
// if (cross<0) angle=-angle;
return angle;
}
void gamma_correct(Mat& img, Mat& dst, double gamma) {
Mat temp;
CvMat tmp;
img.convertTo(temp, CV_32FC1, 1.0 / 255.0, 0.0);
tmp = temp;
cvPow(&tmp, &tmp, gamma);
temp.convertTo(dst, CV_8UC1, 255.0, 0.0);
}
void CSDLMuxerPlayDlg::DisplayYUV420ToRGB(int iW, int iH) //改变分辨率,可自动根据内容调节
{
int w = iW;
int h = iH;
printf("yuv file w: %d, h: %d \n", w, h);
FILE* pFileIn;// = null; //001 FILE 是一个类,*pfile表示指向FILE类的一个指针
int bufLen = w*h * 3 / 2; //002 设置宽高
unsigned char* pYuvBuf = new unsigned char[bufLen];
fopen_s(&pFileIn, "screem.yuv", "rb+"); //003 rb+ 读写打开一个二进制文件,只允许读写数据。
fread(pYuvBuf, bufLen * sizeof(unsigned char), 1, pFileIn); //004 fread是一个函数,它从文件流中读数据
fclose(pFileIn); //005 fclose()函数用来关闭当前文件流,其原型为: int fclose(FILE * stream);
Mat yuvImg; //006 创建
yuvImg.create(h + h / 2, w, CV_8UC1);// (h * 3 / 2, w, CV_8UC1);
memcpy(yuvImg.data, pYuvBuf, bufLen * sizeof(unsigned char)); //007 memcpy函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中
delete[] pYuvBuf;
Mat rgbImg;
//cvtColor(yuvImg, rgbImg, CV_YUV2BGR_I420);
int i, j, k, n, ip;
static char dbgstr[16000]; //008 static这个函数需要调用多次,因此定义一个static的数组保存在静态区中,可以实现多次访问
const static Scalar colors[15] = {
CV_RGB(0, 0,128), // colors[ 0] 深蓝
CV_RGB(0,128, 0), // colors[ 1] 深绿
CV_RGB(0,128,128), // colors[ 2] 深青
CV_RGB(128, 0, 0), // colors[ 3] 深红
CV_RGB(128, 0,128), // colors[ 4] 品红
CV_RGB(128,128, 0), // colors[ 5] 棕
CV_RGB(128,128,128), // colors[ 6] 深灰
CV_RGB(160,160,160), // colors[ 7] 浅灰
CV_RGB(0, 0,255), // colors[ 8] 蓝
CV_RGB(0,255, 0), // colors[ 9] 绿
CV_RGB(0,255,255), // colors[10] 亮青
CV_RGB(255, 0, 0), // colors[11] 红
CV_RGB(255, 0,255), // colors[12] 亮品红
CV_RGB(255,255, 0), // colors[13] 黄
CV_RGB(255,255,255), // colors[14] 白
};
Scalar color; // 标量颜色
vector<Vec4i> hierarchy; // 是openCV里面找边界的程序里面的语句
vector<vector<Point> > contours; // contours被定义成二维浮点型向量,这里面将来会存储找到的边界的(x,y)坐标。vector<Vec4i>hierarchy是定义的层级。这个在找边界findcontours的时候会自动生成,这里只是给它开辟一个空间。将来findContours( src, contours, hierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE, Point(0, 0) );就能算出边界的坐标,存在contours里面
Rect maxrect, brect; // Rect 矩形,maxrect是框住所有轮廓的最小矩形
int idx;
//char *jpgs_name[8] = { "1.jpg","2.jpg","3.jpg","4.jpg","5.jpg","6.jpg","7.jpg","8.jpg" };
char *jpgs_name[15] = { "1.png","4.png","7.png","17.png","24.png","25.png","28.png","37.png","45.png","51.png","53.png","65.png","68.png","90.png","95.png" };
bool ARC_FOUND; // bool表示布尔型变量,也就是逻辑型变量的定义符
for (n = 0; n<15; n++) { //这是多幅图依次去查
//n = 0; //现输入为视频流截图,不需四副已知的测试图片 因此n为0
ARC_FOUND = false;
rgbImg= imread(jpgs_name[n], 1);//0灰度
Mat grayImg;
cvtColor(rgbImg, grayImg, COLOR_BGR2GRAY);
//imshow("grayImg", grayImg); //原截图的灰度图
Mat roiImg;
//grayImg(Rect(grayImg.cols*4 / 10, grayImg.rows / 20, (int)(grayImg.cols*5.5/10),(int)(grayImg.rows*5.5/10))).copyTo(roiImg); //兴趣区域调整
roiImg = grayImg;
equalizeHist(roiImg, roiImg);//直方图均匀化
gamma_correct(roiImg, roiImg, 2.0);//伽马校正
//imshow("roiImg", roiImg); //感兴趣区域的灰度图
Mat bwImg;
threshold(roiImg, bwImg, 250, 255, THRESH_BINARY | CV_THRESH_OTSU); //threshold是设定阈值
//imshow("bwImg", bwImg);
Size small_bwImgSize = Size(200, 160);//图缩小到200x160了
Mat small_bwImg = Mat(small_bwImgSize, bwImg.type());
resize(bwImg, small_bwImg, small_bwImgSize, CV_INTER_LINEAR);
imshow("small_bwImg", small_bwImg);
Mat Structure0=getStructuringElement(MORPH_ELLIPSE,Size(13,13));
erode(small_bwImg,small_bwImg,Structure0,Point(-1,-1));
Mat Structure1 = getStructuringElement(MORPH_ELLIPSE, Size(9, 9));
dilate(small_bwImg, small_bwImg, Structure1, Point(-1, -1));
Mat small_rgbImg;
cvtColor(small_bwImg, small_rgbImg, COLOR_GRAY2BGR); //颜色空间转换cvtColor()
findContours(small_bwImg, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE); //函数findContours( InputOutputArray image, OutputArrayOfArrays contours。第一个参数:image,单通道图像矩阵,可以是灰度图,但更常用的是二值图像,一般是经过Canny、拉普拉斯等边缘检测算子处理过的二值图像;第二个参数:contours,定义为“vector<vector<Point>> contours”,是一个向量,并且是一个双重向量,向量 内每个元素保存了一组由连续的Point点构成的点的集合的向量,每一组Point点集就是一个轮廓
if (!contours.empty() && !hierarchy.empty()) { //OpenCV中通过使用findContours函数,简单几个的步骤就可以检测出物体的轮廓
int idx_of_focus_area = -1;
double min_dist;
double dist,dist1,dist2;
double angle;
Point focus=Point(100,30);//焦点,位于整个图片中心偏上位置。通过观察所有包含气球的图片得到。
circle(small_rgbImg, focus, 5, colors[5]);
min_dist = 1000000.0;
idx = 0;
for (; idx >= 0; idx = hierarchy[idx][0]) { //hierarchy[i] 表示第 i 个轮廓
color = colors[idx % 15]; // 第0~15个轮廓用第0~15种颜色画,第16~31个轮廓用第0~15种颜色画,……
//drawContours(small_rgbImg,contours,idx,color,1,8,hierarchy);
vector<Point> approx; //如 vector<Point2f> point[2]中 Point2f就是个Point,注意二维
approxPolyDP(Mat(contours[idx]), approx, 0.5, true);
const Point* p = &approx[0]; //轮廓提取函数解释 https://blog.csdn.net/wwwssszheren/article/details/76559636
int m = (int)approx.size();
polylines(small_rgbImg, &p, &m, 1, true, color);
circle(small_rgbImg, Point(p[0].x, p[0].y), 6, color);
circle(small_rgbImg, Point(p[1].x, p[1].y), 4, color);
for (i = 2; i<m; i++) circle(small_rgbImg, Point(p[i].x, p[i].y), 2, color); //以拉直后轮廓上每个节点为圆心画大小不等的小圆圈标注
//就是做到让提取出的轮廓,再拉直后,共有几个轮廓,每个轮廓由哪些点构成而已。
double area = contourArea(approx);//contourArea轮廓面积检测
Rect rect = boundingRect(approx); //矩形包围盒
Point ct;//包围盒中心
ct.x=rect.x+rect.width/2;
ct.y=rect.y+rect.height/2;
dist = distanceBtwPoints(ct, focus);//包围盒中心离焦点距离
sprintf_s(dbgstr, "n,idx,area,dist:%2d,%2d,%6.1lf,%5.1lf\n", n, idx, area, dist); OutputDebugStringA(dbgstr);
if (dist<min_dist && 250.0<area) {
min_dist = dist;
idx_of_focus_area = idx;//找出面积足够大,且包围盒中心离焦点最近的那个轮廓
}
}
if (idx_of_focus_area >= 0) {
idx = idx_of_focus_area;
color = colors[11];//用红色画出面积足够大,且包围盒中心离焦点最近的那个轮廓
//drawContours(small_rgbImg,contours,idx,color,1,8,hierarchy);
vector<Point> approx;
approxPolyDP(Mat(contours[idx]), approx, 0.5, true);
const Point* p = &approx[0];
int m = (int)approx.size();
polylines(small_rgbImg, &p, &m, 1, true, color);
circle(small_rgbImg, Point(p[0].x, p[0].y), 6, color);
circle(small_rgbImg, Point(p[1].x, p[1].y), 4, color);
for (i = 2; i<m; i++) circle(small_rgbImg, Point(p[i].x, p[i].y), 2, color);
Rect rect = boundingRect(approx);
//rectangle(small_rgbImg,rect,color);
Point left_bottom = Point(rect.x, rect.y + rect.height);//左下角
int i_of_min_dist = -1;
min_dist = 1000000.0;
for (i = 0; i<m; i++) {
dist = distanceBtwPoints(left_bottom, Point(p[i].x, p[i].y));
if (dist<min_dist) {
min_dist = dist;
i_of_min_dist = i;
}
}
int bp = i_of_min_dist;//找出离轮廓包围盒左下角最近的点
color = colors[0];
circle(small_rgbImg, Point(p[bp].x, p[bp].y), 8, color);
Point right_top = Point(rect.x + rect.width, rect.y);
i_of_min_dist = -1;
min_dist = 1000000.0;
for (i = 0; i<m; i++) {
dist = distanceBtwPoints(right_top, Point(p[i].x, p[i].y));
if (dist<min_dist) {
min_dist = dist;
i_of_min_dist = i;
}
}
int ep = i_of_min_dist;//找出离轮廓包围盒右上角最近的点
color = colors[1];
circle(small_rgbImg, Point(p[ep].x, p[ep].y), 8, color);
color = colors[12];
if (bp>ep) ep += m;
int s = 0;
if (ep - bp >= 4) {//至少由4个线段组成
for (ip = bp; ip<ep; ip++) {
i = ip%m;
j = (ip + 1) % m;
k = (ip + 2) % m;
if (ip != bp) {
circle(small_rgbImg, Point(p[i].x, p[i].y), 8, color);
}
//判断是否存在“连续4段往右上方延伸,且长度在一定范围,且每次拐弯往左偏的角度误差范围也不大”的线段
int sdx = sign(p[j].x - p[i].x);
int sdy = sign(p[j].y - p[i].y);
dist1 = distanceBtwPoints(Point(p[j].x, p[j].y), Point(p[i].x, p[i].y));
dist2 = distanceBtwPoints(Point(p[j].x, p[j].y), Point(p[k].x, p[k].y));
angle = angle3Points(Point(p[i].x, p[i].y), Point(p[k].x, p[k].y), Point(p[j].x, p[j].y));
sprintf_s(dbgstr, "n,i,sdx,sdy,dist1,dist2,angle:%2d,%3d,%2d,%2d,%4.1lf,%4.1lf,%5.1lf\n", n, i, sdx, sdy, dist1, dist2, angle); OutputDebugStringA(dbgstr);
if (s == 0) {
if (sdx == 1 && sdy <= 0 && 1.4 <= dist1 && dist1 <= 16.0 && angle>120) s = 1;
} else if (s == 1) {
if (sdx == 1 && sdy <= 0 && 1.4 <= dist1 && dist1 <= 16.0 && angle>120) s = 2; else s = 0;
} else if (s == 2) {
if (sdx == 1 && sdy <= 0 && 1.4 <= dist1 && dist1 <= 16.0 && angle>120) s = 3; else s = 0;
} else if (s == 3) {
if (sdx == 1 && sdy <= 0 && 1.4 <= dist1 && dist1 <= 16.0 && angle>120) s = 4; else s = 0;
if (s == 4) {
for (ip=0;ip<5;ip++) {
i=(k+m-ip)%m;
circle(small_rgbImg, Point(p[i].x, p[i].y), 8, colors[10]); //用亮青色圈住组成这4段的5个点。调试用
}
ARC_FOUND = true;
break;//
}
}
}
}
if (!ARC_FOUND) {//之前未检测到从左下到右上的圆弧,下面检测从左上到右下的圆弧
Point left_top = Point(rect.x , rect.y);
i_of_min_dist = -1;
min_dist = 1000000.0;
for (i = 0; i<m; i++) {
dist = distanceBtwPoints(left_top, Point(p[i].x, p[i].y));
if (dist<min_dist) {
min_dist = dist;
i_of_min_dist = i;
}
}
int bp = i_of_min_dist;//找出离轮廓包围盒左上角最近的点
color = colors[0];
circle(small_rgbImg, Point(p[bp].x, p[bp].y), 8, color);
Point right_bottom = Point(rect.x+rect.width, rect.y + rect.height);//右下角
int i_of_min_dist = -1;
min_dist = 1000000.0;
for (i = 0; i<m; i++) {
dist = distanceBtwPoints(right_bottom, Point(p[i].x, p[i].y));
if (dist<min_dist) {
min_dist = dist;
i_of_min_dist = i;
}
}
int ep = i_of_min_dist;//找出离轮廓包围盒右下角最近的点
color = colors[1];
circle(small_rgbImg, Point(p[ep].x, p[ep].y), 8, color);
color = colors[12];
if (bp>ep) ep += m;
int s = 0;
if (ep - bp >= 4) {//至少由4个线段组成
for (ip = bp; ip<ep; ip++) {
i = ip%m;
j = (ip + 1) % m;
k = (ip + 2) % m;
if (ip != bp) {
circle(small_rgbImg, Point(p[i].x, p[i].y), 8, color);
}
//判断是否存在“连续4段往右下方延伸,且长度在一定范围,且每次拐弯往左偏的角度误差范围也不大”的线段
int sdx = sign(p[j].x - p[i].x);
int sdy = sign(p[j].y - p[i].y);
dist1 = distanceBtwPoints(Point(p[j].x, p[j].y), Point(p[i].x, p[i].y));
dist2 = distanceBtwPoints(Point(p[j].x, p[j].y), Point(p[k].x, p[k].y));
angle = angle3Points(Point(p[i].x, p[i].y), Point(p[k].x, p[k].y), Point(p[j].x, p[j].y));
sprintf_s(dbgstr, "n,i,sdx,sdy,dist1,dist2,angle:%2d,%3d,%2d,%2d,%4.1lf,%4.1lf,%5.1lf\n", n, i, sdx, sdy, dist1, dist2, angle); OutputDebugStringA(dbgstr);
if (s == 0) {
if (sdx == 1 && sdy >= 0 && 1.4 <= dist1 && dist1 <= 16.0 && angle>120) s = 1;
} else if (s == 1) {
if (sdx == 1 && sdy >= 0 && 1.4 <= dist1 && dist1 <= 16.0 && angle>120) s = 2; else s = 0;
} else if (s == 2) {
if (sdx == 1 && sdy >= 0 && 1.4 <= dist1 && dist1 <= 16.0 && angle>120) s = 3; else s = 0;
} else if (s == 3) {
if (sdx == 1 && sdy >= 0 && 1.4 <= dist1 && dist1 <= 16.0 && angle>120) s = 4; else s = 0;
if (s == 4) {
for (ip=0;ip<5;ip++) {
i=(k+m-ip)%m;
circle(small_rgbImg, Point(p[i].x, p[i].y), 8, colors[10]); //用亮青色圈住组成这4段的5个点。调试用
}
ARC_FOUND = true;
break;//
}
}
}
}
}
}
}
imshow("small_rgbImg", small_rgbImg);
if (ARC_FOUND) {
sprintf_s(dbgstr, "%2d %-6s ARC_FOUND\n", n, jpgs_name[n]); OutputDebugStringA(dbgstr);
}
else {
sprintf_s(dbgstr, "%2d %-6s arc not found\n", n, jpgs_name[n]); OutputDebugStringA(dbgstr);
}
waitKey(0);
//waitKey(1);
}
}
想要识别出的边缘没有这么多噪声的话,可以使用opencv的多边形拟合方法移除多余的噪点,将求边缘线问题转换为光滑物体轮廓的问题。然后在去截取opencv的多边形拟合的效果图。这样子边缘线波动就不会太多。同样,也可提取出边缘点的像素坐标,然后做一个平滑。
这样的直线抓边功能,不知道挑选最优边的规则是什么,无论工具框如何放置,总能给出一些候选边缘点,虽然在某些情况下也输出的结果也不正确,但是就像磁力吸附一样挑出一条轮廓出来一样