OpenCV 例程200篇222. 特征提取之弗里曼链码(Freeman chain code)

Posted YouCans

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了OpenCV 例程200篇222. 特征提取之弗里曼链码(Freeman chain code)相关的知识,希望对你有一定的参考价值。

OpenCV 例程200篇 总目录


【youcans 的 OpenCV 例程200篇】222. 特征提取之弗里曼链码(Freeman chain code)


目标特征的基本概念

通过图像分割获得多个区域,得到区域内的像素集合或区域边界像素集合。我们把感兴趣的人或物称为目标,目标所处的区域就是目标区域。
特征通常是针对于图像中的某个目标而言的。图像分割之后,还要对目标区域进行适当的表示和描述,以便下一步处理。
“表示”是直接具体地表示目标,以节省存储空间、方便特征计算。目标的表示方法,有链码、多边形逼近(MPP)、斜率标记图、边界分段、区域骨架。
“描述”是对目标的抽象表达,在区别不同目标的基础上,尽可能对目标的尺度、平移、旋转变化不敏感。


边界特征描述子

目标的边界描述符(Boundary descriptors),也称为边界描述子。
轮廓就是对目标边界的描述,轮廓属性是基本的边界描述子。

例如:

  • 边界的长度,轮廓线的像素数量是边界周长的近似估计;
  • 边界的直径,边界长轴的长度,等于轮廓最小矩形边界框的长边长度;
  • 边界的偏心率,边界长轴与短轴之比,等于轮廓最小矩形边界框的长宽比;
  • 边界的曲率,相邻边界线段的斜率差;
  • 链码,通过规定长度和方向的直线段来表示边界;
  • 傅里叶描述符,对二维边界点进行离散傅里叶变换得到的傅里叶系数,对旋转、平移、缩放和起点不敏感;
  • 统计矩,把边界视为直方图函数,用图像矩对边界特征进行描述,具有平移、灰度、尺度、旋转不变性。

例程 12.11:闭合曲线的弗里曼链码

链码表示基于线段的 4连通或 8连通。弗里曼链码(Freeman chain code)使用一种编号方案来对每个线段的方向进行编号。

通过对闭合边界曲线向下降采样,简化了初始轮廓。例程中最大轮廓的像素点 1361,简化后轮廓的像素点 34,对应的弗里曼链码长度 34。即用 34位 Freeman 链码可以描述该最大轮廓的边界特征,显著降低了数据量。

相关算法的详细内容,参见 R.C.Gonzalez 《数字图像处理(第四版)》P587-590 例11.1。

#  12.11 闭合曲线的弗里曼链码(Freeman chain code)
import cv2
import numpy as np
from matplotlib import pyplot as plt

def FreemanChainCode(cLoop, gridsep=1):  # 由闭合边界点集生成弗里曼链码
    # Freeman 8 方向链码的方向数
    dictFreeman = (1, 0): 0, (1, 1): 1, (0, 1): 2, (-1, 1): 3, (-1, 0): 4, (-1, -1): 5, (0, -1): 6, (1, -1): 7
    diff_cLoop = np.diff(cLoop, axis=0) // gridsep  # cLoop 的一阶差分码
    direction = [tuple(x) for x in diff_cLoop.tolist()]
    codeList = list(map(dictFreeman.get, direction))  # 查字典获得链码
    code = np.array(codeList)  # 转回 Numpy 数组
    return code

if __name__ == '__main__':
    # 计算目标轮廓曲线的弗里曼链码
    img = cv2.imread("../images/Fig1105.tif", flags=1)
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # 灰度图像
    blur = cv2.boxFilter(gray, -1, (5, 5))  # 盒式滤波器,9*9 平滑核
    _, binary = cv2.threshold(blur, 200, 255, cv2.THRESH_OTSU + cv2.THRESH_BINARY)

    # 寻找二值化图中的轮廓,method=cv2.CHAIN_APPROX_NONE 输出轮廓的每个像素点
    contours, hierarchy = cv2.findContours(binary, cv2.RETR_TREE, cv2.CHAIN_APPROX_NONE)  # OpenCV4~
    # 绘制全部轮廓,contourIdx=-1 绘制全部轮廓
    imgCnts = np.zeros(gray.shape[:2], np.uint8)  # 绘制轮廓函数会修改原始图像
    imgCnts = cv2.drawContours(imgCnts, contours, -1, (255, 255, 255), thickness=2)  # 绘制全部轮廓

    # 获取最大轮廓
    cnts = sorted(contours, key=cv2.contourArea, reverse=True)  # 所有轮廓按面积排序
    cnt = cnts[0]  # 第 0 个轮廓,面积最大的轮廓,(1361, 1, 2)
    maxContour = np.zeros(gray.shape[:2], np.uint8)  # 初始化最大轮廓图像
    cv2.drawContours(maxContour, cnt, -1, (255, 255, 255), thickness=2)  # 绘制轮廓 cnt
    print("len(contours) =", len(contours))  # contours 所有轮廓的列表
    print("area of max contour: ", cv2.contourArea(cnt))  # 轮廓面积
    print("perimeter of max contour: :.1f".format(cv2.arcLength(cnt, True)))  # 轮廓周长

    # 向下降采样,简化轮廓的边界
    gridsep = 50  # 采样间隔
    cntPoints = np.squeeze(cnt)  # 删除维度为1的数组维度,(1361,1,2)->(1361,2)
    subPoints = boundarySubsample(cntPoints, gridsep)  # 自定义函数,通过向下采样简化轮廓
    print("subsample steps:", gridsep)  # 降采样间距
    print("points of contour:", cntPoints.shape[0])  # 原始轮廓点数
    print("points of subsample:", subPoints.shape[0])  # 降采样轮廓点数

    # 绘制简化轮廓图像
    subContour = np.zeros(gray.shape[:2], np.uint8)  # 初始化简化轮廓图像
    [cv2.circle(subContour, point, 1, 160, -1) for point in cntPoints]  # 绘制初始轮廓的采样点
    [cv2.circle(subContour, point, 4, 255, -1) for point in subPoints]  # 绘制降采样轮廓的采样点
    cv2.polylines(subContour, [subPoints], True, 255, thickness=2)  # 绘制多边形,闭合曲线

    # 生成 Freeman 链码
    cntPoints = np.squeeze(cnt)  # 删除维度为1 的数组维度,(1361,1,2)->(1361,2)
    pointsLoop = np.append(cntPoints, [cntPoints[0]], axis=0)  # 首尾循环,结尾添加 cntPoints[0]
    chainCode = FreemanChainCode(pointsLoop, gridsep=1)  # 自定义函数,生成链码 (1361,)
    print("Freeman chain code:", chainCode.shape)  # 链码长度为轮廓长度
    subPointsLoop = np.append(subPoints, [subPoints[0]], axis=0)  # 首尾循环,(34,2)->(35,2)
    subChainCode = FreemanChainCode(subPointsLoop, gridsep=50)  # 自定义函数,生成链码 (34,)
    print("Down-sampling Freeman chain code:", subChainCode.shape)  # 链码长度为简化轮廓程度
    print("youcans code:", subChainCode)

    plt.figure(figsize=(9, 6))
    plt.subplot(231), plt.axis('off'), plt.title("Origin")
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.subplot(232), plt.axis('off'), plt.title("Blurred")
    plt.imshow(cv2.cvtColor(blur, cv2.COLOR_BGR2RGB))
    plt.subplot(233), plt.axis('off'), plt.title("Binary")
    plt.imshow(binary, 'gray')
    plt.subplot(234), plt.axis('off'), plt.title("Contour")
    plt.imshow(imgCnts, 'gray')
    plt.subplot(235), plt.axis('off'), plt.title("Max contour")
    plt.imshow(maxContour, 'gray')
    plt.subplot(236), plt.axis('off'), plt.title("Down sampling")
    plt.imshow(subContour, 'gray')
    plt.tight_layout()
    plt.show()


运行结果:

len(contours) = 24
area of max contour: 152294.5
perimeter of max contour: 1525.4
subsample steps: 50
points of contour: 1361
points of subsample: 34
Freeman chain code: (1361,)
Down-sampling Freeman chain code: (34,)
[4 2 4 2 2 4 2 2 2 2 2 0 2 0 0 0 2 0 0 6 0 6 6 6 6 6 6 6 6 4 6 4 4 4]



【本节完】

说明:本文的案例来自 R.C.Gonzalez 《数字图像处理(第四版)》,本文的程序为作者原创。

版权声明:
youcans@xupt 原创作品,转载必须标注原文链接:(https://blog.csdn.net/youcans/article/details/125534118)
Copyright 2022 youcans, XUPT
Crated:2022-6-30

欢迎关注 『youcans 的 OpenCV 例程 200 篇』 系列,持续更新中
194.寻找图像轮廓(cv.findContours)
222. 特征提取之弗里曼链码

以上是关于OpenCV 例程200篇222. 特征提取之弗里曼链码(Freeman chain code)的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV 例程200篇236. 特征提取之主成分分析(OpenCV)

OpenCV 例程200篇236. 特征提取之主成分分析(OpenCV)

OpenCV 例程200篇224. 特征提取之提取骨架

OpenCV 例程200篇224. 特征提取之提取骨架

OpenCV 例程200篇235. 特征提取之主成分分析(sklearn)

OpenCV 例程200篇235. 特征提取之主成分分析(sklearn)