【OpenCV 例程 300篇】222. 特征提取之弗里曼链码(Freeman chain code)
OpenCV 例程200篇 总目录
【youcans 的 OpenCV 例程 300篇】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 pltdef 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 codeif __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) # 绘制轮廓 cntprint("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 例程 300篇】222. 特征提取之弗里曼链码(Freeman chain code)相关推荐
- 【OpenCV 例程 300篇】224. 特征提取之提取骨架
OpenCV 例程200篇 总目录 [youcans 的 OpenCV 例程 300篇]224. 特征提取之提取骨架 目标特征的基本概念 通过图像分割获得多个区域,得到区域内的像素集合或区域边界像素集 ...
- 【OpenCV 例程 300篇】226. 区域特征之紧致度/圆度/偏心率
『youcans 的 OpenCV 例程200篇 - 总目录』 [youcans 的 OpenCV 例程 300篇]226. 区域特征之紧致度/圆度/偏心率 特征通常是针对于图像中的某个目标而言的. ...
- 【youcans的OpenCV例程300篇】总目录
版权声明: 转载本系列作品时必须标注以下版权内容: [youcans@qq.com, youcans的OpenCV 例程300篇, https://blog.csdn.net/youcans/cate ...
- 【OpenCV 例程 300篇】235. 特征提取之主成分分析(sklearn)
『youcans 的 OpenCV 例程300篇 - 总目录』 [youcans 的 OpenCV 例程 300篇]235. 特征提取之主成分分析(sklearn) 特征提取是指从原始特征中通过数学变 ...
- 【OpenCV 例程 300篇】241. 尺度不变特征变换(SIFT)
『youcans 的 OpenCV 例程300篇 - 总目录』 [youcans 的 OpenCV 例程 300篇]241. 尺度不变特征变换(SIFT) 6.4.1 简介 尺度不变特征转换算法(Sc ...
- 【OpenCV 例程 300篇】219. 添加数字水印(盲水印)
OpenCV 例程200篇 总目录 [youcans 的 OpenCV 例程 300篇]219. 添加数字水印(盲水印) 8.2 添加数字盲水印 数字水印,是指将特征信息嵌入音频.图像或是视频等数字信 ...
- 【OpenCV 例程 300篇】231. 特征描述之灰度共生矩阵(GLCM)
『youcans 的 OpenCV 例程200篇 - 总目录』 [youcans 的 OpenCV 例程 300篇]231. 特征描述之灰度共生矩阵(GLCM) 4.2.4 灰度共生矩阵(GLCM) ...
- 【OpenCV 例程300篇】209. HSV 颜色空间的彩色图像分割
OpenCV 例程200篇 总目录 [youcans 的 OpenCV 例程300篇]209. HSV 颜色空间的彩色图像分割 5.1 HSV 颜色空间的彩色图像分割 HSV 模型是针对用户观感的一种 ...
- 【OpenCV 例程 300篇】256. 特征检测之 CenSurE(StarDetector)算法
『youcans 的 OpenCV 例程300篇 - 总目录』 [youcans 的 OpenCV 例程 300篇]256. 特征检测之 CenSurE(StarDetector)算法 6.9.1 算 ...
最新文章
- 这几个 Python 的小技巧,你会么?
- 在JavaScript中找到数组的最小/最大元素
- 闭眼入!采集 Nginx 日志的几种方式!
- 光盘引导和网络安装linux系统
- C语言的数组基础,C语言基础-数组
- mysql数据库下载压缩包_mysql 8.0.22 zip压缩包版(免安装)下载、安装配置步骤详解...
- c ++类成员函数_C ++编程中的数据成员和成员函数
- 日语+AI语音黑科技,早道开启小语种AI智能时代!
- 25+开源的在线购物软件(PHP, JavaScript 和 ASP.Net)
- wordpress iis php,Windows IIS 上安装部署 WordPress 网站快速简要教程
- 国内银行卡BIN号速查简表(2016)
- 北理乐学大学计算机实验4,北理乐学C语言答案.docx
- 单片机i o口扩展 c语言,基于2片74LS138的单片机I/O口扩展分析
- 深圳高中计算机老师招聘,深圳一所高中招聘20位老师,19人来自清华北大
- Client-Initiated场景下的L2TP实验配置
- Quokka.js and Wallaby.js Crack
- Java日志框架 -- 日志框架介绍、日志门面技术、JUL日志(JUL架构、JUL入门示例、JUL日志级别、JUL日志的配置文件)
- C#中.snk文件的作用
- 如何打出j间隔符号‘·’
- 中外历史纲要(上)第一单元梳理(部分)
热门文章
- 【新观点】孙悟空其实是太上老君炼的丹药变成的
- 至今见过最全的星星*龙套家族*!!! 【 图片较多 显示不了的 多刷一下即可 】
- db.properties 之 root 之空格
- Unity简单实现电量、充电状态显示
- Mac Cordova iOS配置和常见问题
- 工作半年后的一点感悟
- styled-components 基本用法
- Web Components 学习笔记一: Web Components是什么?解决了什么问题?
- Android 应用界面设计
- octobercms mysql_手把手教你选择一款适合自己的CMS/博客程序