OpenCV practical: C + + to achieve low contrast image dirty area detection

preface

It's interesting to read a technical document today about detecting dirty areas in low contrast images (dirty here refers to the parts darker than other areas, which may not be visible to human eyes).

1. Low contrast image dirty area detection

Start with the above figure:


If the first picture is not marked, I can't find the dirty area. The second picture is still clearer. It can be seen that there is a dark area near the left edge of the image, which is what we call the dirty area and the area we want to detect.

Annotation result diagram (Reference) https://jishuin.proginn.com/p/763bfbd62291):

2. Introduction to implementation method

Here are two implementation methods,
The first method is to use C + + to implement the reference blog, that is, using the gradient method to detect. The specific steps are as follows:

  1. Gaussian blur denoising is applied to the image, and gradient calculation is very sensitive to noise;
  2. Call Sobel function to calculate the gradient of image in X and Y directions;
  3. Call the convertScaleAbs function to limit the pixel values of X and Y gradient images to 0-255;
  4. Call addWeight function to fuse X and Y gradient images;
  5. Call the threshold function to binarize the fused image;
  6. The morphological processing method of first corrosion and then expansion is used to filter the non dirty area of the binary image;
  7. Call the findContours method to find the outline of the dirty area.

The second method is realized according to the idea of improving image contrast. The specific steps are as follows:
8. Gaussian blur denoising for image;
9. Using local histogram equalization method to improve image contrast;
10. Use OTSU binarization threshold method to roughly segment the dirty area;
11. Use the morphological operation of corrosion to filter out some non dirty areas on the binary image;
12. Call the findContours method to find the outline of the dirty area.

3. C + + source code implementation

#include <iostream>
#include <opencv2\imgcodecs.hpp>
#include <opencv2\core.hpp>
#include <opencv2\imgproc.hpp>
#include <opencv2\highgui.hpp>
#include <vector>

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());

	//Method 1: detect defects by gradient change
	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());

	//Method 2: local histogram equalization method was used to detect defects
	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;
}

4. Results

Test results of gradient method:

Detection results of local histogram equalization method:

summary

Compared with the gradient method, the local histogram equalization method needs to pay special attention to the selection of local window size parameters and threshold value parameters. I have tried many times to achieve better results. Once again, I realize the pain of traditional image processing. There are no general parameters applicable to all application examples, and different parameters should be configured in different scenes to achieve the desired results result.

reference resources

https://jishuin.proginn.com/p/763bfbd62291

Tags: C++ OpenCV image processing

Posted on Fri, 24 Sep 2021 03:40:00 -0400 by thefisherman