C# 多模板匹配例程
最近在做项目的时候为了检测某一种物品的齐套性,以及为了和写c#的软件负责人配合自己研究了一下opnencv C# 版的模板匹配,对基础的例程做了一下改进,留一份例程。
因为工作性质原因不能直接放项目的实际图片我用visio简单绘制了一个图片,最终结果如下。
在看代码之前滤清一下需求,目的是寻找到图像中目标的个数,这好像听起来很简单,但是实际上我们需要经过一个对模板匹配算法输出的相似度值进行一个分析的过程。
环境配置
首先就是C# opnecv 的环境配置如何安装opencv库
很简单只需要右键解决方案的管理解决方案的程序包然后搜索下载即可。
代码实战注释
我们使用如下图片作为模板
我们实现的功能是寻找图像中和模板图形相似的图像的数量
为了方便使用我将代码封装成了一个函数
在使用时可以直接调用函数即可吗,具体代码在文末给出,先讲一下不同参数之间的变化
recoganize(String srcImg_path, String tempImg_path, String save_path,double threshold = 0.5,double compressed = 1,string name = "face", int space = 10)
- srcImg_path要被匹配的图像的图像
- tempImg_path模板图像
- save_path存储结果的路径
- threshold置信度阈值
- compressed被匹配图像和模板图像在运算时压缩的比例
- name模板的类别名称
- space临近抑制长度(绝对值距离)
首先是观察一下阈值改变的效果先设置阈值为0.75
recoganize(String srcImg_path, String tempImg_path, String save_path,double threshold = 0.75,double compressed = 1,string name = "face", int space = 10)
然后设置阈值为0.5
recoganize(String srcImg_path, String tempImg_path, String save_path,double threshold = 0.5,double compressed = 1,string name = "face", int space = 10)
接下来改变运行时模板和图片的压缩比例观察一下两者的区别,首先是图像变小了那变小有什么用呢
recoganize(String srcImg_path, String tempImg_path, String save_path,double threshold = 0.5,double compressed = 0.5,string name = "face", int space = 10)
我们可以观察一下两者的运行时间
当compressed为1时,识别时间为236毫秒
当compressed为0.5时运行时间173毫秒,当图片特别大检测项多时图像压缩是一个非常好的提升效率的方法
接下来我们研究一下space参数临近抑制我们将space设置为0
recoganize(String srcImg_path, String tempImg_path, String save_path,double threshold = 0.5,double compressed = 1,string name = "face", int space = 0)
结果如下发现数量不对了,那是因为什么,查看图像发现他会在某些图像周围画好几个框因此影响了算法的判断,所以要设置两个框之间的最小间隔所以spece参数起到的就是这个作用
face 就是类别名可以通过修改这个变量实现更改上方的提示元素
实际代码
using OpenCvSharp;
//缩放值减少运算量
String tempImg_path = (@"C:\Users\86176\Desktop\csharp 多模板匹配例程\model.jpg"); //改成自己的
String srcImg_path = (@"C:\Users\86176\Desktop\csharp 多模板匹配例程\picture.jpg"); //改成自己的
String save_path = (@"C:\Users\86176\Desktop\csharp 多模板匹配例程\roi.bmp"); //改成自己的
recoganize(srcImg_path, tempImg_path,save_path,0.5, 1, "face",10);
void recoganize(String srcImg_path, String tempImg_path, String save_path,double threshold = 0.5,double compressed = 0.5,string name = "face", int space = 10)
{
DateTime beginTime = DateTime.Now; //获取开始时间
// 新建图变量并分配内存
Mat srcImg = new Mat();
// 读取要被匹配的图像
srcImg = Cv2.ImRead(srcImg_path);
// 更改尺寸
Cv2.Resize(srcImg, srcImg, new Size((int)srcImg.Cols * compressed, (int)srcImg.Rows * compressed));
// 初始化保存保存匹配结果的横纵坐标列表
List<int> Xlist = new List<int> { };
List<int> Ylist = new List<int> { };
int order = 0;
Mat tempImg = Cv2.ImRead(tempImg_path);
Cv2.Resize(tempImg, tempImg, new Size((int)tempImg.Cols * compressed, (int)tempImg.Rows * compressed));
Mat result = srcImg.Clone();
int dstImg_rows = srcImg.Rows - tempImg.Rows + 1;
int dstImg_cols = srcImg.Cols - tempImg.Cols + 1;
Mat dstImg = new Mat(dstImg_rows, dstImg_cols, MatType.CV_32F, 1);
Cv2.MatchTemplate(srcImg, tempImg, dstImg, TemplateMatchModes.CCoeffNormed);
//Cv2.ImShow("match", dstImg);
int count = 0;
for (int i = 0; i < dstImg_rows; i++)
{
for (int j = 0; j < dstImg_cols; j++)
{
float matchValue = dstImg.At<float>(i, j);
if (matchValue >= threshold && Xlist.Count ==0)
{
count++;
Cv2.Rectangle(result, new Rect(j, i, tempImg.Width, tempImg.Height), new Scalar(0, 255, 0), 2);
Cv2.PutText(result, name, new Point(j, i-(int)20*compressed), HersheyFonts.HersheySimplex, 0.5, new Scalar(0, 0, 0), 1);
Xlist.Add(j);
Ylist.Add(i);
}
if (matchValue >= threshold && Xlist.Count != 0)
{
for(int q = 0; q<Xlist.Count; q++)
{
if (Math.Abs(j - Xlist[q]) + Math.Abs(i - Ylist[q]) < space)
{
order = 1;
break;
}
}
if (order != 1)
{
count++;
Cv2.Rectangle(result, new Rect(j, i, tempImg.Width, tempImg.Height), new Scalar(0, 255, 0), 2);
Cv2.PutText(result, name, new Point(j, i - (int)20 * compressed), HersheyFonts.HersheySimplex, 0.5, new Scalar(0, 0, 0), 1);
Xlist.Add(j);
Ylist.Add(i);
}
order = 0;
}
}
}
Console.WriteLine("目标数量:{0}", count);
Cv2.ImShow("result", result);
Cv2.ImWrite(save_path,result);
DateTime endTime = DateTime.Now; //获取结束时间
TimeSpan oTime = endTime.Subtract(beginTime); //求时间差的函数
Console.WriteLine("程序的运行时间:{0} 毫秒", oTime.TotalMilliseconds);
Cv2.WaitKey(0);
}