Thinking analysis and code

Thought 1: Use the findContours function to set the contours to the most external RETR_EXTERNAL, and find that the result is still all contours. The edge of the bottle is distorted due to the effect of binarization. The edge of the bottle is distorted due to binarization.

The original image Direct canny to get the graph Canny diagram after binarization

Idea 3: First binarization, then close operation, then fill small black area, and then corrosion operation, get a mask that is one circle smaller than the original bottle, and then remove the contour of the picture after Canny (the mask area is black), so maybe it is ok.

Results: It was indeed much better, but there were no results for two pictures. Observation: One was because there was a problem with the selected point of hole filling, and another debug found that there was also a problem with hole filling. For the time being, the big brown bottle is treated differently. Because his otsu binarization results are a little peculiar. The verification results showed that the effect was indeed better than that after the direct template matching canny, but there were still misjudgments. The misjudgments mainly occurred in identifying a part of the larger bottle as a small bottle, so it was necessary to limit from the number of pixels of the bottle, and the rule was that the categories with fewer pixels than the template bottle could not be identified. What to do: Summarize the bottle pixel value in the template graph, and obtain the bottle pixel value of each test graph (the number of pixels using MASK2 here needs to be screened by the connected domain to determine whether it is smaller than the actual bottle). Background color (black or white) starting point for flooding (the background is filled, usually (0,0)) output: filled binary graph

void My_hole_filling(Mat& srcImage, Mat& dstImage,int color,Point &startPoint)
{
	// Floodfill from point (0,0
	/*int x = startPoint.x; int y = startPoint.y; * /
	//srcImage.at<char>(x, y)
	if ( color== 255)		// Background is white
	{
		srcImage = ~srcImage;
	}
	Mat im_floodfill = srcImage.clone();
	floodFill(im_floodfill, startPoint, Scalar(255));
	/ / 255
	// Invert floodfilled image
	Mat im_floodfill_inv;
	bitwise_not(im_floodfill, im_floodfill_inv);

	// Combine the two images to get the foreground. Get prospects
	dstImage = (srcImage | im_floodfill_inv);
	dstImage = ~dstImage;
}
Copy the code

Function to get the outline of the outside of the bottle

Input: original grayscale canny threshold output: contour masK2

void get_external_Contours_function(Mat& srcImage, Mat& dstImage,Mat& dstmask, int canny_thred)
{
	// Fuzzy denoising
	blur(srcImage, srcImage, Size(5.5));
	Mat mask;
	// Otsu binarization
	threshold(srcImage, mask, 100.255, THRESH_OTSU);
	/ / closed operation
	int Abs_offset = 2;
	Mat element = getStructuringElement(MORPH_ELLIPSE, Size(Abs_offset * 2 + 1, Abs_offset * 2 + 1), Point(Abs_offset, Abs_offset));	// Return the kernel matrix
	morphologyEx(mask, mask, MORPH_CLOSE, element);
	// The hole is filled
	Point startpoint = Point(40.40);
	My_hole_filling(mask, mask, 0, startpoint);
	// Shrink the mask one circle
	Mat mask2;
	Mat element_erode = getStructuringElement(MORPH_ELLIPSE, Size(Abs_offset * 2 + 1, Abs_offset * 2 + 1), Point(Abs_offset, Abs_offset));	// Return the kernel matrix
	morphologyEx(mask, mask2, MORPH_DILATE, element_erode);
	dstmask = mask2;
	//mask2 is our mask
	// Canny detection is performed on the gray image after the blur
	Canny(srcImage, dstImage, canny_thred, canny_thred * 2.3);
	// Set all white pixels in mask2 to black
	int height = dstImage.rows;
	int width = dstImage.cols;
	for (int j = 0; j < height; j++)
	{
		for (int i = 0; i < width; i++)
		{
			if (mask2.at<uchar>(j, i) == 0 && dstImage.at<uchar>(j, i) == 255)
			{
				dstImage.at<uchar>(j, i) = 0; }}}}Copy the code

Connected domain test code to find the number of pixels of the largest connected domain in MASK2

int main(a)
{
	cv::Mat srcMat = imread("D:\ opencv_picture_test\ test\ test\ 10+ spot.jpg".1);
	// CV ::Mat srcMat = imread("D: opencv_picture_test ", 1); // CV ::Mat srcMat = imread("D: opencv_picture_test ", 1);
	// CV ::Mat srcMat = imread("D: opencv_picture_test ", 1); // CV ::Mat srcMat = imread("D: opencv_picture_test ", 1);
	Mat dstMat;
	Mat mask2;
	int thred = 40;
	// Convert to grayscale
	cvtColor(srcMat, dstMat, COLOR_BGR2GRAY);
	get_external_Contours_function(dstMat, dstMat, mask2, thred);
	// Select the largest connected domain and invert mask2
	mask2 = 255 - mask2;
	Mat lableMat;
	Mat statsMat;
	Mat centerMat;
	int nComp = cv::connectedComponentsWithStats(mask2,
	lableMat,
	statsMat,
	centerMat,
	8,
	CV_32S);
	// Find the one with the most connected pixels and record the number of pixels
	int max_pixels = 0;
	int max_pixels_label = 0;
	if (nComp == 1) max_pixels = statsMat.at<int> (1.4);
	else
	{
		// Find the connected domain flag with the most pixels
		vector<int > pixels_nums;
		for (int i = 1; i < nComp; i++)
		{
			pixels_nums.push_back(statsMat.at<int>(i, 4));	// Put the connected domain area into the vector
		}
		// Find the maximum value and return its position in the vector
		auto maxPosition = max_element(pixels_nums.begin(), pixels_nums.end());
		max_pixels = *maxPosition;
		max_pixels_label = maxPosition - pixels_nums.begin();
	}
	cout <<"Number of connected domains (counting background)="<< nComp << endl;
	cout << "max_pixels = " << max_pixels << endl;
	cout << endl;
	imshow("die_on_chip", dstMat);
	waitKey(0);
	return 0;
}
Copy the code

Results:

JPG 0 max_pixels = 14347 D:/opencv_picture_test/ opencv_picture_test D:/opencv_picture_test/ Visual project resize folder/test folder/test image \10+ pit.jpg 1 max_pixels = 13617 JPG 2 Max_Pixels = 13368 D:/opencv_picture_test/ Opencv_picture_test/Opencv_picture_test D:/opencv_picture_test/ Test folder/test folder/test image \10.jpg 3 max_Pixels = 14335 D:/opencv_picture_test/ Test folder/test folder/test image \10_2.jpg 4 max_pixels = 12677 D:/opencv_picture_test/ Test folder/test folder/test image \11.jpg 5 max_Pixels = 11718 D:/opencv_picture_test/ Test folder/test folder/test image \12.jpg 6 max_Pixels = 12413 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \13+ explosion.jpg 7 max_Pixels = 8002 D:/opencv_picture_test/ Test folder \14.jpg 8 max_pixels = 8870 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \2+ texture. JPG 9 max_pixels = 18144 D:/opencv_picture_test/ test folder/test folder/test image \3.jpg 10 max_pixels = 16658 D:/opencv_picture_test/ Test folder/test folder/test image \3_2.jpg 11 max_pixels = 15836 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \4+ thick.jpg 12 max_Pixels = 17636 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \4+ thick bottom 2.jpg 13 max_pixels = 16468 JPG 14 max_pixels = 15504 D:/opencv_picture_test/ visual project resize D:/opencv_picture_test/ JPG 15 max_pixels = 19443 D:/opencv_picture_test/ Opencv_picture_test / 5+ shoulder thinness.jpg 16 max_Pixels = 18623 D:/opencv_picture_test/ Image folder/test folder/test image \5+ bubble.jpg 17 max_Pixels = 19209 JPG 18 max_pixels = 20063 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \5+ explosion D:/opencv_picture_test/ Image folder/test image folder/test image \5+ wrinkles bubble.jpg 19 max_Pixels = 19552 D:/opencv_picture_test/ test folder/test folder/test image \6.jpg 20 max_pixels = 14913 D:/opencv_picture_test/ Test folder/test folder/test image \6_2.jpg 21 max_pixels = 15616 D:/opencv_picture_test/ Test folder/test folder/test image \ 63.jpg 22 max_pixels = 15653 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \7+ thick.jpg 23 max_Pixels = 15158 D:/opencv_picture_test/ Visual project resize folder/test folder/test image \7+ thick background 2.jpg 24 max_pixels = 13383 D:/opencv_picture_test/ Test folder/test folder/test image \8.jpg 25 max_pixels = 14950 D:/opencv_picture_test/ Test folder/test folder/test image \8_2.jpg 26 max_pixels = 15271 D:/opencv_picture_test/ Test folder/test folder/test image \9.jpg 27 max_pixels = 13192

Count the number of pixels per bottle (take the minimum and subtract 2000 as standard)

Judge sequence The number of pixels
1 No statistical
2 18144
3 15836
4 15504
5 18623
6 14913
7 13383
8 14950
9 13192
10(A) 12677
11(B) 11718
12 © 12413
13(D) 8002
14(E) 8870

Implementation idea: after obtaining the test graph, get the number of masK2 pixels. Compare to elements in pixels_num_criterion[]. Create a new vector: prepare_template_num and match it with the template whose sequence number is test_mask_pxiels>=pixels_num_criterion[I]-pixels_num_sub. There seems to be something wrong with the code, save a file and check it tomorrow:

// Convert the test graph to a function that matches the template graph
// Input: test graph Canny operator threshold output: outer contour graph
// Return value: the number of bottle pixels in the test graph mask2
int test_covertTo_Outer_contour(Mat& srcImg, Mat& dstImg, int thred)
{
	// Here we do batch processing
	Mat mask2;
	//int thred = 40;
	// Convert to grayscale
	cvtColor(srcImg, dstImg, COLOR_BGR2GRAY);
	get_external_Contours_function(dstImg, dstImg, mask2, thred);
	// Select the largest connected domain and invert mask2
	mask2 = 255 - mask2;
	Mat lableMat;
	Mat statsMat;
	Mat centerMat;
	int nComp = cv::connectedComponentsWithStats(mask2,
		lableMat,
		statsMat,
		centerMat,
		8,
		CV_32S);
	// Find the one with the most connected pixels and record the number of pixels
	int max_pixels = 0;
	int max_pixels_label = 0;
	if (nComp == 1) max_pixels = statsMat.at<int> (1.4);
	else
	{
		// Find the connected domain flag with the most pixels
		vector<int > pixels_nums;
		/ / 0 is background
		for (int i = 1; i < nComp; i++)
		{
			pixels_nums.push_back(statsMat.at<int>(i, 4));	// Put the connected domain area into the vector
		}
		// Find the maximum value and return its position in the vector, and then +1 is required for the position in the connected field label
		auto maxPosition = max_element(pixels_nums.begin(), pixels_nums.end());
		max_pixels = *(maxPosition);
		max_pixels_label = (maxPosition - pixels_nums.begin() + 1);
	}
	return max_pixels;
}
int main(a)
{
	// Change the console font color
	system("color 02");
	/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * [0] the test folder path and templates folder path * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
	// Get the test folder path and template folder path
	cv::String path_test = "D:/opencv_picture_test/ opencv_picture_test/";        
	cv::String path_template = "D:/opencv_picture_test/ visual project resize folder /template folder /template outline /";    
	cout << "Address obtained successfully" << endl;
	/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 【 1 】 to load the template image * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / /
	// Create a template vector
	vector<Mat>tempMat;
	// Insert template elements
	Mat srcImage;
	std::vector<cv::String> temp_filenames;
	cv::glob(path_template, temp_filenames);                 // OpencV is a very useful function to read the file name in the specified path
	for (int i = 0; i < temp_filenames.size(a); i++) { srcImage = cv::imread(temp_filenames[i],0);
		tempMat.push_back(srcImage);
		cout << temp_filenames[i] << endl;
	}
	// Get the number of templates
	int tempMat_Nums = tempMat.size(a);/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * (2) load testing image * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / /
	// Create a test vector
	vector<Mat>testMat;
	// Insert the test element
	std::vector<cv::String> test_filenames;
	cv::glob(path_test, test_filenames);                 // OpencV is a very useful function to read the file name in the specified path
	for (int i = 0; i < test_filenames.size(a); i++) { srcImage = cv::imread(test_filenames[i]);
		testMat.push_back(srcImage);
		//cout << test_filenames[i] << endl;
	}
	// Get the number of test graphs
	int testMat_Nums = testMat.size(a);/ / * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * [3] for each test pattern template matching * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / /
	for (int j = 0; j < testMat_Nums; j++) { cout <<"The first"<< j <<"The test of a test picture."<< endl;
		Mat resultMat;
		Mat CompareMat;
		Mat dispMat;
		// Convert the test diagram to a type that matches the template diagram
		int test_mask_pxiels = 0;
		test_mask_pxiels=test_covertTo_Outer_contour(testMat[j], CompareMat, 40);
		cout << "test_mask_pxiels" << test_mask_pxiels << endl;
		int match_method = TM_CCORR_NORMED;		// This parameter is found to be good after trial and error.
		// Use each template to match the test graph and find the best match for each result, storing the value in the vector
		vector<double>goodval;
		vector<Point>goodlock;
		int matchnum = 0;
		Point matchLoc;
		vector<int>prepare_template_num;
		cout << "Possible template number" << endl;
		for (int i = 0; i <14; i++) {if (test_mask_pxiels >= (pixels_num_criterion[i] - pixels_num_sub))
			{
				// Import the template number that matches the rule into the vector
				prepare_template_num.push_back(i);
				cout << i <<"";
			}
		}
		cout << endl;
		for (int x = 0; x < prepare_template_num.size(a); x++) { cout << prepare_template_num[x] <<"";
		}
		cout << endl;
		for (int i = 0; i < tempMat_Nums; i++) {// The larger the positive value is, the higher the matching degree will be, and the larger the negative value will be, the greater the difference between the image will be. However, if the image has no obvious features (i.e. the pixel value in the image is close to the average value), the closer the returned value will be to 0.
			matchTemplate(CompareMat, tempMat[i], resultMat, match_method);
			// Do not normalize, because the best value after normalization of different templates is 1, which cannot be compared
			//normalize(resultMat, resultMat, 0, 1, NORM_MINMAX, -1, Mat()); / / normalization
			double minVal; double maxVal; Point minLoc; Point maxLoc;	// Define the maximum and minimum values and their position variables
			minMaxLoc(resultMat, &minVal, &maxVal, &minLoc, &maxLoc, Mat());	// Find the maximum and minimum matching values from the result matrix and determine their positions
			// For both methods SQDIFF and SQDIFF_NORMED, smaller values have higher matching results
			// For the rest of the method, the larger the value, the better the match
			if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
			{
				// Set the likelihood of a template that does not meet the pixel count rule to 1
				vector<int>::iterator result = find(prepare_template_num.begin(), prepare_template_num.end(), i); // Check to see if this template wants a template that matches the rule
				if (result == prepare_template_num.end()) / / not found
				{
					minVal = 1;
				}
				goodlock.push_back(minLoc);
				goodval.push_back(minVal);
			}
			else
			{
				// Set the probability of a template that does not meet the pixel count rule to 0
				vector<int>::iterator result = find(prepare_template_num.begin(), prepare_template_num.end(), i); // Check to see if this template wants a template that matches the rule
				if (result == prepare_template_num.end()) / / not found
				{
					maxVal = 0;
				}
				goodlock.push_back(maxLoc);
				goodval.push_back(maxVal);
			}
			show_probability(i, maxVal);
			//cout << i << " " << maxVal << endl;
		}
		// Find the best group of goodval
		if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
		{
			auto goodPosition = min_element(goodval.begin(), goodval.end());
			matchnum = distance(begin(goodval), goodPosition);
		}
		else
		{
			auto goodPosition = max_element(goodval.begin(), goodval.end());
			matchnum = distance(begin(goodval), goodPosition);
		}
		show_text(matchnum, test_filenames[j]);
		matchLoc = goodlock[matchnum];
		testMat[j].copyTo(dispMat);
		// Draw a box of the same size as the template around the best match point
		rectangle(dispMat, matchLoc, Point(matchLoc.x + tempMat[matchnum].cols, matchLoc.y + tempMat[matchnum].rows), Scalar::all(255), 2.8.0);
		namedWindow("testMat", WINDOW_NORMAL);//WINDOW_NORMAL allows users to scale freely
		imshow("testMat", dispMat);
		waitKey(30);
	}
	return 0;
}
Copy the code