【opencv-python测量物体的实际大小】使用opencv-python测量物体的实际大小

效果

【opencv-python测量物体的实际大小】使用opencv-python测量物体的实际大小_第1张图片

第一步,进行轮廓提取,将图片转化为灰度图,然后进行高斯模糊,模糊后提取轮廓,然后进行膨胀收缩使轮廓更加的清晰

【opencv-python测量物体的实际大小】使用opencv-python测量物体的实际大小_第2张图片

def getContours(img):
    img = cv2.resize(img, (0, 0), None, 0.5, 0.5)
    imgG = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    imgBlur = cv2.GaussianBlur(imgG,(5,5),1)
    imgCanny = cv2.Canny(imgBlur,100,100)
    kernel = np.ones((5,5))
    imgDial = cv2.dilate(imgCanny,kernel,iterations=3)
    imgThre = cv2.erode(imgDial,kernel,iterations=2)
    cv2.imshow('img',img)
    cv2.imshow('res',imgCanny)
    cv2.imshow('res2',imgThre)

getContours(img)
cv2.waitKey(0)

第二步,找出最大轮廓

用到的函数

cv2.findContours(image, mode, method[, contours[, hierarchy[, offset ]]]) → image, contours, hierarchy

image-寻找轮廓的图像;
mode-轮廓的检索模式:cv2.RETR_EXTERNAL表示只检测外轮廓
method cv2.CHAIN_APPROX_SIMPLE压缩水平方向,垂直方向,对角线方向的元素,只保留该方向的终点坐标,例如一个矩形轮廓只需4个点来保存轮廓信息

contourArea()函数的作用:主要用于计算图像轮廓的面积。

arcLength()函数的作用:主要是计算图像轮廓的周长

approxPolyDP(InputArray curve, OutputArray approxCurve, double epsilon, bool closed)
主要功能是把一个连续光滑曲线折线化,对图像轮廓点进行多边形拟合

InputArray curve:一般是由图像的轮廓点组成的点集
OutputArray approxCurve:表示输出的多边形点集
double epsilon:主要表示输出的精度,就是另个轮廓点之间最大距离数,5,6,7,,
bool closed:表示输出的多边形是否封闭

boundingRect函数
函数作用:计算轮廓的垂直边界最小矩形,矩形是与图像上下边界平行的

    contours, hiearchy = cv2.findContours(imgThre,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    finalCountours = []
    for i in contours:
        area = cv2.contourArea(i)
        if area> minArea:
            # 计算轮廓的周长,true表示轮廓为封闭
            peri = cv2.arcLength(i,True)
            #进行轮廓的多边拟合
            appprox = cv2.approxPolyDP(i,0.02*peri,True)
            bbox = cv2.boundingRect(appprox)
            if filter > 0 :
                if(len(appprox))==filter:
                    finalCountours.append([len(appprox),area,appprox,bbox,i])
            else:
                finalCountours.append([len(appprox), area, appprox, bbox, i])
    # 对第二个数值面积进行排序,为升序,找出轮廓的最大值
    finalCountours = sorted(finalCountours, key=lambda x: x[1], reverse=True)
    for con in finalCountours:
        cv2.drawContours(img, con[4], -1, (0, 0, 255), 3)

最大轮廓外表
【opencv-python测量物体的实际大小】使用opencv-python测量物体的实际大小_第3张图片

第三步,得出的轮廓四角的顺序可能发生变化,因此需要将四角的坐标固定为矩形一种固定的顺序

这个函数为重新排序函数,无论给什么顺序的四角坐标,都能整理好!

def reorder(myPoints):
    #print(myPoints.shape)
    # zeros_like 输出为形状和x一致的矩阵,其元素全部为0
    myPointsNew = np.zeros_like(myPoints)
    myPoints = myPoints.reshape((4,2))
    add = myPoints.sum(1)
    myPointsNew[0] = myPoints[np.argmin(add)]
    myPointsNew[3] = myPoints[np.argmax(add)]
    diff = np.diff(myPoints,axis=1)
    myPointsNew[1]= myPoints[np.argmin(diff)]
    myPointsNew[2] = myPoints[np.argmax(diff)]
    return myPointsNew

第四步,将最大矩形的A4纸透视还原为正常的长方形

【opencv-python测量物体的实际大小】使用opencv-python测量物体的实际大小_第4张图片
w,h为A4的原本大小

def warpImg (img,points,w,h,pad=20):
    # print(points)
    points =reorder(points)
    pts1 = np.float32(points)
    pts2 = np.float32([[0,0],[w,0],[0,h],[w,h]])
    matrix = cv2.getPerspectiveTransform(pts1,pts2)
    imgWarp = cv2.warpPerspective(img,matrix,(w,h))
    imgWarp = imgWarp[pad:imgWarp.shape[0]-pad,pad:imgWarp.shape[1]-pad]
    return imgWarp

第五步,找出A4纸中的图形轮廓

【opencv-python测量物体的实际大小】使用opencv-python测量物体的实际大小_第5张图片

imgcon, cons= getContours(img)
if(len(cons)!=0):
    maxbox = cons[0][2]
    reorder(maxbox)
    cv2.imshow('img', imgcon)
    imgWarp = warpImg(imgcon, maxbox, wp, hp)
    cv2.imshow('imgWarp',imgWarp)
    imgcon2, cons2 = getContours(imgWarp)
    if(len(cons2)!=0):
        for obj in cons2:
            cv2.polylines(imgcon2,[obj[2]],True,(0,255,0),2)
    cv2.imshow('img2',imgcon2)

第六步,计算实际测量的大小

计算公式 a2+b2=c2
【opencv-python测量物体的实际大小】使用opencv-python测量物体的实际大小_第6张图片

def findDis(pts1,pts2):
    return ((pts2[0]-pts1[0])**2 + (pts2[1]-pts1[1])**2)**0.5

【opencv-python测量物体的实际大小】使用opencv-python测量物体的实际大小_第7张图片

 if(len(cons2)!=0):
        for obj in cons2:
            cv2.polylines(imgcon2,[obj[2]],True,(0,255,0),2)
            nPoints = reorder(obj[2])
            nW = round((findDis(nPoints[0][0] // scale, nPoints[1][0] // scale) / 10), 1)
            nH = round((findDis(nPoints[0][0] // scale, nPoints[2][0] // scale) / 10), 1)
            cv2.arrowedLine(imgcon2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[1][0][0], nPoints[1][0][1]),
                            (255, 0, 255), 3, 8, 0, 0.05)
            cv2.arrowedLine(imgcon2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[2][0][0], nPoints[2][0][1]),
                            (255, 0, 255), 3, 8, 0, 0.05)
            x, y, w, h = obj[3]
            cv2.putText(imgcon2, '{}cm'.format(nW), (x + 30, y - 10), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
                        (255, 0, 255), 2)
            cv2.putText(imgcon2, '{}cm'.format(nH), (x - 70, y + h // 2), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
                        (255, 0, 255), 2)

    cv2.imshow('img2',imgcon2)

代码

import cv2
import numpy as np

img = cv2.imread('3.png')
img = cv2.resize(img, (0, 0), None, 0.5, 0.5)
minArea = 1000
filter = 4
scale =2
wp=210*scale
hp =297*scale


def getContours(img):
    imgG = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    imgBlur = cv2.GaussianBlur(imgG,(5,5),1)
    imgCanny = cv2.Canny(imgBlur,100,100)
    kernel = np.ones((5,5))
    imgDial = cv2.dilate(imgCanny,kernel,iterations=3)
    imgThre = cv2.erode(imgDial,kernel,iterations=2)
    cv2.imshow('res',imgCanny)
    cv2.imshow('res2',imgThre)

    contours, hiearchy = cv2.findContours(imgThre,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    finalCountours = []
    for i in contours:
        area = cv2.contourArea(i)
        if area> minArea:
            # 计算轮廓的周长,true表示轮廓为封闭
            peri = cv2.arcLength(i,True)
            #进行轮廓的多边拟合
            appprox = cv2.approxPolyDP(i,0.02*peri,True)
            bbox = cv2.boundingRect(appprox)
            if filter > 0 :
                if(len(appprox))==filter:
                    finalCountours.append([len(appprox),area,appprox,bbox,i])
            else:
                finalCountours.append([len(appprox), area, appprox, bbox, i])
    # 对第二个数值面积进行排序,为升序,找出轮廓的最大值
    finalCountours = sorted(finalCountours, key=lambda x: x[1], reverse=True)
    for con in finalCountours:
        cv2.drawContours(img,con[4],-1, (0, 0, 255), 4)
    return img,finalCountours



def reorder(myPoints):
    #print(myPoints.shape)
    myPointsNew = np.zeros_like(myPoints)
    myPoints = myPoints.reshape((4,2))
    add = myPoints.sum(1)
    myPointsNew[0] = myPoints[np.argmin(add)]
    myPointsNew[3] = myPoints[np.argmax(add)]
    diff = np.diff(myPoints,axis=1)
    myPointsNew[1]= myPoints[np.argmin(diff)]
    myPointsNew[2] = myPoints[np.argmax(diff)]
    print(myPoints)
    print(myPoints)
    return myPointsNew


def warpImg (img,points,w,h,pad=20):
    # print(points)
    points =reorder(points)
    pts1 = np.float32(points)
    pts2 = np.float32([[0,0],[w,0],[0,h],[w,h]])
    matrix = cv2.getPerspectiveTransform(pts1,pts2)
    imgWarp = cv2.warpPerspective(img,matrix,(w,h))
    imgWarp = imgWarp[pad:imgWarp.shape[0]-pad,pad:imgWarp.shape[1]-pad]
    return imgWarp

def findDis(pts1,pts2):
    return ((pts2[0]-pts1[0])**2 + (pts2[1]-pts1[1])**2)**0.5


imgcon, cons= getContours(img)
if(len(cons)!=0):
    maxbox = cons[0][2]
    reorder(maxbox)
    cv2.imshow('img', imgcon)
    imgWarp = warpImg(imgcon, maxbox, wp, hp)
    cv2.imshow('imgWarp',imgWarp)
    imgcon2, cons2 = getContours(imgWarp)
    if(len(cons2)!=0):
        for obj in cons2:
            cv2.polylines(imgcon2,[obj[2]],True,(0,255,0),2)
            nPoints = reorder(obj[2])
            nW = round((findDis(nPoints[0][0] // scale, nPoints[1][0] // scale) / 10), 1)
            nH = round((findDis(nPoints[0][0] // scale, nPoints[2][0] // scale) / 10), 1)
            cv2.arrowedLine(imgcon2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[1][0][0], nPoints[1][0][1]),
                            (255, 0, 255), 3, 8, 0, 0.05)
            cv2.arrowedLine(imgcon2, (nPoints[0][0][0], nPoints[0][0][1]), (nPoints[2][0][0], nPoints[2][0][1]),
                            (255, 0, 255), 3, 8, 0, 0.05)
            x, y, w, h = obj[3]
            cv2.putText(imgcon2, '{}cm'.format(nW), (x + 30, y - 10), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
                        (255, 0, 255), 2)
            cv2.putText(imgcon2, '{}cm'.format(nH), (x - 70, y + h // 2), cv2.FONT_HERSHEY_COMPLEX_SMALL, 1.5,
                        (255, 0, 255), 2)

    cv2.imshow('img2',imgcon2)

cv2.waitKey(0)

你可能感兴趣的:(opencv,opencv,python,计算机视觉,图像处理,深度学习)