基于openCV的形状模版匹配

初衷

果然halcon用顺手了,人就变懒了,正好有项目需要自己写个形状匹配的程序,就拿来练练手,程序不是很复杂,速度上感觉和halcon里面find_scaled_shape_model还是有差距,目前也不知道如何进一步改进,暂时就先这样了。大佬们如果有什么改进的想法可以评论一下,这样才能不断进步。

思路

主要的想法还是基于散点的重合度,虽然openCV自带matchTemplate和matchShape函数,但其内部是进行遍历像素矩计算,由于检测图中会有干扰边缘的存在,所以这些函数对于形状匹配(局部像素点集匹配)来说是不适合的。当然这些函数运行速度还是非常快的,目前我也不知道用什么办法可以达到同数量级的运算速度。

算法实现过程中主要遇到了几个问题:

1.生成旋转模版的问题

openCV没有自带的旋转函数,需要通过getRotationMatrix2D生成旋转矩阵,再用warpAffine进行仿射变换。但是warpAffine生成的图形会自动进行裁剪,会带来一些问题,因此需要稍微修改(详情见imrotate函数)。

2.遍历边缘点还是进行卷积的问题

由于openCV和halcon对于取像素点的操作都特别慢,所以对比之下只能采取用filter2D进行遍历,不得不说openCV自带的硬件加速库还是高效。

3.遍历运算速度过慢

openCV一些函数的源代码在内部还是进行了hal库的调用,用户没有办法修改,因此没有办法在函数内部进行算法修改。尝试用ppl库进行并行加速,效果还可以。

程序使用说明

(1)优先修改金字塔层数PyrLevel,层数越大,搜索速度越快,主函数中有缩放后的边缘图片,根据边缘是否清晰来修改PyrLevel和Canny函数的阈值,保证待检测图和模版图片的边缘足够清晰完整。
(2)如果找不到,适当减小minScore,越接近0,搜索相似容忍度越大,但运行速度同时也越慢。

函数片段

具体代码工程见
https://download.csdn.net/download/sillykog/10727775

/*************************************************
Function:       //	CreateScaledShapeModel
Description:    //	创建模版集
Input:          //	Template:模版图片的边缘图
					PyrLevel:金字塔缩小层数
					AngleStart,AngleExtent,AngleStep:旋转角度候选参数
					ScaleMin,ScaleMax,ScaleStep:缩放比例候选参数
Output:         //	pModelImageSet,pModelPointSet,pScaleSet,pAngleSet:模版集指针
Return:         //  无
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;
	}
	//模版生成	
	for (int level = 0; level <= PyrLevel; level++)
	{
		Mat pyrmodelImage = Template;
		for (int i = 0; i < level; i++)
		{
			pyrDown(pyrmodelImage, pyrmodelImage);
		}
		//缩放
		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);
			//旋转
			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("旋转", rotateImage);
				//imwrite("旋转.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:    //	在一张图片中搜索与模版相似的图形
Input:          //	Image:待检测图片
					ModelImageSet,ModelPointSet,ScaleSet,AngleSet:模版集
					PyrLevel:金字塔缩小层数
					MinScore:筛选相似度阈值		
Output:         //	pRow,pCol,pScale,pAngle,pScore:输出匹配到的元素参数集合的指针
Return:         //  无
Others:         //	使用该函数前需要先调用CreateScaledShapeModel
*************************************************/
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;
	//金字塔分层匹配
	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("糊化原图", blurImage);
		//moveWindow("糊化原图", 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("模版", tempmodelImage);
			resizeWindow("模版", tempmodelImage.rows, tempmodelImage.cols);		
			moveWindow("模版", blurImage.cols,0);
			waitKey(10);*/
			//double start = static_cast<double>(getTickCount());

			filter2D(tempblurImage, scoreImage, scoreImage.depth(), tempmodelImage);
			//double time = ((double)getTickCount() - start) / getTickFrequency();
			//cout << "所用时间为:" << time << "秒" << 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 << "层" << "第" << k + 1 << "个成绩:" << 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("缩放图位置", blurImage);
					//imshow("原图位置", 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);
}

评论 58
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值