python图像处理-形状提取和识别2
(棋盘图像转为矩阵)
本系列一个分为两大部分:一个是形状提取,一个是形状识别
1.形状提取中,基于Hough函数法进行直线提取和圆形提取,还有即基于颜色的形状提取
这里扩展了一个小程序(识别一张棋盘图片,利用角点检测得到棋盘参数,再利用形状提取得到棋子位置,将棋盘图片转换成矩阵,再利用棋子圆心的RGB识别,用矩阵中的1表示黑子,0表示白子。“后面会把这个小扩展单独写出来玩”)
2.形状识别里用了几个OPEN-CV的函数
目录
第一部分:形状提取
第二部分:扩展-棋盘转换为矩阵
这一部分的灵感是想到阿尔法狗如果是在实际棋盘上下棋,该如何将识别的图片转换为矩阵,再进行后面的公式运算。
第一步用到hough计算先计算出棋子圆心的坐标;第二步利用上周的harris角点对棋盘提取其边缘信息,得到棋盘四个边缘位置的坐标,以及每格间隔的距离。得到棋子中心坐标和棋盘边缘参数和格子间隔后,第三步是将其转换为矩阵表达;第四步是检测每个棋子的中心位置的RGB值,判断是黑棋子还是白棋子,黑棋子矩阵显示1,白棋子显示2,无棋子显示0,得到19*19棋盘情况的矩阵表示,用其他棋盘测试结果也成立。
第一步:利用形状检测捕捉棋子
检测到棋子,并得到棋子圆心坐标
qipan = cv2.imread('qipan.jpg',1)
qipan = cv2.cvtColor(qipan, cv2.COLOR_BGR2RGB)
gray = cv2.cvtColor(qipan, cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,100,200,apertureSize = 3)
plt.figure(figsize=(30, 20));
plt.subplot(121), plt.imshow(qipan)
plt.title('img'), plt.xticks([]), plt.yticks([])
circle1 = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1, 20, param1=100, param2=24, minRadius=5, maxRadius=25)
circles = circle1[0, :, :] # 提取为二维
circles = np.uint16(np.around(circles)) # 四舍五入,取整
for i in circles[:]:
cv2.circle(qipan, (i[0], i[1]), i[2], (255, 0, 0), 5) # 画圆
# cv2.circle(qipan, (i[0], i[1]), 1, (255, 0, 0), 10) # 画圆心
plt.subplot(122), plt.imshow(qipan)
plt.title('circle'), plt.xticks([]), plt.yticks([]);
第二步:进行角点检测,得到棋盘参数
img_c1=cv2.imread('D:/classofmathpicture/qipan.jpg',0)
img_c1_CC=cv2.imread('D:/classofmathpicture/qipan.jpg',1)
img_c1_CC=cv2.cvtColor(img_c1_CC,cv2.COLOR_BGR2RGB)
Harris_c1 = cv2.cornerHarris(img_c1, 3, 3, 0.04)
dst_c1 = cv2.dilate(Harris_c1, None) #将可以标出来的点粗化
plt.figure(figsize=(10, 10));
img_c1_C=img_c1_CC.copy()
thres = 0.1*dst_c1.max()
img_c1_C[dst_c1 > thres] = [255,0,0]
plt.imshow(img_c1_C)
# 从一幅Harris响应图像中返回角点,min_dist为分割角点和图像边界的最少像素数目
def get_harris_points(harrisim,min_dist=10,threshold=0.1):
# 寻找高于阈值的候选角点
corner_threshold = harrisim.max() * threshold
harrisim_t = (harrisim > corner_threshold) * 1
# 得到候选点的坐标
coords = array(harrisim_t.nonzero()).T
# 以及它们的 Harris 响应值
candidate_values = [harrisim[c[0],c[1]] for c in coords]
# 对候选点按照 Harris 响应值进行排序
index = argsort(candidate_values)[::-1]
# 将可行点的位置保存到数组中
allowed_locations = zeros(harrisim.shape)
allowed_locations[min_dist:-min_dist,min_dist:-min_dist] = 1
# 按照 min_distance 原则,选择最佳 Harris 点
filtered_coords = []
for i in index:
if allowed_locations[coords[i,0],coords[i,1]] == 1:
filtered_coords.append(coords[i])
allowed_locations[(coords[i,0]-min_dist):(coords[i,0]+min_dist),
(coords[i,1]-min_dist):(coords[i,1]+min_dist)] = 0
return filtered_coords
from pylab import *
from numpy import *
wid=9 #比较像素点数目
filtered_coords1 = get_harris_points(dst_c1, wid+1,0.1) #图1大于阈值的坐标
maxx=0
for i in range(len(filtered_coords1)):
if filtered_coords1[i][0]>maxx:
maxx=filtered_coords1[i][0]
maxy=0
for i in range(len(filtered_coords1)):
if filtered_coords1[i][1]>maxy:
maxy=filtered_coords1[i][1]
minx=maxx
for i in range(len(filtered_coords1)):
if filtered_coords1[i][0]<minx:
minx=filtered_coords1[i][0]
miny=maxy
for i in range(len(filtered_coords1)):
if filtered_coords1[i][1]<miny:
miny=filtered_coords1[i][1]
lensx=np.uint16(np.around((maxx-minx)/18))
lensy=np.uint16(np.around((maxy-miny)/18))
第三步:棋盘图片转换为矩阵
pan=np.zeros((19,19),int)
for n in range(len(circles[:,0])):
x=np.uint16(np.around((circles[n,0]-minx)/lensx))
y=np.uint16(np.around((circles[n,1]-miny)/lensy))
pan[y,x]=1
pan
可以和上面的棋盘对应,发现到这一步是成功的
第四步:分出黑白棋子
黑棋子在矩阵中用1表示,白棋子用2,若无棋子用0表示
qipan = cv2.imread('D:/classofmathpicture/qipan.jpg',1)
qipan = cv2.cvtColor(qipan, cv2.COLOR_BGR2RGB)
for n in range(len(circles[:,0])):
j=circles[n,0]
i=circles[n,1]
x=np.uint16(np.around((circles[n,0]-minx)/lensx))
y=np.uint16(np.around((circles[n,1]-miny)/lensy))
avg=(int(qipan[i,j,0]) + int(qipan[i,j,1])+ int(qipan[i,j,2]))/3;
if qipan[i,j,0] > 190:
pan[y,x]=2
plt.imshow(qipan)
pan
第三部分:形状识别
形状识别有篇博客不错,这里记载一下:
形状识别
下面用了他的函数也很好用👇
def getContours(img):
# 查找轮廓,cv2.RETR_ExTERNAL=获取外部轮廓点, CHAIN_APPROX_NONE = 得到所有的像素点
contours, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
# 循环轮廓,判断每一个形状
for cnt in contours:
# 获取轮廓面积
area = cv2.contourArea(cnt)
# 当面积大于500,代表有形状存在
if area > 500:
# 绘制所有的轮廓并显示出来
cv2.drawContours(imgContour, cnt, -1, (0, 0, 0), 3)
# 计算所有轮廓的周长,便于做多边形拟合
peri = cv2.arcLength(cnt, True)
# 多边形拟合,获取每个形状的边数目
approx = cv2.approxPolyDP(cnt, 0.02 * peri, True)
objCor = len(approx)
# 获取每个形状的左上角坐标xy和形状的长宽wh
x, y, w, h = cv2.boundingRect(approx)
# 计算出边界后,即边数代表形状,如三角形边数=3
if objCor == 3:
objectType = "Tri"
# 计算出边界后,即边数代表形状,如四边形边数=4
elif objCor == 4:
# 判断是矩形还是正方形
aspRatio = w / float(h)
if aspRatio > 0.98 and aspRatio < 1.03:
objectType = "Square"
else:
objectType = "Rectangle"
# 大于4个边的就是圆形
elif objCor > 4:
objectType = "Circles"
else:
objectType = "None"
# 绘制文本时需要绘制在图形附件
cv2.rectangle(imgContour, (x, y), (x + w, y + h), (255, 0, 0), 2)
cv2.putText(imgContour, objectType,
(x + (w // 2) - 10, y + (h // 2) - 10), cv2.FONT_HERSHEY_COMPLEX, 0.7,
(0, 0, 0), 2)
path = 'D:/classofmathpicture/week4_shibie2.png'
img = cv2.imread(path)
imgContour = img.copy()
# 灰度化
imgGray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# 高斯平滑
imgBlur = cv2.GaussianBlur(imgGray, (7, 7), 1)
# 边缘检测
imgCanny = cv2.Canny(imgBlur, 50, 50)
# 获取轮廓特征点
getContours(imgCanny)
plt.figure(figsize=(30, 20));
plt.subplot(231);plt.imshow(img);plt.title('img');plt.axis('off');
plt.subplot(232);plt.imshow(imgGray,cmap='gray');plt.title('imgGray');plt.axis('off');
plt.subplot(233);plt.imshow(imgBlur,cmap='gray');plt.title('imgBlur');plt.axis('off');
plt.subplot(234);plt.imshow(imgCanny,cmap='gray');plt.title('imgCanny');plt.axis('off');
plt.subplot(235);plt.imshow(imgContour);plt.title('imgContour');plt.axis('off');