001 摄像头拍照+旋转+截取部分+计算棋盘的四个角点坐标+四点定位拉伸

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()



  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值