import cv2
import numpy as np
# 加载原始RGB图像
img_rgb = cv2.imread("capture1.jpg")
# 创建一个原始图像的灰度版本,所有操作在灰度版本中处理,然后在RGB图像中使用相同坐标还原
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
# 加载将要搜索的图像模板
#模板1 筛选图案1
template1 = cv2.imread('template1.png', 0)
#模板2 3 筛选图案2
template2 = cv2.imread('template2.png', 0)
template3 = cv2.imread('template3.png', 0)
# 记录图像模板的尺寸
w1, h1 = template1.shape[::-1]
w2, h2 = template2.shape[::-1]
w3, h3 = template3.shape[::-1]
list[::-1]是将列表反过来,从序列的最后一个元素开始切片
h1,w1 = template1.shape[::1] ----------- 有啥区别???????????????
def rotate_bound(image, angle):#图片旋转但不改变大小,模板匹配中大小改变对匹配效果有影响
(h, w) = image.shape[:2] #取前两位
(cX, cY) = (w // 2, h // 2)#//是向下取整 #取中点x,y坐标
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
cos = np.abs(M[0, 0]) #np.abs(x)、np.fabs(x):计算数组各元素的绝对值
sin = np.abs(M[0, 1])
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
return cv2.warpAffine(image, M, (nW, nH))
但是单纯的这个矩阵是在原点处进行变换的,为了能够在任意位置进行旋转变换,opencv采用了另一种方式:
为了构造这个矩阵,opencv提供了一个函数:cv2.getRotationMatrix2D(),这个函数需要三个参数,旋转中心,旋转角度,旋转后图像的缩放比例,比如下例:
center表示中间点的位置,-5表示逆时针旋转5度,1表示进行等比列的缩放
M是2x3的矩阵
#选出所有匹配旋转模板且不重复的图案
def make_contour(template,w,h,angle,threshold):
rects = []
# 模板旋转匹配
for i in range(0, 360, angle):
new_rotate = rotate_bound(template, i) #注1
# 把图片旋转后黑色的部分填充成白色
new_rotate[new_rotate == 0] = 255
# 使用matchTemplate对原始灰度图像和图像模板进行匹配
res = cv2.matchTemplate(img_gray, new_rotate, cv2.TM_CCOEFF_NORMED)
# 设定阈值
loc = np.where(res >= threshold) #注2
#x,y坐标对调打包
for pt in zip(*loc[::-1]):
point = np.array([[pt[0], pt[1]], [pt[0] + w, pt[1]],
[pt[0], pt[1] + h], [pt[0] + w, pt[1] + h]])
rects.append(cv2.boundingRect(point))
#模板匹配后符合要求的所有图案数量
length = len(rects)
#设定阈值
threshold = 3
i = 0
#如果两个图案距离在阈值范围内,则等同,然后用集合去重
while(i<length):
print(i)
for j in range(length):
if j != i:
if np.abs(rects[j][0]-rects[i][0])<= threshold:
if np.abs(rects[j][1]-rects[i][1]) <= threshold:
rects[j] = rects[i]
i = i+1
return set(rects)
注1:
def rotate_bound1(image, angle):
旋转图片
param image opencv读取后的图像
param angle (逆)旋转角度
注2:
np.where()
numpy中能够返回符合某一条件的下标函数:np.where(),只不过np.where()并不接受list类型的参数。
np.where()可用于接收3个参数,用于三目运算;也可用于接收1个参数,直接返回符合要求的下标。
#在原图把匹配的模板框出来并输出坐标文档
def draw_contour(contours,color):
count = 0
for contour in contours:
cv2.rectangle(img_rgb, (contour[0], contour[1]), (contour[0] + contour[2], contour[1] + contour[3]),
color, 1)
cx = contour[0] + (contour[2] // 2)
cy = contour[1] + (contour[3] // 2)
count = count + 1
cv2.putText(img_rgb, str(count), (cx, cy), cv2.FONT_HERSHEY_SIMPLEX, 1,color, 1, 1) #注1
with open("data.txt", "a+") as f: #注2
f.write("contour" + '\t' + str(count) + '\t' + 'cx: ' + str(cx) + ' \t' + 'cy: ' + str(cy) + '\n')
# 显示图像
cv2.imwrite("after.png", img_rgb)
注1:
cv2.putText(img, str(i), (123,456)), font, 2, (0,255,0), 3)
各参数依次是:图片,添加的文字,左上角坐标,字体,字体大小,颜色,字体粗细
注2:
with…as语句是简化版的try except finally语句
1.as可以省略 2. 有一个句块要执行
从文件中读取数据,然后关闭文件句柄
file = open("/tmp/foo.txt")
data = file.read()
file.close()
这里有两个问题。一是可能忘记关闭文件句柄;二是文件读取数据发生异常,没有进行任何处理。下面是处理异常的加强版本:
file = open("/tmp/foo.txt")
try:
data = file.read()
finally:
file.close()
虽然这段代码运行良好,但是太冗长了。这时候就是with一展身手的时候了。除了有更优雅的语法,with还可以很好的处理上下文环境产生的异常。下面是with版本的代码:
with open("/tmp/foo.txt") as file:
data = file.read()
1.先执行expression,然后执行该表达式返回的对象实例的__enter__函数,然后将该函数的返回值赋给as后面的变量。(注意,是将__enter__函数的返回值赋给变量)
2.然后执行with block代码块,不论成功,错误,异常,在with block执行结束后,会执行第一步中的实例的__exit__函数
if __name__=='__main__':
#a+可读可写覆盖
with open("data.txt", "a+") as f:
f.write('contour1'+'\n')
threshold1 = 0.69
contours1 = make_contour(template1,w1,h1,90,threshold1)
color1 = (255, 0, 0)
draw_contour(contours1,color1)
img_rgb = cv2.imread("after.png")
img_gray = cv2.cvtColor(img_rgb, cv2.COLOR_BGR2GRAY)
with open("data.txt", "a+") as f:
f.write('\n'+'contour2'+'\n')
color2 = (0,0,255)
threshold2 = 0.53
#合并轮廓
contours2 = list(make_contour(template2, w2, h2,30,threshold2))+list(make_contour(template3, w3, h3, 30, threshold2))
draw_contour(contours2,color2)