《数字图像处理》 第11章 表示和描述 学习笔记附部分例子代码(c++opencv)

表示和描述

    • 0. 前言
    • 1. 表示
      • 1.1 边界追踪
      • 1.2 链码
      • 1.3 使用最小周长多边形的多边形近似
    • 2. 边界描绘子
      • 2.1 一些简单的描绘子![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/45dddc76217e4fde93a11e2631b2a71a.png#pic_center =500x)
      • 2.2 形状数
      • 2.3 傅里叶描绘子
      • 2.4 统计矩
    • 3. 区域描绘子
      • 3.1 一些简单的描绘子
      • 3.2 拓扑描绘子
      • 3.3 纹理
    • Opencv补充:

0. 前言

本章只学习了前三节……

VS安装Image watch插件请查看官网OpenCV: Image Watch: viewing in-memory images in the Visual Studio debugger

第三版教材中图片下载地址: book images downloads

vs2019配置opencv可以查看:VS2019 & Opencv4.5.4配置教程

前情回顾:
《数字图像处理》第三章 灰度变换和空间滤波 学习笔记附部分例子代码
《数字图像处理》第四章 频率域滤波 学习笔记附部分例子代码
数字图像处理第五章 图像复原和重建(内容较简单,就没有详细记录笔记)
《数字图像处理》第六章 彩色图像处理 学习笔记附部分例子代码
《数字图像处理》第七章 小波域多分辨率处理 学习笔记附部分例子代码
数字图像处理第八章 图像压缩 非重点
《数字图像处理》第九章 形态学图像处理 学习笔记附部分例子代码
《数字图像处理》第十章 图像分割 学习笔记附部分例子代码

1. 表示

1.1 边界追踪

处理的是二值图像,其目标和背景点分别标为1和0,Moore边界追踪算法的步骤如下:

  1. 找到图像左上角为1的点b0为边界起始点。b0左边的点为c0,从c0开始按顺时针方向考察b0的8邻域,找到的第一个值1的点为b1,令扫描到b1前的点为c1。

  2. 赋值b=b1,c=c1。

  3. 从c开始顺时针方向行进,找到第一个值1的点nk,其之前的点均为背景点。

  4. 赋值b=nk,c=nk-1。

  5. 重复step3和step4,直到b=b0且下一个边界点为b1。

1.2 链码

链码用于表示由顺次连接的具有指定长度和方向的直线段组成的边界

使用了opencv的findContours()drawContours(),具体形参的表示可以看补充

void ch11_test01(string path) {Mat image = imread(path, IMREAD_GRAYSCALE);if (image.empty()) {cout << "Unable to load the image\n";return;}// 创建一个纯黑画布Mat black(image.size(), CV_8U, Scalar(0));Mat filtered;blur(image, filtered, Size(9, 9));Mat result = Otsu(filtered);//该函数的实现请看第十章笔记的3.2节// 寻找轮廓vector<vector<Point>> contours;findContours(result, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);vector<Point> contour = contours[0];vector<int> freemaCode;Point currentPoint = contour[0];Point nextPoint;for (size_t i = 1; i < contour.size(); i++) {nextPoint = contour[i];// 计算方向int dx = nextPoint.x - currentPoint.x;int dy = nextPoint.y - currentPoint.y;// 转换为佛雷曼链码int code = -1;if (dx == 0 && dy == -1)      code = 0;else if (dx == 1 && dy == -1) code = 1;else if (dx == 1 && dy == 0)  code = 2;else if (dx == 1 && dy == 1)  code = 3;else if (dx == 0 && dy == 1)  code = 4;else if (dx == -1 && dy == 1) code = 5;else if (dx == -1 && dy == 0) code = 6;else if (dx == -1 && dy == -1)code = 7;// 添加到链码序列freemaCode.push_back(code);// 更新当前点currentPoint = nextPoint;}// 输出佛雷曼链码cout << "Freeman Chain Code: ";for (int code : freemaCode) {cout << code << " ";}cout << endl;// 在黑布上画出轮廓drawContours(black, contours, -1, Scalar(128), 2);displayImg(image, "原图01");displayImg(black, "轮廓");waitKey(0);
}

在这里插入图片描述

1.3 使用最小周长多边形的多边形近似

数字边界可以用多边形以任意精度来近似。对于一条闭合边界,当多边形的边数等于边界上的点数时,这种近似会变得很精确,此时,每对相邻的点定义了多边形的一条边。多边形近似的目的是使用尽可能少的线段数来获取给定边界的基本形状。

2. 边界描绘子

2.1 一些简单的描绘子在这里插入图片描述

边界的长度是其最简单的描绘子之一。边界B的直径定义为

D i a m ( B ) = m a x [ D ( p i , p j ) ] Diam(B)=max[D(p_i, p_j)] Diam(B)=max[D(pi,pj)]

D是距离度量,pi和pj是边界上的两点。该直线段称为边界的长轴,边界的短边定义为与长轴垂直的直线,长轴与短轴之比称为边界的偏心率,偏心率同样是一个描述子。有时使用相邻边界线段的斜率差作为两条线段交点处曲率的描述子。

2.2 形状数

链码边界的一次差分取决于起始点。形状数的阶n定义为该链码的数字个数,形状编码不知道怎么来的,差分可看下图(方向按逆时针排序):

2.3 傅里叶描绘子

边界由坐标序列s(k)=[x(k), y(k)]表示,每个坐标对可以当做一个复数来处理

s ( k ) = x ( k ) + j y ( k ) s(k) = x(k) + jy(k) s(k)=x(k)+jy(k)

对sk进行离散傅里叶变换,使用前P个傅里叶系数进行逆变换得到

s ^ ( k ) = 1 K ∑ u = 0 P − 1 a ( u ) e j 2 π u k / P \hat s(k)=\frac{1}{K}\sum_{u=0}^{P-1}a(u)e^{j2\pi uk/P} s^(k)=K1u=0P1a(u)ej2πuk/P

由第四章得知,高频分量说明精细细节,而低频分量决定全局形状,因此,P越小,边界丢失的细节就越多。傅里叶描绘子对起始点不敏感,对平移、旋转和尺度变化不敏感。

这里教材的例子,代码复现一直有问题555555,所以就放弃放代码了

2.4 统计矩

对于一段边界,连接两个端点连接起来,然后旋转至水平得到g(r)

在这里插入图片描述

看成一个幅度直方图,纵坐标p(v)是v出现的概率估计,所以关于其均值的v的第n阶矩阵为

μ n ( v ) = ∑ i = 0 A − 1 ( v i − m ) n p ( v i ) \mu _n(v)=\sum_{i=0}^{A-1}(v_i-m)^{n}p(v_i) μn(v)=i=0A1(vim)np(vi)

式中, m = ∑ i = 0 A − 1 v i p ( v i ) m= \sum_{i=0}^{A-1}v_i p(v_i) m=i=0A1vip(vi)

m为v的均值或平均值, μ 2 \mu_2 μ2为v的方差

3. 区域描绘子

3.1 一些简单的描绘子

一个区域的面积定义为该区域中像素的数量,区域的周长是其边界的长度。致密性描绘子圆度率有下式表示:

R c = 4 π A P 2 R_c =\frac{4\pi A}{P^2} Rc=P24πA

A为讨论区域的面积,P是其周长

3.2 拓扑描绘子

拓扑学是研究未受任何变形影响的图形的性质,前提是该图形为被撕裂或粘连。区域描述有两点

  • 区域内的孔洞数量H

  • 连通分量的数量C

可以定义欧拉数E=C-H.

当描述有直线线段表示的区域非常简单,V表示顶点数,Q表示边数,F表示面数,那么欧拉公式如下:

V − Q + F = C − H V-Q+F=C-H VQ+F=CH

opencv可以使用connectedComponents()connectedComponentsWithStats()两个函数,后者会得到stats这一5列矩阵,该矩阵的每一行对应一个连通区域的标签,包含有连通区域左上角的坐标x, y,以及外接矩形的宽高和面积。

void ch11_test03(string path) {Mat image = imread(path, IMREAD_GRAYSCALE);if (image.empty()) {cout << "Unable to load the image\n";return;}Mat binaryImage;threshold(image, binaryImage, 78, 255, THRESH_BINARY); //阈值选择78binaryImage = ~binaryImage;//Mat labeledImg;//int numLabels = connectedComponents(binaryImg, labeledImg, 8);//numLabels--;Mat labeledImage;Mat stats, centroids;int numLabels = connectedComponentsWithStats(binaryImage, labeledImage, stats, centroids, 8);// 减去背景区域numLabels--;cout << "连通个数:" << numLabels << endl;//displayImg(labeledImage, "标记结果");displayImg(image, "原图");displayImg(binaryImage, "阈值处理结果");waitKey(0);
}

在这里插入图片描述

Stats矩阵和centroids矩阵介绍

3.3 纹理

统计方法: 一个区域的灰度级直方图的统计矩,如同3.1。二阶矩在纹理描述中特别重要,度量:

R ( z ) = 1 − 1 1 − σ 2 ( z ) R(z)=1-\frac{1}{1-\sigma ^2(z)} R(z)=11σ2(z)1

可以提现图像的平滑程度。

而三阶矩:

μ 3 ( v ) = ∑ i = 0 A − 1 ( v i − m ) 3 p ( v i ) \mu _3(v)=\sum_{i=0}^{A-1}(v_i-m)^{3}p(v_i) μ3(v)=i=0A1(vim)3p(vi)

是直方图偏斜度的度量。

“一致性”度量:

U ( z ) = ∑ L − 1 i = 0 p 2 ( z i ) U(z)=\sum_{L-1}^{i=0}p^2(z_i) U(z)=L1i=0p2(zi)

平均熵度量:

e ( z ) = ∑ i = 0 L − 1 p ( z i ) l o g 2 p ( z i ) e(z)=\sum_{i=0}^{L-1}p(z_i)log_2p(z_i) e(z)=i=0L1p(zi)log2p(zi)

令 Q 是定义两个像素彼此相对位置的一个算子,并考虑一幅具有L个可能灰度级的图像f。 令 G 为一个矩阵,其元素gij是灰度为zi和zj的像素对出现在f中由Q所指定的位置处的次数。按这种方法形成的矩阵称为灰度级共生矩阵。

![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/c86cbd9e6b3b4c56ba0e03ab332f1c45.png#pic_center = 500x)

图像中可能的灰度级决定了G的大小。

ch11_test04("..\\Images_CH11\\Fig1130(a)(uniform_noise).tif", "a"); //图a显示时需要归一化到0-255
ch11_test04("..\\Images_CH11\\Fig1130(b)(sinusoidal).tif", "b"); //显示时归一化到0-1
ch11_test04("..\\Images_CH11\\Fig1130(c)(cktboard_section).tif", "c");//显示时归一化到0-255//计算共生矩阵,像素对(水平方向一格)
Mat calGLCM(Mat input, int level) {Mat result = Mat::zeros(Size(level, level), CV_32F);//遍历水平像素对for (int x = 0; x + 1 < input.cols; x++) {for (int y = 0; y < input.rows; y++) {int scale1 = input.at<uchar>(y, x);		//at.(row, col)int scale2 = input.at<uchar>(y, x + 1);result.at<float>(scale1, scale2)++;}}return result;
}void ch11_test04(string path, string info) {Mat image = imread(path, IMREAD_GRAYSCALE);if (image.empty()) {cout << "Unable to load the image\n";return;}Mat GLCM = calGLCM(image, 256);//normalize(GLCM, GLCM, 0, 1, NORM_MINMAX);displayImg(image, "原图04" + info);displayImg(GLCM, "共生矩阵" + info);waitKey(0);
}

在这里插入图片描述

在这里插入图片描述

Opencv补充:

  1. void findContours(InputArray image, OutputArrayOfArrays contours, OutputArray hierarchy, int mode, int method)

    • image: 输入的二值化图像,通常是通过阈值处理得到的,要求是8位单通道图像。

    • contours: 输出参数,包含检测到的轮廓的容器,通常是 vector<vector<Point>> 类型。

    • hierarchy: 输出参数,轮廓的层次结构,通常是 vector<Vec4i> 类型。可以为可选参数,如果不需要层次结构,可以设置为 noArray()

    • mode: 轮廓检索模式,可以是 RETR_EXTERNAL(仅检测外部轮廓)、RETR_LIST(检测所有轮廓,不建立层次关系)、RETR_CCOMP(检测所有轮廓,建立两层层次关系)、RETR_TREE(检测所有轮廓,建立层次树结构)。

    • method: 轮廓逼近方法,可以是 CHAIN_APPROX_NONE(保存所有的轮廓点)、CHAIN_APPROX_SIMPLE(压缩水平、垂直、对角方向的元素,只保留其端点)

  2. void drawContours(InputOutputArray image, InputArrayOfArrays contours, int contourIdx, const Scalar& color, int thickness = 1, int lineType = LINE_8);

    • image: 要绘制轮廓的图像。

    • contours: 保存轮廓点的容器,通常是vector<vector<Point>>类型

    • contourIdx: 要绘制的轮廓的索引。如果是负数,表示绘制所有轮廓。

    • color: 绘制轮廓的颜色,通常使用Scalar类表示,例如Scalar(0, 255, 0)表示绿色。

    • thickness: 绘制轮廓线的粗细,如果是负数表示填充轮廓内部。默认值是1。

    • lineType: 绘制轮廓的线型,可以是LINE_8LINE_4LINE_AA
      );`

    • image: 要绘制轮廓的图像。

    • contours: 保存轮廓点的容器,通常是vector<vector<Point>>类型

    • contourIdx: 要绘制的轮廓的索引。如果是负数,表示绘制所有轮廓。

    • color: 绘制轮廓的颜色,通常使用Scalar类表示,例如Scalar(0, 255, 0)表示绿色。

    • thickness: 绘制轮廓线的粗细,如果是负数表示填充轮廓内部。默认值是1。

    • lineType: 绘制轮廓的线型,可以是LINE_8LINE_4LINE_AA

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/325821.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

04 帧 Frame

文章目录 04 帧 Frame4.1 相机相关信息4.2 特征点提取4.2.1 特征点提取 ExtractORB()4.3 ORB-SLAM2对双目/RGBD特征点的预处理4.3.1 双目视差公式4.3.2 双目图像特征点匹配 ComputeStereoMatches()4.3.3 根据深度信息构造虚拟右目图像&#xff1a;ComputeStereoFromRGBD() 4.4 …

【Python机器学习】线性模型——线性回归

线性回归&#xff0c;又叫普通最小二乘法&#xff0c;是回归问题最简单也是最经典的线性方法。线性回归寻找参数w和b&#xff0c;使得对训练集的预测值与真实的回归目标值y之间的均方误差最小。 均方误差是预测值与真实值之差的平方和除以样本差。线性回归没有参数&#xff0c…

计算机毕业设计-----SSM餐厅点餐收银管理系统

项目介绍 用于餐厅的收银管理系统&#xff0c;包含了四个模块 1.桌位模块 桌位模块主要是用于管理桌位的模块&#xff0c;包括点菜到结账的流程 将桌位人数设置为0可以滞空当前桌位 2.账单模块 账单模块记录了每一天的帐单汇总&#xff0c;同时提供了年月日账单的统计&#x…

stable diffusion 基础教程-文生图

置顶大模型插件资源链接 你如果没有魔法上网,请自取 百度云盘链接:链接:https://pan.baidu.com/s/1_xAu47XMdDNlA86ufXqAuQ?pwd=23wi 提取码:23wi 有疑问加微:mincarver 界面介绍 参数解释 参数解释Sampling method扩散去噪算法的采样模式,不同采样模式会带来不一样的效…

极智一周 | 谈谈AI发展、训练算力、推理算力、AI编译框架、Copilot键 And so on

欢迎关注我的公众号 [极智视界]&#xff0c;获取我的更多技术分享 大家好&#xff0c;我是极智视界&#xff0c;带来本周的 [极智一周]&#xff0c;关键词&#xff1a;谈谈AI发展、训练算力、推理算力、AI编译框架、Copilot键 And so on。 邀您加入我的知识星球「极智视界」&a…

栈的数据结构实验报告

一、实验目的&#xff1a; 1、理解栈的定义&#xff1b; 2、利用栈处理实际问题。 二、实验内容&#xff08;实验题目与说明&#xff09; 利用栈实现数据的分类&#xff0c;将输入的整数以奇偶为标准分别存放到两个栈中&#xff0c;并最终从栈1和栈2输出偶数和奇数序列。 …

nodejs安装、nodejs环境变量配置、npm安装、vue安装

官网下载链接:https://nodejs.org/en/download/ 个人下载版本&#xff1a;node-v20.10.0-x64.msi&#xff0c;下载完成后&#xff0c;点击安装&#xff0c;除了更换安装目录&#xff0c;其他直接下一步即可 安装完成后执行&#xff1a;npm -v 下面开始配置环境变量&#xf…

Visual Studio 2017 + opencv4.6 + contribute + Cmake(Aruco配置版本)指南

之前配置过一次这个&#xff0c;想起这玩意就难受&#xff0c;贼难配置。由于要用到里面的一个库&#xff0c;不得已再进行配置。看网上的博客是真的难受&#xff0c;这写一块&#xff0c;那里写一块&#xff0c;乱七八糟&#xff0c;配置一顿发现写的都是错的&#xff0c;还得…

cissp 第10章 : 物理安全要求

10.1 站点与设施设计的安全原则 物理控制是安全防护的第一条防线&#xff0c;而人员是最后一道防线。 10.1.1 安全设施计划 安全设施计划通过关键路径分析完成。 关键路径分析用于找出关键应用、流程、运营以及所有必要支撑元索间的关系。 技术融合指的是各种技术、解决方案…

基于ssm的智慧社区电子商务系统+vue论文

目 录 目 录 I 摘 要 III ABSTRACT IV 1 绪论 1 1.1 课题背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统开发环境 3 2.1 vue技术 3 2.2 JAVA技术 3 2.3 MYSQL数据库 3 2.4 B/S结构 4 2.5 SSM框架技术 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2 操作可行性 5 3…

C#中的值和引用笔记

文章目录 1. 简单介绍2. 如何判断值类型和引用类型3. 语句块4. 变量的生命周期5. 结构体中的值和引用6. 数组中的存储规则7. 结构体继承接口 1. 简单介绍 2. 如何判断值类型和引用类型 在代码中直接转到内部F12 如string类型 值类型int 3. 语句块 4. 变量的生命周期 5. 结构…

2022年多元统计分析期中试卷

多元正态均值检验 一、去年卖出的一岁牛犊的平均身高为 51 英寸&#xff0c;平均背脂厚度是 0.3 英寸&#xff0c;平均肩高是 56 英寸。已知今年卖出的 76 头一岁牛犊的 3 项平均指标为(50, 0.2, 54)‘&#xff0c;样本协差阵及其逆矩阵为 S [ 3.00 − 0.053 2.97 − 0.053 0…