/**************************************************************************** ** File name : HTOpencvImg.cpp ** Description : define 104 worker thread group ** Create date : 2018.09.01 ** Auther by : Liuyx ** Version info : V1.0.01 ** Copyrigth By: xi'an huatek, Inc Co., Ltd ** Update record: ** DATE AUTHER DESC ** ------------------------------------------------------------------------- ** 2018.09.01 Liuyx first build ****************************************************************************/ #include "HTGlobal.h" #include "HTTestOpencv.h" using namespace cv; static const char *_FILE_ = "HTTestOpencv.cpp"; #ifndef ERROR #define ERROR -1 #endif string hehe[100] = { "time_0.jpg", "time_1.jpg", "time_2.jpg", "time_3.jpg", "time_4.jpg", "time_5.jpg", "time_6.jpg", "time_7.jpg", "time_8.jpg", "time_9.jpg", }; //----------------------------------------------------------------------------- //平面几何相关函数http://www.cnblogs.com/zjutlitao/p/3243883.html //----------------------------------------------------------------------------- // file exist bool isExistFile(const char *filename) { FILE *fp = NULL; fp = fopen(filename, "rb"); if (fp == NULL) return false; fclose(fp); return true; } ////求向量的夹角 //double Angle(Point A, Point B) //{ // return acos(Dot(A, B) / Length(A) / Length(B)); //} // ////点到直线的距离 //double DistanceToLine(Point P, Point A, Point B) //{ // Point v1 = B - A, v2 = P - A; // return fabs(Cross(v1, v2)) / Length(v1);//如果不加绝对值是带有方向的距离 //} //度数转换 double DegreeTrans(double theta) { double res = theta / CV_PI * 180; return res; } //逆时针旋转图像degree角度(原尺寸) void rotateImage(Mat src, Mat& img_rotate, double degree) { //旋转中心为图像中心 Point2f center; center.x = float(src.cols / 2.0); center.y = float(src.rows / 2.0); int length = 0; length = (int)sqrt(src.cols*src.cols + src.rows*src.rows); //计算二维旋转的仿射变换矩阵 Mat M = getRotationMatrix2D(center, degree, 1); warpAffine(src, img_rotate, M, Size(length, length), 1, 0, Scalar(255, 255, 255));//仿射变换,背景色填充为白色 showImg("旋转后:", img_rotate); } //通过霍夫变换计算角度 // double CalcDegree(const Mat &srcImage, Mat &dst) { Mat midImage, dstImage; Canny(srcImage, midImage, 50, 200, 3); cvtColor(midImage, dstImage, CV_GRAY2BGR); //通过霍夫变换检测直线 vector lines; HoughLines(midImage, lines, 1, CV_PI / 180, 300, 0, 0);//第5个参数就是阈值,阈值越大,检测精度越高 //cout << lines.size() << endl; //由于图像不同,阈值不好设定,因为阈值设定过高导致无法检测直线,阈值过低直线太多,速度很慢 //所以根据阈值由大到小设置了三个阈值,如果经过大量试验后,可以固定一个适合的阈值。 if (!lines.size()) { HoughLines(midImage, lines, 1, CV_PI / 180, 200, 0, 0); } //cout << lines.size() << endl; if (!lines.size()) { HoughLines(midImage, lines, 1, CV_PI / 180, 150, 0, 0); } //cout << lines.size() << endl; if (!lines.size()) { cout << "没有检测到直线!" << endl; return -1; } float sum = 0; //依次画出每条线段 for (size_t i = 0; i < lines.size(); i++) { float rho = lines[i][0]; float theta = lines[i][1]; Point pt1, pt2; //cout << theta << endl; double a = cos(theta), b = sin(theta); double x0 = a * rho, y0 = b * rho; pt1.x = cvRound(x0 + 1000 * (-b)); pt1.y = cvRound(y0 + 1000 * (a)); pt2.x = cvRound(x0 - 1000 * (-b)); pt2.y = cvRound(y0 - 1000 * (a)); //只选角度最小的作为旋转角度 sum += theta; line(dstImage, pt1, pt2, Scalar(55, 100, 195), 1, CV_AA); //Scalar函数用于调节线段颜色 showImg("直线探测效果图", dstImage); } float average = sum / lines.size(); //对所有角度求平均,这样做旋转效果会更好 cout << "average theta:" << average << endl; double angle = DegreeTrans(average) - 90; rotateImage(dstImage, dst, angle); showImg("直线探测效果图2", dstImage); return angle; } // 图片矫正 void ImageRecify(const char* pInFileName) { double degree; Mat src = imread(pInFileName); showImg("原始图", src); int srcWidth, srcHight; srcWidth = src.cols; srcHight = src.rows; cout << srcWidth << " " << srcHight << endl; Mat dst; src.copyTo(dst); //倾斜角度矫正 degree = CalcDegree(src, dst); if (degree == ERROR) { cout << "矫正失败!" << endl; return; } rotateImage(src, dst, degree); cout << "angle:" << degree << endl; showImg("旋转调整后", dst); Mat resulyImage = dst(Rect(0, 0, srcWidth, srcHight)); showImg("裁剪之后", resulyImage); //imwrite("recified.jpg", resulyImage); } /****************倾斜校正子程序*****************/ //函数名称:IplImage *Rotate(IplImage *RowImage) //功能:对每行数字进行倾斜校正 //入口参数:行图像RowImage //出口参数:旋转后的图像RotateRow //https://blog.csdn.net/ZhtSunday/article/details/52094745 /********************************************/ void ImgRotate(const char *img_file) { IplImage * RowImage = cvLoadImage(img_file); //建立储存边缘检测结果图像canImage IplImage *canImage = cvCreateImage(cvGetSize(RowImage), IPL_DEPTH_8U, 1); //进行边缘检测 cvCanny(RowImage, canImage, 30, 200, 3); //进行hough变换 CvMemStorage *storage = cvCreateMemStorage(); CvSeq *lines = NULL; lines = cvHoughLines2(canImage, storage, CV_HOUGH_STANDARD, 1, CV_PI / 180, 20, 0, 0); //统计与竖直夹角<30度的直线个数以及其夹角和 int numLine = 0; float sumAng = 0.0; for (int i = 0; i < lines->total; i++) { float *line = (float *)cvGetSeqElem(lines, i); float theta = line[1]; //获取角度 为弧度制 if (theta < 30 * CV_PI / 180 || (CV_PI - theta) < 30 * CV_PI / 180) { numLine++; sumAng = sumAng + theta; } } //计算出平均倾斜角,anAng为角度制 double avAng = ((sumAng / numLine) * 180) / CV_PI; //获取二维旋转的仿射变换矩阵 CvPoint2D32f center; center.x = float(RowImage->width / 2.0); center.y = float(RowImage->height / 2.0); float m[6]; CvMat M = cvMat(2, 3, CV_32F, m); cv2DRotationMatrix(center, avAng, 1, &M); //建立输出图像RotateRow double a = sin(sumAng / numLine); double b = cos(sumAng / numLine); int width_rotate = int(RowImage->height*fabs(a) + RowImage->width*fabs(b)); int height_rotate = int(RowImage->width*fabs(a) + RowImage->height*fabs(b)); IplImage *RotateRow = cvCreateImage(cvSize(width_rotate, height_rotate), IPL_DEPTH_8U, 1); //变换图像,并用黑色填充其余值 m[2] += (width_rotate - RowImage->width) / 2; m[5] += (height_rotate - RowImage->height) / 2; cvWarpAffine(RowImage, RotateRow, &M, CV_INTER_LINEAR + CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); // 这有异常报出 //释放 cvReleaseImage(&canImage); cvReleaseMemStorage(&storage); cvNamedWindow("Roter:"); cvShowImage("Roter", RotateRow); return; // RotateRow; } //旋转图像内容不变,尺寸相应变大 //https ://blog.csdn.net/xiaowei_cqu/article/details/7616044 IplImage* rotateImage1(IplImage* img, int degree) { double angle = degree * CV_PI / 180.; // 弧度 double a = sin(angle), b = cos(angle); int width = img->width; int height = img->height; int width_rotate = int(height * fabs(a) + width * fabs(b)); int height_rotate = int(width * fabs(a) + height * fabs(b)); //旋转数组map // [ m0 m1 m2 ] ===> [ A11 A12 b1 ] // [ m3 m4 m5 ] ===> [ A21 A22 b2 ] float map[6]; CvMat map_matrix = cvMat(2, 3, CV_32F, map); // 旋转中心 CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2); cv2DRotationMatrix(center, degree, 1.0, &map_matrix); map[2] += (width_rotate - width) / 2; map[5] += (height_rotate - height) / 2; IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), 8, 3); //对图像做仿射变换 //CV_WARP_FILL_OUTLIERS - 填充所有输出图像的象素。 //如果部分象素落在输入图像的边界外,那么它们的值设定为 fillval. //CV_WARP_INVERSE_MAP - 指定 map_matrix 是输出图像到输入图像的反变换, cvWarpAffine(img, img_rotate, &map_matrix, CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS, cvScalarAll(0)); return img_rotate; } //旋转图像内容不变,尺寸相应变大 //https ://blog.csdn.net/xiaowei_cqu/article/details/7616044 IplImage* rotateImage2(IplImage* img, int degree) { double angle = degree * CV_PI / 180.; double a = sin(angle), b = cos(angle); int width = img->width, height = img->height; //旋转后的新图尺寸 int width_rotate = int(height * fabs(a) + width * fabs(b)); int height_rotate = int(width * fabs(a) + height * fabs(b)); IplImage* img_rotate = cvCreateImage(cvSize(width_rotate, height_rotate), img->depth, img->nChannels); cvZero(img_rotate); //保证原图可以任意角度旋转的最小尺寸 int tempLength = (int)(sqrt((double)width * width + (double)height *height) + 10); int tempX = (tempLength + 1) / 2 - width / 2; int tempY = (tempLength + 1) / 2 - height / 2; IplImage* temp = cvCreateImage(cvSize(tempLength, tempLength), img->depth, img->nChannels); cvZero(temp); //将原图复制到临时图像tmp中心 cvSetImageROI(temp, cvRect(tempX, tempY, width, height)); cvCopy(img, temp, NULL); cvResetImageROI(temp); //旋转数组map // [ m0 m1 m2 ] ===> [ A11 A12 b1 ] // [ m3 m4 m5 ] ===> [ A21 A22 b2 ] double m[6]; int w = temp->width; int h = temp->height; m[0] = b; m[1] = a; m[3] = -m[1]; m[4] = m[0]; // 将旋转中心移至图像中间 m[2] = w * 0.5f; m[5] = h * 0.5f; CvMat M = cvMat(2, 3, CV_32F, m); cvGetQuadrangleSubPix(temp, img_rotate, &M); cvReleaseImage(&temp); return img_rotate; } // 仿照matlab,自适应求高低两个门限 // https://blog.csdn.net/debug__boy/article/details/8179730 void _AdaptiveFindThreshold(CvMat *dx, CvMat *dy, double *low, double *high) { CvSize size; IplImage *imge = 0; int i, j; CvHistogram *hist; int hist_size = 255; float range_0[] = { 0, 256 }; float* ranges[] = { range_0 }; double PercentOfPixelsNotEdges = 0.7; size = cvGetSize(dx); imge = cvCreateImage(size, IPL_DEPTH_32F, 1); // 计算边缘的强度, 并存于图像中 float maxv = 0; for (i = 0; i < size.height; i++) { const short* _dx = (short*)(dx->data.ptr + dx->step*i); const short* _dy = (short*)(dy->data.ptr + dy->step*i); float* _image = (float *)(imge->imageData + imge->widthStep*i); for (j = 0; j < size.width; j++) { _image[j] = (float)(abs(_dx[j]) + abs(_dy[j])); maxv = maxv < _image[j] ? _image[j] : maxv; } } if (maxv == 0) { *high = 0; *low = 0; cvReleaseImage(&imge); return; } // 计算直方图 range_0[1] = maxv; hist_size = (int)(hist_size > maxv ? maxv : hist_size); hist = cvCreateHist(1, &hist_size, CV_HIST_ARRAY, ranges, 1); cvCalcHist(&imge, hist, 0, NULL); int total = (int)(size.height * size.width * PercentOfPixelsNotEdges); float sum = 0; int icount = hist->mat.dim[0].size; float *h = (float*)cvPtr1D(hist->bins, 0); for (i = 0; i < icount; i++) { sum += h[i]; if (sum > total) break; } // 计算高低门限 *high = (i + 1) * maxv / hist_size; *low = *high * 0.4; cvReleaseImage(&imge); cvReleaseHist(&hist); } // 根据图片,自动获取边缘高低阈值 // https://blog.csdn.net/debug__boy/article/details/8179730 //void AdaptiveFindThreshold(const CvArr* image, double *low, double *high, int aperture_size) void AdaptiveFindThreshold(cv::Mat& image, double *low, double *high, int aperture_size) { //cv::Mat src = cv::cvarrToMat(image); const int cn = image.channels(); cv::Mat dx(image.rows, image.cols, CV_16SC(cn)); cv::Mat dy(image.rows, image.cols, CV_16SC(cn)); cv::Sobel(image, dx, CV_16S, 1, 0, aperture_size, 1, 0, cv::BORDER_REPLICATE); cv::Sobel(image, dy, CV_16S, 0, 1, aperture_size, 1, 0, cv::BORDER_REPLICATE); CvMat _dx = dx, _dy = dy; _AdaptiveFindThreshold(&_dx, &_dy, low, high); } // 识别仪表盘读数 double dReadPointerParserImg(const char *img_file) { int i = 0; if (!img_file && strlen(img_file) <= 0) { return -1; } //src-->gray-->dst(src为原图;gaus是经过高斯模糊平滑后的图,gray是gaus经canny提取边缘的图, //用于下面霍夫变换;dst是最终结果图,要在gray灰度图的基础上变为彩色图才能呈现画线效果) Mat src, gray, dst, srcImage; src = imread(img_file); //读取图片到mat // showImg("原始图", src); srcImage = src; /* // 霍夫圆变换 cvtColor(srcImage, midImage, CV_BGR2GRAY); // 转为灰度图 GaussianBlur(midImage, midImage, Size(9, 9), 2, 2); // 模糊 vector circles; HoughCircles(midImage, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0); printf("HoughCircles return resault == %d\n", circles.size()); */ // 霍夫圆变换 cvtColor(src, gray, CV_BGR2GRAY); //转为单通道的灰度图 GaussianBlur(gray, gray, Size(9, 9), 2, 2); // 高斯模糊平滑 //储存检测圆的容器 std::vector circles; //调用Hough变换检测圆 //参数为:待检测图像gray,检测结果circles,检测方法(这个参数唯一),累加器的分辨率2, //两个圆间的距离50,canny门限的上限(下限自动设为上限的一半),圆心所需要的最小的投票数,最大和最小半径 HoughCircles(gray, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0); printf("HoughCircles return resault == %d\n", (int)circles.size()); if (circles.size() <= 0) { printf("HoughCircles return resault == %d\n", (int)circles.size()); return -1; } //找出圆盘(因为最大的不一定是的,所以加了几个限制条件) int pos = 0; int max = -1; for (size_t i = 0; i < circles.size(); i++) { Vec3f f = circles[i]; if (f[2] > max && f[0] + f[2] < gray.rows && f[0] - f[2] >= 0 && f[1] + f[2] < gray.cols && f[1] - f[2]>0) { max = (int)f[2]; pos = i; } } //Point center; //if (circles.size() > 0) { // 找到圆心 Point center((int)circles[pos][0], (int)circles[pos][1]); // 找到的半径 int radius = (int)circles[pos][2]; // 绘制圆心 circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0); // 绘制圆轮廓 circle(src, center, radius, Scalar(255), 2); //} // 效果图 //showImg("霍夫圆变换", src); /*******************************************************************************/ // 霍夫线变换 Canny(src, gray, 100, 300, 3); // 边缘检测 cvtColor(gray, dst, CV_GRAY2BGR); // 转为RGB图 vector lines; // 检测直线,最小投票为100,线条不短于50,间隙不小于10 HoughLinesP(gray, lines, 1, CV_PI / 180, 100, 50, 10); printf("HoughLinesP return resault == %d\n", (int)lines.size()); list list_MyLine; for (size_t i = 0; i < lines.size(); i++) { Vec4i l = lines[i]; Point A(l[0], l[1]), B(l[2], l[3]); if (DistancetoSegment(center, A, B) < 30)//根据圆心到指针的距离阈值滤掉其他线段 { bool down = (A.y + B.y - 2 * center.y > 0);//判断长的在过圆心的水平线上部还是下部 if (A.x == B.x) //斜率为无穷的情况 { list_MyLine.push_back(MyLine(i, 90 + (down ? 180 : 0), (int)Length(Point(A.x - B.x, A.y - B.y)))); } else if (A.y == B.y) //水平的情况 { list_MyLine.push_back(MyLine(i, A.x + B.x - 2 * center.x > 0 ? 0 : 180, (int)Length(Point(A.x - B.x, A.y - B.y)))); } else { if (down) { if (A.y > center.y) list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); else list_MyLine.push_back(MyLine(i, 360 - (int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI), (int)Length(Point(A.x - B.x, A.y - B.y)))); } else { if (A.y < center.y) list_MyLine.push_back(MyLine(i, abs((int)(atan2(A.y - B.y, A.x - B.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); else list_MyLine.push_back(MyLine(i, abs((int)(atan2(B.y - A.y, B.x - A.x) * 180 / PI)), (int)Length(Point(A.x - B.x, A.y - B.y)))); } } //line(dst, A, B, Scalar(0, 0, i * 20 + 40), 2, CV_AA); line(dst, A, B, Scalar(186, 88, 255), 1, CV_AA); } } //根据角度区分所属指针 int now_k, pre_k = 720;//当前线段的角度和前一个线段的角度 int num = 0;//指针数(可能为2或3) int Du[3] = { 0 };//3个指针的度数(每组的平均) int Le[3] = { 0 };//Le[i]=Le_ping[i]*0.2+le_max[i]*0.8; int Le_ping[3] = { 0 };//3个指针的长度(每组的平均) int Le_max[3] = { 0 };//3个指针的长度(每组区最大的) int t_num = 0;//每组的数量(求平均用) MyLine now_Line; list_MyLine.push_back(MyLine(99, 888, 0));//向其中插入一个右边界这样方便处理 list_MyLine.sort(); while (!list_MyLine.empty()) { now_Line = list_MyLine.front(); now_k = now_Line.k; if (abs(now_k - pre_k) > 10)//两个角度之差小于10°的算是同一类 { if (num != 0) //对本组的度数和长度求平均 { Du[num - 1] /= t_num; Le_ping[num - 1] /= t_num; Le[num - 1] = (int)(Le_ping[num - 1] * 0.2 + Le_max[num - 1] * 0.8); } if (now_k == 888) break;//右边界直接跳出 t_num = 0;//重新统计下一组 num++;//组数增加1 cout << "---------------------------\n";//输出分割线 } t_num++;//组内多一条线 Du[num - 1] += now_Line.k; Le_ping[num - 1] += now_Line.l; if (now_Line.l > Le_max[num - 1]) Le_max[num - 1] = now_Line.l; now_Line.print(); list_MyLine.pop_front(); pre_k = now_k; } cout << "---------------------------\n\n"; cout << "---------------------------\n"; int t; for (int i = 0; i < num - 1; i++) { for (int j = i + 1; j < num; j++) { if (Le[i] > Le[j]) { t = Le[i], Le[i] = Le[j], Le[j] = t; t = Du[i], Du[i] = Du[j], Du[j] = t; }//if end }//for end }//for end char s[3][10] = { "point1:", "point2:", "point3:" }; for (int i = 0; i < num; i++) printf("%s k: %3d° l: %3d\n", s[i], Du[i], Le[i]); cout << "---------------------------\n"; if (num == 1) printf("read is: %5.2f\n", (float)abs(((360 - Du[0] + 90) % 360 / 30))); if (num == 2) printf("read is: %5.2f %5.2f\n", (float)((360 - Du[0] + 90) % 360 / 30), (float)((360 - Du[1] + 90) % 360 / 6)); else if (num == 3) printf("read is: %5.2f %5.2f %5.2f\n", (float)((360 - Du[0] + 90) % 360 / 30), (float)((360 - Du[1] + 90) % 360 / 6), (float)((360 - Du[2] + 90) % 360 / 6)); cout << "---------------------------\n"; showImg("src", src); showImg("dst", dst); return 0; } // 霍夫圆、线变换 double dReadValue(const char *img_file) { Mat srcImage = imread(img_file); Mat midImage, dstImage; // 显示原始图 //showImg("原始图", srcImage); // 霍夫圆变换 cvtColor(srcImage, midImage, CV_BGR2GRAY); // 转为灰度图 GaussianBlur(midImage, dstImage, Size(9, 9), 2, 2); // 模糊 vector circles; HoughCircles(midImage, circles, CV_HOUGH_GRADIENT, 1, 30, 115, 90, 0, 0); printf("HoughCircles return resault == %d\n", (int)circles.size()); // 找出圆盘 int pos = 0; int max = -1; for (size_t i = 0; i < circles.size(); i++) { Vec3f f = circles[i]; if (f[2] > max && f[0] + f[2] < midImage.rows && f[0] - f[2] >= 0 && f[1] + f[2] < midImage.cols && f[1] - f[2] > 0) { max = (int)f[2]; pos = i; } } if (circles.size() > 0) { // 找到圆心 Point center((int)circles[pos][0], (int)circles[pos][1]); // 找到的半径 int radius = (int)circles[pos][2]; // 绘制圆心 circle(srcImage, center, 3, Scalar(0, 255, 0), -1, 8, 0); // 绘制圆轮廓 circle(srcImage, center, radius, Scalar(255), 2); } // 效果图 //showImg("霍夫圆变换", srcImage); // 霍夫线变换 Canny(srcImage, midImage, 100, 300, 3); // 边缘检测 cvtColor(midImage, dstImage, CV_GRAY2BGR); // 转为灰度图 vector lines; // 检测直线,最小投票为100,线条不短于50,间隙不小于10 HoughLinesP(midImage, lines, 1, CV_PI / 180, 100, 50, 10); printf("HoughLinesP return resault == %d\n", (int)lines.size()); for (size_t i = 0; i < lines.size(); i++) { Vec4i l = lines[i]; Point pt1(l[0], l[1]); Point pt2(l[2], l[3]); line(dstImage, pt1, pt2, Scalar(186, 88, 255), 1, CV_AA); } // showImg("边缘检测图", midImage); //showImg("霍夫线变换", dstImage); return 0; } //******************双阈值处理************************* //第一个参数imageInput输入和输出的的Sobel梯度幅值图像; //第二个参数lowThreshold是低阈值 //第三个参数highThreshold是高阈值 // 指定一个低阈值A,一个高阈值B,一般取B为图像整体灰度级分布的70%,且B为1.5到2倍大小的A; // 灰度值大于B的,置为255,灰度值小于A的,置为0; //****************************************************** void DoubleThreshold(Mat &imageIput, double lowThreshold, double highThreshold) { for (int i = 0; i < imageIput.rows; i++) { for (int j = 0; j < imageIput.cols; j++) { //printf("[%d:%d] = %d\n", i, j, imageIput.at(i, j)); if (imageIput.at(i, j) > highThreshold) { imageIput.at(i, j) = 255; } if (imageIput.at(i, j) < lowThreshold) { imageIput.at(i, j) = 0; } } } } /* 生成高斯卷积核 kernel */ void Gaussian_kernel(int kernel_size, int sigma, Mat &kernel) { double pi = 3.1415926f; int m = kernel_size / 2; kernel = Mat(kernel_size, kernel_size, CV_32FC1); float s = (float)(2 * sigma*sigma); for (int i = 0; i < kernel_size; i++) { for (int j = 0; j < kernel_size; j++) { int x = i - m; int y = j - m; double w = exp(-(x*x + y * y) / s); kernel.at(i, j) = (int)(w / (pi*s)); } } } /* 计算梯度值和方向 imageSource 原始灰度图 imageX X方向梯度图像 imageY Y方向梯度图像 gradXY 该点的梯度幅值 pointDirection 梯度方向角度 */ void GradDirection(const Mat imageSource, Mat &imageX, Mat &imageY, Mat &gradXY, Mat &theta) { imageX = Mat::zeros(imageSource.size(), CV_32SC1); imageY = Mat::zeros(imageSource.size(), CV_32SC1); gradXY = Mat::zeros(imageSource.size(), CV_32SC1); theta = Mat::zeros(imageSource.size(), CV_32SC1); int rows = imageSource.rows; int cols = imageSource.cols; int stepXY = imageX.step; int step = imageSource.step; /* Mat.step参数指图像的一行实际占用的内存长度, 因为opencv中的图像会对每行的长度自动补齐(8的倍数), 编程时尽量使用指针,指针读写像素是速度最快的,使用at函数最慢。 */ uchar *PX = imageX.data; uchar *PY = imageY.data; uchar *P = imageSource.data; uchar *XY = gradXY.data; for (int i = 1; i < rows - 1; i++) { for (int j = 1; j < cols - 1; j++) { int a00 = P[(i - 1)*step + j - 1]; int a01 = P[(i - 1)*step + j]; int a02 = P[(i - 1)*step + j + 1]; int a10 = P[i*step + j - 1]; int a11 = P[i*step + j]; int a12 = P[i*step + j + 1]; int a20 = P[(i + 1)*step + j - 1]; int a21 = P[(i + 1)*step + j]; int a22 = P[(i + 1)*step + j + 1]; double gradY = double(a02 + 2 * a12 + a22 - a00 - 2 * a10 - a20); double gradX = double(a00 + 2 * a01 + a02 - a20 - 2 * a21 - a22); //PX[i*stepXY + j*(stepXY / step)] = abs(gradX); //PY[i*stepXY + j*(stepXY / step)] = abs(gradY); imageX.at(i, j) = (int)abs(gradX); imageY.at(i, j) = (int)abs(gradY); if (gradX == 0) { gradX = 0.000000000001; } theta.at(i, j) = (int)(atan(gradY / gradX)*57.3); theta.at(i, j) = (theta.at(i, j) + 360) % 360; gradXY.at(i, j) = (int)sqrt(gradX*gradX + gradY * gradY); //XY[i*stepXY + j*(stepXY / step)] = sqrt(gradX*gradX + gradY*gradY); } } convertScaleAbs(imageX, imageX); convertScaleAbs(imageY, imageY); convertScaleAbs(gradXY, gradXY); } /* 局部非极大值抑制 沿着该点梯度方向,比较前后两个点的幅值大小,若该点大于前后两点,则保留, 若该点小于前后两点任意一点,则置为0; imageInput 输入得到梯度图像 imageOutput 输出的非极大值抑制图像 theta 每个像素点的梯度方向角度 imageX X方向梯度 imageY Y方向梯度 */ void NonLocalMaxValue(const Mat imageInput, Mat &imageOutput, const Mat &theta, const Mat &imageX, const Mat &imageY) { imageOutput = imageInput.clone(); int cols = imageInput.cols; int rows = imageInput.rows; for (int i = 1; i < rows - 1; i++) { for (int j = 1; j < cols - 1; j++) { if (0 == imageInput.at(i, j))continue; int g00 = imageInput.at(i - 1, j - 1); int g01 = imageInput.at(i - 1, j); int g02 = imageInput.at(i - 1, j + 1); int g10 = imageInput.at(i, j - 1); int g11 = imageInput.at(i, j); int g12 = imageInput.at(i, j + 1); int g20 = imageInput.at(i + 1, j - 1); int g21 = imageInput.at(i + 1, j); int g22 = imageInput.at(i + 1, j + 1); int direction = theta.at(i, j); //该点梯度的角度值 int g1 = 0; int g2 = 0; int g3 = 0; int g4 = 0; double tmp1 = 0.0; //保存亚像素点插值得到的灰度数 double tmp2 = 0.0; double weight = fabs((double)imageY.at(i, j) / (double)imageX.at(i, j)); if (weight == 0)weight = 0.0000001; if (weight > 1) { weight = 1 / weight; } if ((0 <= direction && direction < 45) || 180 <= direction && direction < 225) { tmp1 = g10 * (1 - weight) + g20 * (weight); tmp2 = g02 * (weight)+g12 * (1 - weight); } if ((45 <= direction && direction < 90) || 225 <= direction && direction < 270) { tmp1 = g01 * (1 - weight) + g02 * (weight); tmp2 = g20 * (weight)+g21 * (1 - weight); } if ((90 <= direction && direction < 135) || 270 <= direction && direction < 315) { tmp1 = g00 * (weight)+g01 * (1 - weight); tmp2 = g21 * (1 - weight) + g22 * (weight); } if ((135 <= direction && direction < 180) || 315 <= direction && direction < 360) { tmp1 = g00 * (weight)+g10 * (1 - weight); tmp2 = g12 * (1 - weight) + g22 * (weight); } if (imageInput.at(i, j) < tmp1 || imageInput.at(i, j) < tmp2) { imageOutput.at(i, j) = 0; } } } } /* 连接处理: 灰度值介于A和B之间的,考察该像素点临近的8像素是否有灰度值为255的, 若没有255的,表示这是一个孤立的局部极大值点,予以排除,置为0; 若有255的,表示这是一个跟其他边缘有“接壤”的可造之材,置为255, 之后重复执行该步骤,直到考察完之后一个像素点。 其中的邻域跟踪算法,从值为255的像素点出发找到周围满足要求的点,把满足要求的点设置为255, 然后修改i,j的坐标值,i,j值进行回退,在改变后的i,j基础上继续寻找255周围满足要求的点。 当所有连接255的点修改完后,再把所有上面所说的局部极大值点置为0;(算法可以继续优化)。 参数1,imageInput:输入和输出的梯度图像 参数2,lowTh:低阈值 参数3,highTh:高阈值 */ void DoubleThresholdLink(Mat &imageInput, double lowTh, double highTh) { int cols = imageInput.cols; int rows = imageInput.rows; for (int i = 1; i < rows - 1; i++) { for (int j = 1; j < cols - 1; j++) { double pix = imageInput.at(i, j); if (pix != 255)continue; bool change = false; for (int k = -1; k <= 1; k++) { for (int u = -1; u <= 1; u++) { if (k == 0 && u == 0)continue; double temp = imageInput.at(i + k, j + u); if (temp >= lowTh && temp <= highTh) { imageInput.at(i + k, j + u) = 255; change = true; } } } if (change) { if (i > 1)i--; if (j > 2)j -= 2; } } } for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { if (imageInput.at(i, j) != 255) { imageInput.at(i, j) = 0; } } } } /* Canny边缘检测主要包括: 图像的灰度化; 图像的高斯滤波,来平滑图像,同时消除和降低图像噪声的影响; 计算出每一个像素点位置的梯度(X方向梯度、Y方向梯度、已经该点的梯度幅值)和方向角度;Y方向和X方向梯度的比值,得出梯度方向,X梯度的平方和+Y梯度的平方和的值,再进行求平方得到该点的梯度幅值。(Sobel算子等) 局部非极大值抑制处理;梯度方向垂直于边缘方向,在梯度方向上进行非极大值抑制可以细化边缘,在梯度方向上比较该点前后两个点的梯度的大小,如果大于两个点则保留,小于任意一个点则置为0。 双阈值处理和连接处理;指定高低阈值,然后高阈值直接赋值为255,低阈值为0,中间的值进行连接处理。如果中间的值八邻域内有255,则该值也变为255,也就是说255往周围进行扩张,收集边缘加闭合边缘。 */ int HTCanny(char *filename) { Mat image = imread(filename, 0); showImg("origin image", image); //转换为灰度图 Mat grayImage; //cvtColor(image, grayImage, CV_RGB2GRAY); cvtColor(image, grayImage, CV_GRAY2BGR); imshow("灰度图", grayImage); //计算XY方向梯度 Mat imageX, imageY, imageXY; Mat theta; GradDirection(grayImage, imageX, imageY, imageXY, theta); imshow("计算XY方向梯度", imageXY); //对梯度幅值进行非极大值抑制 Mat localImage; NonLocalMaxValue(imageXY, localImage, theta, imageX, imageY); imshow("对梯度幅值进行非极大值抑制", localImage); //双阈值算法检测和边缘连接 DoubleThreshold(localImage, 30, 100); DoubleThresholdLink(localImage, 30, 100); imshow("双阈值算法检测和边缘连接", localImage); Mat temMat; vector lines2; Canny(image, temMat, 30, 100); HoughLinesP(temMat, lines2, 1, CV_PI / 180, 50, 30, 10); printf("lines2 = %d\n", (int)lines2.size()); for (size_t i = 0; i < lines2.size(); i++) { Vec4i l = lines2[i]; Point A(l[0], l[1]), B(l[2], l[3]); line(temMat, A, B, Scalar(255, 0, 0), 2, CV_AA); } imshow("线条监测", temMat); waitKey(0); return 0; } /************************************************************************/ ////////////////////////////////////////////////////////////////// //函数功能:用向量来做COSα=两向量之积/两向量模的乘积求两条线段夹角 //输入: 线段3个点坐标pt1,pt2,pt0,最后一个参数为公共点 //输出: 线段夹角,单位为角度 ////////////////////////////////////////////////////////////////// double angle(CvPoint* pt1, CvPoint* pt2, CvPoint* pt0) { 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_line = (dx1*dx2 + dy1 * dy2) / sqrt((dx1*dx1 + dy1 * dy1)*(dx2*dx2 + dy2 * dy2) + 1e-10);//余弦值 return acos(angle_line) * 180 / 3.141592653; } ////////////////////////////////////////////////////////////////// //函数功能:采用多边形检测,通过约束条件寻找矩形 //输入: img 原图像 // storage 存储 // minarea,maxarea 检测矩形的最小/最大面积 // minangle,maxangle 检测矩形边夹角范围,单位为角度 //输出: 矩形序列 ////////////////////////////////////////////////////////////////// CvSeq* findSquares4(IplImage* img, CvMemStorage* storage, int minarea, int maxarea, int minangle, int maxangle, int(&temp)[30]) { CvSeq* contours;//边缘 int N = 6; //阈值分级 CvSize sz = cvSize(img->width & -2, img->height & -2); IplImage* timg = cvCloneImage(img);//拷贝一次img IplImage* gray = cvCreateImage(sz, 8, 1); //img灰度图 IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3); //金字塔滤波3通道图像中间变量 IplImage* tgray = cvCreateImage(sz, 8, 1);; CvSeq* result; double s, t; int sk = 0; CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage); cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height)); //金字塔滤波 cvPyrDown(timg, pyr, 7); cvPyrUp(pyr, timg, 7); //在3个通道中寻找矩形 for (int c = 0; c < 3; c++) //对3个通道分别进行处理 { cvSetImageCOI(timg, c + 1); cvCopy(timg, tgray, 0); //依次将BGR通道送入tgray for (int l = 0; l < N; l++) { //不同阈值下二值化 cvThreshold(tgray, gray, 75, 250, CV_THRESH_BINARY); cvShowImage("111", gray); cvFindContours(gray, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0)); while (contours) { //多边形逼近 result = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0); //如果是凸四边形并且面积在范围内 if (result->total == 4 && fabs(cvContourArea(result, CV_WHOLE_SEQ)) > minarea && fabs(cvContourArea(result, CV_WHOLE_SEQ)) < maxarea && cvCheckContourConvexity(result)) { s = 0; //判断每一条边 for (int i = 0; i < 5; i++) { if (i >= 2) { //角度 t = fabs(angle((CvPoint*)cvGetSeqElem(result, i), (CvPoint*)cvGetSeqElem(result, i - 2), (CvPoint*)cvGetSeqElem(result, i - 1))); s = s > t ? s : t; } } //这里的S为直角判定条件 单位为角度 if (s > minangle && s < maxangle) { for (int i = 0; i < 4; i++) cvSeqPush(squares, (CvPoint*)cvGetSeqElem(result, i)); CvRect rect = cvBoundingRect(contours, 1); // 获取矩形边界框 CvPoint p1; p1 = cvPoint(rect.x + rect.width / 2, rect.y + rect.height / 2); //矩形中心坐标 std::cout << "X:" << p1.x << "Y:" << p1.y << std::endl; } } contours = contours->h_next; } } std::cout << "圆的数量是" << sk << std::endl; temp[26] = sk; sk = 0; } cvReleaseImage(&gray); cvReleaseImage(&pyr); cvReleaseImage(&tgray); cvReleaseImage(&timg); return squares; } ////////////////////////////////////////////////////////////////// //函数功能:画出所有矩形 //输入: img 原图像 // squares 矩形序列 // wndname 窗口名称 //输出: 图像中标记矩形 ////////////////////////////////////////////////////////////////// void drawSquares(IplImage* img, CvSeq* squares, const char* wndname) { CvSeqReader reader; IplImage* cpy = cvCloneImage(img); CvPoint pt[4]; int i; cvStartReadSeq(squares, &reader, 0); for (i = 0; i < squares->total; i += 4) { CvPoint* rect = pt; int count = 4; memcpy(pt, reader.ptr, squares->elem_size); CV_NEXT_SEQ_ELEM(squares->elem_size, reader); memcpy(pt + 1, reader.ptr, squares->elem_size); CV_NEXT_SEQ_ELEM(squares->elem_size, reader); memcpy(pt + 2, reader.ptr, squares->elem_size); CV_NEXT_SEQ_ELEM(squares->elem_size, reader); memcpy(pt + 3, reader.ptr, squares->elem_size); CV_NEXT_SEQ_ELEM(squares->elem_size, reader); //cvPolyLine( cpy, &rect, &count, 1, 1, CV_RGB(0,255,0), 3, CV_AA, 0 ); cvPolyLine(cpy, &rect, &count, 1, 1, CV_RGB(rand() & 255, rand() & 255, rand() & 255), 1, CV_AA, 0);//彩色绘制 } cvShowImage("22", cpy); cvReleaseImage(&cpy); } void SendMessageOne(char *rtsp_url) { String rtsp_addr = rtsp_url; VideoCapture capture(rtsp_addr); //开起摄像头 //capture.open(0); Mat edges; //定义转化的灰度图 const char* winn = "1111"; if (!capture.isOpened()) { namedWindow("【效果图】", CV_WINDOW_NORMAL); } while (1) { int Y = 0, J = 0; Mat frame; capture >> frame; IplImage img0 = frame; //Mat E = frame(Range(1, 320), Range(1, 240)); Mat E = frame(Range(1, 160), Range(1, 240)); cvtColor(frame, edges, CV_BGR2GRAY); //高斯滤波 GaussianBlur(edges, edges, Size(7, 7), 2, 2); std::vector circles;//存储每个圆的位置信息 //霍夫圆 HoughCircles(edges, circles, CV_HOUGH_GRADIENT, 1.5, 5, 100, 240, 0, 50); for (size_t i = 0; i < circles.size(); i++) { Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); int radius = cvRound(circles[i][2]); //std::cout << "圆的X是" << circles[i][0] << "圆的Y是" << circles[i][1] << std:: endl; //绘制圆轮廓 circle(frame, center, radius, Scalar(155, 50, 255), 3, 8, 0); int R = frame.at(cvRound(circles[i][1]), cvRound(circles[i][0]))[2];//R int G = frame.at(cvRound(circles[i][1]), cvRound(circles[i][0]))[1];//G int B = frame.at(cvRound(circles[i][1]), cvRound(circles[i][0]))[0];//B int num = R + G + B; std::cout << "圆心颜色是" << num << std::endl; } imshow("【效果图】", frame); if ('q' == waitKey(30)) break; } } /**************************************************/ using namespace cv; using namespace std; cv::Mat img; bool select_flag = false; cv::Rect m_select; cv::Point origin; int ROI_count; void onMouseRectPicking(int event, int x, int y, int, void*) { if (select_flag) { m_select.x = MIN(origin.x, x);//不一定要等鼠标弹起才计算矩形框,而应该在鼠标按下开始到弹起这段时间实时计算所选矩形框 m_select.y = MIN(origin.y, y); m_select.width = abs(x - origin.x);//算矩形宽度和高度 m_select.height = abs(y - origin.y); m_select &= cv::Rect(0, 0, img.cols, img.rows);//保证所选矩形框在视频显示区域之内 } if (event == CV_EVENT_LBUTTONDOWN) { select_flag = true; //鼠标按下的标志赋真值 origin = cv::Point(x, y); //保存下来单击捕捉到的点 m_select = cv::Rect(x, y, 0, 0); //这里一定要初始化,宽和高为(0,0)是因为在opencv中Rect矩形框类内的点是包含左上角那个点的,但是不含右下角那个点 } else if (event == CV_EVENT_LBUTTONUP) { select_flag = false; ROI_count++; } } // 鼠标选取区域,实现截图功能并保持图片 void vCutPicture(char *filename) { img = imread(filename); bool stop = false; cv::namedWindow("capframe", CV_WINDOW_AUTOSIZE); cv::setMouseCallback("capframe", onMouseRectPicking, 0); char pic_name[40]; ROI_count = 0; while (!stop) { img = imread(filename); cv::rectangle(img, m_select, cv::Scalar(255, 0, 0), 2, 8, 0); // 画矩形框 cv::imshow("capframe", img); if ((m_select.x != 0) && (m_select.y != 0) && (m_select.width != 0) && (m_select.height != 0)) { sprintf(pic_name, "ROI_%d.jpg", ROI_count); Mat ROI = img(m_select); imshow("ROI_WIN", ROI); imwrite(pic_name, ROI); } char key = cv::waitKey(30); if (key == 'q') stop = true; } waitKey(0); return; } ////////////////////////////////// //检测直线方法 int iCheckLine(char *filename) { int i; IplImage* src = cvLoadImage(filename, 0); IplImage* dst; IplImage* color_dst; CvMemStorage* storage = cvCreateMemStorage(0); CvSeq* lines = 0; if (!src) return -1; dst = cvCreateImage(cvGetSize(src), 8, 1); color_dst = cvCreateImage(cvGetSize(src), 8, 3); cvCanny(src, dst, 50, 200, 3); cvCvtColor(dst, color_dst, CV_GRAY2BGR); #if 0 lines = cvHoughLines2(dst, storage, CV_HOUGH_STANDARD, 1, CV_PI / 180, 100, 0, 0); for (i = 0; i < MIN(lines->total, 100); i++) { float* line = (float*)cvGetSeqElem(lines, i); float rho = line[0]; float theta = line[1]; CvPoint pt1, pt2; double a = cos(theta), b = sin(theta); double x0 = a * rho, y0 = b * rho; pt1.x = cvRound(x0 + 1000 * (-b)); pt1.y = cvRound(y0 + 1000 * (a)); pt2.x = cvRound(x0 - 1000 * (-b)); pt2.y = cvRound(y0 - 1000 * (a)); cvLine(color_dst, pt1, pt2, CV_RGB(255, 0, 0), 3, CV_AA, 0); } #else lines = cvHoughLines2(dst, storage, CV_HOUGH_PROBABILISTIC, 1, CV_PI / 180, 50, 50, 10); for (i = 0; i < lines->total; i++) { CvPoint* line = (CvPoint*)cvGetSeqElem(lines, i); cvLine(color_dst, line[0], line[1], CV_RGB(255, 0, 0), 3, CV_AA, 0); } #endif cvNamedWindow("Source", 1); cvShowImage("Source", src); cvNamedWindow("Hough", 1); cvShowImage("Hough", color_dst); cvReleaseMemStorage(&storage); cvWaitKey(0); return 0; } //检测圆方法 int iCheckCircles(char *filename) { IplImage* img; if (img = cvLoadImage(filename, 1)) { IplImage* gray = cvCreateImage(cvGetSize(img), 8, 1); CvMemStorage* storage = cvCreateMemStorage(0); cvCvtColor(img, gray, CV_BGR2GRAY); cvSmooth(gray, gray, CV_GAUSSIAN, 9, 9); // smooth it, otherwise a lot of false circles may be detected CvSeq* circles = cvHoughCircles(gray, storage, CV_HOUGH_GRADIENT, 2, gray->height / 5, 200, 100); int i; for (i = 0; i < circles->total; i++) { float* p = (float*)cvGetSeqElem(circles, i); cvCircle(img, cvPoint(cvRound(p[0]), cvRound(p[1])), 3, CV_RGB(0, 255, 0), -1, 8, 0); cvCircle(img, cvPoint(cvRound(p[0]), cvRound(p[1])), cvRound(p[2]), CV_RGB(0, 255, 0), 3, 8, 0); } cvNamedWindow("circles", 1); cvShowImage("circles", img); cvReleaseImage(&gray); cvReleaseMemStorage(&storage); } return 0; } //检测矩形代码: /* 在程序里找寻矩形 */ #ifdef _CH_ #pragma package #endif IplImage* imgfx = 0; CvMemStorage* storage = 0; static const char* names[] = { "pic1.png", "pic2.png", "pic3.png", "pic4.png", "pic5.png", "pic6.png", 0 }; int thresh = 50; // helper function: // finds a cosine of angle between vectors // from pt0->pt1 and from pt0->pt2 //double angle(CvPoint* pt1, CvPoint* pt2, CvPoint* pt0) //{ // double dx1 = pt1->x - pt0->x; // double dy1 = pt1->y - pt0->y; // double dx2 = pt2->x - pt0->x; // double dy2 = pt2->y - pt0->y; // return (dx1*dx2 + dy1*dy2) / sqrt((dx1*dx1 + dy1*dy1)*(dx2*dx2 + dy2*dy2) + 1e-10); //} // returns sequence of squares detected on the image. // the sequence is stored in the specified memory storage CvSeq* findSquares4(IplImage* img, CvMemStorage* storage) { CvSeq* contours; int i, c, l, N = 11; CvSize sz = cvSize(img->width & -2, img->height & -2); IplImage* timg = cvCloneImage(img); // make a copy of input image IplImage* gray = cvCreateImage(sz, 8, 1); IplImage* pyr = cvCreateImage(cvSize(sz.width / 2, sz.height / 2), 8, 3); IplImage* tgray; CvSeq* result; double s, t; // create empty sequence that will contain points - // 4 points per square (the square's vertices) CvSeq* squares = cvCreateSeq(0, sizeof(CvSeq), sizeof(CvPoint), storage); // select the maximum ROI in the image // with the width and height divisible by 2 cvSetImageROI(timg, cvRect(0, 0, sz.width, sz.height)); // down-scale and upscale the image to filter out the noise cvPyrDown(timg, pyr, 7); cvPyrUp(pyr, timg, 7); tgray = cvCreateImage(sz, 8, 1); // find squares in every color plane of the image for (c = 0; c < 3; c++) { // extract the c-th color plane cvSetImageCOI(timg, c + 1); cvCopy(timg, tgray, 0); // try several threshold levels for (l = 0; l < N; l++) { // hack: use Canny instead of zero threshold level. // Canny helps to catch squares with gradient shading   if (l == 0) { // apply Canny. Take the upper threshold from slider // and set the lower to 0 (which forces edges merging) cvCanny(tgray, gray, 0, thresh, 5); // dilate canny output to remove potential // holes between edge segments cvDilate(gray, gray, 0, 1); } else { // apply threshold if l!=0: //     tgray(x,y) = gray(x,y) < (l+1)*255/N ? 255 : 0 cvThreshold(tgray, gray, (l + 1) * 255 / N, 255, CV_THRESH_BINARY); } // find contours and store them all as a list cvFindContours(gray, storage, &contours, sizeof(CvContour), CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE, cvPoint(0, 0)); // test each contour while (contours) { // approximate contour with accuracy proportional // to the contour perimeter result = cvApproxPoly(contours, sizeof(CvContour), storage, CV_POLY_APPROX_DP, cvContourPerimeter(contours)*0.02, 0); // square contours should have 4 vertices after approximation // relatively large area (to filter out noisy contours) // and be convex. // Note: absolute value of an area is used because // area may be positive or negative - in accordance with the // contour orientation if (result->total == 4 && fabs(cvContourArea(result, CV_WHOLE_SEQ)) > 1000 && cvCheckContourConvexity(result)) { s = 0; for (i = 0; i < 5; i++) { // find minimum angle between joint // edges (maximum of cosine) if (i >= 2) { t = fabs(angle( (CvPoint*)cvGetSeqElem(result, i), (CvPoint*)cvGetSeqElem(result, i - 2), (CvPoint*)cvGetSeqElem(result, i - 1))); s = s > t ? s : t; } } // if cosines of all angles are small // (all angles are ~90 degree) then write quandrange // vertices to resultant sequence if (s < 0.3) for (i = 0; i < 4; i++) cvSeqPush(squares, (CvPoint*)cvGetSeqElem(result, i)); } // take the next contour contours = contours->h_next; } } } // release all the temporary images cvReleaseImage(&gray); cvReleaseImage(&pyr); cvReleaseImage(&tgray); cvReleaseImage(&timg); return squares; } // the function draws all the squares in the image void drawSquares(IplImage* img, CvSeq* squares) { CvSeqReader reader; IplImage* cpy = cvCloneImage(img); int i; CvPoint pt[4]; // initialize reader of the sequence cvStartReadSeq(squares, &reader, 0); // read 4 sequence elements at a time (all vertices of a square) for (i = 0; i < squares->total; i += 4) { CvPoint* rect = pt; int count = 4; // read 4 vertices memcpy(pt, reader.ptr, squares->elem_size); CV_NEXT_SEQ_ELEM(squares->elem_size, reader); memcpy(pt + 1, reader.ptr, squares->elem_size); CV_NEXT_SEQ_ELEM(squares->elem_size, reader); memcpy(pt + 2, reader.ptr, squares->elem_size); CV_NEXT_SEQ_ELEM(squares->elem_size, reader); memcpy(pt + 3, reader.ptr, squares->elem_size); CV_NEXT_SEQ_ELEM(squares->elem_size, reader); // draw the square as a closed polyline cvPolyLine(cpy, &rect, &count, 1, 1, CV_RGB(255, 255, 255), 3, CV_AA, 0); } // show the resultant image cvShowImage("Square Detection Demo", cpy); cvReleaseImage(&cpy); } void on_trackbar(int a) { if (imgfx) drawSquares(imgfx, findSquares4(imgfx, storage)); } // 检测矩形 int iCheckFangXiang(char *filename) { IplImage* img0 = 0; int c; // create memory storage that will contain all the dynamic data storage = cvCreateMemStorage(0); // load i-th image img0 = cvLoadImage(filename, 1); cvShowImage("原图", img0); if (!img0) { printf("Couldn't load %s/n", filename); return 0; } imgfx = cvCloneImage(img0); // create window and a trackbar (slider) with parent "image" and set callback // (the slider regulates upper threshold, passed to Canny edge detector) //cvNamedWindow("Square Detection Demo1",1); cvCreateTrackbar("canny thresh", "Square Detection Demo2", &thresh, 1000, on_trackbar); // force the image processing on_trackbar(0); // wait for key. // Also the function cvWaitKey takes care of event processing c = cvWaitKey(0); // release both images cvReleaseImage(&imgfx); cvReleaseImage(&img0); // clear memory storage - reset free space position cvClearMemStorage(storage); cvDestroyWindow("Square Detection Demo3"); return 0; } /////////////////////////////////////////////////////////////////////////////////// double Angles(Point cen, Point first, Point second) { double M_PIs = 3.1415926535897; double ma_x = first.x - cen.x; double ma_y = first.y - cen.y; double mb_x = second.x - cen.x; double mb_y = second.y - cen.y; double v1 = (ma_x * mb_x) + (ma_y * mb_y); double ma_val = sqrt(ma_x * ma_x + ma_y * ma_y); double mb_val = sqrt(mb_x * mb_x + mb_y * mb_y); double cosM = v1 / (ma_val * mb_val); double angleAMB = acos(cosM) * 180 / M_PIs; return angleAMB; } /************************************************************************ *函数名: get_point_angle * *函数作用: 已知2个坐标点,求从 0------->x 逆时针需旋转多少角度到该位置 * * | * | * | * | *------------------------------------> x * | 0 * | * | * | * v * y * *函数参数: *CvPoint2D32f pointO - 起点 *CvPoint2D32f pointA - 终点 * *函数返回值: *double 向量OA,从 0------->x 逆时针需旋转多少角度到该位置 **************************************************************************/ double get_point_angles(CvPoint pointO, CvPoint pointA) { double angle = 0; CvPoint point; double temp; point = cvPoint((pointA.x - pointO.x), (pointA.y - pointO.y)); if ((0 == point.x) && (0 == point.y)) { return 0; } if (0 == point.x) { angle = 90; return angle; } if (0 == point.y) { angle = 0; return angle; } temp = fabsf(float(point.y) / float(point.x)); temp = atan(temp); temp = temp * 180 / CV_PI; if ((0 < point.x) && (0 < point.y)) { angle = 360 - temp; return angle; } if ((0 > point.x) && (0 < point.y)) { angle = 360 - (180 - temp); return angle; } if ((0 < point.x) && (0 > point.y)) { angle = temp; return angle; } if ((0 > point.x) && (0 > point.y)) { angle = 180 - temp; return angle; } printf("sceneDrawing :: getAngle error!"); return -1; } Mat src, src_gray; Mat dst, detected_edges; int edgeThresh = 1; int lowThreshold = 77; int max_lowThreshold = 100; int ratio = 3; int kernel_size = 3; vector circles; Point g_Center(0, 0); int g_radius = 0; /** * @函数 CannyThreshold * @简介: trackbar 交互回调 - Canny阈值输入比例1:3 */ void CannyThreshold(int, void*) { int radius = 0; int iMaxLimit = 0; double min = 0; string window_name = "Edge Map"; /// 使用 3x3内核降噪 //while (lowThreshold > 0) { blur(src_gray, detected_edges, Size(3, 3)); /// 运行Canny算子 Canny(detected_edges, detected_edges, lowThreshold, lowThreshold*ratio, kernel_size); //printf("LowThresold : %d -- %d \n", lowThreshold, lowThreshold*ratio); /// 使用 Canny算子输出边缘作为掩码显示原图像,声明一个三通道图像,像素值全为0,用来将霍夫变换检测出的圆画在上面 dst = Scalar::all(0); src.copyTo(dst, detected_edges); HoughCircles(detected_edges, circles, CV_HOUGH_GRADIENT, 1, //dp,累加器图像的分辨率,增大则分辨率变小 100, // 100, //minDist,很重要的一个参数,告诉两个圆之间的距离的最小距离,如果已知一副图像,可以先行计 //算出符合自己需要的两个圆之间的最小距离。 150, //param1,canny算法的阈值上限,下限为一半(即100以上为边缘点,50以下抛弃,中间视是否相连而定) 100, //param2,决定成圆的多寡 ,一个圆上的像素超过这个阈值,则成圆,否则丢弃 230, //minRadius,最小圆半径,这个可以通过图片确定你需要的圆的区间范围 460 //maxRadius,最大圆半径 ); //imshow(window_name, dst); // if (circles.size() <= 0) { // lowThreshold--; // continue; //} // break; //} if (circles.size() <= 0) { printf("Can not circles, return, thred=%d\n", lowThreshold); return; } for (size_t i = 0; i < circles.size(); i++)//把霍夫变换检测出的圆画出来 { Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); radius = cvRound(circles[i][2]); g_Center = center; g_radius = radius; line(dst, center, center, Scalar(0, 0, 255), 8); //circle(dst, center, 0, Scalar(0, 255, 0), -1, 8, 0); int a = cvRound(circles[i][0]); int b = cvRound(circles[i][1]); circle(dst, center, radius, Scalar(255, 0, 0), 2); //printf("low=%.4f max=%d\n", lowThreshold, iMaxLimit); printf("No%d - {%d} %4d %dx%d %d %d %d\n", i + 1, lowThreshold, (int)circles.size(), a, b, radius, dst.cols, dst.rows); //在控制台输出圆心坐标和半径 } imshow(window_name, dst); #if 1 // 找线段 vector lines2; int Len = 0, iCirLen = 0; HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); double vv = 0.0f; double fmin = 26.0f; int p = 0; //while (p <= 0) { // min++; for (int n = 0; n < (int)lines2.size(); n++) { // minangle = 60 maxangle = 320 最大刻度量 10,theta = 220 Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 double vv = DistancetoSegment(C, A, B); Len = (int)Length(Point(A.x - B.x, A.y - B.y)); //printf("Len=%d vv = %.4f\n", Len, vv); if (Len > 150 && vv < fmin) { fmin = vv; line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); printf("OK_Len=%d vv = %.4f\n", Len, vv); imshow(window_name, dst); } //iCirLen = (int)Length(Point((int)C.x - B.x, (int)C.y - B.y)); //if (Len > g_radius) // && vv < 13.0f) { //if (Len > 100 && vv < 10.0f) { // line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); // min = vv; // p = n; // // // //int x_angle = B.x - A.x; // //int y_angle = A.y - B.y; // //if (x_angle <= 0) continue; // //double theta = atan(tan(y_angle / x_angle)); // //double final_theta = 0; // //// 左右分 // ////if ((x_angle > 0 && y_angle > 0) || (x_angle > 0 && y_angle < 0)) // //// final_theta = 270 - theta; // ////if ((x_angle < 0 && y_angle >0) || (x_angle < 0 && y_angle < 0)) // //// final_theta = 90 - theta; // ////上下分 // //if ((x_angle > 0 && y_angle > 0) || (x_angle < 0 && y_angle > 0)) // // final_theta = 360 - theta; // //if ((x_angle > 0 && y_angle <0) || (x_angle < 0 && y_angle < 0)) // // final_theta = 180 - theta; // // minangle = 48, maxangle = 320 最大刻度量 10 // double du = get_point_angles(B, A); // //double val = (final_theta - 48) * 1.6f / (270 - 24); // //double val = dGetReadValueOfYZFTH(du, 360, Len); // double val = (du * 10) / (270); // //printf_s(" = %.2f° %.2f %.2f %.2f %.2f [%d]\n", Angles(C, B, A), val, final_theta, theta, vv, Len); // //printf_s(" = %.2f° %.2f %.2f %.2f %.2f [%d]\n", get_point_angles(B,A), val, final_theta, theta, vv, Len); // printf(" = %.2f° [%.2f] %.2f [%d]\n", get_point_angles(B, A), val, vv, Len); //} } printf("OK_Len=%d vv = %.4f\n", Len, fmin); //} #endif imshow(window_name, dst); } // 检测线段 void vHoughLines(int, void*) { string window_name = "Edge Map"; // 找线段 vector lines2; int Len = 0, iCirLen = 0; HoughLinesP(detected_edges, lines2, 1, CV_PI / 180, 50, 50, 10); for (int n = 0; n < (int)lines2.size(); n++) { // minangle = 60 maxangle = 320 最大刻度量 10,theta = 220 Point A(lines2[n][0], lines2[n][1]), B(lines2[n][2], lines2[n][3]); // 一条线段的2头坐标点 Point C((int)circles[0][0], (int)circles[0][1]); // 圆心点坐标 double vv = DistancetoSegment(C, A, B); Len = (int)Length(Point(A.x - B.x, A.y - B.y)); iCirLen = (int)Length(Point((int)C.x - B.x, (int)C.y - B.y)); if (Len > g_radius && vv < 10.0f) { line(dst, A, B, Scalar(0, 255, 0), 2, CV_AA); // int x_angle = B.x - C.x; int y_angle = C.y - B.y; if (x_angle <= 0) continue; double theta = atan(tan(y_angle / x_angle)); double final_theta = 0; // 左右分 //if ((x_angle > 0 && y_angle > 0) || (x_angle > 0 && y_angle < 0)) // final_theta = 270 - theta; //if ((x_angle < 0 && y_angle >0) || (x_angle < 0 && y_angle < 0)) // final_theta = 90 - theta; //上下分 if ((x_angle > 0 && y_angle > 0) || (x_angle < 0 && y_angle > 0)) final_theta = 360 - theta; if ((x_angle > 0 && y_angle < 0) || (x_angle < 0 && y_angle < 0)) final_theta = 180 - theta; double val = (final_theta - 60) * 10 / (320 - 60); //double val = (final_theta - 48) * 10 / (270 - 24); printf("%.2f %.2f %.2f %.2f %.2f\n", Angles(C, A, B), val, final_theta, theta, vv); } } imshow(window_name, dst); } // 滑动窗口检测图片圆和线段 void HTImgAnalys(const char *img_file) { char *window_name = NULL; /// 装载图像 src = imread(img_file); //AdaptiveFindThreshold(src, (double*)&lowThreshold, (double*)&max_lowThreshold, 3); //AdaptiveFindThreshold(src, &lowThreshold, &max_lowThreshold, 3); printf("lowThreshold = %f max_lowThreshold=%f\n", lowThreshold, max_lowThreshold); window_name = (char*)img_file; if (!src.data) { perror("error"); return; } printf("width x higth = %dx%d\n", src.cols, src.rows); printf("No. Ltld Htld cirs center radius w_cols h_rows Angles value final_theta theta vv Len\n"); // 创建与src同类型和大小的矩阵(dst) dst.create(src.size(), src.type()); // 原图像转换为灰度图像 cvtColor(src, src_gray, CV_BGR2GRAY); // 创建显示窗口 namedWindow(window_name, CV_WINDOW_AUTOSIZE); // 创建trackbar createTrackbar("Min Threshold:", window_name, &lowThreshold, max_lowThreshold, CannyThreshold); // createTrackbar("Min Threshold:", window_name, (int*)&low, max, CannyThreshold); // 显示图像 CannyThreshold(0, 0); //vHoughLines(0, 0); // 等待用户反应 waitKey(0); dst.release(); } // 上海自动化仪表股份有限公司OCr17Ni12Mo2压力表,min~max=0-1.6MPa void vInitPress_OCR17NI12MO2_16() { // radio, du, val float initdu = 225.0f; //max 315-1.6, 225-0.0 float du = 225.0f; float val = 0; int i = 0; for (i = 0; i < 80; i++) { printf("{%d,%.2f,%.2f},\n", 183, du, val); du -= 5.40f; if (du < 0) du = 360 - du; val += 0.05f; } printf("{%d,%.2f,%.2f},\n", 180, du, val); } // 上海自动化仪表股份有限公司OCr17Ni12Mo2压力表,min~max=0-2.5MPa void vInitPress_OCR17NI12MO2_25() { }