Template matching program

Posted by shanejeffery86 on Tue, 23 Nov 2021 07:08:35 +0100

 

original intention
Sure enough, halcon is easy to use, and people become lazy. There is a project that needs to write a shape matching program, so they use it to practice. The program is not very complex, and the speed feels like find in halcon_ scaled_ shape_ There is still a gap in the model. At present, I don't know how to further improve it. That's it for the time being. If the big guys have any ideas for improvement, they can comment on it, so as to make continuous progress.

thinking
The main idea is based on the coincidence degree of scattered points. Although openCV has its own matchTemplate and matchShape functions, it internally calculates the traversal pixel moment. Because there will be interference edges in the detection image, these functions are not suitable for shape matching (local pixel set matching). Of course, the running speed of these functions is still very fast. At present, I don't know how to achieve the operation speed of the same order of magnitude.

There are several problems in the implementation of the algorithm:

1. Generate rotation template
openCV does not have its own rotation function. You need to generate the rotation matrix through getRotationMatrix2D, and then use warpAffine for affine transformation. However, the graphics generated by warpAffine will be automatically cropped, which will cause some problems, so it needs to be modified slightly (see imrotate function for details).

2. The problem of traversing edge points or convolution
Because openCV and halcon are particularly slow to take pixels, in contrast, they can only traverse with filter2D. It has to be said that the hardware acceleration library provided by openCV is still efficient.

3. Traversal operation speed is too slow
The source code of some openCV functions is internally called by hal library, and the user has no way to modify it, so there is no way to modify the algorithm inside the function. Try parallel acceleration with ppl library, and the effect is OK.

Program instructions
(1) The number of pyramid layers PyrLevel shall be modified first. The larger the number of layers, the faster the search speed. There are scaled edge images in the main function. The thresholds of PyrLevel and Canny functions shall be modified according to whether the edges are clear to ensure that the edges of the image to be detected and the template image are clear and complete.
(2) If it cannot be found, reduce minScore appropriately. The closer it is to 0, the greater the similarity tolerance of the search, but the slower the running speed.

Function fragment

/*************************************************
Function:       //	CreateScaledShapeModel
Description:    //	Create template set
Input:          //	Template:Edge map of template picture
					PyrLevel:Pyramid reduction layers
					AngleStart,AngleExtent,AngleStep:Rotation angle candidate parameters
					ScaleMin,ScaleMax,ScaleStep:Scale candidate parameters
Output:         //	pModelImageSet,pModelPointSet,pScaleSet,pAngleSet:Template set pointer
Return:         //  nothing
Others:         //	
*************************************************/
void CreateScaledShapeModel(Mat Template, int PyrLevel, int AngleStart, int AngleExtent, int AngleStep, float ScaleMin, float ScaleMax, float ScaleStep, \
	vector<Mat>* pModelImageSet, vector<int>* pModelPointSet, vector<float>* pScaleSet, vector<float>* pAngleSet)
{
	vector<Mat> ModelImageSet;
	vector<int> ModelPointSet;
	vector<float> AngleSet;
	vector<float> ScaleSet;
	while (ScaleMin <= ScaleMax)
	{
		cout << ScaleMax << endl;
		ScaleSet.push_back(ScaleMax);
		ScaleMax -= ScaleStep;
	}
	while (AngleStart <= AngleExtent)
	{
		cout << AngleExtent << endl;
		AngleSet.push_back(AngleExtent);
		AngleExtent -= AngleStep;
	}
	//Template generation	
	for (int level = 0; level <= PyrLevel; level++)
	{
		Mat pyrmodelImage = Template;
		for (int i = 0; i < level; i++)
		{
			pyrDown(pyrmodelImage, pyrmodelImage);
		}
		//zoom
		for (int i = 0; i < ScaleSet.size(); i++)
		{
			Mat scaleImage;
			resize(pyrmodelImage, scaleImage, Size(round(pyrmodelImage.cols*ScaleSet[i]), round(pyrmodelImage.cols*ScaleSet[i])), 0, 0, INTER_LINEAR);
			//rotate
			for (int j = 0; j < AngleSet.size(); j++)
			{
				Mat rotateImage;
				imrotate(scaleImage, rotateImage, AngleSet[j]);
				//threshold(rotateImage, rotateImage, 1, 255, 0);
				Canny(rotateImage, rotateImage, 50, 100, 3, false);
				rotateImage /= 255;
				//imshow("rotation", rotateImage);
				//imwrite("rotate. jpg", rotateImage);
				//waitKey(0);
				ModelImageSet.push_back(rotateImage);
				int pointNum = 0;
				for (int i = 0; i < rotateImage.cols; i++)
				{
					for (int j = 0; j < rotateImage.rows; j++)
					{
						if (rotateImage.at<uchar>(Point(i, j)) != 0)
							pointNum++;
					}
				}
				ModelPointSet.push_back(pointNum);
				rotateImage.release();
			}
			scaleImage.release();
		}
	}
	*pModelImageSet = ModelImageSet;
	*pModelPointSet = ModelPointSet;
	*pAngleSet = AngleSet;
	*pScaleSet = ScaleSet;
}



/*************************************************
Function:       //	FindScaledShapeModel
Description:    //	Search for graphics similar to the template in a picture
Input:          //	Image:Pictures to be tested
					ModelImageSet,ModelPointSet,ScaleSet,AngleSet:Template set
					PyrLevel:Pyramid reduction layers
					MinScore:Screening similarity threshold		
Output:         //	pRow,pCol,pScale,pAngle,pScore:Outputs a pointer to the set of element parameters that match
Return:         //  nothing
Others:         //	You need to call CreateScaledShapeModel before using this function
*************************************************/
void FindScaledShapeModel(Mat Image, vector<Mat> ModelImageSet, vector<int> ModelPointSet, vector<float> ScaleSet, vector<float> AngleSet, int PyrLevel, float MinScore,\
	vector<int>* pRow, vector<int> * pCol, vector<float>* pScale, vector<float>* pAngle, vector<float>* pScore)
{
	mutex mt;
	Mat modelImage = ModelImageSet[0];
	vector<int> Row;
	vector<int> Col;
	vector<float> Scale;
	vector<float> Angle;
	vector<float> Score;
	bool findFlag = false;
	//Pyramid hierarchical matching
	for (int level = PyrLevel; !findFlag && level >= PyrLevel; level--)
	{		
		Mat pyrsrcImage = Image;
		for (int i = 0; i < level; i++)
		{
			pyrDown(pyrsrcImage, pyrsrcImage);
		}

		int kernSize = floor(sqrt(min(pyrsrcImage.rows / 100, pyrsrcImage.cols / 100)));		
		Mat kern = Mat::ones(2 * kernSize + 1, 2 * kernSize + 1, CV_8U);
			
		Mat blurImage;
		filter2D(pyrsrcImage, blurImage, pyrsrcImage.depth(), kern);		
		//imshow("gelatinized original", blurImage);
		//moveWindow("paste original", 0, 0);
		//waitKey(10);

		Mat tempblurImage;
		blurImage.convertTo(tempblurImage, CV_8U);
		tempblurImage /= 255;
		int parallelnum = ScaleSet.size() *AngleSet.size();

		parallel_for(0, parallelnum, [&](int k)	
		{
			Mat scoreImage(tempblurImage.size(), CV_16U);
			Mat tempmodelImage = ModelImageSet.at(ModelImageSet.size()- 1 - k);
			int temppointNum = ModelPointSet.at(ModelPointSet.size() - 1 - k);
			float max_score = 0;		
			/*imshow("Template ", tempmodelImage);
			resizeWindow("Template ", tempmodelImage.rows, tempmodelImage.cols);		
			moveWindow("Template ", blueimage.cols, 0);
			waitKey(10);*/
			//double start = static_cast<double>(getTickCount());

			filter2D(tempblurImage, scoreImage, scoreImage.depth(), tempmodelImage);
			//double time = ((double)getTickCount() - start) / getTickFrequency();
			//Cout < < time used is: < < time < < seconds < < endl;
			mt.lock();
			while (1)
			{
				double v_min, v_max;
				int idx_min[2] = { 255,255 }, idx_max[2] = { 255, 255 };
				minMaxIdx(scoreImage, &v_min, &v_max, idx_min, idx_max);
				scoreImage.at<ushort>(idx_max[0], idx_max[1]) = 0;
				
				max_score = v_max / temppointNum;

				//Cout < < level < < layer < < K + 1 < < score: "< max_score < < endl;
				if (max_score > MinScore)
				{
					float scale = ScaleSet[ScaleSet.size() - 1 - (k) / AngleSet.size()];
					float angle = AngleSet[AngleSet.size() - 1 - (k) % AngleSet.size()];
					//int selectx = (idx_max[1] - (tempmodelImage.cols - 1) / 2)*pow(2, level);
					//int selecty = (idx_max[0] - (tempmodelImage.rows - 1) / 2)*pow(2, level);
					//int pyrselectx = idx_max[1] - tempmodelImage.cols / 2;
					//int pyrselecty = idx_max[0] - tempmodelImage.rows / 2;
					Row.push_back(idx_max[1] * pow(2, level));
					Col.push_back(idx_max[0] * pow(2, level));
					Scale.push_back(scale);
					Angle.push_back(angle);
					Score.push_back(max_score);
					//cout << Point(selectx, selecty) << " " << Point(pyrselectx, pyrselecty) << endl;
					//rectangle(blurImage, Rect(pyrselectx, pyrselecty, tempmodelImage.cols, tempmodelImage.rows), 255, 2, 8);
					//rectangle(Image, Rect(selectx, selecty, modelImage.cols / scale, modelImage.rows / scale), 255, 2, 8);
					//imshow("zoom position", blueimage);
					//imshow("original position", Image);
					//waitKey(0);
					//findFlag = true;				
				}		
				else break;
			}
			tempmodelImage.release();
			scoreImage.release();
			mt.unlock();
		}
		);
		for (int m = 0; m < Row.size() ; m++)
		{
			for (int n = m+1; n < Row.size() ; n++)
			{
				if (abs(Row[n] - Row[m])<modelImage.rows*0.3  && abs(Col[n] - Col[m])< modelImage.cols*03)
				{
					if (Score[n] < Score[m])
					{
						Row.erase(Row.begin() + n);
						Col.erase(Col.begin() + n);
						Scale.erase(Scale.begin() + n);
						Angle.erase(Angle.begin() + n);
						Score.erase(Score.begin() + n);
						n--;
					}
					else if (Score[n] >= Score[m])
					{
						swap(Row[m], Row[n]);
						swap(Col[m], Col[n]);
						swap(Scale[m], Scale[n]); 
						swap(Angle[m], Angle[n]); 
						swap(Score[m], Score[n]);
						Row.erase(Row.begin() + n);
						Col.erase(Col.begin() + n);
						Scale.erase(Scale.begin() + n);
						Angle.erase(Angle.begin() + n);
						Score.erase(Score.begin() + n);
						n = m ;
					}
				
				}
			}
		}
		*pRow = Row;
		*pCol = Col;
		*pScale = Scale;
		*pAngle = Angle;
		*pScore = Score;
	}
}
void imrotate(Mat& img, Mat& newIm, double angle) 
{
	int heightNew = int(img.cols*fabs(sin(angle*3.14 / 180)) + img.rows * fabs(cos(angle*3.14 / 180)));
	int widthNew = int(img.rows*fabs(sin(angle*3.14 / 180)) + img.cols * fabs(cos(angle*3.14 / 180)));
	int len = max(img.cols, img.rows);
	Point2f pt(len / 2., len / 2.);
	Mat r = getRotationMatrix2D(pt, angle, 1.0);
	r.at<double>(0, 2) += (widthNew - img.cols) / 2;
	r.at<double>(1, 2) += (heightNew - img.rows) / 2;	
	warpAffine(img, newIm, r, Size(widthNew, heightNew), INTER_LINEAR, BORDER_REPLICATE);
}

--------
Original link: https://blog.csdn.net/sillykog/article/details/83116793

Topics: OpenCV Computer Vision machine vision