04-多阈值检测多边形

根据多种阈值进行多边形检测

import matplotlib.pyplot as plt
import numpy as np
import cv2 as cv

%matplotlib inline

初始化图片

加载图片,并将图片进行处理(高斯模糊),去掉细节信息方便检测边缘

img = cv.imread("images/pic10.png")
img = cv.cvtColor(img, cv.COLOR_BGR2RGB)

# 高斯模糊
img_dst = cv.GaussianBlur(img, (5, 5), 0)
# img_dst = cv.bilateralFilter(img, 0, 30, 20)

f, (ax1, ax2) = plt.subplots(1,2, figsize=(15, 10))
ax1.set_title("img")
ax1.imshow(img)
ax2.set_title("img_blur")
ax2.imshow(img_dst)

png

查找边缘

  1. 拆分不同颜色通道
  2. 使用不同的lower阈值进行二值化,范围0-255,步长26
  3. 查找并打印边缘
# 边缘列表
squares = []

# 分割图片所有的颜色通道
colors = cv.split(img_dst)
print("共有颜色通道:",len(colors))

def angle_cos(p0, p1, p2):
    d1, d2 = (p0-p1).astype('float'), (p2-p1).astype('float')
    return abs( np.dot(d1, d2) / np.sqrt( np.dot(d1, d1)*np.dot(d2, d2) ) )

def findCurve(color_channel, squares):
    f, axs = plt.subplots(3, 4, figsize=(15, 10))
    for i, thrs in enumerate(range(0, 255, 26)):
        if thrs == 0:
            # 首次循环时,进行Canny边缘查找
            bin = cv.Canny(color_channel, 100, 255, apertureSize=5)
            # 形态学膨胀
            bin = cv.dilate(bin, None)

            ax = axs[0][0]
            ax.set_title("{}: thrs:{}".format("canny", thrs))
            ax.imshow(bin, cmap='gray')
        else:
            # 二值化
            _retval, bin = cv.threshold(color_channel, thrs, 255, cv.THRESH_BINARY)
            # 查找二值化后的边缘
            _, contours, _hierarchy = cv.findContours(bin, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)

            img_temp = color_channel.copy()
            cv.drawContours(img_temp, contours, -1, (0, 255, 255), 2)
            ax = axs[int(i / 4)][i % 4]
            ax.set_title("{}: thrs:{}".format(i, thrs))
            ax.imshow(img_temp, cmap='gray')

            for cnt in contours:
                # 计算周长
                cnt_len = cv.arcLength(cnt, True)
                # 多边形逼近(顶点之间的距离小于或等于指定的精度)
                approxCurve = cv.approxPolyDP(cnt, 0.02*cnt_len, True)
                # 计算曲线内面积
                area = cv.contourArea(approxCurve)
                if len(approxCurve) == 4 and 3000 < area < 20000 and cv.isContourConvex(approxCurve):
                    approxCurve = approxCurve.reshape(-1, 2) # 由Nx1x2 -> Nx2
                    # 求出最大角的余弦值,如果接近90度,说明是矩形
                    max_cos = np.max([angle_cos( approxCurve[i], approxCurve[(i+1) % 4], approxCurve[(i+2) % 4] ) for i in range(4)])
                    if max_cos < 0.2:
                        print("找到一个轮廓 {}: thrs:{} cnt: {} max_cos: {}".format(i, thrs, repr(approxCurve), max_cos))
                        squares.append(approxCurve)

output: 共有颜色通道: 3

在不同通道查找轮廓

  • 通道[0] Red通道
color_channel = colors[0]
findCurve(color_channel, squares)

png

  • 通道[1] Green通道
color_channel = colors[1]
findCurve(color_channel, squares)

png

  • 通道[2] Blue通道
color_channel = colors[2]
findCurve(color_channel, squares)

output:

找到一个轮廓 8: thrs:208 cnt: array([[ 47, 171],
       [ 42, 220],
       [255, 281],
       [266, 234]], dtype=int32) max_cos: 0.1774740150218166
找到一个轮廓 8: thrs:208 cnt: array([[154,  11],
       [125,  47],
       [286, 203],
       [317, 166]], dtype=int32) max_cos: 0.09137963891469728

png

绘制轮廓逼近后的多边形

img_copy = img.copy()
cv.drawContours(img_copy, squares, -1, (0, 255, 0), 3 )
plt.imshow(img_copy)

png