近期在尝试使用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);
}