在网上搜了许多文章和博客居然没有一个介绍如何使用java opencv 进行模板匹配多目标的,翻看了opencv官网的文档也都没有找到,网上基本上都是介绍如何用python 和 c++ 进行opencv操作的,哎~ 看来只能靠自己了,废话不多说 先看效果图:
这里把小日本的图标都给匹配到了,用到的模板图片是下面这个:
具体代码
public void Matching() {
Mat muban = Imgcodecs.imread("/storage/emulated/0/muban2.jpg"); //todo 模板图
Mat src = Imgcodecs.imread(getExternalFilesDir("") + "/jietu.jpg"); //todo 待匹配的图
Mat clone = src.clone();
if(muban.empty()){
Log.e(TAG, "未找到资源");
return;
}
int templatW, templatH, resultH, resultW;
templatW = muban.width();
templatH = muban.height();
resultH = src.rows() - muban.rows() + 1;
resultW = src.cols() - muban.cols() + 1;
Mat result = new Mat(new Size(resultH,resultW), CvType.CV_32FC1);
Imgproc.matchTemplate(clone, muban, result, Imgproc.TM_CCOEFF_NORMED); //是标准相关性系数匹配 值越大越匹配
Core.MinMaxLocResult mmr;
mmr = Core.minMaxLoc(result);
//在原图上的对应模板可能位置画一个绿色矩形
Imgproc.rectangle(src, mmr.maxLoc, new Point( mmr.maxLoc.x + templatW, mmr.maxLoc.y + templatH), new Scalar(0, 255, 0),5);
Log.e(TAG, "匹配的值:"+mmr.maxVal+" ------坐标:"+mmr.maxLoc.x+","+mmr.maxLoc.y);
for (int i=0;i<8;i++){
if(mmr.maxVal>0.8){ //这里是判断相似程度的
mmr = getMaxLoc(clone,muban,templatW,templatH,mmr.maxLoc);
if(mmr.maxVal>0.8){
Imgproc.rectangle(src, mmr.maxLoc, new Point( mmr.maxLoc.x + templatW, mmr.maxLoc.y + templatH), new Scalar(0, 255, 0),5);
Log.e(TAG, "匹配的值:"+mmr.maxVal+" ------坐标:"+mmr.maxLoc.x+","+mmr.maxLoc.y);
}
}
}
//将结果输出到对应位置
Imgcodecs.imwrite(getExternalFilesDir("") +"/匹配结果.jpeg", src);
}
这里自己写了个 getMaxLoc 方法循环调用把匹配的目标全部找出来
private Core.MinMaxLocResult getMaxLoc(Mat clone,Mat result, int templatW, int templatH, Point maxLoc){
int startY,startX,endY,endX;
//计算大矩形的坐标
startY = (int)maxLoc.y;
startX = (int)maxLoc.x;
//计算大矩形的的坐标
endY = (int)maxLoc.y + templatH;
endX = (int)maxLoc.x + templatW;
//将大矩形内部 赋值为最大值 使得 以后找的最小值 不会位于该区域 避免找到重叠的目标
int ch = clone.channels(); //通道数 (灰度: 1, RGB: 3, etc.)
for (int i = startX; i < endX; i++) {
for (int j = startY; j < endY ; j++) {
double[] data = clone.get(j, i); //读取像素值,并存储在double数组中
for (int k = 0; k < ch; k++){ //RGB值或灰度值
data[k] =255; //对每个像素值(灰度值或RGB通道值,取值0~255)进行处理
}
clone.put(j, i,data); //把处理后的像素值写回到Mat
}
}
int resultH = clone.rows() - result.rows() + 1;
int resultW = clone.cols() - result.cols() + 1;
Mat result2 = new Mat(new Size(resultH,resultW), CvType.CV_32FC1);
Imgproc.matchTemplate(clone, result, result2, Imgproc.TM_CCOEFF_NORMED); //是标准相关性系数匹配 值越大越匹配
// Imgcodecs.imwrite(getExternalFilesDir("") +"/匹配结果.jpeg", clone);
//查找result中的最大值 及其所在坐标
return Core.minMaxLoc(result2);
}
**思路主要是 循环调用 opencv的 matchTemplate 方法不断进行匹配,每次匹配的时候把上次的目标位置在图片中抹除,这样下次再匹配的时候就不会匹配到重复的目标,匹配是从大到小依次把目标位置筛选出来的
注意事项
[1] 该博客是本人原创博客,转载时请注明出处~
[2] 由于个人能力有限,上面实现的方案可能不是最优的,如果你有更好的实现方案,欢迎留言告诉我
我会及时回复