|
@@ -9,7 +9,9 @@
|
|
|
#define new DEBUG_NEW
|
|
|
#endif
|
|
|
|
|
|
-
|
|
|
+#ifdef _DEBUG
|
|
|
+extern void test();
|
|
|
+#endif
|
|
|
// 唯一的应用程序对象
|
|
|
|
|
|
CWinApp theApp;
|
|
@@ -34,15 +36,7 @@ int main()
|
|
|
else
|
|
|
{
|
|
|
// TODO: 在此处为应用程序的行为编写代码。
|
|
|
- printf("开始程序……");
|
|
|
-
|
|
|
- cv::Mat thresholdImg;
|
|
|
- if (GetBinaryImage("E:\\bin\\test.jpg", 100, 255, thresholdImg))
|
|
|
- {
|
|
|
- cv::imwrite("E:\\bin\\thresholdImg.jpg", thresholdImg);
|
|
|
- thresholdImg.release();
|
|
|
- }
|
|
|
-
|
|
|
+ test();
|
|
|
}
|
|
|
}
|
|
|
else
|
|
@@ -52,8 +46,6 @@ int main()
|
|
|
nRetCode = 1;
|
|
|
}
|
|
|
|
|
|
- //system("pause");
|
|
|
-
|
|
|
return nRetCode;
|
|
|
}
|
|
|
|
|
@@ -71,7 +63,7 @@ CV_API bool ReadImage(std::string strImgPath, cv::Mat& img)
|
|
|
// 以BGR三通道方式读取图片;
|
|
|
img = cv::imread(strImgPath.c_str(), cv::IMREAD_COLOR);
|
|
|
// 判断图片是否为空;
|
|
|
- if (!img.empty()) {
|
|
|
+ if (img.empty()) {
|
|
|
#ifdef _DEBUG
|
|
|
OutputDebugString("读取图片失败!");
|
|
|
#endif
|
|
@@ -107,7 +99,7 @@ CV_API bool GetBinaryImage(std::string strImgPath, long nThresholdVal, long nMax
|
|
|
gaussImg.release();
|
|
|
|
|
|
// 判断图片是否空;
|
|
|
- if (!thresholdImg.empty())
|
|
|
+ if (thresholdImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -138,7 +130,7 @@ CV_API bool GetOTSUBinaryImage(std::string strImgPath, long nThresholdVal, long
|
|
|
gaussImg.release();
|
|
|
|
|
|
// 判断图片是否空;
|
|
|
- if (!thresholdImg.empty())
|
|
|
+ if (thresholdImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -176,7 +168,7 @@ CV_API bool GetAdaptiveBinaryImage(std::string strImgPath, long nMaxThresholdVal
|
|
|
blurImg.release();
|
|
|
|
|
|
// 判断图片是否空;
|
|
|
- if (!thresholdImg.empty())
|
|
|
+ if (thresholdImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -184,13 +176,13 @@ CV_API bool GetAdaptiveBinaryImage(std::string strImgPath, long nMaxThresholdVal
|
|
|
|
|
|
CV_API bool GaussianBlur(cv::Mat& img, cv::Size cvSize, cv::Mat& gaussImg)
|
|
|
{
|
|
|
- if (!img.empty())
|
|
|
+ if (img.empty())
|
|
|
return false;
|
|
|
|
|
|
// 再高斯模糊处理(滤波);
|
|
|
cv::GaussianBlur(img, gaussImg, cvSize, 0, 0);
|
|
|
// 判断图片是否空;
|
|
|
- if (!gaussImg.empty())
|
|
|
+ if (gaussImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -206,7 +198,7 @@ CV_API bool GaussianBlur(std::string strImgPath, cv::Size cvSize, cv::Mat& gauss
|
|
|
// 再高斯模糊处理(滤波);
|
|
|
cv::GaussianBlur(img, gaussImg, cvSize, 0, 0);
|
|
|
// 判断图片是否空;
|
|
|
- if (!gaussImg.empty())
|
|
|
+ if (gaussImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -214,13 +206,13 @@ CV_API bool GaussianBlur(std::string strImgPath, cv::Size cvSize, cv::Mat& gauss
|
|
|
|
|
|
CV_API bool Color2Gray(cv::Mat& img, cv::Mat& grayImg)
|
|
|
{
|
|
|
- if (!img.empty())
|
|
|
+ if (img.empty())
|
|
|
return false;
|
|
|
|
|
|
// 再高斯模糊处理(滤波);
|
|
|
cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY);
|
|
|
// 判断图片是否空;
|
|
|
- if (!grayImg.empty())
|
|
|
+ if (grayImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -236,7 +228,7 @@ CV_API bool Color2Gray(std::string strImgPath, cv::Mat& grayImg)
|
|
|
// 再高斯模糊处理(滤波);
|
|
|
cv::cvtColor(img, grayImg, cv::COLOR_BGR2GRAY);
|
|
|
// 判断图片是否空;
|
|
|
- if (!grayImg.empty())
|
|
|
+ if (grayImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -244,13 +236,13 @@ CV_API bool Color2Gray(std::string strImgPath, cv::Mat& grayImg)
|
|
|
|
|
|
CV_API bool Gray2Color(cv::Mat& img, cv::Mat& colorImg)
|
|
|
{
|
|
|
- if (!img.empty())
|
|
|
+ if (img.empty())
|
|
|
return false;
|
|
|
|
|
|
// 再高斯模糊处理(滤波);
|
|
|
cv::cvtColor(img, colorImg, cv::COLOR_GRAY2BGR);
|
|
|
// 判断图片是否空;
|
|
|
- if (!colorImg.empty())
|
|
|
+ if (colorImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -258,7 +250,7 @@ CV_API bool Gray2Color(cv::Mat& img, cv::Mat& colorImg)
|
|
|
|
|
|
CV_API bool ErodeImg(cv::Mat& img, cv::MorphShapes kernelShape, cv::Size kernelSize, cv::Mat& erodeImg)
|
|
|
{
|
|
|
- if (!img.empty())
|
|
|
+ if (img.empty())
|
|
|
return false;
|
|
|
|
|
|
// 获取自定义核(核形状:矩形MORPH_RECT、十字架CROSS、椭圆ELLIPSE )
|
|
@@ -266,7 +258,7 @@ CV_API bool ErodeImg(cv::Mat& img, cv::MorphShapes kernelShape, cv::Size kernelS
|
|
|
// 再高斯模糊处理(滤波);
|
|
|
cv::erode(img, erodeImg, element);
|
|
|
// 判断图片是否空;
|
|
|
- if (!erodeImg.empty())
|
|
|
+ if (erodeImg.empty())
|
|
|
return false;
|
|
|
|
|
|
return true;
|
|
@@ -274,7 +266,7 @@ CV_API bool ErodeImg(cv::Mat& img, cv::MorphShapes kernelShape, cv::Size kernelS
|
|
|
|
|
|
CV_API bool DilateImg(cv::Mat& img, cv::MorphShapes kernelShape, cv::Size kernelSize, cv::Mat& dilateImg)
|
|
|
{
|
|
|
- if (!img.empty())
|
|
|
+ if (img.empty())
|
|
|
return false;
|
|
|
|
|
|
// 获取自定义核(核形状:矩形MORPH_RECT、十字架CROSS、椭圆ELLIPSE )
|
|
@@ -282,8 +274,292 @@ CV_API bool DilateImg(cv::Mat& img, cv::MorphShapes kernelShape, cv::Size kernel
|
|
|
// 再高斯模糊处理(滤波);
|
|
|
cv::dilate(img, dilateImg, element);
|
|
|
// 判断图片是否空;
|
|
|
- if (!dilateImg.empty())
|
|
|
+ if (dilateImg.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+CV_API bool CutPictures(cv::Mat& img, cv::Rect cvRect, cv::Mat& roiImg)
|
|
|
+{
|
|
|
+ if (img.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (cvRect.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 裁剪ROI区域;
|
|
|
+ roiImg = img(cvRect);
|
|
|
+ // 判断图片是否空;
|
|
|
+ if (roiImg.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+CV_API bool CutPictures(std::string strImgPath, cv::Rect cvRect, cv::Mat& roiImg)
|
|
|
+{
|
|
|
+ cv::Mat img;
|
|
|
+ // 读取图片;
|
|
|
+ if (!ReadImage(strImgPath, img))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (cvRect.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 裁剪ROI区域;
|
|
|
+ roiImg = img(cvRect);
|
|
|
+ // 释放原图;
|
|
|
+ img.release();
|
|
|
+ // 判断图片是否空;
|
|
|
+ if (roiImg.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+CV_API bool CutPictures(std::string strImgPath, cv::Rect cvRect, std::string strRoiImgPath)
|
|
|
+{
|
|
|
+ cv::Mat img;
|
|
|
+ // 读取图片;
|
|
|
+ if (!ReadImage(strImgPath, img))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (cvRect.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ cv::Mat roiImg;
|
|
|
+ // 裁剪ROI区域;
|
|
|
+ roiImg = img(cvRect);
|
|
|
+ // 释放原图;
|
|
|
+ img.release();
|
|
|
+ // 判断图片是否空;
|
|
|
+ if (roiImg.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ cv::imwrite(strRoiImgPath.c_str(), roiImg);
|
|
|
+ roiImg.release();
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+CV_API bool DrawContours(cv::Mat& binImg, cv::Mat& drawImg, bool InternalContour, bool bPerimeter, long nMinPerimeter, long nMaxPerimeter, bool bArea, long nMinArea, long nMaxArea)
|
|
|
+{
|
|
|
+ // 图片空或都不是单通道;
|
|
|
+ if (binImg.empty() || binImg.channels() > 1)
|
|
|
+ return false;
|
|
|
+
|
|
|
+ if (drawImg.empty()) {
|
|
|
+ // 复制到目标中;
|
|
|
+ binImg.copyTo(drawImg);
|
|
|
+ // 再转为BGR;
|
|
|
+ cv::cvtColor(drawImg, drawImg, cv::COLOR_GRAY2BGR);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 轮廓;
|
|
|
+ vector<vector<cv::Point>> contours;
|
|
|
+ // 轮廓关系;
|
|
|
+ vector<cv::Vec4i> hierarchy;
|
|
|
+ // 获取轮廓区域;
|
|
|
+ if (InternalContour)
|
|
|
+ {
|
|
|
+ // 所有轮廓,内外轮廓分等级;
|
|
|
+ findContours(binImg, contours, hierarchy, cv::RETR_CCOMP, cv::CHAIN_APPROX_SIMPLE);
|
|
|
+ // 所有轮廓,内外轮廓不分等级,独立;
|
|
|
+ //findContours(m_ImgThreshold, m_contours, m_hierarchy, cv::RETR_LIST, cv::CHAIN_APPROX_SIMPLE);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ // RETR_EXTERNAL=只检索最外面的轮廓;
|
|
|
+ findContours(binImg, contours, hierarchy, cv::RETR_EXTERNAL, cv::CHAIN_APPROX_SIMPLE);
|
|
|
+ }
|
|
|
+
|
|
|
+ vector<vector<cv::Point>>::iterator it = contours.begin();
|
|
|
+ for (int i = 0; it != contours.end(); it++, i++)
|
|
|
+ {
|
|
|
+ if (bPerimeter && !bArea)
|
|
|
+ {
|
|
|
+ if (arcLength(*it, true) < nMinPerimeter || arcLength(*it, true) > nMaxPerimeter)
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (!bPerimeter && bArea)
|
|
|
+ {
|
|
|
+ if (contourArea(*it) < nMinArea || contourArea(*it) > nMaxArea)
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ else if (bArea && bPerimeter)
|
|
|
+ {
|
|
|
+ if ((arcLength(*it, true) < nMinPerimeter || arcLength(*it, true) > nMaxPerimeter) || (contourArea(*it) < nMinArea || contourArea(*it) > nMaxArea))
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 随机色填充轮廓;
|
|
|
+ cv::Scalar color = cv::Scalar(rand() % 255, rand() % 255, rand() % 255);
|
|
|
+ // 输出轮廓图;
|
|
|
+ cv::drawContours(drawImg, contours, i, color, cv::FILLED, 8, hierarchy);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (drawImg.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+CV_API bool MatchSingleImage(cv::Mat& img, cv::Mat templImg, double& matchVal, cv::Rect& matchRect)
|
|
|
+{
|
|
|
+ if (img.empty() || templImg.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ /*
|
|
|
+ 模板匹配的匹配方式:
|
|
|
+ 0:CV_TM_SQDIFF 平方差匹配法:最好匹配为0。 匹配越差,匹配值越大。
|
|
|
+ 这类方法利用图像与模板各个像素差值的平方和来进行匹配,最好匹配为 0。 匹配越差,匹配值越大。
|
|
|
+ 1:CV_TM_SQDIFF_NORMED 归一化平方差匹配法:最好匹配为0。 匹配越差,匹配值越大。
|
|
|
+ 这个方法其实和差值平方和算法是类似的。只不过对图像和模板进行了标准化操作
|
|
|
+ 2:CV_TM_CCORR 相关匹配法:0最差匹配,越接近1越完美;
|
|
|
+ 这类方法采用模板和图像的互相关计算作为相似度的度量方法,所以较大的数表示匹配程度较高,0标识最坏的匹配效果。
|
|
|
+ 3:CV_TM_CCORR_NORMED 归一化相关匹配法:0最差匹配,越接近1越完美;
|
|
|
+ 这个方法和 标准化差值平方和匹配 类似,都是去除了亮度线性变化对相似度计算的影响。可以保证图像和模板同时变亮或变暗k倍时结果不变。
|
|
|
+ 4:CV_TM_CCOEFF 相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
|
|
|
+ 这种方法也叫做相关匹配,但是和上面的 CV_TM_CCORR 匹配方法还是有不通过的。简单的说,这里是把图像和模板都减去了各自的平均值,使得这两幅图像都没有直流分量
|
|
|
+ 5:CV_TM_CCOEFF_NORMED 归一化相关系数匹配法:1表示完美的匹配;-1表示最差的匹配。
|
|
|
+ 这是 OpenCV 支持的最复杂的一种相似度算法。
|
|
|
+ 这里的相关运算就是数理统计学科的相关系数计算方法。
|
|
|
+ 具体的说,就是在减去了各自的平均值之外,还要各自除以各自的方差。
|
|
|
+ 经过减去平均值和除以方差这么两步操作之后,无论是我们的待检图像还是模板都被标准化了,
|
|
|
+ 这样可以保证图像和模板分别改变光照亮不影响计算结果。计算出的相关系数被限制在了 -1 到 1 之间,
|
|
|
+ 1 表示完全相同,-1 表示两幅图像的亮度正好相反,0 表示两幅图像之间没有线性关系。
|
|
|
+ */
|
|
|
+ cv::Mat resultImg;
|
|
|
+ cv::matchTemplate(img, templ, resultImg, cv::TM_CCOEFF_NORMED);
|
|
|
+ if (resultImg.empty())
|
|
|
return false;
|
|
|
|
|
|
+ // 最小和最大匹配值;
|
|
|
+ double lv_nMinVal = 0.0;
|
|
|
+ double lv_nMaxVal = 0.0;
|
|
|
+ // 匹配的坐标点;
|
|
|
+ cv::Point lv_nMinLoc = cv::Point(0, 0);
|
|
|
+ cv::Point lv_nMaxLoc = cv::Point(0, 0);
|
|
|
+ cv::minMaxLoc(resultImg, &lv_nMinVal, &lv_nMaxVal, &lv_nMinLoc, &lv_nMaxLoc);
|
|
|
+
|
|
|
+ // TM_CCOEFF_NORMED匹配是取最大值为匹配值;
|
|
|
+ matchVal = lv_nMaxVal;
|
|
|
+ // 匹配区域,使用最大区域;
|
|
|
+ matchRect.x = lv_nMaxLoc.x;
|
|
|
+ matchRect.y = lv_nMaxLoc.y;
|
|
|
+ matchRect.width = templ.cols;
|
|
|
+ matchRect.height = templ.rows;
|
|
|
+
|
|
|
+#ifdef _DEBUG
|
|
|
+ // 在原图上框出模板位置;
|
|
|
+ cv::Mat showImg;
|
|
|
+ img.copyTo(showImg);
|
|
|
+ cv::rectangle(showImg,
|
|
|
+ cv::Point(lv_nMaxLoc.x, lv_nMaxLoc.y),
|
|
|
+ cv::Point(lv_nMaxLoc.x + templ.cols, lv_nMaxLoc.y + templ.rows),
|
|
|
+ cv::Scalar(rand() % 255, rand() % 255, rand() % 255),
|
|
|
+ 2,
|
|
|
+ cv::LINE_AA,
|
|
|
+ 0);
|
|
|
+ // 输出到文件中;
|
|
|
+ imwrite("showImg.png", showImg);
|
|
|
+#endif
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+CV_API bool MatchSingleImage(cv::Mat& img, std::string strTmpImg, double& matchVal, cv::Rect& matchRect)
|
|
|
+{
|
|
|
+ if (img.empty() || !PathFileExists(strTmpImg.c_str()))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 读取模板图片;
|
|
|
+ cv::Mat templ = imread(strTmpImg.c_str(), cv::IMREAD_COLOR);
|
|
|
+ if (templ.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ cv::Mat resultImg;
|
|
|
+ cv::matchTemplate(img, templ, resultImg, cv::TM_CCOEFF_NORMED);
|
|
|
+ if (resultImg.empty() )
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 最小和最大匹配值;
|
|
|
+ double lv_nMinVal = 0.0;
|
|
|
+ double lv_nMaxVal = 0.0;
|
|
|
+ // 匹配的坐标点;
|
|
|
+ cv::Point lv_nMinLoc = cv::Point(0, 0);
|
|
|
+ cv::Point lv_nMaxLoc = cv::Point(0, 0);
|
|
|
+ cv::minMaxLoc(resultImg, &lv_nMinVal, &lv_nMaxVal, &lv_nMinLoc, &lv_nMaxLoc);
|
|
|
+
|
|
|
+ // TM_CCOEFF_NORMED匹配是取最大值为匹配值;
|
|
|
+ matchVal = lv_nMaxVal;
|
|
|
+ // 匹配区域,使用最大区域;
|
|
|
+ matchRect.x = lv_nMaxLoc.x;
|
|
|
+ matchRect.y = lv_nMaxLoc.y;
|
|
|
+ matchRect.width = templ.cols;
|
|
|
+ matchRect.height = templ.rows;
|
|
|
+
|
|
|
+#ifdef _DEBUG
|
|
|
+ // 在原图上框出模板位置;
|
|
|
+ cv::Mat showImg;
|
|
|
+ img.copyTo(showImg);
|
|
|
+ cv::rectangle(showImg,
|
|
|
+ cv::Point(lv_nMaxLoc.x, lv_nMaxLoc.y),
|
|
|
+ cv::Point(lv_nMaxLoc.x + templ.cols, lv_nMaxLoc.y + templ.rows),
|
|
|
+ cv::Scalar(rand() % 255, rand() % 255, rand() % 255),
|
|
|
+ 2,
|
|
|
+ cv::LINE_AA,
|
|
|
+ 0);
|
|
|
+ // 输出到文件中;
|
|
|
+ imwrite("showImg.png", showImg);
|
|
|
+#endif
|
|
|
+
|
|
|
+ return true;
|
|
|
+}
|
|
|
+
|
|
|
+CV_API bool MatchSingleImage(std::string strImg, std::string strTmpImg, double& matchVal, cv::Rect& matchRect)
|
|
|
+{
|
|
|
+ cv::Mat img;
|
|
|
+ cv::Mat templ;
|
|
|
+ if (!ReadImage(strImg.c_str(), img) || !ReadImage(strTmpImg.c_str(), templ))
|
|
|
+ return false;
|
|
|
+
|
|
|
+ cv::Mat resultImg;
|
|
|
+ cv::matchTemplate(img, templ, resultImg, cv::TM_CCOEFF_NORMED);
|
|
|
+ if (resultImg.empty())
|
|
|
+ return false;
|
|
|
+
|
|
|
+ // 最小和最大匹配值;
|
|
|
+ double lv_nMinVal = 0.0;
|
|
|
+ double lv_nMaxVal = 0.0;
|
|
|
+ // 匹配的坐标点;
|
|
|
+ cv::Point lv_nMinLoc = cv::Point(0, 0);
|
|
|
+ cv::Point lv_nMaxLoc = cv::Point(0, 0);
|
|
|
+ cv::minMaxLoc(resultImg, &lv_nMinVal, &lv_nMaxVal, &lv_nMinLoc, &lv_nMaxLoc);
|
|
|
+
|
|
|
+ // TM_CCOEFF_NORMED匹配是取最大值为匹配值;
|
|
|
+ matchVal = lv_nMaxVal;
|
|
|
+ // 匹配区域,使用最大区域;
|
|
|
+ matchRect.x = lv_nMaxLoc.x;
|
|
|
+ matchRect.y = lv_nMaxLoc.y;
|
|
|
+ matchRect.width = templ.cols;
|
|
|
+ matchRect.height = templ.rows;
|
|
|
+
|
|
|
+#ifdef _DEBUG
|
|
|
+ // 在原图上框出模板位置;
|
|
|
+ cv::Mat showImg;
|
|
|
+ img.copyTo(showImg);
|
|
|
+ cv::rectangle(showImg,
|
|
|
+ cv::Point(lv_nMaxLoc.x, lv_nMaxLoc.y),
|
|
|
+ cv::Point(lv_nMaxLoc.x + templ.cols, lv_nMaxLoc.y + templ.rows),
|
|
|
+ cv::Scalar(rand() % 255, rand() % 255, rand() % 255),
|
|
|
+ 2,
|
|
|
+ cv::LINE_AA,
|
|
|
+ 0);
|
|
|
+ // 输出到文件中;
|
|
|
+ imwrite("showImg.png", showImg);
|
|
|
+#endif
|
|
|
+
|
|
|
return true;
|
|
|
}
|