原模板匹配方法中,是无法进行任意角度和旋转匹配的,但我们在实际的使用中,模板图像的位置和方向都是不确定的,因此,就需要我们对模板图像进行预处理,处理后在进行模板匹配。
基本处理方法如下:
- 模板图像读取
- 进行模糊
- 绘制轮廓
- 形态学处理
- 提取轮廓
- 获取当前位置角度
- 图像旋转。
- 原图读取
- 从4个角度进行模板匹配(0°,90°,180°,270°)
- 记录每个角度的匹配值
- 进行后续操作。
1:原图如下所示:
2:模板图像如下所示:
3:思路:在模板图像中,抠出图像,并且找到当前的旋转角度,将其恢复为正向0°,在进行对其模板匹配,给出匹配结果。处理过程图像如下:
分别是将其调整为正向后,0°,90°,180°,270°的旋转匹配,同时记录匹配度的值。
源码图下:
public list<point2d> getresult(mat tempimage, string srcimgpath, out double robotrotateangel, out double arcval)
{
list<point2d> point2ds = new list<point2d>();
robotrotateangel = 999;
arcval = 0;
try
{
double rotangel;
point2f centerpoint;
rect rect;
mat tempmat = settemplateimage1(tempimage, out rotangel, out rect, out centerpoint);
bool matchret = getmatchret1(srcimgpath, tempmat, rotangel, out robotrotateangel, out arcval);
if (!matchret)
{
//图像识别失败
return null;
}
}
catch (exception ex)
{
}
return point2ds;
}
public mat settemplateimage1(mat image, out double rotangel, out rect rect, out point2f centerpoint)
{
rotangel = -1;
rect = new rect();
centerpoint = new point2f();
mat blurimg = gaussblurimage(image);//ip.medblurimage(image);//
mat cannyimg = new mat();
cv2.canny(blurimg, cannyimg, 30, 90);
mat morphimg = morphimage(cannyimg);
//cv2.imshow("morphimg1", morphimg);
mat roiimage = getriooutline(morphimg, image, out rotangel, out rect, out centerpoint);
//cv2.imshow("roiimage", roiimage);
mat rotimg = imagerotate(roiimage, rotangel, rect);
//cv2.imshow("rotimg", rotimg);
mat mat = getroi(rotimg, rect);
cv2.imshow("mat", mat);
return mat;
}
/// <summary>
/// 高斯 模糊
/// </summary>
/// <param name="img"></param>
/// <returns></returns>
public mat gaussblurimage(mat image)
{
mat gaussianimage = new mat();
cv2.gaussianblur(image, gaussianimage, new opencvsharp.size(3, 3), 3, 3);//尽可能多的去除杂质
return gaussianimage;
}
/// <summary>
/// 图像形态学操作
/// </summary>
/// <param name="image"></param>
/// <returns></returns>
public mat morphimage(mat image)
{
mat morphimage = new mat();
mat kernel = cv2.getstructuringelement(morphshapes.rect, new opencvsharp.size(3, 3), new point(-1, -1));
cv2.morphologyex(image, morphimage, morphtypes.gradient, kernel, new point(-1, -1), 2);
return morphimage;
}
/// <summary>
/// 提取图像轮廓
/// </summary>
/// <param name="morphimage">形态学图片</param>
/// <param name="srcimage">原图</param>
/// <param name="rotateangel">返回旋转角度</param>
/// <param name="rect">返回获取的矩形边框</param>
/// <returns></returns>
public mat getriooutline(mat morphimage, mat srcimage, out double rotateangel, out rect rect, out point2f minrectcenterpoint)
{
opencvsharp.point[][] contours;
hierarchyindex[] hierarchies;
//cv2.imshow(",mmmm", morphimage);
cv2.findcontours(morphimage, out contours, out hierarchies, retrievalmodes.external, contourapproximationmodes.approxtc89kcos, new point());
mat connimg = mat.zeros(morphimage.size(), mattype.cv_8uc3);
rotateangel = 0;
//point2f[] vertices = new point2f[4];
minrectcenterpoint = new point2f();
rect = new rect();
int index = 0;
int count = -1;
//寻找最大轮廓
for (int i = 0; i < contours.length; i++)
{
for (int j = 0; j < contours[i].length; j++)
{
if (count < contours[i].length)
{
count = contours[i].length;
index = i;
}
}
}
//获取轮廓点的矩形区域
rect = cv2.boundingrect(contours[index]);
//minarearect():以x轴正方形为起点,顺时针为正,逆时针为负
//绘制rio区域最小矩形
#region 绘制rio区域最小矩形
rotatedrect minrect = cv2.minarearect(contours[index]);
rotateangel = minrect.angle;
minrectcenterpoint = minrect.center;
//vertices = minrect.points();
#endregion
//绘制最小矩形
#region 绘制最小矩形
//cv2.line(srcimage, convert.toint32(vertices[0].x), convert.toint32(vertices[0].y), convert.toint32(vertices[1].x), convert.toint32(vertices[1].y), new scalar(0, 255, 255));
//cv2.line(srcimage, convert.toint32(vertices[0].x), convert.toint32(vertices[0].y), convert.toint32(vertices[3].x), convert.toint32(vertices[3].y), new scalar(0, 255, 255));
//cv2.line(srcimage, convert.toint32(vertices[1].x), convert.toint32(vertices[1].y), convert.toint32(vertices[2].x), convert.toint32(vertices[2].y), new scalar(0, 255, 255));
//cv2.line(srcimage, convert.toint32(vertices[2].x), convert.toint32(vertices[2].y), convert.toint32(vertices[3].x), convert.toint32(vertices[3].y), new scalar(0, 255, 255));
//console.writeline("anglerect_angle: {0}", minrect.angle);
#endregion
//cv2.imshow("srcimage", srcimage);
return srcimage;
}
/// <summary>
/// 图像旋转
/// </summary>
/// <param name="image">旋转图像</param>
/// <param name="rotateangel">旋转角度</param>
/// <returns></returns>
public mat imagerotate(mat image, double rotateangel, rect rect)
{
if (rotateangel > -90 && rotateangel <= -45)
{
rotateangel += 90;
}
else if (!(rotateangel > -45 && rotateangel < 0))
{
//degree = degree;
rotateangel = 0;
}
//opencvsharp.point center = new opencvsharp.point(image.cols / 2, image.rows / 2);
//以被截取的区域中心位置旋转
//边缘放大截取
opencvsharp.point center = new opencvsharp.point(rect.topleft.x + (rect.width / 2), rect.topleft.y + (rect.height / 2));
//截取最小矩形
//opencvsharp.point center = new opencvsharp.point(image.cols / 2, image.rows / 2);
double angle = rotateangel;
double scale = 1;
mat rotate = new mat(2, 3, mattype.cv_32fc1);
rotate = cv2.getrotationmatrix2d(center, angle, scale);//getrotationmatrix2d():以x轴正方形为起点,顺时针为负,逆时针为正
mat rotimage = new mat();
cv2.warpaffine(image, rotimage, rotate, image.size(), interpolationflags.linear, bordertypes.wrap);
int winsize = 0;
if (image.cols > image.rows)
{
winsize = image.cols;
}
else
{
winsize = image.rows;
}
return rotimage;
}
/// <summary>
/// 获取感兴趣区域
/// </summary>
/// <param name="image">图像</param>
/// <param name="rect">面积</param>
/// <returns></returns>
public mat getroi(mat image, rect rect)
{
//mat imageroi = new mat(image, new rect(new point(rect.topleft.x - rect.width / 2, rect.topleft.y), new size(rect.height, rect.height)));
mat imageroi = new mat(image, new rect(new point(rect.topleft.x, rect.topleft.y), new size(rect.width, rect.height)));
return imageroi;
}
dictionary<int, double> angeldic1;
/// <summary>
/// 模板匹配是否成功
/// </summary>
/// <param name="srcbmp">原图</param>
/// <param name="templatemat">模板图像</param>
/// <param name="rotangel">初始旋转角度</param>
/// <param name="robotrotateangel">机器人需要旋转的角度</param>
/// <returns></returns>
public bool getmatchret1(string srcimgfilepath, mat templatemat, double rotangel, out double robotrotateangel, out double arcvalue)
{
double matchval = 0;
//最大旋转角度360
robotrotateangel = 999;
arcvalue = 0;
try
{
mat src = cv2.imread(@"e:\softwarepackage\codepackage\imgpro\img\t1.bmp");
//src.convertto(src, mattype.cv_8uc4);
if (src.empty())
{
//console.writeline("src could not load image...\n");
return false;
}
cv2.imshow("src", src);
//mat templatemat = cv2.imread(@"e:\softwarepackage\codepackage\imgpro\img\t21.bmp");
if (templatemat.empty())
{
//console.writeline("src could not load image...\n");
return false;
}
angeldic1 = new dictionary<int, double>(); ;
//记录角度
int rotateangel = 0;
//第一张调正之后的图像
//cv2.imshow(0.tostring(), mat);
point p;
bitmap rotatebmp;
//旋转图像4个角度
for (int i = 0; i < 4; i++)
{
if (templatemat.rows > src.rows || templatemat.cols > src.cols)
{
continue;
}
bitmap bmp = matchtemplate(templatemat, src, out p, out matchval);
if (matchval > templatematchthreshold)
{
angeldic1.add(rotateangel, matchval);
}
//mat rotimg1 = ip.imagerotate(mat, 90);
rotatebmp = bitmapconverter.tobitmap(templatemat);
rotatebmp.rotateflip(rotatefliptype.rotate90flipxy);
rotateangel += 90;
templatemat = bitmapconverter.tomat(rotatebmp);
if (1 + i > 3)
{
break;
}
cv2.imshow((i + 1).tostring(), templatemat);
}
//若匹配值没有符合条件
if (angeldic1.count < 1)
{
return false;
}
list<double> matchret = new list<double>();
foreach (keyvaluepair<int, double> kvp in angeldic1)
{
matchret.add(kvp.value);
}
//判断匹配最大值是否大于设定阈值 否则 匹配失败
//if (matchret.max() < templatematchthreshold)
//{
// return false;
//}
//抠出模板图像 与原图进行模板匹配,并且记住最大匹配度的角度
var ag = angeldic1.where(q => q.value == matchret.max()).select(q => q.key);
robotrotateangel = calcrotateangel(rotangel, ag.max());
//角度转弧度
arcvalue = robotrotateangel * arc;
}
catch (exception ex)
{
//loghelper.writelog(ex.message, ex);
}
return true;
}
private double arc1 = math.pi / 180;
/// <summary>
/// 模板匹配
/// </summary>
/// <param name="tempalte">模板图片像</param>
/// <param name="srcpic">被匹配图像</param>
/// <returns>标注匹配区域的图像</returns>
public bitmap matchtemplate(mat tempalte, mat srcpic, out opencvsharp.point temppoint, out double matchvalue)
{
using (mat result = new mat()) //匹配结果
{ //模板匹配
double minvul;
double maxvul;
opencvsharp.point minloc = new opencvsharp.point(0, 0);
opencvsharp.point maxloc = new opencvsharp.point(0, 0);
opencvsharp.point matchloc = new opencvsharp.point(0, 0);
cv2.matchtemplate(srcpic, tempalte, result, templatematchmodes.ccoeffnormed);//ccoeffnormed 最好匹配为1,值越小匹配越差 xxxnormed的算法无需归一化
//cv2.normalize(result, result, 0, 1, normtypes.minmax, -1);//归一化
cv2.minmaxloc(result, out minvul, out maxvul, out minloc, out maxloc);//查找极值
//cv2.minmaxloc(result, out minvul, out maxvul, out minloc, out maxloc, null);//查找极值
matchloc = maxloc;//最大值坐标
//result.set(matchloc.y, matchloc.x, 0);//改变最大值为最小值
mat mask = srcpic.clone();//复制整个矩阵
//console.writeline("最大值:{0},x:{1},y:{2}", maxvul, matchloc.y, matchloc.x);
matchvalue = maxvul;
temppoint.x = matchloc.x;
temppoint.y = matchloc.y;
//画框显示 :对角线画框,起点和终点,都用point,线宽
if (matchvalue > templatematchthreshold)
{
cv2.rectangle(mask, matchloc, new opencvsharp.point(matchloc.x + tempalte.cols, matchloc.y + tempalte.rows), scalar.green, 2);//2代表画的线条的宽细程度
}
return opencvsharp.extensions.bitmapconverter.tobitmap(mask);
}
}
/// <summary>
/// 计算角度
/// </summary>
/// <param name="srcangel"></param>
/// <param name="rotateangle"></param>
/// <returns></returns>
public double calcrotateangel(double srcangel, int rotateangle)
{
//逆时针旋转为负 顺时针旋转为正
double tempsrcangel = math.abs(srcangel);
double resultangel = -1;
if (tempsrcangel >= 45)
{
switch (rotateangle)
{
case 0:
resultangel = 90 - tempsrcangel;
break;
case 90:
resultangel = 180 - tempsrcangel;
break;
case 180:
//逆时针
resultangel = -(90 + tempsrcangel);
break;
case 270:
//逆时针
resultangel = -tempsrcangel;
break;
}
}
else if (tempsrcangel < 45)
{
switch (rotateangle)
{
case 0:
//逆时针
resultangel = -tempsrcangel;
break;
case 90:
resultangel = 90 - tempsrcangel;
break;
case 180:
resultangel = 90 + tempsrcangel;
break;
case 270:
//逆时针
resultangel = -(90 + tempsrcangel);
break;
}
}
return resultangel;
}
完整代码都在上面了,感兴趣的小伙伴可以在优化优化。下个案例见。
发表评论