#pragma once
#include <iostream>
#include <string>
#include "opencv2/opencv.hpp"
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
using namespace std;
using namespace cv;
int morph_elem = 0;
int morph_size = 0;
int morph_operator = 0;
int whiteSpotAreaThreshold = 15; //负极白点面积阈值
int normalAreaThreshold = 6210; //负极轮廓面积阈值
//定义点线距离的函数
double getDistance(Point2f point1, Point2f point2, Point2f point3)
{
//建立直线方程Ax+By+C=0-->(y2-y1)*x+(x1-x2)*y+(x2*y1-x1*y2)=0
double A = point2.y - point1.y;
double B = point1.x - point2.x;
double C = point2.x * point1.y - point1.x * point2.y;
double a = A * A;
double b = B * B;
//直线距离公式D=|A*x3+B*y3+C|/sqrt(A^2+B^2)
double w = (A * point3.x + B * point3.y + C);
if (w < 0) w = 0 - w;//取绝对值
return double(w / sqrt(a + b));
}
//根据阈值统计像素点
int countSums(Mat src)
{
int counter = 0;
//迭代器访问像素点
Mat_<uchar>::iterator it = src.begin<uchar>();
Mat_<uchar>::iterator itend = src.end<uchar>();
for (; it != itend; ++it)
{
if ((*it) > 160) counter += 1;//二值化后,像素点是0或者255
}
return counter;
}
int main()
{
//*************** 【正反有无检测】第一步:霍夫圆变换找圆形轮廓(如果有则返回朝上,没有进行第二步) *******************
cv::Mat unknownSurfaceGry, unknownSurfaceBlur, unknownSurfaceCny, unknownSurfaceThresh;
const char* unknownSurfaceImagePath = "E:/projects/pyHome/learningPython/day_06_openCV/dianrong/21.bmp";
cv::Mat unknownSurfaceImgBig = cv::imread(unknownSurfaceImagePath, -1);
if (unknownSurfaceImgBig.empty()) return -1;
// 截取ROI区域 x,y,w,h 后灰度化、高斯模糊、阈值、边缘检测
cv::Rect resultRect = cv::Rect(550, 250, 700, 550);
cv::Mat unknownSurfaceImg = unknownSurfaceImgBig(resultRect).clone();
cv::cvtColor(unknownSurfaceImg, unknownSurfaceGry, cv::COLOR_BGR2GRAY);
cv::GaussianBlur(unknownSurfaceGry, unknownSurfaceBlur, Size(7, 7), 0, 0);
cv::threshold(unknownSurfaceBlur, unknownSurfaceThresh, 100, 255, THRESH_BINARY + THRESH_OTSU);
cv::Canny(unknownSurfaceThresh, unknownSurfaceCny, 40, 80, 3, true);
vector<Vec3f> unknownSurfaceCircles;
cv::HoughCircles(unknownSurfaceCny, unknownSurfaceCircles, HOUGH_GRADIENT, 1, 80, 100, 35, 70, 180);
if (unknownSurfaceCircles.size() < 1) {
cout << "没有圆" << endl;
}else{
cout << "检测到圆,当前电容朝上" << endl;
return -1;
}
//*************** 【正反有无检测】第二步:轮廓匹配找底座轮廓(如果有则返回朝下,没有则返回空电容) *******************
vector<vector<Point> > unknownSurfaceContoursTemp, unknownSurfaceContours; // 轮廓
vector<Vec4i> unknownSurfaceHierarchy;
cv::findContours(unknownSurfaceCny, unknownSurfaceContoursTemp, unknownSurfaceHierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
for (int i = 0; i < unknownSurfaceContoursTemp.size();i++) {
cout << "轮廓面积为-----:" << contourArea(unknownSurfaceContoursTemp[i]) << endl;
}
unknownSurfaceContours.push_back(unknownSurfaceContoursTemp[0]);
drawContours(unknownSurfaceImg, unknownSurfaceContours, -1, Scalar(255, 0, 0), 1, 8);
double unknownSurfaceMatch = cv::matchShapes(unknownSurfaceContours[0], unknownSurfaceContours[0], 2, 0.0);
cout << "底座轮廓匹配差异度为-----:" << unknownSurfaceMatch << endl;
cv::imshow("unknownSurfaceCny", unknownSurfaceCny);
cv::imshow("unknownSurfaceImg", unknownSurfaceImg);
*************** 【正面检测】第一步:矫正电容之找负极右边缘直线和其角度 *******************
预声明【字符面】图片输出对象
//cv::Mat imgTopGry, imgTopCny, imgTopThresh, imgTopEnd, imgTopGryEnd;
//const char* imageTopPath = "E:/projects/pyHome/learningPython/day_06_openCV/dianrong/16.bmp";
//cv::Mat imgTopWhole = cv::imread(imageTopPath, -1);
//if (imgTopWhole.empty()) return -1;
//cv::Rect resultRect = cv::Rect(555, 370, 410, 360);
//cv::Mat imgTop = imgTopWhole(resultRect).clone();
//cv::cvtColor(imgTop, imgTopGry, cv::COLOR_BGR2GRAY);
//cv::threshold(imgTopGry, imgTopThresh, 100, 255, THRESH_BINARY + THRESH_OTSU);
//cv::Canny(imgTopThresh, imgTopCny, 40, 80, 3, true);
//vector<Vec4i> lines;
//cv::HoughLinesP(imgTopCny, lines, 0.7, CV_PI / 180, 30, 50, 10);
//Point p1 = Point(lines[0][0], lines[0][1]);
//Point p2 = Point(lines[0][2], lines[0][3]);
//float angle = atan2(p1.y - p2.y, p2.x - p1.x) * 57.29577;
//cout << "直线个数:" << lines.size() << endl
// << "角度:" << angle << endl;
*************** 【正面检测】第二步:矫正电容之旋转图片矫正角度 *******************
//int w = imgTop.rows;
//int h = imgTop.cols;
//Mat rotate = cv::getRotationMatrix2D(Point(h / 2, w / 2), 90 - angle, 1);
//cv::warpAffine(imgTop, imgTopEnd, rotate, Point(h, w));
//cv::warpAffine(imgTopGry, imgTopGryEnd, rotate, Point(h, w));
//vector<Vec3f> circles;
//cv::HoughCircles(imgTopGry, circles, HOUGH_GRADIENT, 1, 80, 100, 35, 70, 180);
//if (circles.size() < 1) return -1;
//float r = circles[0][2];
//float x = circles[0][0];
//float y = circles[0][1];
//cout << "圆的个数:" << angle << endl
// << "圆心坐标为:(" << x << ", " << y << ")" << endl
// << "圆的半径是:" << r << endl;
*************** 【正面检测】第三步:矫正电容之根据平移矩阵进行仿射变换 *******************
定义平移矩阵
//cv::Mat t_mat = cv::Mat::zeros(2, 3, CV_32FC1);
//t_mat.at<float>(0, 0) = 1;
//t_mat.at<float>(0, 2) = 20; //水平平移量
//t_mat.at<float>(1, 1) = 1;
//t_mat.at<float>(1, 2) = 10; //竖直平移量
根据平移矩阵进行仿射变换
//cv::warpAffine(imgTopEnd, imgTopEnd, t_mat, imgTop.size());
//cv::warpAffine(imgTopGryEnd, imgTopGryEnd, t_mat, imgTop.size());
*************** 【正面检测】第四步:负极缺陷检测之检测负极大小 *******************
//cv::threshold(imgTopGryEnd, imgTopThresh, 100, 255, THRESH_BINARY + THRESH_OTSU); // 阈值化矫正后的图片
//cv::Canny(imgTopThresh, imgTopCny, 40, 80, 3, true); // 边缘检测
//vector<Vec4i> linesEnd;
//cv::HoughLinesP(imgTopCny, linesEnd, 0.7, CV_PI / 180, 30, 50, 10);
///*for (size_t i = 0; i < linesEnd.size(); i++)
//{
// line(imgTopEnd, Point(linesEnd[i][0], linesEnd[i][1]), Point(linesEnd[i][2], linesEnd[i][3]), Scalar(0, 0, 255), 2, 8);
//}*/
//double distance0 = getDistance(Point(linesEnd[0][0], linesEnd[0][1]), Point(linesEnd[0][2], linesEnd[0][3]), Point(linesEnd[0][0], linesEnd[0][1]));
//cout << "当前负极边缘与模板边缘距离:" << distance0 << endl;
*************** 【正面检测】第五步:负极缺陷检测之检测负极有无白点 *******************
//vector<vector<Point> > imgTopCnyContoursTemp;
//vector<Vec4i> imgTopCnyHierarchy;
//cv::findContours(imgTopCny, imgTopCnyContoursTemp, imgTopCnyHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE); // 找轮廓
根据面积阈值把白点去掉
//vector<vector<Point> > imgTopCnyContours;
//for (int i = 0; i < imgTopCnyContoursTemp.size(); i++) {
// int xTemp = imgTopCnyContoursTemp[i][0].x;
// if (xTemp > linesEnd[0][0]) { // 负极右边缘线右侧的轮廓去掉
// continue;
// }
// double areaNegativePoleTemp = cv::contourArea(imgTopCnyContoursTemp[i]);
// if (areaNegativePoleTemp < normalAreaThreshold) {
// if (areaNegativePoleTemp > whiteSpotAreaThreshold) {
// cout << "停止遍历,第" << i << "个白点轮廓是破皮,阈值为" <<
// whiteSpotAreaThreshold << ",当前白点面积为:" << areaNegativePoleTemp << endl;
// break;
// }
// cout << "第" << i << "个白点轮廓面积在正常范围内,面积为:" << areaNegativePoleTemp << endl;
// }
// else {
// cout << "第" << i << "个非白点轮廓面积为:" << areaNegativePoleTemp << endl;
// imgTopCnyContours.clear(); // RETR_TREE这种检索方式负极轮廓出现了两个,保留最后一个
// imgTopCnyContours.push_back(imgTopCnyContoursTemp[i]);
// }
//}
//drawContours(imgTopEnd, imgTopCnyContours, -1, Scalar(0, 255, 0), 2, 8);
*************** 【正面检测】第六步:字符检测之利用图像灰度图差值、均值、方差、统计像素个数判断相似度 *******************
尝试了灰度图后加大津法处理发现效果没有明显改观,因此只对图像做灰度处理,直接相减,设定阈值即可
//cv::Mat charOneWholeImg, charOneImgGry, charThreeWholeImg, charThreeImgGry, charOneWholeImg2, charOneImgGry2;
//charOneWholeImg = cv::imread("E:/projects/pyHome/learningPython/day_06_openCV/dianrong/8.bmp", -1);
//charOneWholeImg2 = cv::imread("E:/projects/pyHome/learningPython/day_06_openCV/dianrong/2.bmp", -1);
//if (charOneWholeImg.empty() || charOneWholeImg2.empty()) return -1;
//cv::Mat charOneImg = charOneWholeImg(Rect(655, 465, 15, 20)).clone();
//cv::Mat charOneImg2 = charOneWholeImg2(Rect(582, 498, 15, 20)).clone();
//cv::Mat charThreeImg = charOneWholeImg(Rect(655, 484, 15, 20)).clone();
//cv::cvtColor(charOneImg, charOneImgGry, cv::COLOR_BGR2GRAY);
//cv::cvtColor(charOneImg2, charOneImgGry2, cv::COLOR_BGR2GRAY);
//cv::cvtColor(charThreeImg, charThreeImgGry, cv::COLOR_BGR2GRAY);
灰度差
//Mat subtractRes, subtractRes1, subtractRes2;
//Scalar subtractSumRes, subtractSumRes1, subtractSumRes2;
//cv::subtract(charOneImgGry2, charOneImgGry, subtractRes);
//cv::subtract(charThreeImgGry, charOneImgGry, subtractRes1);
//cv::subtract(charOneImgGry, charOneImgGry, subtractRes2);
//subtractSumRes = sum(subtractRes);
//subtractSumRes1 = sum(subtractRes1);
//subtractSumRes2 = sum(subtractRes2);
均值和方差
//Mat mean, stddv, mean1, stddv1, mean2, stddv2;
//cv::meanStdDev(charOneImgGry, mean, stddv);
//cv::meanStdDev(charOneImgGry2, mean1, stddv1);
//cv::meanStdDev(charThreeImgGry, mean2, stddv2);
设定阈值160统计像素个数
//int pixelSums = countSums(charOneImgGry);
//int pixelSums1 = countSums(charOneImgGry2);
//int pixelSums2 = countSums(charThreeImgGry);
//cout << "灰度差subtractSumRes,subtractSumRes1,subtractSumRes2分别为------:" <<
// subtractSumRes << ", " << subtractSumRes1 << ", " << subtractSumRes2 << endl
// << "均值mean,mean1,mean2分别为------:" <<
// mean << ", " << mean1 << ", " << mean2 << endl
// << "方差stddv,stddv1,stddv2分别为------:" <<
// stddv << ", " << stddv1 << ", " << stddv2 << endl
// << "阈值160以上像素个数pixelSums,pixelSums1,pixelSums2分别为------:" <<
// pixelSums << ", " << pixelSums1 << ", " << pixelSums2 << endl;
*************** 【正面检测】第七步:字符检测之轮廓相似度 *******************
//Mat charOneImgThresh, charOneImgThresh2, charThreeImgThresh;
//cv::threshold(charOneImgGry, charOneImgThresh, 100, 255, THRESH_BINARY + THRESH_OTSU);
//cv::threshold(charOneImgGry2, charOneImgThresh2, 100, 255, THRESH_BINARY + THRESH_OTSU);
//cv::threshold(charThreeImgGry, charThreeImgThresh, 100, 255, THRESH_BINARY + THRESH_OTSU);
//vector<vector<Point> > charOneImgContours, charOneImgContours2, charThreeImgContours;
//vector<Vec4i> charOneImgHierarchy, charOneImgHierarchy2, charThreeImgHierarchy;
//cv::findContours(charOneImgThresh, charOneImgContours, charOneImgHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
//cv::findContours(charOneImgThresh2, charOneImgContours2, charOneImgHierarchy2, RETR_TREE, CHAIN_APPROX_SIMPLE);
//cv::findContours(charThreeImgThresh, charThreeImgContours, charThreeImgHierarchy, RETR_TREE, CHAIN_APPROX_SIMPLE);
//charOneImgContours.erase(charOneImgContours.begin());
charOneImgContours2.erase(charOneImgContours2.begin());
//charThreeImgContours.erase(charThreeImgContours.begin());
//drawContours(charOneImg, charOneImgContours, -1, Scalar(0, 0, 255), 1, 8);
//drawContours(charOneImg2, charOneImgContours2, -1, Scalar(0, 0, 255), 1, 8);
//drawContours(charThreeImg, charThreeImgContours, -1, Scalar(0, 0, 255), 1, 8);
//cv::imshow("charOneImg", charOneImg);
//cv::imshow("charOneImg2", charOneImg2);
//cv::imshow("charThreeImg", charThreeImg);
//double match = cv::matchShapes(charOneImgContours[0], charOneImgContours[0], 2, 0.0);
//double match1 = cv::matchShapes(charOneImgContours[0], charThreeImgContours[0], 2, 0.0);
//double match2 = cv::matchShapes(charOneImgContours[0], charOneImgContours2[0], 2, 0.0);
//cout << "第一个字符与自己做模板形状匹配差异度为:" << match << endl
// << "第一个字符与第二个字符做模板形状匹配差异度为:" << match1 << endl
// << "第一个字符与第三个字符做模板形状匹配差异度为:" << match2 << endl;
*************** 【反面检测】第一步:计算底座轮廓面积 *******************
预声明【底座】图片输出对象
//cv::Mat base_gaussianBlur, base_gry, base_cny, base_thresh, base_opening;
//const char* imagePath = "E:/projects/pyHome/learningPython/day_06_openCV/dianrong/21.bmp";
//cv::Mat img_big = cv::imread(imagePath, -1);
//if (img_big.empty()) return -1;
// 截取ROI区域 x,y,w,h
cv::Rect resultRect = cv::Rect(550, 250, 700, 550);
cv::Mat img = img_big(resultRect).clone();
//cv::Mat img = img_big.clone();
灰度化
//cv::cvtColor(img, base_gry, cv::COLOR_BGR2GRAY);
cv::imshow("gray", base_gry);
先高斯模糊 采用7*7大卷积核,过滤大噪声
//GaussianBlur(base_gry, base_gaussianBlur, Size(7, 7), 0, 0);
大津法阈值分割(因为图片明暗不均)
//cv::threshold(base_gaussianBlur, base_thresh, 100, 255, THRESH_BINARY + THRESH_OTSU);
开运算腐蚀一下阈值图
//int operation = morph_operator + 2;
//Mat element = getStructuringElement(morph_elem, Size(2 * morph_size + 7, 2 * morph_size + 7), Point(morph_size, morph_size));
//morphologyEx(base_thresh, base_opening, operation, element);
边缘检测
//cv::Canny(base_opening, base_cny, 40, 80, 3, true);
提取轮廓
//vector<vector<Point> > base_contours;
//vector<Vec4i> base_hierarchy;
//cv::findContours(base_cny, base_contours, base_hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
画轮廓
//Scalar color(rand() & 255, rand() & 255, rand() & 255);
//drawContours(img, base_contours, -1, color, 2, 8);
底座最小外接矩形
//Point2f vtx[4];
//RotatedRect box = cv::minAreaRect(base_contours[0]);
//box.points(vtx); // 底座最小外接矩形的四个角
Draw the bounding box
//for (int i = 0; i < 4; i++)
// line(img, vtx[i], vtx[(i + 1) % 4], Scalar(0, 255, 0), 2, LINE_AA);
计算面积
//double area0 = cv::contourArea(base_contours[0]);
//cout << "轮廓数量:" << base_contours.size() << endl <<
// "底座的面积为:" << area0 << endl;
*************** 【反面检测】第二步:模板匹配底座相似度 *******************
轮廓形状匹配
//double match = cv::matchShapes(base_contours[0], base_contours[0], 1, 0.0);
//cout << "与底座模板形状匹配差异度为:" << match << endl;
*************** 【反面检测】第三步:计算针脚角度 *******************
预声明【针脚】图片输出对象
//cv::Mat pin_cny, pin_thresh;
//cv::threshold(base_gaussianBlur, pin_thresh, 100, 255, THRESH_BINARY_INV);
//cv::Canny(pin_thresh, pin_cny, 40, 80, 3, true);
提取针脚轮廓
//vector<vector<Point> > pin_contours;
//vector<Vec4i> pin_hierarchy;
//cv::findContours(pin_cny, pin_contours, pin_hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
根据面积阈值筛选轮廓,记录针脚最小外接矩形角度
//vector<vector<Point> > pin_contours_end;
//vector<Point2f> pinVtxs;
//for (int i = 0; i < pin_contours.size(); i++) {
// if (cv::contourArea(pin_contours[i]) > 3000) {
// pin_contours_end.push_back(pin_contours[i]);
// // 最小外接矩形
// Point2f pin_vtx[4];
// RotatedRect pin_box = cv::minAreaRect(pin_contours[i]);
// cout << "针脚角度为:" << pin_box.angle << endl;
// pin_box.points(pin_vtx); // 针脚最小外接矩形的四个角
// pinVtxs.push_back(pin_vtx[0]);
// pinVtxs.push_back(pin_vtx[3]);
// // Draw the bounding box
// for (int i = 0; i < 4; i++)
// line(img, pin_vtx[i], pin_vtx[(i + 1) % 4], Scalar(0, 255, 0), 2, LINE_AA);
// }
//}
//cout << "针脚数目为:" << pin_contours_end.size() << endl;
*************** 【反面检测】第四步:计算点到线距离判断针脚是否错位、针脚是不是过长 *******************
//Point2f pointA = vtx[0]; // 底座最小外接矩形左上角 A
//Point2f pointB = vtx[1]; // 底座最小外接矩形右下角 B
//Point2f pointC = vtx[2]; // 底座最小外接矩形左上角 C
//Point2f pointD = vtx[3]; // 底座最小外接矩形左下角 D
//Point2f point_a = pinVtxs[1]; // 下边针脚最小外接矩形左下角 a
//Point2f point_b = pinVtxs[2]; // 上边针脚最小外接矩形左上角 b
//circle(img, pointA, 2, Scalar(0, 0, 255), 2);//用小圆标记点
//circle(img, pointB, 2, Scalar(0, 0, 255), 2);//用小圆标记点
//circle(img, pointC, 2, Scalar(0, 0, 255), 2);//用小圆标记点
//circle(img, pointD, 2, Scalar(0, 0, 255), 2);//用小圆标记点
//circle(img, point_a, 2, Scalar(255, 0, 0), 2);//用小圆标记点
//circle(img, point_b, 2, Scalar(255, 0, 0), 2);//用小圆标记点
//cv::putText(img, "A", pointA, FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2, 8);
//cv::putText(img, "B", pointB, FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2, 8);
//cv::putText(img, "C", pointC, FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2, 8);
//cv::putText(img, "D", pointD, FONT_HERSHEY_SIMPLEX, 1, Scalar(0, 0, 255), 2, 8);
//cv::putText(img, "a", point_a, FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 0, 0), 2, 8);
//cv::putText(img, "b", point_b, FONT_HERSHEY_SIMPLEX, 1, Scalar(255, 0, 0), 2, 8);
//double distance1 = getDistance(pointA, pointD, point_a);
//double distance2 = getDistance(pointA, pointD, point_b);
//double distance3 = getDistance(pointC, pointD, point_a);
//double distance4 = getDistance(pointA, pointB, point_b);
//cout << "第一个针脚到底座【左边界】距离为:" << distance1 << endl
// << "第二个针脚到底座【左边界】距离为:" << distance2 << endl
// << "第一个针脚到底座【下边界】距离为:" << distance3 << endl
// << "第二个针脚到底座【上边界】距离为:" << distance4 << endl;
//cv::imshow("img", img);
cv::waitKey(0);
cv::destroyWindow("Example 2-1");
}