本文主要介绍 OpenCV 的以下几个操作:
霍夫变换重映射直方图反向投影模板匹配
霍夫变换是一种特征提取技术,主要应用于检测图像中的直线或者圆。 OpenCV 中分为霍夫线变换和霍夫圆变换。
注意:在使用霍夫线变换之前,首先要对图像进行边缘检测的处理,即霍夫线变换的直接输入只能是边缘二值图像
使用极坐标来表示直线,对于在笛卡尔坐标上直线上所有给定的点,在极坐标上都能转换成正弦曲线,直线上所有点绘制出来的正弦曲线交与一点,若交于交点的曲线数量超过一定阈值,说明在笛卡尔坐标上表示一条直线。[更多资料] (https://blog.csdn.net/ycj9090900/article/details/52944708)
HoughLines(image, rho, theta, threshold[, lines[, srn[, stn[, min_theta[, max_theta]]]]]) -> lines
import cv2
import numpy as np
img = cv2.imread("./sample_img/HoughLines.jpg")
edges = cv2.Canny(img, 50, 200, apertureSize=3)
gray = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
# 经典的霍夫变换
lines = cv2.HoughLines(edges, 1, np.pi/180, 180, 0, 0) # 在图像中找到的所有直线都存储在这里
for i in range(lines.shape[0]-1):
rho, theta = lines[i][0][0], lines[i][0][1] # rho 为距离, theta 为角度,把每条直线的参数分离出来
# 坐标转换
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
# 绘制直线
cv2.line(gray, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow("img", img)
cv2.imshow("gray", gray)
cv2.imshow("edges", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
[图像卷积与滤波的一些知识点] (https://blog.csdn.net/zouxy09/article/details/49080029)
注意,下面的坐标变换不是很清楚
img = np.zeros((200, 200, 3), dtype=np.uint8)
cv2.line(img, (0, 100), (100, 0), (0, 255, 0), 2)
cv2.line(img, (0, 150), (150, 0), (0, 255, 0), 2)
edges = cv2.Canny(img, 50, 200, apertureSize=3)
gray = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
# 经典的霍夫变换
lines = cv2.HoughLines(edges, 1, np.pi/180, 100, 0, 0) # 在图像中找到的所有直线都存储在这里
for i in range(lines.shape[0]-1):
rho, theta = lines[i][0][0], lines[i][0][1] # rho 为距离, theta 为角度,把每条直线的参数分离出来
# 坐标转换
a = np.cos(theta)
b = np.sin(theta)
x0 = a*rho
y0 = b*rho
x1 = int(x0 + 1000*(-b))
y1 = int(y0 + 1000*(a))
x2 = int(x0 - 1000*(-b))
y2 = int(y0 - 1000*(a))
# 绘制直线
cv2.line(gray, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow("img", img)
cv2.imshow("gray", gray)
cv2.imshow("edges", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
累计概率霍夫变换可以找出图像中直线大概的起始和终止坐标,返回 4 个元素,分别代表起始坐标(x1, y1), 终止坐标(x2, y2)
HoughLinesP(image, rho, theta, threshold[, lines[, minLineLength[, maxLineGap]]]) -> lines
import cv2
import numpy as np
img = cv2.imread("./sample_img/HoughLines.jpg")
edges = cv2.Canny(img, 50, 200, apertureSize=3) # 边缘检测出来就是二值图像
gray = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
threshold = 80
minLineLength = 40
maxLineGap = 10
linesP = cv2.HoughLinesP(edges, 1, np.pi/180, threshold, minLineLength, maxLineGap)
for i in range(linesP.shape[0]):
x1 = linesP[i][0][0]
y1 = linesP[i][0][1]
x2 = linesP[i][0][2]
y2 = linesP[i][0][3]
cv2.line(gray, (x1, y1), (x2, y2), (0, 0, 255), 2)
cv2.imshow("img", img)
cv2.imshow("gray", gray)
cv2.imshow("edges", edges)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
def nothing(x):
pass
img = cv2.imread("./sample_img/HoughLines.jpg")
edges = cv2.Canny(img, 50, 200, apertureSize=3) # 边缘检测出来就是二值图像
cv2.namedWindow("gray")
cv2.createTrackbar("minLineLength", "gray", 0, 200, nothing)
cv2.createTrackbar("maxLineGap", "gray", 0, 200, nothing)
while(1):
gray = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
minLineLength = cv2.getTrackbarPos("minLineLength", "gray")
maxLineGap = cv2.getTrackbarPos("maxLineGap", "gray")
linesP = cv2.HoughLinesP(edges, 1, np.pi/180, 70, minLineLength, maxLineGap)
for i in range(linesP.shape[0]):
x1 = linesP[i][0][0]
y1 = linesP[i][0][1]
x2 = linesP[i][0][2]
y2 = linesP[i][0][3]
cv2.line(gray, (x1, y1), (x2, y2), (0, 0, 255), 2)
k = cv2.waitKey(1) & 0xff
if k == 27:
break
#cv2.imshow("img", img)
cv2.imshow("gray", gray)
#cv2.imshow("edges", edges)
cv2.destroyAllWindows()
圆的表达式为 (x-a)^2+(y-b)^2=r^2,将圆上的任意点 (x, y) 变换成 (a, b, r) 坐标结果是一个圆锥,同一个圆上的点形成的圆锥会交于一点,从该交点可以得出圆的信息。
HoughCircles(image, method, dp, minDist[, circles[, param1[, param2[, minRadius[, maxRadius]]]]]) -> circles
cv2.HOUGH_GRADIENT
霍夫梯度法一种import cv2
import numpy as np
"""
img = np.zeros((200, 200, 3), dtype=np.uint8)
cv2.circle(img, (100, 100), 50, (0, 255, 255))
cv2.circle(img, (95, 100), 50, (0, 255, 255))
cv2.circle(img, (80, 100), 50, (0, 255, 255))
"""
img = cv2.imread("./sample_img/opencv-logo.png")
img1 = cv2.imread("./sample_img/opencv-logo.png", 0)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gaussian = cv2.GaussianBlur(gray, (9, 9), 2, 2)
circle = cv2.HoughCircles(gaussian, cv2.HOUGH_GRADIENT, dp=1, minDist=20, param1=50, param2=50, minRadius=0, maxRadius=0)
for i in range(circle.shape[1]):
center = (circle[0][i][0], circle[0][i][1])
radius = circle[0][i][2]
cv2.circle(img, center, radius, (100, 100, 100), 4)
cv2.circle(img, center, 3, (100, 100, 100))
cv2.imshow("img", img)
cv2.imshow("img1", cv2.Canny(img1, 50, 200, apertureSize=3))
cv2.imshow("gray", gray)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
cap = cv2.VideoCapture(0)
# 设置摄像头分辨率
cap.set(cv2.CAP_PROP_FRAME_WIDTH, 400)
cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 400)
while(1):
# 提取每一帧, frame 源视频
_, frame = cap.read()
frame = cv2.flip(frame, 1)
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
#gaussian = cv2.GaussianBlur(gray, (9, 9), 2, 2)
#canny = cv2.Canny(gaussian, 20, 100)
circle = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=1,
minDist=20, param1=50, param2=50, minRadius=0, maxRadius=0)
if circle is not None:
center = (circle[0][0][0], circle[0][0][1])
radius = circle[0][0][2]
cv2.circle(frame, center, radius, (0, 0, 255), 4)
cv2.imshow("frame", frame) # 源视频
#cv2.imshow("gray", gray) # 源视频
k = cv2.waitKey(1) & 0xFF
if k == 27:
break
cap.release() # 记得释放掉捕获的视频
cv2.destroyAllWindows()
image
效果并不是很好,是由于霍夫变换存在一定缺陷
[霍夫圆变换原理] (https://blog.csdn.net/yuwuzhi1989/article/details/18614727)
重映射:把一副图像中某位置的像素放置到另一个图片指定位置的过程,为了完成映射过程,需要获得一些插值为非整数像素的坐标,因为原图像和目标图像的像素坐标是不一一对应的。
remap(src, map1, map2, interpolation[, dst[, borderMode[, borderValue]]]) -> dst
import cv2
def remap_mode(mode, map_x, map_y):
if mode == 0: # 倒置
for i in range(map_x.shape[0]):
map_x[i,:] = [x for x in range(map_x.shape[1])]
for j in range(map_y.shape[1]):
#map_y[:,j] = [map_y.shape[0]-y for y in range(map_y.shape[0])]
map_y[:,j] = [map_y.shape[0]-y-1 for y in range(map_y.shape[0])]
elif mode == 1: # 竖直对称
for i in range(map_x.shape[0]):
map_x[i,:] = [map_x.shape[1]-x for x in range(map_x.shape[1])]
for j in range(map_y.shape[1]):
map_y[:,j] = [y for y in range(map_y.shape[0])]
elif mode == 2: # mode=0 和 mode=1 的组合
for i in range(map_x.shape[0]):
map_x[i,:] = [map_x.shape[1]-x for x in range(map_x.shape[1])]
for j in range(map_y.shape[1]):
map_y[:,j] = [map_y.shape[0]-y for y in range(map_y.shape[0])]
elif mode == 3: # 缩小后在中间显示
for i in range(map_x.shape[0]):
for j in range(map_x.shape[1]):
if j > map_x.shape[1]*0.25 and j < map_x.shape[1]*0.75 and i > map_x.shape[0]*0.25 and i < map_x.shape[0]*0.75:
map_x[i,j] = 2 * (j-map_x.shape[1]*0.25) + 0.5
map_y[i,j] = 2 * (i-map_y.shape[0]*0.25) + 0.5
else:
map_x[i,j] = 0
map_y[i,j] = 0
#img = cv2.imread("./sample_img/apple.jpg")
img = cv2.imread("./sample_img/Back_Projection_Theory2.jpg", 0)
# 创建两个映射矩阵 x, y
map_x = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
map_y = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
cv2.imshow("img", img)
for i in range(4):
remap_mode(i, map_x, map_y)
dst = cv2.remap(img, map_x, map_y, cv2.INTER_LINEAR)
cv2.imshow("dst"+str(i), dst)
#print(map_x, map_y, map_x.shape, map_y.shape)
cv2.waitKey(0)
cv2.destroyAllWindows()
img = np.array(np.arange(10, 30).reshape(4, 5), dtype=np.uint8)
map1 = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
map2 = np.zeros((img.shape[0], img.shape[1]), dtype=np.float32)
# 倒置
for i in range(map1.shape[0]):
map1[i,:] = [x for x in range(map1.shape[1])] # 行坐标
for j in range(map2.shape[1]):
map2[:,j] = [map2.shape[0]-y-1 for y in range(map2.shape[0])] # 列坐标
print(map1, map2, sep='\n')
print(img, cv2.remap(img, map1, map2, cv2.INTER_LINEAR), sep='\n')
[[0. 1. 2. 3. 4.]
[0. 1. 2. 3. 4.]
[0. 1. 2. 3. 4.]
[0. 1. 2. 3. 4.]]
[[3. 3. 3. 3. 3.]
[2. 2. 2. 2. 2.]
[1. 1. 1. 1. 1.]
[0. 0. 0. 0. 0.]]
[[10 11 12 13 14]
[15 16 17 18 19]
[20 21 22 23 24]
[25 26 27 28 29]]
[[25 26 27 28 29]
[20 21 22 23 24]
[15 16 17 18 19]
[10 11 12 13 14]]
cv2.remap()
函数中的 map1, map2 参数代表目标图中的 (x,y) 点在原图中的 x 坐标(由 map2 提供)与 y 坐标(由 map1 提供)待定
flip(src, flipCode[, dst]) -> dst
import cv2
img = cv2.imread("./sample_img/apple.jpg")
flip = cv2.flip(img, 0)
flip_2 = cv2.flip(img, 1)
flip_3 = cv2.flip(img, -1)
cv2.imshow("img", img)
cv2.imshow("flip", flip)
cv2.imshow("flip_2", flip_2)
cv2.imshow("flip_3", flip_3)
cv2.waitKey(0)
cv2.destroyAllWindows()
学习目标:
直方图就是灰色图像每个像素,横坐标代表像素值(0 - 255),纵坐标代表每个像素值的个数。直方图可以直观了解该图像的对比度,亮度,强度分布。 类似:
calcHist(images, channels, mask, histSize, ranges[, hist[, accumulate]]) -> hist
import cv2
import numpy as np
img = cv2.imread("./sample_img/apple.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hue = np.zeros(hsv.shape, dtype=np.uint8)
hue = cv2.mixChannels(hsv, hue, [0, 0])
hist = cv2.calcHist(hue, [0], None, [180], [0, 180]) # 返回的是 (0, 256) 每个像素值的个数
import numpy as np
hist, bins = np.histogram(img.ravel(), 256, [0, 256])
hist = np.bincount(img.ravel(), minlength=256)
对于一维直方图,使用 np.bincount () 的速度比 np.histogram () 快 10 倍,但是 OpenCV 的方法更快,所以还是使用 OpenCV 的直方图方法
plt.hist(x, bins=None, range=None, density=None, weights=None, cumulative=False, bottom=None, histtype='bar', align='mid', orientation='vertical', rwidth=None, log=False, color=None, label=None, stacked=False, normed=None, *, data=None, **kwargs)
import cv2
import matplotlib.pyplot as plt
img = cv2.imread("./sample_img/apple.jpg", 0)
plt.hist(img.ravel(), 256, [0, 256])
plt.show()
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./sample_img/apple.jpg')
color = ('b', 'g', 'r')
for i, col in enumerate(color):
histr = cv2.calcHist([img], [i], None, [256], [0, 256])
plt.plot(histr, color=col)
plt.xlim([0, 256]) # 横坐标限制在 0 - 256
plt.show()
cv2.imshow("img", img)
import cv2
import matplotlib.pyplot as plt
import numpy as np
img = cv2.imread('./sample_img/apple.jpg', 0)
# create a mask
mask = np.zeros(img.shape[:2], np.uint8)
mask[100:300, 100:400] = 255
masked_img = cv2.bitwise_and(img, img, mask=mask)
# Calculate histogram with mask and without mask
# Check third argument for mask
hist_full = cv2.calcHist([img], [0], None, [256], [0, 256])
hist_mask = cv2.calcHist([img], [0], mask, [256], [0, 256])
plt.subplot(221), plt.imshow(img, 'gray')
plt.subplot(222), plt.imshow(mask, 'gray')
plt.subplot(223), plt.imshow(masked_img, 'gray')
plt.subplot(224), plt.plot(hist_full), plt.plot(hist_mask)
plt.xlim([0, 256])
plt.show()
直方图均衡化被用来改善图像的对比度
直方图均衡化简单来说,就是把原始图像的直方图的分布均匀到所有像素。
import cv2
img = cv2.imread("./sample_img/Histograms.jpg")
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
histograms = cv2.equalizeHist(gray)
cv2.imshow("img", img)
cv2.imshow("histograms", histograms)
cv2.waitKey(0)
cv2.destroyAllWindows()
对图像的灰度图像进行直方图均衡化
import cv2
img = cv2.imread("./sample_img/Histograms.jpg")
blue, green, red = cv2.split(img)
blue_histograms = cv2.equalizeHist(blue)
green_histograms = cv2.equalizeHist(green)
red_histograms = cv2.equalizeHist(red)
new_img = cv2.merge((blue_histograms, green_histograms, red_histograms))
cv2.imshow("img", img)
cv2.imshow("new_img", new_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
对图像是 BGR 三通道进行直方图均衡化
import cv2
img = cv2.imread("./sample_img/Histograms.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HLS)
h, l, s = cv2.split(img)
#h = cv2.equalizeHist(h)
l = cv2.equalizeHist(l)
#s = cv2.equalizeHist(s)
new_img = cv2.merge((h, s, v))
cv2.imshow("img", img)
cv2.imshow("new_img", new_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
对图像的 HLS 三通道的 L(亮度) 通道进行直方图均衡化
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./sample_img/wiki.jpg', 0)
hist, bins = np.histogram(img.flatten(), 256, [0, 256])
cdf = hist.cumsum() # 返回一个给定轴上的元素的累积和
cdf_normalized = cdf * hist.max() / cdf.max()
plt.plot(cdf_normalized, color='b')
plt.hist(img.flatten(), 256, [0, 256], color='r')
plt.xlim([0, 256])
plt.legend(('cdf', 'histogram'), loc='upper left')
plt.show()
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./sample_img/wiki.jpg', 0)
equ = cv2.equalizeHist(img)
new_img = np.hstack((img, equ))
cv2.imshow("new_img", new_img)
#cv2.imwrite("./sample_img/equalizeHist.jpg", new_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
以上图像的均衡化使用的是全局的对比度,但有时候全局的对比度不一定是最好的,故引申出自适应均衡化
import numpy as np
import cv2
img = cv2.imread('./sample_img/tsukuba_l.png',0)
histograms = cv2.equalizeHist(img)
hstack_img = np.hstack((img, histograms))
cv2.imshow("hstack_img", hstack_img)
cv2.waitKey(0)
cv2.destroyAllWindows()
直方图均衡后背景对比度有所提高。但比较两个图像中的雕像的脸。由于亮度过高,我们丢失了大部分信息。这是因为它的直方图并不局限于特定区域。
自适应均衡化的原理是:图像被分成称为 “tile” 的小块(在 OpenCV 中,tileSize 默认为 8x8)。然后像往常一样对这些块中的每一个进行直方图均衡。所以在一个小区域内,直方图会限制在一个小区域(除非有噪音)。如果有噪音,它会被放大。为避免这种情况,应用对比度限制。如果任何直方图区间高于指定的对比度限制(在 OpenCV 中默认为 40 ),则在应用直方图均衡之前,将这些像素剪切并均匀分布到其他区间。均衡后,为了去除图块边框中的瑕疵,应用双线性插值。
cv2.createCLAHE([, clipLimit[, tileGridSize]]) -> retval
apply(src, [, dst]) -> dst
: 利用对比度有限的自适应直方图均衡化来均衡灰度图像的直方图。
import numpy as np
import cv2
img = cv2.imread('./sample_img/tsukuba_l.png', 0)
# create a CLAHE object (Arguments are optional).
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)
cv2.imshow('clahe_2', cl1)
cv2.waitKey(0)
cv2.destroyAllWindows()
雕像区域的轮廓变得清晰可见了
import cv2
import numpy as np
img = cv2.imread("./sample_img/brain.jpg", 0)
hist = cv2.equalizeHist(img) # 直方图均衡
threshold = img.copy()
threshold[threshold > 127] = 255
threshold[threshold <= 127] = 0
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8, 8))
cl1 = clahe.apply(img)
cv2.imshow("img", img)
cv2.imshow("hist", hist)
cv2.imshow("threshold", threshold)
cv2.imshow("cl1", cl1)
cv2.waitKey(0)
cv2.destroyAllWindows()
以上绘制的是一维直方图(只考虑一个特征,即灰度强度值),本节将讨论 2D 直方图,考虑两个特征(色调和饱和度),用于查找颜色直方图。
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread("./sample_img/home.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
# OpenCV 方法
# [0, 1] 代表 H 和 S 平面
# [190, 256] 180 是 H 平面最大幅值,256 是 S 平面最大幅值
hist_cv = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
# Numpy 方法
hist_numpy, xbins, ybins = np.histogram2d(hsv[0].ravel(), hsv[1].ravel(),
[180, 256], [[0, 180], [0, 256]])
cv2.imshow("original", img)
cv2.imshow("hist_cv", hist_cv)
cv2.imshow("hist_numpy", hist_numpy)
cv2.waitKey(0)
cv2.destroyAllWindows()
plt.imshow(hist_cv, interpolation="nearest")
plt.imshow(hist_numpy, interpolation="nearest")
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('./sample_img/home.jpg')
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hist = cv2.calcHist([hsv], [0, 1], None, [180, 256], [0, 180, 0, 256])
plt.imshow(hist, interpolation='nearest')
plt.show()
import cv2
base_img = cv2.imread("./sample_img/comparehist (1).jpg")
half_base = base_img[0:base_img.shape[0]//2, 0:base_img.shape[1]//2]
test_img = cv2.imread("./sample_img/comparehist (2).jpg")
test2_img = cv2.imread("./sample_img/comparehist (3).jpg")
base_hsv = cv2.cvtColor(base_img, cv2.COLOR_BGR2HSV)
half_hsv = cv2.cvtColor(half_base, cv2.COLOR_BGR2HSV)
test_hsv = cv2.cvtColor(test_img, cv2.COLOR_BGR2HSV)
test2_hsv = cv2.cvtColor(test2_img, cv2.COLOR_BGR2HSV)
base_hist = cv2.calcHist([base_hsv], [0, 1], None, [
180, 256], [0, 180, 0, 256])
half_hist = cv2.calcHist([half_hsv], [0, 1], None, [
180, 256], [0, 180, 0, 256])
test_hist = cv2.calcHist([test_hsv], [0, 1], None, [
180, 256], [0, 180, 0, 256])
test2_hist = cv2.calcHist([test2_hsv], [0, 1], None, [
180, 256], [0, 180, 0, 256])
base_hist = cv2.normalize(base_hist, base_hist, 0, 1, cv2.NORM_MINMAX, -1)
half_hist = cv2.normalize(half_hist, half_hist, 0, 1, cv2.NORM_MINMAX, -1)
test_hist = cv2.normalize(test_hist, test_hist, 0, 1, cv2.NORM_MINMAX, -1)
test2_hist = cv2.normalize(test2_hist, test2_hist, 0, 1, cv2.NORM_MINMAX, -1)
methods = ['cv2.HISTCMP_CORREL', 'cv2.HISTCMP_CHISQR', 'cv2.HISTCMP_INTERSECT',
'cv2.HISTCMP_BHATTACHARYYA', 'cv2.HISTCMP_HELLINGER', 'cv2.HISTCMP_CHISQR_ALT', 'cv2.HISTCMP_KL_DIV']
for i in range(5):
base_base = cv2.compareHist(base_hist, base_hist, i)
half_base = cv2.compareHist(base_hist, half_hist, i)
base_test = cv2.compareHist(base_hist, test_hist, i)
base_test2 = cv2.compareHist(base_hist, test2_hist, i)
print("method:"+str(methods[i]), sep='\n')
print("base_base:", base_base, "half_base", half_base,
"base_test", base_test, "base_test2", base_test2)
结果
cv2.HISTCMP_CORRE
和 cv2.HISTCMP_INTERSECT
是
[直方图 API] (https://docs.opencv.org/3.1.0/d6/dc7/group__imgproc__hist.html#ga41a249ba9a536bf2b47e95f8c7dd42b0)
本节资料 Cambridge in Color website
维基百科直方图均衡化
How can I adjust contrast in OpenCV in C?
反向投影可以用来做图像分割,寻找感兴趣区间。
import cv2
import numpy as np
def nothing(x):
pass
img = cv2.imread("./sample_img/back.jpg")
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
hue = np.zeros(hsv.shape, dtype=np.uint8)
cv2.createTrackbar("bins", "dst", 0, 255, nothing)
while(1):
bins = cv2.getTrackbarPos("bins", "dst")
hue = cv2.mixChannels(hsv, hue, [0, 0])
hist = cv2.calcHist(hue, [0], None, [180], [0, 180])
hist = cv2.normalize(hist, hist, 0, 1, cv2.NORM_MINMAX, -1)
dst = cv2.calcBackProject([hsv], [0], hist, [0, 180], 1)
cv2.imshow("dst", dst)
k = cv2.waitKey(1) & 0xff
if k == 27:
break
cv2.destroyAllWindows()
import cv2
import numpy as np
import matplotlib.pyplot as plt
def calc_backproject():
sample = cv2.imread("./sample_img/sample.png")
target = cv2.imread("./sample_img/target.png")
roi_hsv = cv2.cvtColor(sample, cv2.COLOR_BGR2HSV)
target_hsv = cv2.cvtColor(target, cv2.COLOR_BGR2HSV)
cv2.imshow("sample", sample)
cv2.imshow("target", target)
roiHist = cv2.calcHist(roi_hsv, [0, 1], None,
[30, 30], [0, 180, 0, 256])
cv2.normalize(roiHist, roiHist, 0, 255, cv2.NORM_MINMAX)
dst = cv2.calcBackProject([target_hsv], [0, 1],
roiHist, [0, 180, 0, 256], 1)
cv2.imshow("dst", dst)
plt.imshow(dst)
calc_backproject()
cv2.waitKey(0)
cv2.destroyAllWindows()
学习目标
在一副图像中查找与模板图像最匹配(相似)的部分。对模板图像进行滑动匹配源图像
matchTemplate(image, templ, method[, result[, mask]]) -> result
简单法:平方差方法,速度快,但不是很精确 复杂法:相关系数法,计算量大,较精确 综合考虑选择运用哪种匹配方法
minMaxLoc(src[, mask]) -> minVal, maxVal, minLoc, maxLoc
import numpy as np
img = np.array(np.arange(25)).reshape(5, 5)
print(img, cv2.minMaxLoc(img), sep='\n')
[[ 0 1 2 3 4]
[ 5 6 7 8 9]
[10 11 12 13 14]
[15 16 17 18 19]
[20 21 22 23 24]]
(0.0, 24.0, (0, 0), (4, 4))
eval () 用法说明
import cv2
methods = ['cv2.TM_CCOEFF', 'cv2.TM_CCOEFF_NORMED', 'cv2.TM_CCORR',
'cv2.TM_CCORR_NORMED', 'cv2.TM_SQDIFF', 'cv2.TM_SQDIFF_NORMED']
for method in methods:
print(eval(method)) # 可以判断 methods 里面的值在 OpenCV 中对应的值
# 运行结果
'''
4
5
2
3
0
1
'''
import cv2
import numpy as np
from matplotlib import pyplot as plt
img = cv2.imread('./sample_img/template (1).jpg', 0)
img = cv2.resize(img, (600, 600))
img2 = img.copy()
template = cv2.imread('./sample_img/template (2).jpg', 0)
w, h = template.shape[::-1]
# All the 6 methods for comparison in a list
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) # 可以判断 methods 里面的值在 OpenCV 中对应的值
# Apply template Matching
res = cv2.matchTemplate(img, template, method)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
#print(res)
# If the method is TM_SQDIFF or TM_SQDIFF_NORMED, take minimum
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, (0, 0, 255), 2)
#cv2.imshow('Matching Result'+str(meth), res)
cv2.imshow('Detected Point'+str(meth), img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv2.TM_CCORR
方法的效果不是很好
import cv2
import numpy as np
import time
t0 = time.time()
img = cv2.imread("./sample_img/mario.jpg")
template = img[170:220, 334:373, :]
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCOEFF_NORMED) # 用归一化的方法,阈值的设定比较方便
threshold = 0.8 # 表示模板和检测目标 80% 的匹配程度就认为是所要查找的目标
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
print("time:", time.time()-t0)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
time: 0.07096028327941895
import matplotlib.pyplot as plt
plt.hist(res.ravel());
import cv2
import numpy as np
import time
t0 = time.time()
img = cv2.imread("./sample_img/mario.jpg")
template = img[170:220, 334:373, :]
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_SQDIFF_NORMED) # 在
threshold = 0.2 # 表示模板和检测目标 80% 的匹配程度就认为是所要查找的目标
loc = np.where(res <= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
print("time:", time.time()-t0)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
time: 0.09594225883483887
import cv2
import numpy as np
import time
t0 = time.time()
img = cv2.imread("./sample_img/mario.jpg")
template = img[170:220, 334:373, :]
img_gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
template = cv2.cvtColor(template, cv2.COLOR_BGR2GRAY)
w, h = template.shape[::-1]
res = cv2.matchTemplate(img_gray, template, cv2.TM_CCORR_NORMED) # 在
threshold = 0.95 # 表示模板和检测目标 80% 的匹配程度就认为是所要查找的目标
loc = np.where(res >= threshold)
for pt in zip(*loc[::-1]):
cv2.rectangle(img, pt, (pt[0] + w, pt[1] + h), (0, 0, 255), 2)
print("time:", time.time()-t0)
cv2.imshow("img", img)
cv2.waitKey(0)
cv2.destroyAllWindows()
time: 0.05097007751464844
此方法把一个不是很明显的目标也检测出来,此方法的性能估计比较高
import matplotlib.pyplot as plt
plt.hist(res.ravel());
a = [1, 2, 3, 4]
a[::-1] # 反转
#结果 [4, 3, 2, 1]
Python zip 功能说明
a = [1, 2, 3]
b = [4, 5, 6]
c = [4, 5, 6, 7, 8]
zipped = zip(a, b) # 打包为元组的列表
[k for k in zip(*zipped)] # 与 zip 相反,*zipped 可理解为解压,返回二维矩阵式
# 结果 [(1, 2, 3), (4, 5, 6)]
额 感觉在公众号上发太长的代码,太影响阅读了,想把本系列做为一个文档以便翻阅的话,可以访问我的 Github https://github.com/FLyingLSJ/OpenCV-Python-Tutorial/tree/master/Tutorial 大多数代码使用 jupyter notebook 实现。方便查阅!