模板匹配是一种最原始、最基本的模式识别方法,研究某一特定对象物的图案位于图像的什么地方,进而识别对象物,这就是一个匹配问题。它是图像处理中最基本、最常用的匹配方法。模板匹配具有自身的局限性,主要表现在它只能进行平行移动,若原图像中的匹配目标发生旋转或大小变化,该算法无效。
从简单的测量(平方差)到更复杂的测量(相关系数),可获得越来越准确的匹配(同时也意味着越来越大的计算代价)。
opencv的目标匹配函数
cv2.matchTemplate(image, templ, method, result=None, mask=None) -> result
mage参数表示待检测源图像,必须是8位整数或32位浮点。
templ参数表示模板图像,必须不大于源图像并具有相同的数据类型。
method参数表示计算匹配程度的方法。
result参数表示匹配结果图像,必须是单通道32位浮点。如果image的尺寸为W x H,templ的尺寸为w x h,则result的尺寸为(W-w+1)x(H-h+1)。
opencv的函数minMaxLoc:在给定的矩阵中寻找最大和最小值,并给出它们的位置。 该功能不适用于多通道阵列,如果需要在所有通道中查找最小或最大元素,要先将阵列重新解释为单通道。
minMaxLoc(src, mask=None) -> minVal, maxVal, minLoc, maxLoc
src参数表示输入单通道图像
mask参数表示用于选择子数组的可选掩码
minVal参数表示返回的最小值,如果不需要,则使用NULL。
maxVal参数表示返回的最大值,如果不需要,则使用NULL。
minLoc参数表示返回的最小位置的指针(在2D情况下); 如果不需要,则使用NULL。
maxLoc参数表示返回的最大位置的指针(在2D情况下); 如果不需要,则使用NULL。
opencv的函数rectangle用于绘制矩形
cv2.rectangle(img, pt1, pt2, color, thickness=None, lineType=None, shift=None) -> img
img参数表示源图像
pt1参数表示矩形的一个顶点(左上)
pt2参数表示与pt1相对的对角线上的另一个顶点(右下)
color参数表示矩形线条颜色 (RGB) 或亮度(灰度图像)
thickness参数表示组成矩形的线条的粗细程度,取负值时(如 CV_FILLED)函数绘制填充了色彩的矩形。
lineType参数表示线条的类型
shift参数表示坐标点的小数点位数
代码如下:
import cv2 as cv
import numpy as np
def template_matching():
sample = cv.imread(r'./test/031.png') # 模板图像
target = cv.imread(r'./test/030.jpg') # 待检测图像
cv.imshow('sample', sample)
cv.imshow('target', target)
# 三种模板匹配算法
methods = [cv.TM_SQDIFF_NORMED, cv.TM_CCORR_NORMED, cv.TM_CCOEFF_NORMED]
height, width = sample.shape[:2] # 模板图像的高 宽
for method in methods:
print(method)
result = cv.matchTemplate(image=target, templ=sample, method=method) # 计算那个区域匹配最好
# 在匹配的结果中寻找 最小值 最大值及最小、最大值的位置
min_val, max_val, min_loc, max_loc = cv.minMaxLoc(result)
if method == cv.TM_SQDIFF_NORMED: # 如果是标准平方差匹配 取最小值位置
left_top = min_loc
else:
left_top = max_loc
right_bottom = (left_top[0] + width, left_top[1] + height) # 加上宽 高
# 匹配到最佳位置 画小矩形
cv.rectangle(img=target, pt1=left_top, pt2=right_bottom, color=(0, 0, 255), thickness=2)
cv.imshow('match-' + np.str(method), target)
template_matching()
cv.waitKey(0)
cv.destroyAllWindows()
运行效果如下:
模板图像
在数字图像处理中,二值图像占有非常重要的地位,图像的二值化使图像中数据量大为减少,从而能凸显出目标的轮廓。
该函数的阈值操作属于像素级的操作,在灰度图中,每个像素都对应一个灰度值(0~255,0黑、255白),我们将阈值函数 threshold() 应用于图像,图像的灰度值与阈值进行比较,从而实现二值化处理,目的是滤除太大或太小值像素、消除噪声,从而从灰度图中获取二值图像(将图像的灰度值设置为0或255),实现增强整个图像呈现更为明显的黑白效果,同时也大大减少了数据量。
import cv2 as cv
import numpy as np
# 全局
def threshold_image(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(src=gray, thresh=0, maxval=255, type=cv.THRESH_BINARY | cv.THRESH_OTSU)
# ret, binary = cv.threshold(src=gray, thresh=0, maxval=255, type=cv.THRESH_BINARY | cv.THRESH_TRIANGLE)
# ret, binary = cv.threshold(src=gray, thresh=115, maxval=255, type=cv.THRESH_BINARY)
# ret, binary = cv.threshold(src=gray, thresh=127, maxval=255, type=cv.THRESH_BINARY_INV) # 相反
# ret, binary = cv.threshold(src=gray, thresh=127, maxval=255, type=cv.THRESH_TRUNC)
# ret, binary = cv.threshold(src=gray, thresh=130, maxval=255, type=cv.THRESH_TOZERO)
print(f'threshold value:{ret}') # 阈值 看图像信息丢失情况
cv.imshow('threshold binary image', binary)
# 局部 自适应阈值
def local_image(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
# dst = cv.adaptiveThreshold(src=gray, maxValue=255, adaptiveMethod=cv.ADAPTIVE_THRESH_MEAN_C,
# thresholdType=cv.THRESH_BINARY, blockSize=25, C=10) # blockSize必须是奇数 C 常量
dst = cv.adaptiveThreshold(src=gray, maxValue=255, adaptiveMethod=cv.ADAPTIVE_THRESH_GAUSSIAN_C,
thresholdType=cv.THRESH_BINARY, blockSize=25, C=10) # blockSize必须是奇数 C 常量
cv.imshow('local binary image', dst)
# 自定义 均值作为阈值
def custom_image(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
h, w = gray.shape[:2]
m = np.reshape(gray, [1, h * w])
mean = m.sum() / (w * h)
print(f'mean:{mean}')
ret, binary = cv.threshold(gray, mean, 255, cv.THRESH_BINARY)
cv.imshow('custom binary image', binary)
src = cv.imread(r'./test/014.png')
cv.imshow('input image', src)
threshold_image(src)
local_image(src)
custom_image(src)
cv.waitKey(0)
cv.destroyAllWindows()
cv2.threshold(src, thresh, maxval, type, dst=None)
src - 输入图像(多通道,8位或32位浮点)
thresh - 阈值
maxval - 最大值
type - 阈值类型
dst - 输出图像(与src相同大小和类型以及相同通道数的数组/图像)
阈值类型
这些函数都有两个返回值,第一个返回值为使用的阈值,第二个就是阈值化后的图像。
最大类间方差法
对于图像二值化的简单阈值法,我们需要自己提供一个阈值,而最大类间方差法可以根据图像特性,选择最佳的阈值,故它也被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。按照最大类间方差法求得的阈值进行图像二值化分割后,前景与背景图像的类间方差最大。
它是按图像的灰度特性,将图像分成背景和前景两部分。因方差是灰度分布均匀性的一种度量,背景和前景之间的类间方差越大,说明构成图像的两部分的差别越大,当部分前景错分为背景或部分背景错分为前景都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。
应用:是求图像全局阈值的最佳方法,应用不言而喻,适用于大部分需要求图像全局阈值的场合。
优点:计算简单快速,不受图像亮度和对比度的影响。
缺点:对图像噪声敏感;只能针对单一目标分割;当目标和背景大小比例悬殊、类间方差函数可能呈现双峰或者多峰,这个时候效果不好。
代码如下:
import cv2 as cv
# 全局
def threshold_image(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(src=gray, thresh=0, maxval=255, type=cv.THRESH_BINARY | cv.THRESH_OTSU)
print(f'threshold value:{ret}') # 阈值 看图像信息丢失情况
cv.imshow('threshold binary image', binary)
# 局部 自适应阈值
def local_image(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
dst = cv.adaptiveThreshold(src=gray, maxValue=255, adaptiveMethod=cv.ADAPTIVE_THRESH_GAUSSIAN_C,
thresholdType=cv.THRESH_BINARY, blockSize=25, C=10) # blockSize必须是奇数 C 常量
cv.imshow('local binary image', dst)
src = cv.imread(r'./test/014.png')
cv.imshow('input image', src)
threshold_image(src)
local_image(src)
cv.waitKey(0)
cv.destroyAllWindows()
运行效果如下:
全局阈值法对于某些光照不均的图像,这种全局阈值分割的效果不好。
而利用局部阈值法,根据图像上的每一个小区域计算与其对应的阀值。因此在同一幅图像上的不同区域采用的是不同的阀值,从而使我们在亮度不同的情况下得到更好的结果。
cv2.adaptiveThreshold(src, maxValue, adaptiveMethod, thresholdType, blockSize, C, dst=None):
src - 输入图像(8位单通道图像)
maxValue - THRESH_BINARY 和 THRESH_BINARY_INV 的最大值
adaptiveMethod - 自适应阈值算法,平均 (ADAPTIVE_THRESH_MEAN_C)或高斯(ADAPTIVE_THRESH_GAUSSIAN_C)
thresholdType - 阈值类型,必须为THRESH_BINARY或THRESH_BINARY_INV的阈值类型
blockSize - 块大小(奇数且大于1)
C - 常量,从平均值或加权平均值中减去的数
代码如下:
import cv2 as cv
import numpy as np
# 全局
def threshold_image(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(src=gray, thresh=0, maxval=255, type=cv.THRESH_BINARY | cv.THRESH_OTSU)
print(f'threshold value:{ret}') # 阈值 看图像信息丢失情况
cv.imshow('threshold binary image', binary)
# 局部 自适应阈值
def local_image(image):
gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
dst = cv.adaptiveThreshold(src=gray, maxValue=255, adaptiveMethod=cv.ADAPTIVE_THRESH_GAUSSIAN_C,
thresholdType=cv.THRESH_BINARY, blockSize=25, C=10) # blockSize必须是奇数 C 常量
cv.imshow('local binary image', dst)
src = cv.imread(r'./test/032.png')
cv.imshow('input image', src)
threshold_image(src)
local_image(src)
cv.waitKey(0)
cv.destroyAllWindows()