今天看了一篇技术文档感觉挺有意思,是关于在低对比度图像中检测脏污区域(这里的脏污指的是比其他区域暗的部分,人眼都不一定能看出来)。
先上图:
第一张图如果不是标注结果,我都没有发现脏污区域在哪里,第二张图还清晰一些,基本可以看出来图像靠近左边缘的位置有偏暗的区域,这就是我们所说的脏污区域了,也是我们要检测的区域。
标注结果图(引用https://jishuin.proginn.com/p/763bfbd62291):
这里介绍两种实现方法,
第一种是用C++实现参考博文的方法,即利用梯度方法来检测,具体步骤如下:
第二种方法是本人根据提高图像对比度思路实现的,具体步骤如下:
8. 对图像进行高斯模糊去噪;
9. 使用局部直方图均衡化方法来提高图像对比度;
10. 使用OTSU二值化阈值方法来粗略分割脏污区域;
11. 对二值图像使用腐蚀的形态学操作过滤掉部分非脏污区域;
12. 调用findContours方法查找脏污区域轮廓。
#include
#include
#include
#include
#include
#include
int main()
{
using namespace cv;
std::string strImgFile = "C:\\Temp\\common\\Workspace\\Opencv\\images\\led1.jpg";
Mat mSrc = imread(strImgFile);
CV_Assert(mSrc.empty() == false);
Mat mSrc2 = mSrc.clone();
CV_Assert(mSrc2.empty() == false);
Mat mGray;
cvtColor(mSrc, mGray, COLOR_BGR2GRAY);
GaussianBlur(mGray, mGray, Size(5, 5), 1.0);
Mat mGray2 = mGray.clone();
CV_Assert(mGray.empty() == false);
imshow("gray", mGray.clone());
//方法1:利用梯度变化检测缺陷
Mat mSobelX, mSobelY;
Sobel(mGray, mSobelX, CV_16S, 1, 0, 7);
Sobel(mGray, mSobelY, CV_16S, 0, 1, 7);
convertScaleAbs(mSobelX, mSobelX);
convertScaleAbs(mSobelY, mSobelY);
Mat mEdge;
addWeighted(mSobelX, 1, mSobelY, 1, 0, mEdge);
imshow("edge", mEdge);
Mat mThresh;
threshold(mEdge, mThresh, 0, 255, THRESH_BINARY | THRESH_OTSU);
imshow("thresh", mThresh);
Mat kernel1 = getStructuringElement(MORPH_RECT, Size(11, 11));
CV_Assert(kernel1.empty() == false);
Mat mMorph;
morphologyEx(mThresh, mMorph, MORPH_ERODE, kernel1);
imshow("erode", mMorph);
Mat kernel2 = getStructuringElement(MORPH_RECT, Size(5, 5));
morphologyEx(mMorph, mMorph, MORPH_DILATE, kernel2);
imshow("dilate", mMorph);
std::vector<std::vector<Point>> contours;
findContours(mMorph, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
for (int i = 0; i < contours.size(); i++)
{
float area = contourArea(contours[i]);
if (area > 200)
{
drawContours(mSrc, contours, i, Scalar(0, 0, 255));
}
}
imshow("result1", mSrc.clone());
//方法2: 利用局部直方图均衡化方法检测缺陷
Ptr<CLAHE> ptrCLAHE = createCLAHE(20, Size(30, 30));
ptrCLAHE->apply(mGray2, mGray2);
imshow("equalizeHist", mGray2);
Mat mThresh2;
threshold(mGray2, mThresh2, 0, 255, THRESH_BINARY_INV | THRESH_OTSU);
CV_Assert(mThresh2.empty() == false);
imshow("thresh", mThresh2);
Mat kernel2_1 = getStructuringElement(MORPH_RECT, Size(9, 9));
Mat mMorph2;
morphologyEx(mThresh2, mMorph2, MORPH_ERODE, kernel2_1);
CV_Assert(mMorph2.empty() == false);
imshow("morph2", mMorph2);
std::vector<std::vector<Point>> contours2;
findContours(mMorph2, contours2, RETR_EXTERNAL, CHAIN_APPROX_NONE);
for (int i = 0; i < contours2.size(); i++)
{
float area = contourArea(contours2[i]);
if (area > 200)
{
drawContours(mSrc2, contours2, i, Scalar(0, 0, 255));
}
}
imshow("result2", mSrc2);
waitKey(0);
destroyAllWindows();
system("pause");
return 0;
}
相对于梯度方法,局部直方图均衡化方法需要特别注意局部窗口大小参数以及阈限值参数的选择,本人也是尝试了多次才达到比较好的效果。再一次体会到传统图像处理的痛处,没有通用的参数适用于所有的应用实例,不同的场景要配置不同的参数才能达到想要的结果。
https://jishuin.proginn.com/p/763bfbd62291