图像处理模板匹配

2018-07-05 15:24:20 sinat_31425585 阅读数 5843

模板匹配就是按照一定的相似性规则,在一张图像中寻找与模板最相似的区域。

参考opencv官网:https://docs.opencv.org/3.4.1/de/da9/tutorial_template_matching.html

模板匹配的过程如下:

    左上角小狗图像为定义的模板,下面的图片为待匹配图像,匹配的过程就是按照从左到右,从上到下的顺序,依次遍历全图,通过定义的相似度评判准则,计算模板与当前位置图像块的相似度,进而得到最相似位置,实现目标定位。

    常见的相似度准则有:

    即:平方差、归一化平方差、相关系数、归一化相关系数、互相关五种。

    下面就是代码,这里只实现了两种:平方误差及绝对误差

#include <opencv2/opencv.hpp>
#include <math.h>
#include <iostream>

using namespace cv;
using namespace std;

#define SQDIFF 0
#define SADIFF 1

float templateMatch(const Mat & src, const Mat & temp, int & i_match, int & j_match, int Match_methold)
{
	int src_cols = src.cols;
	int src_rows = src.rows;
	int temp_cols = temp.cols;
	int temp_rows = temp.rows;

	int i_end = src_rows - temp.rows + 1;
	int j_end = src.cols - temp.cols + 1;

	float match_degree = FLT_MAX;

	for (int i = 0; i < i_end; i++)
	{
		for (int j = 0; j < j_end; j++)
		{
			float match_ij = 0.0;

			for (int m = 0; m < temp_rows; m++)
			{
				for (int n = 0; n < temp_cols; n++)
				{
					uchar val_s = src.at<uchar>(i + m, j + n);
					uchar val_t = temp.at<uchar>(m, n);
					if (Match_methold == SQDIFF)
					{
						match_ij += float((val_t - val_s) * (val_t - val_s));
					}
					else if (Match_methold == SADIFF)
					{
						match_ij += float(abs(val_t - val_s));
					}
				}
			}
			
			//cout << match_ij << endl;
			if (match_ij < match_degree)
			{
				match_degree = match_ij;
				i_match = i;
				j_match = j;
			}
		}
	}
	

	return match_degree;

}

int main()
{
	Mat src = imread("messi5.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	Mat temp = imread("face.png", CV_LOAD_IMAGE_GRAYSCALE);
	
	float match_degree = 0;
	int i_match = -1, j_match = -1;
	match_degree = templateMatch(src, temp, i_match, j_match, SQDIFF);
	
	cout << i_match << j_match << match_degree << endl;

	Mat src_color;
	cvtColor(src, src_color, COLOR_GRAY2BGR);
	rectangle(src_color, Rect(j_match, i_match, temp.cols, temp.rows), Scalar(255, 0, 0));
	imshow("result", src_color);
	waitKey(0);

	return 0;
}

匹配结果:

改进方法:

1)增加步长

2)随机抽样匹配

    增加步长就不多讨论了,随机抽样,利用的是目标所在矩形区域位置中,目标应该位于矩形区域中央,边界部分大部分为背景,因此考虑对模版进行随机采样,采样方法为:越靠近中心位置,生成越多的采样点,反之,越远离中心位置,生成较少的采样点,这里采用的随机生成函数为Gaussian函数。

int main()
{
	Mat src = imread("messi5.jpg", CV_LOAD_IMAGE_GRAYSCALE);
	Mat temp = imread("messi_face.jpg", CV_LOAD_IMAGE_GRAYSCALE);

	float match_degree = 0;
	int i_match = -1, j_match = -1;

	
	vector <Point2i> real_sample_points;
	computeSamplePoints(real_sample_points, Rect(0, 0, temp.cols - 1, temp.rows - 1));

	match_degree = templateMatch(src, temp, i_match, j_match, SQDIFF, real_sample_points);

	cout << i_match << j_match << match_degree << endl;


	Mat src_color;
	cvtColor(src, src_color, COLOR_GRAY2BGR);
	for (int i = 0; i < 1000; i++){
		circle(src_color, Point(j_match, i_match) + real_sample_points[i], 1, Scalar(0, 255, 0));
	}
	rectangle(src_color, Rect(j_match, i_match, temp.cols, temp.rows), Scalar(255, 0, 0));
	imshow("result", src_color);
	waitKey(0);

	return 0;

}

匹配结果如下:

里面绿色点表示采样位置。

参考资料:

opencv 官网

2019-07-12 18:11:00 weixin_30655569 阅读数 72

试一下多种方式的模板匹配:

dev_close_window ()
read_image(Image, '2008531173479_2')
dev_open_window_fit_image(Image, 0, 0, 512, 512, WindowHandle)
dev_display(Image)

* 从原图中裁切一块作为模板
crop_part(Image, ImagePart, 445, 250, 240, 220)
dev_set_draw('margin')
mean_image(ImagePart, ImageMean, 3, 3)
rgb1_to_gray(ImageMean, GrayImage)
* 将原图缩小到一半
zoom_image_size(GrayImage, GrayImage, 120, 110, 'constant')
* 使用切出来创建一个ncc模板(模板生成方式1)
create_ncc_model(GrayImage, 'auto', -0.39, 0.79, 'auto', 'use_polarity', ModelID)
rotate_image(Image, ImageRotate, 45, 'constant')
rgb1_to_gray(Image, GrayImage1)
* 匹配
find_ncc_model(GrayImage1, ModelID, -0.39, 0.79, 0.8, 1, 0.1, 'true', 0, Row, Column, Angle, Score)
if (|Row| > 0)
    gen_rectangle1(Rectangle, Row - 100, Column - 100, Row + 100, Column + 100)
endif

* 模板生成方式2,这个模板是可以旋转匹配的,也就是说,如果图像旋转了,上面那个模板就匹配不到
create_ncc_model(GrayImage, 'auto', 0, rad(360), 'auto', 'use_polarity', ModelID1)
rgb1_to_gray(ImageRotate, GrayImage2)
find_ncc_model(GrayImage2, ModelID1, 0, rad(360), 0.8, 1, 0.1, 'true', 0, Row1, Column1, Angle1, Score1)
if (|Row1| > 0)
    gen_rectangle2(Rectangle1, Row1, Column1, Angle1, 100, 100)
endif

* 模板生成方式3,这个模板是适合有缩放或有旋转的匹配的,如果模板进行了缩放或者图像进行了缩放,上面那两个模板都会匹配不了
* 旋转角度范围和图片缩放比例可配,如果支持的范围太大的话,会影响匹配速度。
create_scaled_shape_model(GrayImage, 'auto', 0, rad(140), 'auto', 0.5, 10, 'auto', 'auto', 'use_polarity', 'auto', 'auto', ModelID2)
find_scaled_shape_model(GrayImage2, ModelID2, 0, rad(140), 0.5, 10, 0.5, 1, 0.5, 'least_squares', 0, 0.1, Row2, Column2, Angle2, Scale, Score2)
if (|Row2| > 0)
    gen_rectangle2(Rectangle2, Row2, Column2, Angle2, 100, 100)
endif

1032712-20190712160918972-707555669.png

看一下最后一种方式,
create_scaled_shape_model(Template, 输出值,模板
NumLevels, 图像金字塔层数,用来降低匹配时长,每增加一层图像的宽高就会减半,图像分辨率会降低
AngleStart, 旋转开始的弧度
AngleExtend, 旋转的角度范围
AngleStep, 旋转步长,如果不知道怎么设置,可以设置为auto,步长设置的越小,越占用内存,越慢
ScaleMin, 最小缩放倍数
ScaleMax, 最大缩放倍数
ScaleStep, 缩放步长,可以设置为auto
Optimization, 优化方式
Metric, 是否使用极性,如果使用极性,那么如果模板背景灰度弱,前景灰度强,那么匹配的图片也必须满足这样的条件;如果不要求极性,当匹配的图片背景灰度强,前景灰度弱的时候也会匹配到,忽略极性会导致匹配耗时增加
Contrast, 和模板图片对比度相关的一个阈值,可以直接使用auto
MinContrast, 用来匹配的图片的最小对比度
ModelID) 输出值,模板ID

find_scaled_shape_model(Image, 用来查找的图片
ModelID, 模板ID
AngleStart,
AngleExtent,
ScaleMin,
ScaleMax,
MinScore, 匹配的最低分数,大于这个分数的才是被接受的匹配
NumMatches, 需要找的匹配数量
MaxOverlap, 最大重叠
SubPixel, 是否亚像素精度
NumLevels, 图像金字塔层数
Greediness, 搜索贪婪度,0-1,越大搜索越快,可以理解为搜得越不仔细
Row, 搜索到的行坐标
Column, 搜索到的列坐标
Angle, 相对于x轴正半轴的旋转角度
Scale, 图片相对于模板的缩放倍数bc
Score) 得分

搜索到的行列坐标都是匹配上的模板中心坐标。模板的中心坐标默认是模板的原点坐标,可以通过set_shape_model_origin来进行调整。
当模板是对称的时候,需要控制旋转角度来防止搜索到多个角度不同的相同匹配

转载于:https://www.cnblogs.com/yutou2016/p/11177711.html

2019-06-01 20:57:00 Yoci98 阅读数 170

演示用局部图片匹配整张图片

VS2017 + OpenCV4.0.1编译

 1 #include <iostream>
 2 #include <opencv2/opencv.hpp>
 3 #include <opencv2/core.hpp>
 4 #include <opencv2/highgui.hpp>
 5 #include <opencv2/imgproc.hpp>
 6 using namespace std;
 7 using namespace cv;
 8 
 9 //定义全局变量
10 Mat src, temp, res;
11 
12 //显示窗口名
13 string img_window = "source image";
14 string res_window = "result image";
15 
16 int match_method;
17 int max_tracker = 5;
18 
19 //滑动条回调函数
20 void match(int pos, void*)
21 {
22     Mat subImg;
23     src.copyTo(subImg);//拷贝副本
24     int res_row = src.rows - temp.rows + 1;
25     int res_col = src.cols - temp.cols + 1;
26     res.create(res_row, res_col, CV_32FC1);//创建结果矩阵
27 
28     //模板匹配
29     matchTemplate(subImg, temp, res, match_method);
30     normalize(res, res, 0, 1, NORM_MINMAX, -1, Mat());
31 
32     //定位
33     double minVal, maxVal;
34     Point minLoc, maxLoc, matchLoc;
35     minMaxLoc(res, &minVal, &maxVal, &minLoc, &maxLoc, Mat());
36 
37     //分类讨论
38     if (match_method == TM_SQDIFF || match_method == TM_SQDIFF_NORMED)
39         matchLoc = minLoc;
40     else
41         matchLoc = maxLoc;
42 
43     //矩形框显示匹配结果
44     rectangle(subImg, matchLoc, Point(matchLoc.x + temp.cols, matchLoc.y + temp.rows), Scalar(0, 255, 0), 2);
45     rectangle(res, matchLoc, Point(matchLoc.x + temp.cols, matchLoc.y + temp.rows), Scalar(0, 255, 0), 2);
46     
47     imshow(img_window, subImg);
48     imshow(res_window, res);
49 }
50 
51 int main()
52 {
53     //读入原图和模板图
54     src = imread("D:\\trashBox\\testIMG\\memory.jpg");
55     temp = imread("D:\\trashBox\\testIMG\\temp.png");
56     imshow("模板", temp);
57     
58     //创建显示窗口
59     namedWindow(img_window);
60     namedWindow(res_window);
61 
62     //创建滑动条
63     match(2, 0);
64     createTrackbar("匹配方法", img_window, &match_method, max_tracker, match);
65     
66     waitKey(0);
67     return 0;
68 }

参考资料

【1】http://www.opencv.org.cn/opencvdoc/2.3.2/html/doc/tutorials/imgproc/histograms/template_matching/template_matching.html#template-matching

2017-08-16 22:05:16 wu_lian_nan 阅读数 5087

一、理论基础

  • 基于相关的模板匹配技术可直接用于在一幅图像中,寻找某种子图像模式。对于大小为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实现》,这本书写的很基础,适合初学者和研究人员

2018-05-30 15:31:55 u014403318 阅读数 3120

注释:本文翻译自OpenCV3.0.0 document->OpenCV-Python Tutorials,包括对原文档种错误代码的纠正

3.10 OpenCV中的图像变换

第一节:傅里叶变换(Fourier Transform)

1.目标

  • 使用OpenCV查找图像的傅里叶变换
  • 利用Numpy中可用的FFT函数
  • 傅里叶变换的一些应用
  • 我们将学习以下函数:cv2.dft()、cv2.idft()等

2.原理

傅里叶变换用于分析各种滤波器的频率特性。对于图像,使用2D离散傅里叶变换(DFT)来查找频域。称为快速傅里叶变换(FFT)的快速算法用于计算DFT。有关这些细节可以在任何图像处理或信号处理教科书中找到。请参考其它资源部份。

对于一个正弦信号, ,我们可以说f是信号的频率,如果取其频率域,我们可以在f处看到一个尖峰。如果信号被采样以形成离散信号,我们得到相同的频域。但是在范围 你可以将图像视为在两个方向上采样的信号。因此,在X方向和Y方向进行傅里叶变换将为你提供图像的频率表示。

更直观地说,对于正弦信号,如果幅度在短时间内变化得如此之快,则可以说它是高频信号。如果变化缓慢,则是低频信号。你可以将相同的想法扩展到图像。图像中振幅的变化幅度如何?在边缘点,或噪音?所以我们可以说,边缘和噪音是图像中的高频内容,如果振幅没有太大的变化,则它是低频分量。(一些链接被添加到附加资源中,通过示例直观地解释频率转换)。

现在我们将看到如何找到傅里叶变换。

3.Numpy中的傅里叶变换

首先,我们将看到如何使用Numpy查找傅里叶变换。Numpy有一个FFT软件包来做到这一点。np.fft.fft2()为我们提供了频率变换,这将是一个复杂的数组。它的第一个参数是灰度的输入图像,第二个参数是可选的,它决定输出数组的大小。如果它大于输入图像的大小,则在计算FFT之前,输入图像用0填充。如果它小于输入图像,输入图像将被裁剪。如果没有参数传递,则输出数组大小将与输入相同。

现在一旦得到结果,零频率分量(直流分量)将位于左上角。如果要将它置于中心,则需要在两个方向上将结果移动 这只是通过np.fft.fft2()完成的(分析起来更容易)。一旦你找到频率变换,就可以找到幅度谱。

示例如下:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('5.jpg', 0)
f = np.fft.fft2(img)  # 快速傅里叶变换算法得到频率分布
fshift = np.fft.fftshift(f)  # 默认结果中心点位置是在左上角,转移到中间位置
magnitude_spectrum = 20 * np.log(np.abs(fshift))  # 结果是复数,求绝对值才是振幅

# 结果展示
plt.subplot(121), plt.imshow(img, 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(magnitude_spectrum, 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()

结果:


看,你可以在中心看到更多更白的区域,显示低频内容更多。

所以你找到了频率变换,现在你可以在频域中做一些操作,如高通滤波和重构图像,即找到逆DFT。为此,你只需通过使用尺寸为60*60的矩形窗口进行遮盖来移除低频。然后使用np.fft.ifftshit()应用反向偏移,以便DC分量再次出现在左上角。然后使用np.ifft2()函数来找到反FFT。结果也是一个复杂的数字。你可以拿它的绝对值。


import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('5.jpg', 0)
f = np.fft.fft2(img)  # 快速傅里叶变换算法得到频率分布
fshift = np.fft.fftshift(f)  # 默认结果中心点位置是在左上角,转移到中间位置
magnitude_spectrum = 20 * np.log(np.abs(fshift))  # 结果是复数,求绝对值才是振幅

# 找到频域变换后,可以在频域中做一些操作,如高通滤波和重构图像,即找到逆DFT
rows, cols = img.shape
crow, ccol = rows / 2, cols / 2
fshift[crow - 30:crow + 30, ccol - 30:ccol + 30] = 0
f_ishift = np.fft.ifftshift(fshift)
img_back = np.fft.ifft2(f_ishift)
img_back = np.abs(img_back)

plt.subplot(131), plt.imshow(img, 'gray')
plt.title("Input Image"), plt.xticks([]), plt.yticks([])
plt.subplot(132), plt.imshow(img_back, 'gray')
plt.title('Image After HPF'), plt.xticks([]), plt.yticks([])
plt.subplot(133), plt.imshow(img_back)
plt.title('Result in JET'), plt.xticks([]), plt.yticks([])

plt.show()

结果:


结果显示高通滤波是边缘检测操作。这就是我们在Image Gradients章节看到的。这也表明大部分图像数据存在于频谱的低频区域。无论如何,我们已经看到如何在Numpy中找到DFT,IDFT等。现在让我们看看如何在OpenCV中完成它。

如果仔细观察结果,尤其是JET颜色中的最后一幅图像,可以看到一些工作,它显示了一些波纹状结构,它被称为震荡效应。这是由于我们用于掩蔽的矩形窗口引起的。这个蒙版被转换为正弦形状,导致了问题。所以矩形窗口不用于过滤。更好的选择是高斯窗口。


4.OpenCV中的傅里叶变换

OpenCV为此提供了cv2.dft()cv2.idft().它返回与以前相同的结果,但具有两个通道。第一个通道将具有结果的实际部分,第二个通道将具有结果的虚部。输入图像应首先转换为np.float32.我们将看到如何去做。

注意:你也可以使用cv2.cartToPolar(),它可以一次性返回幅值和相位。

所以,现在我们必须做逆DFT。在之前的会话中,我们创建了一个HPF,这次我们将看到如何去除图像中的高频内容,即我们将LPF应用于图像。它实际上模糊了图像,为此,我们首先在低频处创建一个高值(1)的掩膜,即我们通过LF内容,在HF区域传递0.


# -*- coding: utf-8 -*-
'''
OpenCV中的傅里叶变换:
1.cv2.dft()和cv2.idft(),输入图像应先转换成np.float32
attention:也可以使用cv2.cartToPolar(),可以一次性返回幅值和相位
          cv2.dft()和cv2.idft()比Numpy对应的函数更快,但是Numpy函数更友好。
'''

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('5.jpg', 0)
dft = cv2.dft(np.float32(img), flags=cv2.DFT_COMPLEX_OUTPUT)
dft_shift = np.fft.fftshift(dft)

magnitude_spectrum = 20 * np.log(cv2.magnitude(dft_shift[:, :, 0], dft_shift[:, :, 1]))
'''
plt.subplot(121), plt.imshow(img, 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(magnitude_spectrum, 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])
plt.show()
'''

rows, cols = img.shape
crow, ccol = int(rows / 2), int(cols / 2)  # 这里还是必须转换为整数哦

# 首先创建一个掩膜,中心点为1,其余值为0
mask = np.zeros((rows, cols, 2), np.uint8)
mask[crow - 30:crow + 30, ccol - 30: ccol + 30] = 1

# 应用掩膜和逆DFT
fshift = dft_shift * mask
f_ishift = np.fft.ifftshift(fshift)
img_back = cv2.idft(f_ishift)

img_back = cv2.magnitude(img_back[:, :, 0], img_back[:, :, 1])
plt.subplot(121), plt.imshow(img, 'gray')
plt.title('Input Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122), plt.imshow(img_back, 'gray')
plt.title('Magnitude Spectrum'), plt.xticks([]), plt.yticks([])

plt.show()

结果:



5.DFT的性能优化

对于某些数组大小,DFT计算的性能更好。当阵列大小是2的幂时,它是最快的。大小为2,3和5的乘积的数组也被相当有效地处理。因此,如果你担心代码的性能,可以在查找DFT之前将数组的大小修改为任何最佳大小(通过填充零)。对于OpenCV,你必须手动填充零。但对于Numpy,你指定了FFT计算的新大小,并且它会自动为你填充零。

那么我们如何寻找这个最佳尺寸?OpenCV为此专门提供了一个函数cv2.getOptimalDFTSize().它适用于cv2.dft()np.fft.fft2()。让我们使用Python的命令好%timeit来检查它们的性能。

In [16]: img = cv2.imread('messi5.jpg',0)
In [17]: rows,cols = img.shape
In [18]: print rows,cols
342 548
 
In [19]: nrows = cv2.getOptimalDFTSize(rows)
In [20]: ncols = cv2.getOptimalDFTSize(cols)
In [21]: print nrows, ncols
360 576

看,(342,548)被修改为(360,576).现在让我们用零来填充(对于OpenCV)并且找到它们的DFT计算性能。你可以通过创建一个新的空数组并将数据复制给它,或者使用cv2.copyMakeBorder()来完成。

nimg = np.zeros((nrows,ncols))
nimg[:rows,:cols] = img

或者:

right = ncols - cols
bottom = nrows - rows
bordertype = cv2.BORDER_CONSTANT #just to avoid line breakup in PDF file
nimg = cv2.copyMakeBorder(img,0,bottom,0,right,bordertype, value = 0)

现在我们来计算Numpy函数的DFT性能比较:

In [22]: %timeit fft1 = np.fft.fft2(img)
10 loops, best of 3: 40.9 ms per loop
In [23]: %timeit fft2 = np.fft.fft2(img,[nrows,ncols])
100 loops, best of 3: 10.4 ms per loop

它显示了四倍的加速,现在我们尝试OpenCV函数。

In [24]: %timeit dft1= cv2.dft(np.float32(img),flags=cv2.DFT_COMPLEX_OUTPUT)
100 loops, best of 3: 13.5 ms per loop
In [27]: %timeit dft2= cv2.dft(np.float32(nimg),flags=cv2.DFT_COMPLEX_OUTPUT)
100 loops, best of 3: 3.11 ms per loop

它也显示了四倍加速。你还可以看到OpenCV函数比Numpy函数大约快倍。这也可以针对逆FFT进行测试。对你来说是一个练习。

6.为什么拉普拉斯算子是高通滤波器

在论坛上提出了类似的问题。问题是,为什么拉普拉斯是高通滤波器?为什么Sobel是HPF,等等。第一个答案就是傅里叶变换。只需对拉普拉斯算子进行傅里叶变换即可获得更高的FFT大小。分析它:

# -*- coding: utf-8 -*-
'''
集中不同滤波器的对比:均值滤波,高斯滤波、scharr滤波、sobel_x,sobel_y,拉普拉斯算子
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt

#简单的均值滤波器没有参数
mean_filter=np.ones((3,3))

#创建一个高斯滤波器
x=cv2.getGaussianKernel(5,10)
gaussian=x*x.T

#不同的边缘检测滤波器
#x方向上的scharr
scharr=np.array([[-3,0,3],
                 [-10,0,10],
                 [-3,0,3]])

#x方向上的sobel
sobel_x=np.array([[-1,0,1],
                  [-2,0,2],
                  [-1,0,1]])

#y方向上的sobel
sobel_y=np.array([[-1,-2,-1],
                  [0,0,0],
                  [1,2,1]])

#拉普拉斯算子
laplacian=np.array([[0,1,0],
                    [1,-4,1],
                    [0,1,0]])

filters=[mean_filter,gaussian,scharr,sobel_x,sobel_y,laplacian]
filter_name=['mean_filter','gaussian','scharr','sobel_x','sobel_y','laplacian']
fft_filter=[np.fft.fft2(x) for x in filters]
fft_shift=[np.fft.fftshift(y) for y in fft_filter]
#地图谱
map_spectrum=[np.log(np.abs(z)+1) for z in fft_shift]

for i in range(6):
    plt.subplot(2,3,i+1),plt.imshow(map_spectrum[i],'gray')
    plt.title(filter_name[i]),plt.xticks([]),plt.yticks([])

plt.show()

结果:


从图像中,可以看到每个内核阻塞的频率区域以及它传递的区域。从这些信息中,我们可以说出为什么每个内核都是HPF或LPF。


3.11 模板匹配

1.目标

  • 使用模板匹配查找图像中的对象
  • 学会这些函数:cv2.matchTemplate()、cv2.minMaxLoc()

2.原理

模板匹配是一种在较大图像中搜索和查找模板图像位置的方法。OpenCV为此提供了函数cv2.matchTemplate().它只是将模板图像滑过输入图像(如2D卷积),并比较模板图像下的输入图像的模板和补丁。OpenCV中实现了集中比较方法。(你可以查看文档了解更多详情)。它返回有一个灰度图像,其中每个像素表示该像素的领域和模板匹配的程度。

如果输入 图像的大小是(W*H)和模板图像的大小(w*h),则输出图像的大小为(W-w+1,H-h+1)。一旦得到结果,就可以使用cv2.minMaxLoc()函数来查找最大值/最小值。将它作为矩形的左上角,并将(w,h)作为矩形的宽度和高度。该矩形是你的模板区域。

注意:如果你使用cv2.TM_SQDIFF作为比较方法,最小值会给出最佳匹配。


3.OpenCV中的模板匹配

在这个栗子中将在图像中搜索下面同学的这张脸,所以创建模板如下:


我们将尝试所有比较方法,以便我们可以看到它们的结果如何:

# -*- coding: utf-8 -*-
'''
OpenCV中的模板匹配
1.cv2.matchTemplate()、cv2.minMaxLoc()
2.OpenCV中实现了集中比较方法,返回一个灰度图像,其中每个像素表示该像素的领域和模板匹配的程度
3.输入图像大小若是(w*h),则输出图像大小为(W-w+1,H-h+1).一旦得到结果,可以使用cv2.minMaxLoc()函数来查找最大值和最小值
下面的栗子:在图像中搜索一张脸
可以看到cv2.TM_CCORR的结果并不如预期那样好
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('1.jpg', 0)
img2 = img.copy()
template = cv2.imread('template.jpg', 0)

w, h = template.shape[::-1]
# 6中类型的比较
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
           'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']

for meth in methods:
    img = img2.copy()
    method = eval(meth)
    # 应用模板匹配
    res = cv2.matchTemplate(img, template, method)
    min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)

    # 如果方法是cv2.TM_SQDIFF或cv2.TM_SQDIFF_NORMED
    if method in [cv2.TM_SQDIFF, cv2.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc

    bottom_right = (top_left[0] + w, top_left[1] + h)
    cv2.rectangle(img, top_left, bottom_right, 255, 2)

    plt.subplot(121), plt.imshow(res, 'gray')
    plt.title('Matching Result'), plt.xticks([]), plt.yticks([])
    plt.subplot(122), plt.imshow(img, 'gray')
    plt.title('Detected Point'), plt.xticks([]), plt.yticks([])
    plt.suptitle(meth)

    plt.show()

结果:

cv2.TM_CCOEFF


cv2.TM_CCOEFF_NORMED


cv2.TM_CCORR


cv2.TM_CCORR_NORMED


cv2.TM_SQDIFF


cv2.TM_SQDIFF_NORMED

你可以看到cv2.TM_CCORR的结果并不像我们预期的那样好。


4.多目标的模板匹配

在上一节中,我们搜索了梅西的脸部图像,该图像只在图像中出想过一次。假设你正在搜索一个多次出现的对象,cv2.minMaxLoc()不会给你所有的位置。在这种情况下,我们将使用阈值。所以在这个例子中,我们将使用游戏Mario的屏幕截图,并在其中找到硬币。

# -*- coding: utf-8 -*-
'''
多目标模板匹配
'''

import cv2
import numpy as np
from matplotlib import pyplot as plt

img_rgb = cv2.imread('2.jpg')
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
template = cv2.imread('temp.jpg', 0)
w, h = template.shape[::-1]

res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED)
threshold = 0.8
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
    cv2.rectangle(img_rgb, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)

cv2.imwrite('res.jpg', img_rgb)
test = cv2.imread('2.jpg')
cv2.imshow('original', test)
cv2.imshow('res', img_rgb)
cv2.waitKey(0) & 0xFF
cv2.destroyAllWindows()

结果: