图像处理模板匹配源代码

2015-10-02 00:48:06 jia20003 阅读数 24825
  • Android APP开发之真机调试环境实现(上)

    该课程会为大家讲解如何实现在Android真机上调试自己的项目,实现eclipse与真机的整合使用。 同样,本期公开课会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项...

    10721人学习 CSDN讲师
    免费试看

一:基本原理

NCC是一种基于统计学计算两组样本数据相关性的算法,其取值范围为[-1, 1]之间,而对图像来说,每个像素点都可以看出是RGB数值,这样整幅图像就可以看成是一个样本数据的集合,如果它有一个子集与另外一个样本数据相互匹配则它的ncc值为1,表示相关性很高,如果是-1则表示完全不相关,基于这个原理,实现图像基于模板匹配识别算法,其中第一步就是要归一化数据,数学公式如下:


二:实现步骤

(1)      获取模板像素并计算均值与标准方差、像素与均值diff数据样本

(2)      根据模板大小,在目标图像上从左到右,从上到下移动窗口,计

算每移动一个像素之后窗口内像素与模板像素的ncc值,与阈值比较,大于

阈值则记录位置

(3)      根据得到位置信息,使用红色矩形标记出模板匹配识别结果。

(4)      UI显示结果

 

三:编程实现

基于JAVA语言完成了整个算法编程实现与演示,其中第一步的代码如下:

		int tw = template.getWidth();
		int th = template.getHeight();
		int[] tpixels = new int[tw * th];
		getRGB(template, 0, 0, tw, th, tpixels);
		for(int i=0; i<tpixels.length; i++)
		{
			tpixels[i] = (tpixels[i] >> 16) & 0xff;
		}
		double[] meansdev = getPixelsMeansAndDev(tpixels);
		double[] tDiff = calculateDiff(tpixels, meansdev[0]);
		int raidus_width = tw / 2;
		int raidus_height = th / 2;
第二步的实现代码如下:

		int[] windowPixels = new int[tw * th];
		Arrays.fill(windowPixels, 0);
		for (int row = 0; row < height; row++) {
			for (int col = 0; col < width; col++) {
				// calculate the means and dev for each window
				if(row <  raidus_height || (row + raidus_height) >= height)
					continue;
				if(col < raidus_width || (col + raidus_width) >= width) 
					continue;
				int wrow = 0;
				Arrays.fill(windowPixels, 0);
				for(int subrow = -raidus_height; subrow <= raidus_height; subrow++ )
				{
					int wcol = 0;
					for(int subcol = -raidus_width; subcol <= raidus_width; subcol++ )
					{
						if(wrow >= th || wcol >= tw)
						{
							continue;
						}
						windowPixels[wrow * tw + wcol] = getPixelValue(width, col + subcol, row + subrow, inPixels);
						wcol++;
					}
					wrow++;
				}
				// calculate the ncc
				double[] _meansDev = getPixelsMeansAndDev(windowPixels);
				double[] diff = calculateDiff(windowPixels, _meansDev[0]);
				double ncc = calculateNcc(tDiff, diff, _meansDev[1], meansdev[1]);
				if(ncc > threhold) {
					Point mpoint = new Point();
					mpoint.x = col;
					mpoint.y  = row;
					points.add(mpoint);
				}
			}
		}
第三步的实现代码如下:

		// draw matched template on target image according position
		setRGB( dest, 0, 0, width, height, inPixels );
		Graphics2D g2d = dest.createGraphics();
		g2d.setPaint(Color.RED);
		g2d.setStroke(new BasicStroke(4));
		for(Point p : points)
		{
			g2d.drawRect(p.x - raidus_width, p.y - raidus_height, tw, th);
		}
其中第二步用到的计算NCC的方法实现如下:

	private double calculateNcc(double[] tDiff, double[] diff, double dev1, double dev2) {
		// TODO Auto-generated method stub
		double sum = 0.0d;
		double count = diff.length;
		for(int i=0; i<diff.length; i++)
		{
			sum += ((tDiff[i] * diff[i])/(dev1 * dev2));
		}
		return (sum / count);
	}
UI部分完整源代码如下:

package com.gloomyfish.image.templae.match;

import java.awt.BorderLayout;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.io.IOException;

import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;

public class DemoUI extends JComponent {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 1L;
	private BufferedImage targetImage;
	private BufferedImage template;
	
	public DemoUI()
	{
		super();
		java.net.URL imageURL = this.getClass().getResource("words.png");
		java.net.URL templateURL = this.getClass().getResource("template.png");
		
		try {
			template = ImageIO.read(templateURL);
			targetImage = ImageIO.read(imageURL);
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	public void setTarget(BufferedImage target) {
		this.targetImage = target;
	}

	@Override
	protected void paintComponent(Graphics g) {
		Graphics2D g2 = (Graphics2D) g;
		if(targetImage != null) {
			g2.drawImage(targetImage, 10, 10, targetImage.getWidth(), targetImage.getHeight(), null);
		}
		if(template != null) {
			g2.drawImage(template, 20+targetImage.getWidth(), 10, template.getWidth(), template.getHeight(), null);
		}
	}
	
	public static void main(String[] args) {
		JFrame f = new JFrame("模板匹配与识别");
		JButton okBtn = new JButton("匹配");
		final DemoUI ui = new DemoUI();
		okBtn.addActionListener(new ActionListener() {

			@Override
			public void actionPerformed(ActionEvent e) {
				
				ui.process();
			}
		});
		
		JPanel btnPanel = new JPanel();
		btnPanel.setLayout(new FlowLayout(FlowLayout.RIGHT));
		btnPanel.add(okBtn);
		
		f.getContentPane().add(btnPanel, BorderLayout.SOUTH);
		f.getContentPane().add(ui, BorderLayout.CENTER);
		f.setSize(500, 500);
		f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		f.setVisible(true);
	}

	protected void process() {
		NccTemplateMatchAlg algo = new NccTemplateMatchAlg(template);
		targetImage = algo.filter(targetImage, null);
		this.repaint();
	}

}
四:程序运行效果如下


其中左边是目标图像、右边为模板图像

PS:博客从10月份开始每月都有多篇相关图像处理文章更新

欢迎大家继续关注


2018-08-16 11:21:50 qq_26907755 阅读数 1010
  • Android APP开发之真机调试环境实现(上)

    该课程会为大家讲解如何实现在Android真机上调试自己的项目,实现eclipse与真机的整合使用。 同样,本期公开课会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项...

    10721人学习 CSDN讲师
    免费试看

模板匹配:
模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域,所以模板匹配首先需要一个模板图像T(给定的子图像),另外需要一个待检测的图像-源图像s工作方法,在待检测图像上,从左到右,从上往下计算模板图像与重叠子图像的匹配度,匹配度越大两者相同的可能性就越大。
OpenCV中提供了六种常见的匹配算法如下:

  1. 计算平方不同 , 计算出来的值越小,越相关 TM_SQDIFF= 0
  2. 计算相关性 , 计算出来的值越大,越相关 TM_CCORR= 2
  3. 计算相关系数 , 计算出来的值越大,越相关 TM_CCOEFF = 4
  4. 计算归一化平方不同 , 计算出来的值越接近0,越相关 TM_SQDIFF_NORMED = 1
  5. 计算归一化相关性, 计算出来的值越接近1,越相关 TM_CCORR_NORMED = 3
  6. 计算归一化相关系数 , 计算出来的值越接近1,越相关 TM_CCOEFF_NORMED = 5

建议使用归一化的方法
API:

matchTemplate(
InputArray image,// 源图像,必须是8-bit或者32-bit浮点数图像
InputArray templ,// 模板图像,类型与输入图像一致
OutputArray result,// 输出结果,必须是单通道32位浮点数,假设源图像W*H,模板图像w*h, 则结果必须为W-w+1, H-h+1的大小。
int method,// 使用的匹配方法
InputArray mask=noArray()//(optional)
)

代码:

#include <opencv2/opencv.hpp>
#include<iostream>
#include<math.h>
#include <string> 
#include<fstream> 
using namespace cv;
using namespace std;
Mat src, temp, dst;

int match_method = TM_SQDIFF;
int max_track = 5;

void match(int, void*) {
    int width = src.cols - temp.cols + 1;
    int height = src.rows - temp.rows + 1;
    Mat result(width, height, CV_32FC1);

    matchTemplate(src, temp, result, match_method);//模板匹配
    normalize(result, result, 0, 1, NORM_MINMAX);//归一化

    Point minLoc, maxLoc;
    double min, max;
    src.copyTo(dst);
    minMaxLoc(result, &min, &max, &minLoc, &maxLoc);//寻找result中最大值和最小值以及他们的位置

    Point temLoc;
    if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED) {
        temLoc = minLoc;
    }
    else {
        temLoc = maxLoc;
    }
    //绘制矩形
    rectangle(dst, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255));
    rectangle(result, Rect(temLoc.x, temLoc.y, temp.cols, temp.rows), Scalar(0, 0, 255));

    imshow("result", result);
    imshow("matchtemplate", dst);
}
int main() {
    src = imread("C:\\Users\\Administrator\\Desktop\\pic\\5.jpg");
    temp = imread("C:\\Users\\Administrator\\Desktop\\pic\\8.jpg");
    imshow("temp", temp);
    match(0, 0);
    createTrackbar("match_method:", "result", &match_method, max_track, match);
    waitKey(0);
}

结果:
这里写图片描述

2019-06-02 17:00:00 weixin_40063211 阅读数 1989
  • Android APP开发之真机调试环境实现(上)

    该课程会为大家讲解如何实现在Android真机上调试自己的项目,实现eclipse与真机的整合使用。 同样,本期公开课会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项...

    10721人学习 CSDN讲师
    免费试看

模板匹配法

       模板匹配是一种用于在源图像S中寻找定位给定目标图像T(即模板图像)的技术。其原理很简单,就是通过一些相似度准则来衡量两个图像块之间的相似度Similarity(S,T)。

模板匹配的工作方式

       模板匹配的工作方式跟直方图的反向投影基本一样,大致过程是这样的:通过在输入图像上滑动图像块对实际的图像块和输入图像进行匹配。假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:

 (1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;

 (2)用临时图像和模板图像进行对比,对比结果记为c;

 (3)对比结果c,就是结果图像(0,0)处的像素值;

 (4)切割输入图像从(0,1)至(10,11)的临时图像,对比,并记录到结果图像;

 (5)重复(1)~(4)步直到输入图像的右下角。

模板匹配方法的优缺点:

       优点:简单、直接

       缺点:不具有旋转不变性、不具有尺度不变性

	代码如下:  
	int main()  
	{  
	    Mat img, templ, result;  
	    img = imread("match_dst.jpg");  
	    templ = imread("match_src.jpg");  
	    /*img = imread("1.jpg"); 
	    templ = imread("2.jpg");*/  
	
        int result_cols = img.cols - templ.cols + 1;  
	    int result_rows = img.rows - templ.rows + 1;  
	    result.create(result_cols, result_rows, CV_32FC1);  
	  
	    //进行匹配和标准化  
	    matchTemplate(img, templ, result, CV_TM_SQDIFF_NORMED);  
	    normalize(result, result, 0, 1, NORM_MINMAX, -1, Mat());  
	  
	    double minVal;  //匹配最小值  
	    double maxVal;  //匹配最大值  
	    Point maxLoc;  //匹配最大值的位置坐标  
        Point minLoc;   //匹配最小值的位置坐标  
	    Point matchLoc;    
	  
	    //通过函数minMaxLoc定位最匹配的位置  
	    minMaxLoc(result, &minVal, &maxVal, &minLoc, &maxLoc, Mat());  
	  
	    cout << "匹配度:" << minVal << endl;  
	    cout << "匹配度最小值的坐标" << minLoc.x << "," << minLoc.y << endl;  
	  
	    /*Mat H = findHomography(templ,img, CV_RANSAC); 
	 
	    Mat trans1; 
	    perspectiveTransform(img, trans1, H); 
	    imshow("trans1", trans1);*/  
	  
	    matchLoc = minLoc;  
	    rectangle(img, matchLoc, Point(matchLoc.x + templ.cols, matchLoc.y + templ.rows), Scalar(0, 255, 0), 2, 8, 0);  
	  
	    imshow("img", img);  
	    imshow("templ", templ);  
	    //imshow("result", result);  

	    waitKey(0);  	  
	    return 0;  
	}  

效果如下:

       但是,在该方法中,最佳匹配点位于大图匹配位置的左上部分,且只能用矩形框选匹配位置。若小图视角变化,框选无效。

Surf之图像匹配

       利用Surf算法进行图像匹配其一般流程为:检测物体特征点->计算特征点描述子->使用BurteForceMatcher或FLANN进行特征点匹配->匹配到的特征点进行透视变换findHomography()->透视矩阵变换perspectiveTransform()->绘制匹配物体轮廓。

#include<opencv2\core\core.hpp>
#include<opencv2\highgui\highgui.hpp>
#include<opencv2\nonfree\features2d.hpp>
#include <opencv2\imgproc\imgproc.hpp>
#include<opencv2\calib3d\calib3d.hpp>
#include<iostream>
#include<stdio.h>

using namespace std;
using namespace cv;

int main()
{
	Mat image_object = imread("template.png", IMREAD_GRAYSCALE);
	Mat image_scene = imread("image.png", IMREAD_GRAYSCALE);
	
	//检测特征点
	const int minHessian = 400;
	SurfFeatureDetector detector(minHessian);
	vector<KeyPoint>keypoints_object, keypoints_scene;
	detector.detect(image_object, keypoints_object);
	detector.detect(image_scene, keypoints_scene);

	//计算特征点描述子
	SurfDescriptorExtractor extractor;
	Mat descriptors_object, descriptors_scene;
	extractor.compute(image_object, keypoints_object, descriptors_object);
	extractor.compute(image_scene, keypoints_scene, descriptors_scene);

	//使用FLANN进行特征点匹配
	FlannBasedMatcher matcher;
	vector<DMatch>matches;
	matcher.match(descriptors_object, descriptors_scene, matches);

	//计算匹配点之间的最大和最小距离
	double max_dist = 0;
	double min_dist = 100;
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		double dist = matches[i].distance;
		if (dist < min_dist)
		{
			min_dist = dist;
		}
		else if (dist > max_dist)
		{
			max_dist = dist;
		}
	}
	printf("Max dist: %f \n", max_dist);
	printf("Min dist: %f \n", min_dist);
   //绘制好的匹配点
	vector<DMatch>good_matches;
	for (int i = 0; i < descriptors_object.rows; i++)
	{
		if (matches[i].distance<2 * min_dist)
		{
			good_matches.push_back(matches[i]);
		}
	}
	Mat image_matches;
	drawMatches(image_object, keypoints_object, image_scene, keypoints_scene, good_matches, image_matches,
		Scalar::all(-1), Scalar::all(-1), vector<char>(), DrawMatchesFlags::NOT_DRAW_SINGLE_POINTS);

	//定位好的匹配点
	vector<Point2f> obj;
	vector<Point2f> scene;
	for (int i = 0; i < good_matches.size(); i++)
	{
		//DMathch类型中queryIdx是指match中第一个数组的索引,keyPoint类型中pt指的是当前点坐标
		obj.push_back(keypoints_object[good_matches[i].queryIdx].pt);
		scene.push_back(keypoints_scene[good_matches[i].trainIdx].pt);
	}

	Mat H = findHomography(obj, scene, CV_RANSAC);

	vector<Point2f> obj_corners(4), scene_corners(4);
	obj_corners[0] = cvPoint(0, 0);
	obj_corners[1] = cvPoint(image_object.cols, 0);
	obj_corners[2] = cvPoint(image_object.cols, image_object.rows);
	obj_corners[3] = cvPoint(0, image_object.rows);

	perspectiveTransform(obj_corners, scene_corners, H);

	//绘制角点之间的直线
	line(image_matches, scene_corners[0] + Point2f(image_object.cols, 0),
		scene_corners[1] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
	line(image_matches, scene_corners[1] + Point2f(image_object.cols, 0),
		scene_corners[2] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
	line(image_matches, scene_corners[2] + Point2f(image_object.cols, 0),
		scene_corners[3] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);
	line(image_matches, scene_corners[3] + Point2f(image_object.cols, 0),
		scene_corners[0] + Point2f(image_object.cols, 0), Scalar(0, 0, 255), 2);

	//输出图像
	namedWindow("匹配图像", WINDOW_AUTOSIZE);
	imshow("匹配图像", image_matches);
	waitKey(0);

	return 0;

}

程序说明:

       在定位匹配点中用到了DMatchqueryIdxtrainIdx成员变量和keyPoint的成员变量pt

cv::DMatch::DMatch(int  queryIdx,  //在对描述子匹配时,第一组特征点的索引  
int  trainIdx,  //在对描述子匹配时,第二组特征点的索引  
int  imgIdx,    //多个图像中图像的索引  
float   distance  //两个特征向量间的欧氏距离,越小表明匹配度越高 )  

       对于DrawMatch函数:

void drawMatches( const Mat& img1, const vector<KeyPoint>& keypoints1,  
	              const Mat& img2, const vector<KeyPoint>& keypoints2,  
	              const vector<vector<DMatch> >& matches1to2, Mat& outImg,  
	              const Scalar& matchColor=Scalar::all -1), 
	              const Scalar& singlePointColor=Scalar::all(-1),  
	              const vector<vector<char> >& matchesMask=vector<vector<char> >(), 
                  int flags=DrawMatchesFlags::DEFAULT );  

/其中参数如下:  
img1 – 源图像1  
keypoints1 –源图像1的特征点.  
img2 – 源图像2.  
keypoints2 – 源图像2的特征点  
matches1to2 – 源图像1的特征点匹配源图像2的特征点[matches[i]] .  
outImg – 输出图像具体由flags决定.  
matchColor – 匹配的颜色(特征点和连线),若matchColor==Scalar::all(-1),颜色随机.  
singlePointColor – 单个点的颜色,即未配对的特征点,若matchColor==Scalar::all(-1),颜色随机.  
matchesMask – Mask决定哪些点将被画出,若为空,则画出所有匹配点.  
* flags – Fdefined by DrawMatchesFlags.  

效果图:

 

FAsT-Match

        FAsT-Match是在2D仿射变换下用于近似模板匹配的快速算法,其最小化绝对差分和(SAD)误差测量。 通过图像平滑度的密度对其进行采样。 对于每个可能的变换,使用次线性算法来近似SAD误差, 同时使用分支定界法进一步加速算法。 由于已知图像是分段平滑的,因此结果是具有近似保证的实际仿射模板匹配算法。

算法流程:                                                                                                      

代码地址

结果如下:

 

2019-04-27 10:32:16 weixin_42521239 阅读数 4164
  • Android APP开发之真机调试环境实现(上)

    该课程会为大家讲解如何实现在Android真机上调试自己的项目,实现eclipse与真机的整合使用。 同样,本期公开课会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项...

    10721人学习 CSDN讲师
    免费试看

我先介绍一下模板匹配的原理

原图像:我们期望找到与模板图像匹配的图像

模板图像:将于模板图像进行比较的图像

一次移动一个像素(从左到右,从上到下)。在每个位置,计算相似度度量,以便它表示在该位置处的匹配的“好”还是“坏”。

下面直接用opencv进行实现

头文件

#include "opencv2/highgui/highgui.hpp"
#include "opencv2/imgproc/imgproc.hpp"

定义一些后面要用到的图像

	Mat srcImage; //原图像
	Mat templateImage;//模板图像
	Mat resultImage; //输出结果

载入原图像和模板图像

srcImage = imread("map.png", 1);
templateImage = imread("img1.png", 1);
/*
	Mat imread(const string& filename, int flags)
	filename为文件路径
	flags为读入类型,-1为解码得到的图像,0为单通道读入图像,即灰白图像,1为三通道读入图像,即彩色        
    图像
*/

初始化用于结果输出的矩阵

int resultImage_rows = srcImage.rows - templateImage.rows + 1;
int resultImage_cols = srcImage.cols - templateImage.cols + 1;
resultImage.create(resultImage_rows, resultImage_cols, CV_32FC1);
/*create(rows,cols,type)CV_32FC1为单通道图像,CV_32FC3为三通道图像*/

进行匹配和标准化

matchTemplate(srcImage, templateImage, resultImage, MatchMethod);
/*
	void matchTemplate(InputArray image, InputArray temp, OutputArray result, int         
    method)
	image: 输入原图像
	temp: 输入模板图像
	result: 输出的相关系数矩阵
	method: 匹配方法
		平方差匹配法CV_TM_SQDIFF
		归一化平方差匹配法CV_TM_SQDIFF_NORMED
		相关匹配法CV_TM_CCORR
		归一化相关匹配法CV_TM_CCORR_NORMED
		相关系数匹配法CV_TM_CCOEFF
		归一化相关系数匹配法CV_TM_CCOEFF_NORMED
*/

 这五种匹配方法的公式分别是

normalize(resultImage, resultImage, 0, 1, NORM_MINMAX, -1, Mat());
/*
	void normalize(const InputArray src, OutputArray dst, double alpha, double beta, int 
    normType, int rtype, InputArray mask)
	src为输入数组
	dst为输出数组
	alpha为规范下限
	beta为规范上限
	normtype为公式类型
	rtype为负数时,输出数组的type与输入数组的type相同;否则,输出数组与输入数组只是通道数相同
	mask为掩膜,选择感兴趣区域,选定后只能对该区域进行操作。Mat()代表整幅图像
*/

通过函数 minMaxLoc 定位最匹配的位置

double minValue; double maxValue; Point minLocation; Point maxLocation;
/*定义最大值、最小值和最大值位置、最小值位置*/
	Point matchLocation;
	minMaxLoc(resultImage, &minValue, &maxValue, &minLocation, &maxLocation, Mat());
/*
	寻找图像中最小值最大值及它们的位置
	void minMaxLoc(InputArray src, double* minVal, double* maxVal=0, Point* minLoc=0,     
    Point* maxLoc=0, InputArray mask=noArray())
	src:输入图像。
	minVal:最小值,可輸入NULL表示不需要。
	maxVal :最大值,可輸入NULL表示不需要。
	minLoc:最小值的位置,可输入NULL表示不需要,Point类型。
	maxLoc:最大值的位置,可输入NULL表示不需要,Point类型。
	mask:可有可无的掩模。
*/

对于方法 SQDIFF 和 SQDIFF_NORMED, 越小的数值有着更高的匹配结果. 而其余的方法, 数值越大匹配效果越好

if (MatchMethod == TM_SQDIFF || MatchMethod == TM_SQDIFF_NORMED)
	{
		matchLocation = minLocation;
	}
	else
	{
		matchLocation = maxLocation;
	}

绘制出矩形,并显示最终结果

rectangle(tempImage, matchLocation, Point(matchLocation.x + templateImage.cols, matchLocation.y + templateImage.rows), Scalar(0, 0, 255), 2, 8, 0);
/*
	void cvRectangle( CvArr* img, CvPoint pt1, CvPoint pt2, CvScalar color,int 
    thickness=1, int line_type=8, int shift=0 );
	img: 图像.
	pt1: 矩形的一个顶点。
	pt2: 矩形对角线上的另一个顶点
	color: 线条颜色 (RGB) 或亮度(灰度图像 )(grayscale image)。
	thickness: 组成矩形的线条的粗细程度。取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形。
	line_type: 线条的类型。见cvLine的描述
	shift: 坐标点的小数点位数。
*/

源代码的下载链接:

https://download.csdn.net/download/weixin_42521239/11147622

如果没有积分的小伙伴也可以在评论里留下你的邮箱,我发给你。

2017-08-16 22:05:16 wu_lian_nan 阅读数 5087
  • Android APP开发之真机调试环境实现(上)

    该课程会为大家讲解如何实现在Android真机上调试自己的项目,实现eclipse与真机的整合使用。 同样,本期公开课会为大家讲解Android开发环境的各种坑以及如何解决,让大家可以更高效率、更清晰的认识开发环境中的各项...

    10721人学习 CSDN讲师
    免费试看

一、理论基础

  • 基于相关的模板匹配技术可直接用于在一幅图像中,寻找某种子图像模式。对于大小为MxN的图像f(x,y)和大小为JxK的子图像模式w(x,y),f与w的相关可表示为:

这里写图片描述

  • 其种,x=0,1,2,…N-K,y=0,1,2,…M-J。此处
    的目的是寻找匹配而不是对f(x,y)进行滤波操作,因此w的原点被设置在子图像的左上角,
    并且式(11-3) 给出的形式也完全适用于J和K 为偶数的情况。

  • 计算相关c(x,y)的过程就是在图像f(x,y)中逐点地移动子图像w(x,小),使w 的原点和点
    (x,y)重合,然后计算w与f中被w覆盖的图像区域对应像素的乘积之和,以此计算结果作
    为相关图像c(x,)在(x,y)点的响应。

  • 相关可用于在图像f(x,y)中找到与子图像w(x,y)匹配的所有位置。实际上,当w 按照上
    段中描述的过程移过整幅图像f之后,最大的响应点(xo,yo)即为最佳匹配的左上角点。我们
    也可以设定一个阈值T,认为响应值大于该阈值的点均是可能的匹配位置。

  • 相关的计算是通过将图像元素和子模式图像元素联系起来获得的,将相关元素相乘后累
    加。我们完全可以将子图像w视为一个按行或按列存储的向量这里写图片描述,将计算过程中被w覆盖的图像区域视为另一个按照同样的方式存储的向量这里写图片描述.这样一来,相关计算就- 成了向量之间的点积运算。

两个向量的点积为:
这里写图片描述

  • 其中,Θ 为向量这里写图片描述这里写图片描述之间的夹角。显然,当这里写图片描述这里写图片描述具有完全相同的方向(平行) 时,这里写图片描述,从而式(11-4) 取得其最大值这里写图片描述,这就意味着当图像的局部区域类似于子图像模式时,相关运算产生最大的响应。然而,式(11-4) 最终的取值还与向量这里写图片描述这里写图片描述自身的模有关,这将导致按照式(11-4) 计算的相关响应存在着对f和w 的灰度幅值比较敏感的缺陷。这样一来,在f的高灰度区域,可能尽管其内容与子图像w的内容并不相近,但由于这里写图片描述自身较大而同样产生一个很高的响应。可通过对向量以其模值来归一化解决这一问题,即通过来计算。
    这里写图片描述

改进的用于匹配的相关计算公式如下:
这里写图片描述

二、代码部分

  • 看了脑疼的理论,下面看看让人愉快的代码吧。

TemplateMatch.h

#pragma once
#include<opencv2\opencv.hpp>
using namespace cv;
typedef unsigned char BYTE;
void TemplateMatch(Mat * pTo, Mat * pTemplate,Mat * src);

TemplateMatch.cpp

 #include"TemplateMatch.h"

void TemplateMatch(Mat * pTo, Mat * pTemplate, Mat * src)
{

    //循环变量
    int i, j, m, n;

    double dSumT; //模板元素的平方和
    double dSumS; //图像子区域元素的平方和
    double dSumST; //图像子区域和模板的点积    

                   //响应值
    double R;

    //记录当前的最大响应
    double MaxR;

    //最大响应出现位置
    int nMaxX;
    int nMaxY;

    int nHeight = src->rows;
    int nWidth = src->cols;
    //模板的高、宽
    int nTplHeight = pTemplate->rows;
    int nTplWidth = pTemplate->cols;

    //计算 dSumT
    dSumT = 0;
    for (m = 0; m < nTplHeight; m++)
    {
        for (n = 0; n < nTplWidth; n++)
        {
            // 模板图像第m行,第n个象素的灰度值
            int nGray =*pTemplate->ptr(m, n);

            dSumT += (double)nGray*nGray;
        }
    }

    //找到图像中最大响应的出现位置
    MaxR = 0;
    for (i = 0; i < nHeight - nTplHeight + 1; i++)
    {
        for (j = 0; j < nWidth - nTplWidth + 1; j++)
        {
            dSumST = 0;
            dSumS = 0;

            for (m = 0; m < nTplHeight; m++)
            {
                for (n = 0; n < nTplWidth; n++)
                {
                    // 原图像第i+m行,第j+n列象素的灰度值
                    int nGraySrc = *src->ptr(i + m, j + n);

                    // 模板图像第m行,第n个象素的灰度值
                    int nGrayTpl = *pTemplate->ptr(m, n);

                    dSumS += (double)nGraySrc*nGraySrc;
                    dSumST += (double)nGraySrc*nGrayTpl;
                }
            }

            R = dSumST / (sqrt(dSumS)*sqrt(dSumT));//计算相关响应

            //与最大相似性比较
            if (R > MaxR)
            {
                MaxR = R;
                nMaxX = j;
                nMaxY = i;
            }
        }
    }

    //将找到的最佳匹配区域复制到目标图像
    for (m = 0; m < nTplHeight; m++)
    {
        for (n = 0; n < nTplWidth; n++)
        {
            int nGray = *src->ptr(nMaxY + m, nMaxX + n);
            //pTo->setTo(nMaxX + n, nMaxY + m, RGB(nGray, nGray, nGray));
            pTo->at<BYTE>(nMaxY + m, nMaxX + n) = nGray;
        }
    }

}   

测试代码

#include"TemplateMatch.h"
#include<iostream>

using namespace std;
int main()
{
    Mat src = imread("./src.jpg", 0);
    Mat Template = imread("./template.jpg", 0);
    Mat pt=src;
    pt.data = new BYTE[src.cols*src.rows];
    memset(pt.data, 255, src.cols*src.rows);
    TemplateMatch(&pt, &Template, &src);
    imshow("S", src);
    imshow("T", Template);
    imshow("P", pt);

    imwrite("S.jpg", src);
    imwrite("T.jpg", Template);
    imwrite("P.jpg", pt);
    waitKey(0);
    return 0;
}

效果如下:
这里写图片描述

    下面给出这个工程的百度云连接:链接:http://pan.baidu.com/s/1jIuuT3w 密码:veto

写的不好的地方,还希望指正,O(∩_∩)O谢谢。
在这我要强烈推荐一本书:《数字图像处理与机器视觉 Visual C 与Matlab实现》,这本书写的很基础,适合初学者和研究人员

Opencv模板匹配运用

阅读数 3524