使用OpenCV 实现Halcon 的形状匹配 shapeMatch函数

 近期在尝试使用opencv替代Halcon,其中涉及到形状匹配。这是使用OpenCV。当然OpenCV也有自己的形状匹配函数。

//*********Models.h*************
#pragma once
#include 
using namespace std;
using namespace cv;
using namespace concurrency;

struct Grad
{
	float x;
	float y;
	float g_rec;
};


Mat rotateImg(Mat src, double degree)
{
	//计算旋转后的图像的宽高
	double angle = degree  * CV_PI / 180.; // 弧度
	double a = sin(angle), b = cos(angle);
	int width = src.cols;
	int height = src.rows;
	int width_rotate = int(height * fabs(a) + width * fabs(b));
	int height_rotate = int(width * fabs(a) + height * fabs(b));

	//定义旋转中心,并根据旋转角度计算旋转矩阵
	CvPoint2D32f center = cvPoint2D32f(width / 2, height / 2);
	Mat r = getRotationMatrix2D(center, degree, 1);

	//加入旋转中心的偏移
	r.at(0, 2) += (width_rotate - width) / 2;
	r.at(1, 2) += (height_rotate - height) / 2;

	//利用反射变换得到旋转后的图像
	Mat src_rotate = Mat::zeros(height_rotate, width_rotate, src.type());
	warpAffine(src, src_rotate, r, Size(width_rotate, height_rotate), CV_INTER_LINEAR | CV_WARP_FILL_OUTLIERS);
	return src_rotate;
}

struct Models
{
	int numLevels;
	vector angles;

	vector centers;
	vector> positions;
	vector> grads;
	vector pxCounts;
};


void createModels(Mat src, float angleStart, float angleEnd, float angleStep, int lowThresh, int highThresh, int numLevels, Models& models)
{

	//首先利用ppl多线程计算旋转模板
	int angleNum = (angleEnd - angleStart) / angleStep;
	int ksize = 2 * MIN(src.rows, src.cols) / 100 + 1;
	vector edgeImages(angleNum);
	vector gx(angleNum);
	vector gy(angleNum);
	vector g(angleNum);
	models.angles = vector(angleNum);
	mutex mt;
	parallel_for(0, angleNum, [&](int t)
	{
		Mat blurImage, edgeImage;
		float angle = angleStart + t * angleStep;
		models.angles[t] = angle;
		blur(src, blurImage, Size(ksize, ksize));
		Canny(blurImage, edgeImage, lowThresh, highThresh);
		Mat rotateEdgeImage = rotateImg(edgeImage, angle);
		Mat rotateBlurImage = rotateImg(blurImage, angle);

		Mat _gx, _gy;
		threshold(rotateEdgeImage, rotateEdgeImage, 10, 255, THRESH_BINARY);
		edgeImages[t] = rotateEdgeImage;
		Sobel(rotateBlurImage, _gx, CV_8U, 1, 0, 3);
		Sobel(rotateBlurImage, _gy, CV_8U, 0, 1, 3);
		_gx = _gx & rotateEdgeImage;
		_gy = _gy & rotateEdgeImage;
		gx[t] = _gx;
		gy[t] = _gy;
	});

	//然后对所有旋转后的模板进行下采样
	models.numLevels = numLevels;
	int totalNum = numLevels * models.angles.size();
	models.pxCounts = vector(totalNum);
	models.centers = vector(totalNum);
	models.grads = vector>(totalNum);
	models.positions = vector>(totalNum);

	parallel_for(0, totalNum, [&](int k)
	{
		
		int pryLevel = k / models.angles.size();
		int rotateIndex =  k % models.angles.size();
		Mat edgeImage = edgeImages[rotateIndex];
		Mat _gx = gx[rotateIndex];
		Mat _gy = gy[rotateIndex];
		for (size_t i = pryLevel; i > 0; i--)
		{
			pyrDown(_gx, _gx);
			pyrDown(_gy, _gy);
			pyrDown(edgeImage, edgeImage);
		}
		threshold(edgeImage, edgeImage, 10, 255, THRESH_BINARY);
		//统计每个旋转模板的非零点个数,用于后面计算匹配度
		models.pxCounts[k] = countNonZero(edgeImage);
		models.centers[k] = Point(edgeImage.cols / 2, edgeImage.rows / 2);
		int length = edgeImage.rows* edgeImage.cols;
		Mat g(edgeImage.rows, edgeImage.cols, CV_8UC1);
		vector pos;
		vector grads;
		Grad grad = { 0 };
		for (int i = 0; i < length; i++)
		{
			int y = i / edgeImage.cols;
			int x = i % edgeImage.cols;
			int igx = _gx.at(y, x);
			int igy = _gy.at(y, x);
			if (igx != 0 || igy != 0)
			{
				int ig = sqrt(float(igx*igx) + float(igy*igy));
				g.at(y, x) = ig;
				pos.push_back(Point(x, y));
				if (ig==0)
					grad.g_rec = 0;
				else
					grad.g_rec = 1./ig;
				grad.x = igx;
				grad.y = igy;
				grads.push_back(grad);
			}
			else continue;
		}
		models.positions[k] = pos;
		models.grads[k] = grads;
	});
}



void calcGradientImage(Mat src, int ksize, int lowThresh, int highThresh,int numLevels, Mat& gx, Mat& gy, Mat& g_rec)
{
	blur(src, src, Size(ksize, ksize));//模糊图像,降低噪点干扰
	Mat edge;
	Canny(src, edge, lowThresh, highThresh);//利用canny提取边缘
	Sobel(src, gx, CV_8U, 1, 0, 3); //计算X方向梯度
	Sobel(src, gy, CV_8U, 0, 1, 3); //计算y方向梯度
	gx = gx & edge;
	gy = gy & edge;

	for (size_t i = 0; i < numLevels; i++)
	{
		pyrDown(edge, edge);
		pyrDown(gx, gx);
		pyrDown(gy, gy);
	}

	int rows = edge.rows;
	int cols = edge.cols;
	int cn = edge.channels();
	int length = rows * cols * cn;
	// 计算梯度大小
	Mat g = Mat::zeros(edge.size(), CV_8U);
	g_rec = Mat::zeros(edge.size(), CV_32F);
	for (int i = 0; i < length; i++)
	{
		int y = i / cols;
		int x = i % cols;
		uchar _gx = gx.at(y, x);
		uchar _gy = gy.at(y, x);
		if (_gx != 0 || _gy != 0)
		{
			uchar _g = sqrt(uchar(_gx*_gx) + uchar(_gy*_gy));
			if (_g==0)
				g_rec.at(y, x) = 0;
			else
				g_rec.at(y, x) = 1./_g;
		}
	}
}

//*******main.cpp**************
#include
#include
#include
#include "Models.h"
#include 

void main()
{
	//提取模板的梯度与坐标
	int numLevels = 1;
	Mat tempImage = imread("img/model3.bmp", IMREAD_ANYCOLOR);
	cvtColor(tempImage, tempImage, COLOR_BGR2GRAY);
	Models model;
	createModels(tempImage, 0, 60, 10, 60, 150, 3, model);


	Mat src = imread("img/model3_src3.bmp", IMREAD_ANYCOLOR);
	cvtColor(src, src, COLOR_BGR2GRAY);

	Mat gx, gy, g_rec;
	calcGradientImage(src, 3, 60, 150, numLevels, gx, gy, g_rec);
	float minScore = 0.9;
	float norMinScore = minScore / model.pxCount;
	float greediness = 0.7;
	float normGreediness = ((1 - greediness * minScore) / (1 - greediness)) / model.pxCount; // precompute greedniness 
	

	clock_t start = clock();
	int parallel_num = gx.rows * gx.cols;
	float resultScore =0;
	Point resultPoint(-1,-1);
	mutex mt;
	parallel_for(0, parallel_num, [&](int k)
	{
		int srcx = k % gx.cols;
		int srcy = k / gx.cols;
		float partialSum = 0;
		int numCount = 0;
		float partialScore = 0;
		int _parallel_num = model.pxCount;
		int sumCount;
		mutex _mt;
		parallel_for(0, _parallel_num, [&](int m)
		{
			int curx = srcx + model.pos[m].x - model.center.x;
			int cury = srcy + model.pos[m].y - model.center.y;
			if (curx > 0 && curx < gx.cols - 1 && cury > 0 && cury < gx.rows - 1)
			{
				float Gsx = gx.at(cury, curx);
				float Gsy = gy.at(cury, curx);
				float Gs_rec = g_rec.at(cury, curx);
				float Gtx = model.grad[m].x;
				float Gty = model.grad[m].y;
				float Gt_rec = model.grad[m].g_rec;
				_mt.lock();
				if ((Gtx != 0 || Gty != 0) && (Gsx != 0 || Gsy != 0))
					partialSum = partialSum + (Gsx*Gtx + Gsy*Gty)*Gt_rec*Gs_rec;
				numCount ++;
				partialScore = partialSum / numCount;
				_mt.unlock();
			}
		});

		if (partialScore > resultScore)
		{
			mt.lock();
			resultScore = partialScore;
			resultPoint.x = srcx;
			resultPoint.y = srcy;
			mt.unlock();
		}
	});

	clock_t end = clock();
	cout << "time cost = " << end - start << endl;

	resultPoint.x = pow(2, numLevels)* resultPoint.x;
	resultPoint.y = pow(2, numLevels)* resultPoint.y;

	circle(src, resultPoint, 30, Scalar(255), 3);
	imshow("src", src);
	imwrite("result1.bmp", src);
	waitKey(0);
}

 

 

你可能感兴趣的:(opencv,计算机视觉,cv)