python+opencv实现二维码定位(一)

参考:
思路和代码
二维码补充知识

基本思路是:
1)定位点的轮廓有三层轮廓
2)每个定位点的轮廓中心点一样的
3)三个定位点可以围成一个等腰直角三角形

补充思路:
当背景过于复杂的使用时候无法正确找到二维码位置,按以下思路尝试改进:
1)阈值分割
2)膨胀或者腐蚀
3)扣取所有正方形区域,逐一检测
4)然后再进行上面的操作

import numpy as np
import copy

def reshape_image(image):
    '''归一化图片尺寸:短边400,长边不超过800,短边400,长边超过800以长边800为主'''
    width,height=image.shape[1],image.shape[0]
    min_len=width
    scale=width*1.0/400
    new_width=400
    new_height=int(height/scale)
    if new_height>800:
        new_height=800
        scale=height*1.0/800
        new_width=int(width/scale)
    out=cv2.resize(image,(new_width,new_height))
    return out

def detecte(image):
    '''提取所有轮廓'''
    gray=cv2.cvtColor(image,cv2.COLOR_BGR2GRAY)
    _,gray=cv2.threshold(gray,0,255,cv2.THRESH_OTSU+cv2.THRESH_BINARY_INV)
    img,contours,hierachy=cv2.findContours(gray,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
    return image,contours,hierachy
 
def compute_1(contours,i,j):
    '''最外面的轮廓和子轮廓的比例'''
    area1 = cv2.contourArea(contours[i])
    area2 = cv2.contourArea(contours[j])
    if area2==0:
        return False
    ratio = area1 * 1.0 / area2
    if abs(ratio - 49.0 / 25):
        return True
    return False
def compute_2(contours,i,j):
    '''子轮廓和子子轮廓的比例'''
    area1 = cv2.contourArea(contours[i])
    area2 = cv2.contourArea(contours[j])
    if area2==0:
        return False
    ratio = area1 * 1.0 / area2
    if abs(ratio - 25.0 / 9):
        return True
    return False
def compute_center(contours,i):
    '''计算轮廓中心点'''
    M=cv2.moments(contours[i])
    cx = int(M['m10'] / M['m00'])
    cy = int(M['m01'] / M['m00'])
    return cx,cy
def detect_contours(vec):
    '''判断这个轮廓和它的子轮廓以及子子轮廓的中心的间距是否足够小'''
    distance_1=np.sqrt((vec[0]-vec[2])**2+(vec[1]-vec[3])**2)
    distance_2=np.sqrt((vec[0]-vec[4])**2+(vec[1]-vec[5])**2)
    distance_3=np.sqrt((vec[2]-vec[4])**2+(vec[3]-vec[5])**2)
    if sum((distance_1,distance_2,distance_3))/3<3:
        return True
    return False
def juge_angle(rec):
    '''判断寻找是否有三个点可以围成等腰直角三角形'''
    if len(rec)<3:
        return -1,-1,-1
    for i in range(len(rec)):
        for j in range(i+1,len(rec)):
            for k in range(j+1,len(rec)):
                distance_1 = np.sqrt((rec[i][0] - rec[j][0]) ** 2 + (rec[i][1] - rec[j][1]) ** 2)
                distance_2 = np.sqrt((rec[i][0] - rec[k][0]) ** 2 + (rec[i][1] - rec[k][1]) ** 2)
                distance_3 = np.sqrt((rec[j][0] - rec[k][0]) ** 2 + (rec[j][1] - rec[k][1]) ** 2)
                if abs(distance_1-distance_2)<5:
                    if abs(np.sqrt(np.square(distance_1)+np.square(distance_2))-distance_3)<5:
                        return i,j,k
                elif abs(distance_1-distance_3)<5:
                    if abs(np.sqrt(np.square(distance_1)+np.square(distance_3))-distance_2)<5:
                        return i,j,k
                elif abs(distance_2-distance_3)<5:
                    if abs(np.sqrt(np.square(distance_2)+np.square(distance_3))-distance_1)<5:
                        return i,j,k
    return -1,-1,-1
def find(image,contours,hierachy,root=0):
    '''找到符合要求的轮廓'''
    rec=[]
    for i in range(len(hierachy)):
        child = hierachy[i][2]
        child_child=hierachy[child][2]
        if child!=-1 and hierachy[child][2]!=-1:
            if compute_1(contours, i, child) and compute_2(contours,child,child_child):
                cx1,cy1=compute_center(contours,i)
                cx2,cy2=compute_center(contours,child)
                cx3,cy3=compute_center(contours,child_child)
                if detect_contours([cx1,cy1,cx2,cy2,cx3,cy3]):
                    rec.append([cx1,cy1,cx2,cy2,cx3,cy3,i,child,child_child])
    '''计算得到所有在比例上符合要求的轮廓中心点'''
    i,j,k=juge_angle(rec)
    if i==-1 or j== -1 or k==-1:
        return
    ts = np.concatenate((contours[rec[i][6]], contours[rec[j][6]], contours[rec[k][6]]))
    rect = cv2.minAreaRect(ts)
    box = cv2.boxPoints(rect)
    box = np.int0(box)
    result=copy.deepcopy(image)
    cv2.drawContours(result, [box], 0, (0, 0, 255), 2)
    cv2.drawContours(image,contours,rec[i][6],(255,0,0),2)
    cv2.drawContours(image,contours,rec[j][6],(255,0,0),2)
    cv2.drawContours(image,contours,rec[k][6],(255,0,0),2)
    cv2.imshow('img',image)
    cv2.waitKey(0)
    cv2.imshow('img',result)
    cv2.waitKey(0)
    
    return
    
if __name__ == '__main__':

	imgpath = "path/mypic.jpg"
    image=cv2.imread(imgpath)
    image=reshape_image(image)
    cv2.imshow('sb', image)
    cv2.waitKey(0)
    image,contours,hierachy=detecte(image)
    find(image,contours,np.squeeze(hierachy))

其中函数的用法说明:

threshold()固定阈值二值化函数

ret, dst = cv2.threshold(src, thresh, maxval, type)

参数

src
输入图,只能输入单通道图像,通常来说为灰度图

thresh
阈值

maxval
当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值

type
二值化操作的类型,包含以下5种类型: 
cv2.THRESH_BINARY; (两极化)
cv2.THRESH_BINARY_INV;(对调两极化)
cv2.THRESH_TRUNC; (过度)
cv2.THRESH_TOZERO;(亮色过度)
cv2.THRESH_TOZERO_INV;(对调亮色过度)

返回值:

dst 
输出图

cv2.findContours()寻找轮廓函数

img, countour,hierarchy = cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]])  

参数

image
表示寻找轮廓的图像;

mode
表示轮廓的检索模式,有四种(本文介绍的都是新的cv2接口):
	cv2.RETR_EXTERNAL表示只检测外轮廓
	cv2.RETR_LIST检测的轮廓不建立等级关系
	cv2.RETR_CCOMP建立两个等级的轮廓,上面的一层为外边界,里面的一层为内孔的边界信息。如果内孔内还有一个连通物体,这个物体的边界也在顶层。
	cv2.RETR_TREE建立一个等级树结构的轮廓。
    
method
表示轮廓的近似办法
	cv2.CHAIN_APPROX_NONE存储所有的轮廓点,相邻的两个点的像素位置差不超过1,即max(abs(x1-x2),abs(y2-y1))==1
	cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息
	cv2.CHAIN_APPROX_TC89_L1,CV_CHAIN_APPROX_TC89_KCOS使用teh-Chinl chain 近似算法

返回值

img
表示返回的图像;

coutour
是一个list,每个元素都是一个轮廓(彻底围起来算一个轮廓),用numpy中的ndarray表示。

hierarchy
也是一个ndarry,其中的元素个数和轮廓个数相同,
每个轮廓contours[i]对应4个hierarchy元素hierarchy[i][0] ~hierarchy[i][3],
分别表示后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号,如果没有对应项,则该值为负数。

drawContours()画轮廓函数

cv2.drawContours(img,contours,num,(r,g,b),width)

参数:

img
在这个图像上画

contours
引用这个轮廓列表

num
画轮廓列表准第num个轮廓,如果为-1,则全部画出

rgb
线的颜色

width
线宽

contourArea()计算轮廓下的面积

area = cv2.contourArea(contours[i])

参数

contours[i]
第i个轮廓

返回值

area
面积值

cv2.moments()计算轮廓下的质心

M=cv2.moments(contours[i])  
#得到轮廓的矩M,按以下方法可以得到质心坐标。
cx = int(M['m10'] / M['m00'])    
cy = int(M['m01'] / M['m00'])

np.concatenate()矩阵拼接

cv2.minAreaRect()生成最小外接矩形

cv2.boxPoints()提取矩形四个顶点

deepcopy()独立复制

你可能感兴趣的:(python)