OpenCV(python)一键入门--十五篇(矩形弧长面积计算,轮廓逼近及其横纵比过滤)

1:矩形弧长面积计算

import cv2 as cv
import numpy as np

def canny_demo(image):
    t = 80
    canny_output = cv.Canny(image, t, t * 2)
    cv.imshow("canny_output", canny_output)
    return canny_output

src = cv.imread("D:/coin.jpg")
cv.imshow("input", src)

binary = canny_demo(src)
k = np.ones((3, 3), dtype=np.uint8)
binary = cv.morphologyEx(binary, cv.MORPH_DILATE, k)

# 轮廓发现
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for c in range(len(contours)):

    area = cv.contourArea(contours[c])
    arclen = cv.arcLength(contours[c], True)
    if area < 100 or arclen < 100:
        continue
    
    rect = cv.minAreaRect(contours[c])
    cx, cy = rect[0]
    box = cv.boxPoints(rect)
    box = np.int0(box)
    cv.drawContours(src,[box],0,(0,0,255),2)
    cv.circle(src, (np.int32(cx), np.int32(cy)), 2, (255, 0, 0), 2, 8, 0)


cv.imshow("contours_analysis", src)
cv.waitKey(0)
cv.destroyAllWindows()

建议结合上一篇文章一起看:OpenCV(python)一键入门--十四篇(二值图像分析入门)

cv.findContours,cv.drawContours,cv.morphologyEx,cv.minAreaRect,cv.boxPoints的函数介绍都在上一篇博客中:

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

1):cv.boundingRect 矩形边框

这里的for循环如果整个换成:

for c in range(len(contours)):
    x, y, w, h = cv.boundingRect(contours[c])
    cv.drawContours(src, contours, c, (0, 0, 255), 2, 8)
    cv.rectangle(src, (x, y), (x+w, y+h), (0, 0, 255), 1, 8, 0)

那么效果如下:

boundingRect的输入是轮廓,在示例代码中我们使用cv.findContours返回的list。

他会返回四个值,一个矩形   左上角的【x,y】坐标,右下角的【x,y】坐标。在绘制矩形的时候,直接用这四个值即可。

绘制图像函数可以参考:OpenCV(python)——一键入门--第4篇

- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

2):cv.contourArea  计算轮廓面积

该函数使用格林公式计算。不过,对于具有自交点的轮廓,该函数几乎肯定会给出错误的结果。

该函数读取cv.findContours给出的轮廓,返回一个轮廓面积


3):cv.arcLength 计算轮廓弧长

也是输入轮廓,返回弧长(周长

这个函数的第二参数可以用来指定对象的形状是闭合的(True),还是打开的(一条曲线)。

弧长 = cv2.arcLength(图像,True)


· 其余部分函数还请移步参考上一篇博客:OpenCV(python)一键入门--十四篇(二值图像分析入门)


2:轮廓逼近

import cv2 as cv
import numpy as np

src = cv.imread("D:/cycle.png")
cv.imshow("input", src)
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)

# 轮廓发现
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for c in range(len(contours)):
    rect = cv.minAreaRect(contours[c])
    #print(f"rect:{rect}")
    #print(f"result:\n{result}\n")

    cx, cy = rect[0]
    result = cv.approxPolyDP(contours[c], 4, True)#连续光滑曲线折线化
    vertexes = result.shape[0]
    #print(f"result.shape:{result.shape}\n")


    if vertexes == 3:
        cv.putText(src, "triangle", (np.int32(cx), np.int32(cy)),
                   cv.FONT_HERSHEY_SIMPLEX, .7, (0, 0, 255), 2, 8);
        _ = "triangle"
    if vertexes == 4:
        cv.putText(src, "rectangle", (np.int32(cx), np.int32(cy)),
                   cv.FONT_HERSHEY_SIMPLEX, .7, (0, 0, 255), 2, 8);
        _ = "rectangle"
    if vertexes == 6:
        cv.putText(src, "poly", (np.int32(cx), np.int32(cy)),
                   cv.FONT_HERSHEY_SIMPLEX, .7, (0, 0, 255), 2, 8);
        _ = "poly"
    if vertexes > 10:
        cv.putText(src, "circle", (np.int32(cx), np.int32(cy)),
                   cv.FONT_HERSHEY_SIMPLEX, .7, (0, 0, 255), 2, 8);
        _ = "circle"
    print(f"vertexes: {vertexes}  class: {_}\n")


cv.imshow("contours_analysis", src)

cv.waitKey(0)
cv.destroyAllWindows()

1):cv.approxPolyDP

可以看成是:  cv.approxPolyDP(轮廓,参数epsilon,轮廓是否闭合

将其获取到的  result  shape 一下,即可获取其边的个数。通过result.shape[0] 就可以判断。

其效果如下(附上测试原图)

cycle.png


3:使用几何矩阵计算轮廓中心与横纵比过滤

import cv2 as cv
import numpy as np


def canny_demo(image):
    t = 80
    canny_output = cv.Canny(image, t, t * 2)
    cv.imshow("canny_output", canny_output)
    return canny_output

src = cv.imread("D:/cycle.png")
cv.imshow("input", src)

binary = canny_demo(src)
k = np.ones((3, 3), dtype=np.uint8)
binary = cv.morphologyEx(binary, cv.MORPH_DILATE, k)

# 轮廓发现
contours, hierarchy = cv.findContours(binary, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
for c in range(len(contours)):
    rect = cv.minAreaRect(contours[c])
    print(f"rect: {rect}")
    cx, cy = rect[0]
    ww, hh = rect[1]

    ratio = np.minimum(ww, hh) / np.maximum(ww, hh)
    print(f"ratio: {ratio}")

    mm = cv.moments(contours[c])
    m00 = mm['m00']
    m10 = mm['m10']
    m01 = mm['m01']
    cx = np.int(m10 / m00)
    cy = np.int(m01 / m00)

    box = cv.boxPoints(rect)
    box = np.int0(box)
    if ratio > 0.9:
        cv.drawContours(src, [box], 0, (0, 0, 255), 2)
        cv.circle(src, (np.int32(cx), np.int32(cy)), 2, (255, 0, 0), 2, 8, 0)
    if ratio < 0.5:
        cv.drawContours(src, [box], 0, (255, 0, 255), 2)
        cv.circle(src, (np.int32(cx), np.int32(cy)), 2, (0, 0, 255), 2, 8, 0)

cv.imshow("contours_analysis", src)

cv.waitKey(0)
cv.destroyAllWindows()

这里我们还使用到rect里的宽和高,用于ratio的计算


1):cv.moments() 
参数: 
图像的矩可以帮助我们计算图像的质心,面积等 
cv.moments(轮廓) 

这个函数会给你一个字典,包含所有矩值
根据这些矩的值,我们可以计算出对象的重心

效果如下:

(完)