001 摄像头拍照+旋转+截取部分+计算棋盘的四个角点坐标+四点定位拉伸
这样,顶上的摄像头即便稍有一些移位或歪斜,也不影响了。
最终得到的是棋盘的规范图,为后续识别做好了准备。
# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
def imgCapture(ImageName):
print("拍照")
#video="http://admin:admin@192.168.1.109:8081/"
#cap=cv2.VideoCapture(video)
#测试过用手机当ip摄像头了,别的都还好,就是拍照分辨率不能调整,所以还是用usb有线摄像头
cap=cv2.VideoCapture(0)
cap.set(3,1920)
cap.set(4,1080)
#cap.set(10,1) #亮度参数 感觉其实没什么用
i=10
while i>0:
i=i-1
ret, frame = cap.read()
cap.release()
#time.sleep(3) # 有些时候 USB摄像头不知道为什么拍的一片漆黑 猜测需要略休眠 实测无效
frame = rotate_bound(frame, 180) #旋转角度180度
cv2.imwrite(ImageName,frame, [int( cv2.IMWRITE_JPEG_QUALITY), 100]) # 默认95
#旋转图像的函数
def rotate_bound(image, angle):
print("旋转图片")
# grab the dimensions of the image and then determine the
# center
(h, w) = image.shape[:2]
(cX, cY) = (w // 2, h // 2)
# grab the rotation matrix (applying the negative of the
# angle to rotate clockwise), then grab the sine and cosine
# (i.e., the rotation components of the matrix)
M = cv2.getRotationMatrix2D((cX, cY), -angle, 1.0)
cos = np.abs(M[0, 0])
sin = np.abs(M[0, 1])
# compute the new bounding dimensions of the image
nW = int((h * sin) + (w * cos))
nH = int((h * cos) + (w * sin))
# adjust the rotation matrix to take into account translation
M[0, 2] += (nW / 2) - cX
M[1, 2] += (nH / 2) - cY
# perform the actual rotation and return the image
#return cv2.warpAffine(image, M, (nW, nH))
return cv2.warpAffine(image, M, (nW, nH),borderValue=(255,255,255))
# 切出矩形棋盘的一系列函数 开始
# 参考 https://blog.csdn.net/sinat_36458870/article/details/78825571
def get_image(path):
#获取图片
img=cv2.imread(path)
gray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
return img, gray
def Gaussian_Blur(gray):
# 高斯去噪
blurred = cv2.GaussianBlur(gray, (3, 3),0)
return blurred
def Sobel_gradient(blurred):
# 索比尔算子来计算x、y方向梯度
gradX = cv2.Sobel(blurred, ddepth=cv2.CV_32F, dx=1, dy=0)
gradY = cv2.Sobel(blurred, ddepth=cv2.CV_32F, dx=0, dy=1)
gradient = cv2.subtract(gradX, gradY)
gradient = cv2.convertScaleAbs(gradient)
return gradX, gradY, gradient
def Thresh_and_blur(gradient):
blurred = cv2.GaussianBlur(gradient, (3, 3),0)
#(_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_BINARY)
(_, thresh) = cv2.threshold(blurred, 90, 255, cv2.THRESH_BINARY)
return thresh
def image_morphology(thresh,juanjihe): #定义卷积核大小,juanjihe=50可以把棋盘框出来 =20的时候可以用于识别顶点
# 建立一个椭圆核函数
kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (juanjihe, juanjihe)) #把卷积核大一些 就可以获得整个棋盘 不然太小会只截取一部分
# 执行图像形态学, 细节直接查文档,很简单
closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)
closed = cv2.erode(closed, None, iterations=4)
closed = cv2.dilate(closed, None, iterations=4)
return closed
def findcnts_and_box_point(closed):
# 这里opencv3返回的是三个参数
#(_, cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
(cnts, _) = cv2.findContours(closed.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) #opencv版本 如果不行就用上面的那个
c = sorted(cnts, key=cv2.contourArea, reverse=True)[0]
# compute the rotated bounding box of the largest contour
rect = cv2.minAreaRect(c)
box = np.int0(cv2.boxPoints(rect))
return box
def drawcnts_and_cut(original_img, box):
# 因为这个函数有极强的破坏性,所有需要在img.copy()上画
# draw a bounding box arounded the detected barcode and display the image
draw_img = cv2.drawContours(original_img.copy(), [box], -1, (0, 0, 255), 3)
Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
print(Xs)
print(Ys)
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
hight = y2 - y1
width = x2 - x1
#crop_img = original_img[y1:y1+hight, x1:x1+width]
crop_img = original_img[(y1-20):(y1+hight+20), (x1-20):(x1+width+20)]
#多切一点儿 切得太准确了 会导致后面角点检测找不到正确的四个角点
return draw_img, crop_img
# 切出矩形棋盘的一系列函数 结束
# 角点检测函数
def jiaodian(image_p):
print("进行角点检测")
img =cv2.imread(image_p)
imgray=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
#harris角点检测图像需为float32
gray=np.float32(imgray)
dst=cv2.cornerHarris(gray,4,3,0.04) #如果不准 可以改gray后面那个参数 越改小则检测出来的角点越多
dst=cv2.dilate(dst,None)
ret,dst=cv2.threshold(dst,0.01*dst.max(),255,0)
dst=np.uint8(dst)
#图像连通域
ret,labels,stats,centroids=cv2.connectedComponentsWithStats(dst)
#迭代停止规则
criteria=(cv2.TERM_CRITERIA_EPS+cv2.TERM_CRITERIA_MAX_ITER,100,0.0001) #如果不准 可以改最后一个参数 越改小则检测出来的角点越多
corners=cv2.cornerSubPix(gray,np.float32(centroids),(5,5),(-1,-1),criteria)
res=np.hstack((centroids,corners))
res=np.int0(res)
#print(res)
'''
#img[res[:,1],res[:,0]]=[0,120,255]
#img[res[:,3],res[:,2]]=[45,255,100]
for i in res:
x1,y1,x2,y2=i.ravel()
cv2.circle(img,(x1,y1),3,255,-1)
cv2.circle(img,(x2,y2),3,(0,255,0),-1)
img=img[:,:,::-1]
cv2.imshow('dst',img)
if cv2.waitKey(0) & 0xff == 27:
cv2.destroyAllWindows()
'''
return res
# 确定四点 矩形拉伸
def img_lashen(img,new_image,x1,y1,x2,y2,x3,y3,x4,y4):
print("四点定位拉伸图片")
imgages = cv2.imread(img)
rows, cols = imgages.shape[:2]
# 原图 四个角点
'''
x1 = 746
y1 = 243
x2 = 1374
y2 = 230
x3 = 752
y3 = 866
x4 = 1384
y4 = 863
'''
x2x1 = (x2-x1)/9
x4x3 = (x4-x3)/9
y3y1 = (y3-y1)/10
y4y2 = (y4-y2)/10
x1new = x1 - x2x1
y1new = y1 - y3y1
x2new = x2 + x2x1
y2new = y2 - y4y2
x3new = x3 - x4x3
y3new = y3 + y3y1
x4new = x4 + x4x3
y4new = y4 + y4y2
#pts1 = np.float32([[628, 36], [1426, 177], [600, 1055], [1394, 947]])
pts1 = np.float32([[x1new, y1new], [x2new, y2new], [x3new, y3new], [x4new, y4new]])
# 变换后分别在左上、右上、左下、右下四个点
#pts2 = np.float32([[628, 36], [1426, 36], [628, 1055], [1426, 1055]])
#pts2 = np.float32([[0, 0], [900, 0], [0, 1000], [900, 1000]])
pts2 = np.float32([[0, 0], [1100, 0], [0, 1200], [1100, 1200]])
# 生成透视变换矩阵
M = cv2.getPerspectiveTransform(pts1, pts2)
# 进行透视变换
dst = cv2.warpPerspective(imgages, M, (1100, 1200))
#plt.subplot(121), plt.imshow(imgages[:, :, ::-1]), plt.title('b.png')
#plt.subplot(122), plt.imshow(dst[:, :, ::-1]), plt.title('c.png')
# imgages[:, :, ::-1]是将BGR转化为RGB
#plt.show()
cv2.imwrite(new_image, dst)
MyImageName = 'a.png'
My_new_image = 'b.png'
My_last_image = 'c.png'
imgCapture(MyImageName)
#img0 = cv2.imread(MyImageName)
#plt.imshow(img0),plt.show()
print("切出矩形棋盘")
# 切出矩形棋盘
img_path = MyImageName
save_path = My_new_image
original_img, gray = get_image(img_path)
blurred = Gaussian_Blur(gray)
gradX, gradY, gradient = Sobel_gradient(blurred)
thresh = Thresh_and_blur(gradient)
closed = image_morphology(thresh,50) #50可以识别出棋盘
box = findcnts_and_box_point(closed)
draw_img, crop_img = drawcnts_and_cut(original_img,box)
cv2.imwrite(save_path, crop_img)
# 截成四个象限 用于分别计算最远角点 以便使棋盘拉伸更精确
imgb = Image.open(My_new_image)
imgb_size = imgb.size
hb = imgb_size[1] # 图片高度
wb = imgb_size[0] # 图片宽度
regionb1 = imgb.crop((0, 0, (wb/2), (hb/2)))
regionb2 = imgb.crop(((wb/2), 0, wb, (hb/2)))
regionb3 = imgb.crop((0, (hb/2), (wb/2), hb))
regionb4 = imgb.crop(((wb/2), (hb/2), wb, hb))
# 保存图片
regionb1.save("b_1.png")
regionb2.save("b_2.png")
regionb3.save("b_3.png")
regionb4.save("b_4.png")
# 切出之后 进行角点查找
# image_p1
px = wb/2
py = hb/2
res = jiaodian('b_1.png')
x_max = 0
y_max = 0
z_max = 0 #面积
for i in res:
x1,y1,x2,y2=i.ravel()
lx = abs(px - x1)
ly = abs(py - y1)
z = lx*ly
if z > z_max:
x_max = x1
y_max = y1
z_max = z
tx1 = x_max
ty1 = y_max
print(tx1,ty1)
# image_p2
px = 0
py = hb/2
res = jiaodian('b_2.png')
x_max = 0
y_max = 0
z_max = 0 #面积
for i in res:
x1,y1,x2,y2=i.ravel()
lx = abs(px - x1)
ly = abs(py - y1)
z = lx*ly
if z > z_max:
x_max = x1
y_max = y1
z_max = z
tx2 = x_max+(wb/2)
ty2 = y_max
print(tx2,ty2)
# image_p3
px = wb/2
py = 0
res = jiaodian('b_3.png')
x_max = 0
y_max = 0
z_max = 0 #面积
for i in res:
x1,y1,x2,y2=i.ravel()
lx = abs(px - x1)
ly = abs(py - y1)
z = lx*ly
if z > z_max:
x_max = x1
y_max = y1
z_max = z
tx3 = x_max
ty3 = y_max+(hb/2)
print(tx3,ty3)
# image_p4
px = 0
py = 0
res = jiaodian('b_4.png')
x_max = 0
y_max = 0
z_max = 0 #面积
for i in res:
x1,y1,x2,y2=i.ravel()
lx = abs(px - x1)
ly = abs(py - y1)
z = lx*ly
if z > z_max:
x_max = x1
y_max = y1
z_max = z
tx4 = x_max+(wb/2)
ty4 = y_max+(hb/2)
print(tx4,ty4)
#拉伸
print("根据四点拉伸图片")
img_lashen(My_new_image,My_last_image,tx1,ty1,tx2,ty2,tx3,ty3,tx4,ty4)
# 显示一下 原照片a 截取的区域b 最终拉伸成为规范矩形的c
plt.figure()
plt.subplot(1,3,1) # 将画板分为1行3列,本幅图位于第1个位置
imga = plt.imread('a.png')
plt.imshow(imga)
plt.subplot(1,3,2) # 将画板分为1行3列,本幅图位于第2个位置
imgb = plt.imread('b.png')
plt.imshow(imgb)
plt.subplot(1,3,3) # 将画板分为1行3列,本幅图位于第3个位置
imgc = plt.imread('c.png')
plt.imshow(imgc)
plt.show()