2018-08-15 17:10:44 qq_41905045 阅读数 142

目标

在这一章中,你将学习

使用模板匹配来查找图像中的对象

您将看到这些功能:cv.matchTemplate()、cv.minMaxLoc()

理论

模板匹配是一种在较大的图像中搜索和查找模板图像位置的方法。OpenCV有一个函数cv.matchTemplate()来实现这个目的。它只是将模板图像滑过输入图像(就像在2D卷积中一样),并比较模板图像下的输入图像的模板和补丁。在OpenCV中实现了几种比较方法。(你可以查看更多的详细信息)。它返回一个灰度图像,其中每个像素表示该像素的邻居与模板匹配的程度。

如果输入图像大小(WxH)和模板图像大小(WxH),输出图像将有一个大小(W-w+1、H-h+1)的大小。一旦您得到了结果,您就可以使用cvminmaxloc()函数来找到最大值/最小值的位置。将它作为矩形的左上角,并将(w,h)作为矩形的宽度和高度。这个矩形是你的模板区域。

请注意

如果你使用的是cv。tmsqdiff作为比较方法,最小值提供了最好的匹配。

OpenCV的模板匹配

在这里,作为一个例子,我们将在他的照片中寻找梅西的脸。所以我创建了一个模板如下:

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

img = cv.imread('lenna.jpg',0)
img2 = img.copy()
template = cv.imread('lennatou.jpg',0)
w, h = template.shape[::-1]

# All the 6 methods for comparison in a list
methods = ['cv.TM_CCOEFF', 'cv.TM_CCOEFF_NORMED', 'cv.TM_CCORR',
            'cv.TM_CCORR_NORMED', 'cv.TM_SQDIFF', 'cv.TM_SQDIFF_NORMED']

for meth in methods:
    img = img2.copy()
    method = eval(meth)

    # Apply template Matching
    res = cv.matchTemplate(img,template,method)
    min_val, max_val, min_loc, max_loc = cv.minMaxLoc(res)

    # If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
    if method in [cv.TM_SQDIFF, cv.TM_SQDIFF_NORMED]:
        top_left = min_loc
    else:
        top_left = max_loc
    bottom_right = (top_left[0] + w, top_left[1] + h)

    cv.rectangle(img,top_left, bottom_right, 255, 2)

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

    plt.show()

找连连看中的所有橘子

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

img_rgb = cv.imread('llk.jpg')
img_gray = cv.cvtColor(img_rgb, cv.COLOR_BGR2GRAY)
template = cv.imread('llkjz.jpg',0)
w, h = template.shape[::-1]

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

cv.imwrite('res.png',img_rgb)

 

 

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

注释:本文翻译自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()

结果:



2018-10-17 08:36:03 claroja 阅读数 195

模板匹配是在较大的图像中搜索和查找模板图像的位置的方法。
将模板图像划过输入图像,并比较模板图像下的输入头图像的模板补丁。

参考文献:
https://blog.csdn.net/qq_41905045/article/details/81707019
https://docs.opencv.org/3.4.3/d4/dc6/tutorial_py_template_matching.html

2019-09-05 14:28:21 qq_28691955 阅读数 80

形态学

滤波器分为线型滤波与非线性滤波。线型滤波,是指两个信号之和的响应和它们各自响应之和相等,即每个像素的输出值是一些输入像素的加权和。线型滤波器主要有方框滤波、均值滤波和高斯滤波。非线性滤波主要有中值滤波和双边滤波。

邻域算子(局部算子)是利用给定像素周围的像素值决定此像素的最终输出值的一种算子。线性邻域滤波就是属于一种常用的邻域算子。

在这里插入图片描述

在这里插入图片描述
h(k,l)称之为核,是滤波器的加权系数。

方框滤波

​ 方框滤波(box Filter)被封装在 boxblur 函数中。函数原型为:

void boxFilter(InputArray src,OutputArray dst,int ddepth,Size ksize,Point anchor=Point(-1,-1),boolnormalize=true,int borderType=BORDER DEFAULT)
/*
第一个参数是输入图像。
第二个参数即目标图像。
第三个参数是输出图像的深度,-1代表使用原图深度。
第四个参数是内核的大小。一般用size(w,h)表示。
第五个参数是锚点(即被平滑的那个点)。默认值Point(-1-1)表示这个锚点在核的中心。
第六个参数默认值为true,表示内核被其区域归一化了。
第七个参数一般不去管它。*/

​ 方框滤波的核如下,当第六个参数为true,被归一化时,就变成了均值滤波。归一化就是把要处理的量缩放在一个范围内,以便统一处理核直观量化。

在这里插入图片描述

均值滤波

​ 均值滤波,是核窗口内输入图像对应像素的平均值。它的缺陷是,它不能很好地保护图像细节,去燥的同时破坏了细节。

void blur(InputArray src,OutputArray dst,Size ksize,Point anchor=Point(-1,-1),int borderType=BORDER DEFAULT)
//第五个参数一般不用管它

高斯模糊

​ 高斯滤波,广泛应用于图像处理的减噪过程。通俗讲,每个像素的值都是由本身和邻域内的其他像素值经过加权平均得到。高斯模糊过程就是图像与正态分布做卷积。所以这项技术就叫作高斯模糊。高斯模糊得到的图像,它的视觉效果就像是经过一个半透明屏幕在观察图像,

高斯滤波器是一类根据高斯函数的形状来选择权值的线性平滑滤波器。高斯平滑滤波器对于抑制服从正态分布的噪声非常有效。

void GaussianBlur(InputArray src,OutputArray dst,Size ksize, double sigmax,double sigmaY=0,intborderType=BORDER_DEFAULT)
/*
第四个参数表示高斯核函数在X方向的的标准偏差。
第五个参数表示高斯核函数在Y方向的的标准偏差。
第六个参数一般不管。*/

若噪声是散粒噪声,即图像偶尔会出现很大的值,用高斯滤波器处理,噪声像素是不会被去除的,使用非线性滤波则会得到更好的效果。

中值滤波

​ 中值滤波是一种典型的非线性滤波技术,基本思想是用像素点邻域灰度值的中值来代替该像素点的灰度值该方法在去除脉冲噪声、椒盐噪声的同时又能保留图像的边缘细节。

​ 与均值滤波相比,中值滤波在消除噪声和保存边缘方面都很好,但是它花费的时间是均值滤波的5倍以上,并且不适合一些细节多的图像。

void medianBlur(InputArray src,OutputArray dst,int ksize)
//第三个参数是孔径的线性尺寸,必须是大于1的奇数。

双边滤波

​ 双边滤波是结合图像的空间邻近度和像素值相似度的一种折中处理,同时考虑空域信息和灰度相似性,达到保边去噪的目的,具有简单、非迭代、局部的特点。

​ 双边滤波器比高斯滤波多了一个高斯方差sigma-d,它是基于空间分布的高斯滤波函数,所以在边缘附近,离得较远的像素不会对边缘上的像素值影响太多,这样就保证了边缘附近像素值的保存。

void bilateralFilter(InputArray src,OutputArraydst,int d,double sigmaColor,double sigmaSpace,int borderType=BORDER_DEFAULT)
/*第三个参数表示在过滤过程中每个像素邻域的直径。
第四个参数是颜色空间滤波器的sigma值。这个参数的值越大,就表明该像素邻域内有越宽广的颜色会被混合到一起,产生较大的半相等颜色区域。
第五个参数是坐标空间中滤波器的sigma值,坐标空间的标注方差。它的数值越大,意味着越远的像素会相互影响。
第六个参数有默认值。

腐蚀与膨胀

腐蚀和膨胀是对白色部分(高亮部分),不是黑色部分。膨胀是图像中的高亮部分进行膨胀,类似于“领域扩张”,效果图拥有比原图更大的高亮区域;腐蚀是原图中的高亮部分被腐蚀,类似于“领域被蚕食”,效果图拥有比原图更小的高亮区域。

​ 膨胀就是求局部最大值的操作。核B与图形卷积,即计算核B覆盖的区域的像素点的最大值,并把这个最大值赋值给参考点指定的像素。这样就会使图像中的高亮区域逐渐增长,如图6.20所示。这就是膨胀操作的初衷。

在这里插入图片描述

void dilate(
InputArray src,               //输入图像
OutputArray dst,			 //输出图像
InputArray kernel,            //膨胀操作的核 
Point anchor=Point(-1-1),  //锚点位置
int iterations=1//迭代erode()的次数,默认为1
int borderType=BORDER_CONSTANT,//默认值
const Scalar&borderValue=morphologyDefaultBorderValue()//一般不用管
);

一般使用函数getStructuringElement配合第三个参数的使用。getStructuringElement函数会返回指定形状和尺寸的结构元素(内核矩阵)。其中,getStructuringElement的第一个参数表示内核的形状,第二第三个参数分别是核的尺寸与锚点位置。

腐蚀就是求局部最小值的操作。腐蚀操作示例

在这里插入图片描述

void erode(
InputArray src,
OutputArray dst,
InputArray kernel,
Point anchor=Point(-1-1),
int iterations=1int borderType=BORDER CONSTANT,
const Scalar&borderValue=morphologyDefaultBorderValue()
);

开运算与闭运算

开运算就是先腐蚀后膨胀的过程,能够排除小团块物体。

dst=open(src,element)=dilate(erode(src,element))

闭运算就是先膨胀后腐蚀的过程,能够排除小型黑洞。

dst=clese(src,element)=erode(dilate(src,element))

形态学梯度

形态学梯度是膨胀图与腐蚀图之差,能够保留边缘轮廓,数学表达式如下:

dst=morph-grad(src,element)=dilate(src,element)-erode(src,element)

顶帽

顶帽是原图像与开运算结果之差。顶帽运算往往用来分离比邻近点亮一些的斑块。在一幅图像具有大幅的背景,而微小物品比较有规律的情况下,可以使用顶帽运算进行背景提取。

dst=tophat(src,element)=src-open(src,element)

黑帽

黑帽是闭运算结果图与原图像之差。黑帽运算用来分离比邻近点暗一些的斑块,效果图有着非常完美的轮廓。

dst=blackhat (src,element)=close(src,element)-src

c++:void morphologyEx(
	InputArray src,
    OutputArray dst,
    int op, 
    InputArray kernel,                //内核
    Pointanchor=Point(-1,-1),         //锚点位置
    intiterations=1,                  //迭代次数默认为1
    intborderType=BORDER_CONSTANT,    //默认值
    constScalar& borderValue=morphologyDefaultBorderValue());//一般不用管

第三个参数表示形态学运算的类型。如下表

标识符 含义
MORPH_OPEN 开运算
MORPH_CLOSE 闭运算
MORPH_GRADIENT 形态学梯度
MORPH_TOPHAT 项帽
MORPH_BLACKHAT 黑帽
MORPH_ERODE 腐蚀
MORPH_DILATE 膨胀

直方图与匹配

图像直方图

​ 图像直方图是用以表示数字图像中亮度分布的直方图,标绘了图像中每个亮度值的像素数。可以借助观察该直方图了解需要如何调整亮度分布。

​ 直方图的意义如下。

  1. 直方图是图像中像素强度分布的图形表达方式。
  2. 它统计了每一个强度值所具有的像素个数。

直方图的计算与绘制

使用calcHist()函数可以来计算直方图,计算完成之后,可以使用绘制矩形rectangle()函数或绘制线段的line()函数来完成。

计算直方图
void calcHist(
    const Mat*images,     //输入的数组集
    int nimages,          //输入数组的个数,即第一个参数存放图像的张数
    const int* channels,  //统计的通道索引
    InputArray mask,      //可选的操作掩码
    OutputArray hist,     //输出的目标直方图,一个二维数组。
    int dims,             //直方图的维数,必须是正数
    const int* histSize,  //存放每个维度的直方图尺寸的数组
    const float**ranges,  //每一维数组的取值范围
    bool uniform=true,    //直方图是否均匀的标识符
    bool accumulate=false)//累计标识符,默认false

寻找最值

在数组中找到全局最小值和最大值。

void minMaxLoc(InputArray src,double* minVal,double* maxVal=0, Point*minLoc=0,Point*maxLoc=0,InputArray mask=noArray())

直方图均衡化

直方图均衡化是通过拉伸像素强度分布范围来增强图像对比度的一种方法.直方图均衡化是灰度变换的一个重要应用,广泛应用于图像增强处理中。均衡化图像的动态范围扩大了,但其本质是扩大了量化间隔,而量化级别反而减少了。

void equalizeHist(InputArray src,OutputArray dst)//第二个参数需和原图像同样尺寸和类型

直方图对比

要对两个直方图(比如说H和H2)进行比较,首先必须选择一个衡量直方图相似度的对比标准d(H1,H2),返回值是d(H1,H2)

double compareHist(InputArray H1,InputArray H2,int method)
//前两个参数是要比较的大小相同的直方图,第三个变量是所选择的距离标准。

第三个参数可采用4种方法,1、相关2、卡方3、直方图相交4、Bhattacharyya距离。

模板匹配

​ 模板匹配是在一幅图像中寻找与另一幅模板图像最匹配(相似)部分的技术。模板匹配由MatchTemplate()函数完成。模板匹配不是基于直方图的,而是通过在输入图像上滑动图像块,对实际的图像块和输入图像进行匹配的一种匹配方法。

matchTemplate()用于匹配出和模板重叠的图像区域。

void matchTemplate(
    InputArray image,            //待搜索图像
    InputArray temp1,            //搜索模板
    OutputArray result,          //比较结果的映射图像
    int method)

第四个参数是指定的匹配方法,有1、平方差匹配法2、归一化平方差匹配法3、相关匹配法4、归一化相关匹配法5、系数匹配法6、化相关系数匹配法

图像轮廓与图像分割修复

查找并绘制轮廓

一个轮廓一般对应一系列的点,也就是图像中的一条曲线。在OpenCV中,可以用findContoursO函数从二值图像中查找轮廓。

寻找轮廓(在二值图像中寻找轮廓)

void findContours(
    InputOutputArray image,         //输入图像
    OutputArrayofArrays contours,   //运算结果存在这里
    OutputArray hierarchy,          //可选的输出向量
    int mode,                       //轮廓检索模式
    int method,                     //轮廓的近似办法
    Point offset=Point())           //每个轮廓点的可选偏移量有默认值

绘制轮廓(绘制外部或内部轮廓)

void drawContours(
    InputoutputArray image,            //目标图像
    InputArrayofArrayscontours,        //所有的输入轮廓
    int contourIdx,                    //轮廓绘制指示变量
    const Scalar& color,               //轮廓颜色
    int thickness=1,                   //轮廓线条粗细
    int lineType=8,                    //线条类型
    InputArray hierarchy=noArray(),    //可选的层次结构,有默认值
    int maxLevel=INT_MAX,              //绘制的轮廓最大等级,有默认值
    Point offset=Point())              //可选的轮廓偏移参数 

寻找物体凸包

​ 给定二维平面上的点集,凸包就是将最外层的点连接起来构成的凸多边型,它是能包含点集中所有点的。理解物体形状或轮廓的一种比较有用的方法便是计算一个物体的凸包,然后计算其凸缺陷。很多复杂物体的特性能很好地被这种缺陷表现出来。
寻找凸包函数convexHul()函数

void convexHul1(
    InputArray points,              //输入的二维点集
    OutputArray hull,               //找到的凸包
    bool clockwise=false,           //操作方向标识符
    bool returnPoints=true)         //操作标志符

第三个参数为真时,输出的凸包为顺时针,否则逆时针。第四个参数为真时,返回凸包的各个点,否则返回各点的指数。

使用多边形将轮廓包围

在实际应用中,常常会有将检测到的轮廓用多边形表示出来的需求。或者说如何根据轮廓提取出多边形。

返回外部矩形边界
Rect boundingRect(InputArray points)
返回最小包围矩形
RotatedRect minAreaRect(InputArray points)
返回最小包围圆
void minEnclosingCircle(InputArray points,Point2f& center,float& radius)
用椭圆拟合二维点集
RotatedRect fitEllipse(InputArray points)
逼近多边形曲线
void approxPolyDP(InputArray curve,OutputArray approxcurve, double epsilon,bool closed)

图像的矩

一个从一幅数字图形中计算出来的矩集,通常描述了该图像形状的全局特征,并提供了大量的关于该图像不同类型的几何特性信息,比如大小、位置、方向及形状等。

一阶矩与形状有关,二阶矩显示曲线围绕直线平均值的扩展程度,三阶矩则是关于平均值的对称性的测量。由二阶矩和三阶矩可以导出一组共7个不变矩。而不变矩是图像的统计特性,满足平移、伸缩、旋转均不变的不变性,在图像识别领域得到了广泛的应用。

矩的计算

Moments moments(
    InputArray array,                //输入参数
    bool binaryImage=false)          //默认值false

计算轮廓面积

double contourArea(
    InputArray contour,      //输入的向量
    bool oriented=false)     //区域标识符,默认false

计算轮廓长度

double arcLength(
	InputArray curve,     //输入的二维点集
    bool closed            //曲线是否封闭的标识符 
)

图像修补

图像修补,就是利用那些已经被破坏区域的边缘,即边缘的颜色与结构,繁殖和混合到损坏的图像中,以达到图像修补的目的。

如果被破坏的区域不是太大,并且在被破坏区域边缘包含足够多的纹理和颜色,那么图像修补技术可以很好地恢复图像,当图像损坏区域过大时,图像修补的能力也是有限的。

void inpaint( 
    InputArray src,               //输入图像
    InputArray inpaintMask,       //修复掩码
    OutputArray dst,              //输出图像
    double inpaintRadius,         //修复算法的参考半径
    int flags );                  //修复算法标识符,有两种方法

ORB特征提取

ORB是brief算法的改进版。ORB算法比sift算法效率高两个数量级,而在计算速度上,ORB是sift的100倍,是surf的10倍。

在这里插入图片描述

ORB算法分为两步,分别是特征点提取和特征点描述。特征提取是由FAST算法发展来的,特征点描述是根据BRIEF特征描述算法改进的。ORB特征是将FAST特征点的检测方法与BRIEF特征描述子结合起来,并在它们原来的基础上做了改进与优化。

特征提取

  1. 粗提取。从图像中选取一点P,我们判断该点是不是特征点的方法是,以P为圆心画一个半径为3pixel的圆。圆周上如果有连续n个像素点的灰度值比P点的灰度值大或者小,则认为P为特征点。一般n设置为12。
  2. 机器学习的方法筛选最优特征点。简单来说就是使用ID3算法训练一个决策树,将特征点圆周上的16个像素输入决策树中,以此来筛选出最优的FAST特征点。
  3. 非极大值抑制去除局部较密集特征点。为每一个特征点计算出其响应大小。计算方式是特征点P和其周围16个特征点偏差的绝对值和。在比较临近的特征点中,保留响应值较大的特征点,删除其余的特征点。
  4. 特征点的尺度不变形。建立金字塔,来实现特征点的多尺度不变性。
  5. 特征点的旋转不变性。通过矩来计算特征点以r为半径范围内的质心,特征点坐标到质心形成一个向量作为该特征点的方向。矩定义如下:

特征描述

  1. 使用统计学习的方法特征点邻域内选择点对集合。
  2. 进行T操作,生成长度为n的二进制串,作为特征描述符。

特征点匹配

用构建的特征描述子(二进制序列)进行比较,相似度=(相同的个数)/(总个数);这就要求两个特征的描述子大小和顺序必须相同。

//ORB是一个纯虚类,opencv3中提供了静态成员函数以供使用
Static Public Member Functions:
static Ptr< ORB > 	create (int nfeatures=500, float scaleFactor=1.2f, int nlevels=8, int edgeThreshold=31, int firstLevel=0, int WTA_K=2, int scoreType=ORB::HARRIS_SCORE, int patchSize=31, int fastThreshold=20)

//使用实例
Ptr<ORB> orb = ORB::create();
orb->detectAndCompute(rgbd1, Mat(), Keypoints1, descriptors1);
2016-01-05 22:19:20 gu_gu_ 阅读数 6374

最近看图像处理反向投影,看到几本书上说的都不是很清楚,自己也不是十分的理解。就在网上找资料,终于找到了一个说的非常直白清楚的文章:原文

现贴出非常核心的原理:
假设我们有一张100x100的输入图像,有一张10x10的模板图像,查找的过程是这样的:
(1)从输入图像的左上角(0,0)开始,切割一块(0,0)至(10,10)的临时图像;
(2)生成临时图像的直方图;
(3)用临时图像的直方图和模板图像的直方图对比,对比结果记为c;
(4)直方图对比结果c,就是结果图像(0,0)处的像素值;
(5)切割输入图像从(0,1)至(10,11)的临时图像,对比直方图,并记录到结果图像;
(6)重复(1)~(5)步直到输入图像的右下角。

其中第3步用到的进行直方图对比方法应该就是标准的哪几种标准的直方图对比方法。(也可以自己定义,即本着越相似值越大的原则就可以)。在Opencv中可以用compareHist()函数进行直方图对比,当然,我想在opencv中反向投影是默认选择了一种直方图对比方法,因为其函数calcBackProject()并没有提供选择直方图对比的算法。知道了此原理后,自己实现反向投影也会比较简单。

其实相当于直方图匹配。

参考资料:

  1. http://blog.csdn.net/yee_yj/article/details/6035913
  2. Opencv3编程入门

模板匹配函数详解

阅读数 1081

模板匹配函数详解

博文 来自: duan19920101
没有更多推荐了,返回首页