OpenCV学习 基础图像操作(十六):图像距离变换

基础原理

顾名思义,我们可以利用像素之间的距离作为对该像素的一种刻画,并将其运用到相应的计算之中。然而,在一幅图像之中,某种类型的像素并不是唯一的,因此我门常计算的是一类像素到另一类的最小距离,并且如何去定义和求解对应的像素也是可以根据问题去选择的

朴素的来看,这是一个计算复杂度很高的运算,因为我们需要对两种类别中的像素进行两两遍历求得起距离才能找到最小距离,然而实际中我们会充分利用空间位置关系,以及采样的方法来加速这个算法过程。

下面参考Distance Transforms of Sampled Functions中所述,来简单地介绍一下整个算法的思路:

首先,我们从问题转换为数学表示,假设在坐标轴上,G= 0,...,n-1,有如下变换:

D_f(p)=\underset{q\in \mathfrak{g}}{min}(d(p,q)+f(q))

其中D_f为从q变换到p的距离,d(p,q)为度量距离的方式,q为坐标中任意一点,f(q)表示点q的集合

f(q) =\begin{cases} 0 & \text{ if } q\epsilon P \\ \infty & \text{ othersize} \end{cases}

最小值卷积

此处由于相似的形式,我们引入最小卷积,与正常的卷积一样最小卷积,遵守交换律与结合率。

(f\bigotimes g)(p)=\underset{q}{min}(f(q)+g(p-q))

上式中,我们假设在一维的轴上进行测量,那么将d(p,q)替换为g(p-q),则距离变换就变为了fgp处的卷积了。

式子的右半边是一个最优化问题,即调整q寻找到最小值,那么将左半边的卷积也写为与右边相似的形式,即将卷积拆为一下形式:

{\delta}'(q)=b(q)+min(\delta (p)+a(p,q))

{\delta}'(q)=b(q)+D_\delta (q)

经过这样的变换后,可以看到式子迭代已经将另一个点排除在外了,即所求解的是一个全局的值。但是由于空间中点与点是有一定顺序的,我们可以利用动态规划等手段,优化这个线性的求解过程,最终将算法复杂度由O(n^2)优化到O(n)

一维上的变换

结合实际例子来看一下,假设在数轴上使用欧式距离进行距离变换:

D_f(p)=\underset{q\in \mathfrak{g}}{min}((p-q)^2+f(q))

形象的可视化下,每一个可能的点p的距离变换后的值为

我们需要找到就是在各个点距离变换后,在点rq中的最小值处s,以此作为采样点,进行实际的剧离运算,s的计算可表示为:

s=\frac{(f(r)+r^2)-(f(q)+q^2)}{2r-2q}

我们从左往右,依次记录每个抛物线交点的下包络并存到v[i]中,下包络线的第 i 条抛物线低于其他抛物线的范围由 z[i]z[i+1]给出,k表示第几根抛物线。则推理计算过程可以写为:

可视化其中s的更新过程为下图:

API介绍


void cv::distanceTransform	(	InputArray 	src,  //输入的原图
OutputArray 	dst,                              //输出的距离变换map
OutputArray 	labels,                           //输出的标签map
int 	distanceType,                             //距离的类型
int 	maskSize,                                 //距离变换掩码矩阵的大小
int 	labelType = DIST_LABEL_CCOMP              //类别的类型
)		

这意味着,对于一个像素,该函数会找到到最接近的零像素的最短路径,该路径由基本位移组成:水平、垂直、对角线或骑士移动(最新的可用于5×5面具)。总距离计算为这些基本距离的总和。由于距离函数应该是对称的,因此所有水平和垂直移动必须具有相同的成本(表示为 ),所有对角线移动必须具有相同的成本(表示为 ),并且所有骑士的移动必须具有相同的成本(表示为 )。对于DIST_C和DIST_L1类型,距离是精确计算的,而对于DIST_L2(欧几里得距离),距离只能通过相对误差(abc5×5mask 提供更准确的结果)。对于 、 和 ,OpenCV 使用原始论文中建议的值:abc

  • DIST_L1:a = 1, b = 2
  • DIST_L2:
    • 3 x 3:a=0.955, b=1.3693
    • 5 x 5:a=1, b=1.4, c=2.1969
  • DIST_C:a = 1, b = 1

通常,对于快速、粗略的距离估计DIST_L2,一个3×3使用面具。为了更准确地估计距离DIST_L2,一个5×5掩码或使用精确算法。请注意,精确算法和近似算法在像素数上都是线性的。

该函数的此变体不仅计算每个像素的最小距离(x,y)还可以标识由零像素 (labelType==DIST_LABEL_CCOMP) 或最接近的零像素 (labelType==DIST_LABEL_PIXEL) 组成的最近连接组件。分量/像素的索引存储在 中。当 labelType==DIST_LABEL_CCOMP 时,该函数会自动在输入图像中查找零像素的连接组件,并用不同的标签标记它们。当 labelType==DIST_LABEL_PIXEL 时,该函数会扫描输入图像,并使用不同的标签标记所有零像素。labels(x, y)

在这种模式下,复杂度仍然是线性的。也就是说,该函数提供了一种非常快速的方法来计算二进制图像的 Voronoi 图。目前,第二个变体只能使用近似距离变换算法,即尚不支持 maskSize=DIST_MASK_PRECISE。

简单使用一下,来找一下毛笔字的骨骼,自制一个描红本

import cv2
import matplotlib.pyplot as plt
def main():#读取图像img = cv2.imread("D:\code\src\code\zhen.jpg",cv2.IMREAD_GRAYSCALE)#二值化分割thresh,binary = cv2.threshold(img,50,255,cv2.THRESH_BINARY_INV)#利用距离变换找到骨骼脉络dist = cv2.distanceTransform(binary,cv2.DIST_L2,3)#利用自适应二值化保存脉络上局部最大值sk = cv2.adaptiveThreshold(dist.astype("uint8"),255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY_INV,9,0)plt.subplot(411),plt.imshow(img,cmap='gray'),plt.title('Source Image'), plt.xticks([]), plt.yticks([])plt.subplot(412),plt.imshow(binary,cmap='gray'),plt.title('Binary Image'), plt.xticks([]), plt.yticks([])plt.subplot(413),plt.imshow(dist),plt.title('Dist Map'), plt.xticks([]), plt.yticks([])plt.subplot(414),plt.imshow(sk,cmap='gray'),plt.title('Skelet Image'), plt.xticks([]), plt.yticks([])plt.show()if __name__ == "__main__":main() 

参考链接

[OpenCV] 图像分割之利用 cv2.distanceTransform 提取前景-CSDN博客

OpenCV学习三十五:distanceTransform 距离变换函数_c++ opencv distancetransform-CSDN博客OpenCV—python 图片细化(骨架提取)二_opencv骨架提取算法函数-CSDN博客

Distance Transforms of Sampled Functions (theoryofcomputing.org)

OpenCV:基于距离变换和分水岭算法的图像分割

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

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

相关文章

香橙派KunPengPro评测

一、引言 二、开箱 2.1、主要包含说明 1、充电器(赠typec-c线) 2、香橙派kunpengpro(已经带装好带散热器) 3、SD卡(32G)(已经带装好系统openEuler 22.03 (LTS-SP3)) (注意:上电接HDMI线可直接用,账号:openEuler 密码:openEuler)…

vue使用tailwindcss

安装依赖 pnpm add -D tailwindcss postcss autoprefixer创建配置文件tailwind.config.js npx tailwindcss init在配置文件content中添加所有模板文件的路径 /** type {import(tailwindcss).Config} */ export default {content: [./index.html, ./src/**/*.{vue,js,ts,jsx,…

【Linux】开发工具入门指南,轻松掌握你的开发利器

开发工具 1. 软件包管理器yum1.1 软件包安装方式1.2 yum的"三板斧"1.3 yum的周边 2. 开发工具3. 编辑器vim4. 编译器gcc、g5. 项目自动化构建工具make、Makefile6. 进度条小程序7. 调试器gdb 1. 软件包管理器yum 1.1 软件包安装方式 源代码安装:用户手动…

微信小程序 npm构建+vant-weaap安装

微信小程序:工具-npm构建 报错 解决: 1、新建miniprogram文件后,直接进入到miniprogram目录,再次执行下面两个命令,然后再构建npm成功 npm init -y npm install express(Node js后端Express开发&#xff…

智慧校园的机遇与挑战

随着5G、物联网、大数据等技能的日渐老练,数字化正在渗透到各行各业中,为事务立异和价值增加供给支撑。在教育职业,运用智能化体系赋能教育办理越来越受欢迎,教育信息化方针一再出台,进一步加快了智慧校园落地的脚步。…

Linux - 文件管理高级 sed

3.处理字符 sed ① sed 默认情况下不会修改原文件内容 ② sed 是一种非交互式的编辑器 3.1 工作原理 将原文件一行一行的进行处理,取出一行,放入“模式空间进行处理”,处理完成之后将结果输出到屏幕上,然后读取下一行&#xf…

彭涛 | 2024年5月小结

5月份还是蛮有刺激的,做了蛮多的事情,但是没赚到钱,真是一屯操作猛如虎,一看账户0.5。 就喜欢创业这种一天天累死累活还不赚钱的感觉,哈哈哈哈 老规矩简单说下这个月的情况,如果对你有收获就最好了。 游学丹…

【Tlias智能学习辅助系统】04 部门管理 删除 和 新增

Tlias智能学习辅助系统 04 部门管理 删除 和 新增 删除部门APIDeptController.javaDeptService.javaDeptServiceImpl.javaDeptMapper.java前端联调 新增部门API有一步简化DeptController.javaDeptService.javaDeptServiceImpl.javaDeptMapper.java前端联调 删除部门API 请求路径…

Selenium+Java 环境搭建

selenium 介绍 Selenium 是 web 应用中基于 UI 的自动化测试框架,支持多平台、多浏览器、多语言。 早期的 selenium RC 已经被现在的 webDriver 所替代,可以简单的理解为selenium1.0webdriver 构成 现在的 Selenium2.0 。现在我们说起 selenium &#xf…

适合学生写作业的台灯有哪些?台灯怎么选详细攻略!

在数字化飞速发展的今天,孩子们的学习和生活越来越离不开电子屏幕。然而,长时间盯着屏幕,不仅容易让眼睛感到疲劳,更是近视问题日益严重的元凶之一。每一位家长都希望孩子能拥有健康的视力,因此会为孩子挑选一台护眼灯…

MySQL十部曲之九:MySQL优化理论

文章目录 前言概述查询优化查询执行计划EXPLAIN获取表结构信息获取执行计划信息 EXPLAIN 输出格式如何使用EXPLAIN进行优化 范围访问优化单列索引的范围访问多列索引的范围访问 索引合并优化索引合并交叉访问算法索引合并联合访问算法索引合并排序联合访问算法 索引下推优化连接…

豆包浏览器插件会造成code标签内容无法正常显示

启用状态:页面的代码会显示不正常 禁用后,正常显示 害得我重置浏览器设置,一个个测试

spring mvc 中怎样定位到请求调用的controller

前言 在java web开发过程中,正常情况下controller都是我们自己写的,我们可以很方便的定位到controller的位置。但是有些时候我们引入的其他依赖中可能也有controller,为了找到并方便的调试jar包中的controller,我们一般会进行全局…

【CPP】双端队列简介(deque)

简介:双端队列(deque) 目录 1.概述2.特点3.底层原理 1.概述 双端队列:是一种顺序表和顺序表的结合数据结构,不是队列。 它提供顺序表的[]下标访问和链表的中间头部的较高效率插入删除操作。 2.特点 顺序表的优缺点: 优点&…

linux之docker- image.tar 的导出和导入

一、情况 docker 镜像有时无法从外网访问,需要把docker 打包导出到本地,然后以文件的形式,发送给其他人,再然后其他人把docker 镜像文件导入到自己的服务器本地镜像仓库,方可使用。也可把镜像上传到公司内网。下面就开…

Verilog HDL基础知识(二)

引言:本文继续介绍Verilog HDL基础知识,重点介绍赋值语句、阻塞与非阻塞、循环语句、同步与异步、函数与任务语法知识。 1. 赋值语句 在Verilog中,有两种进行赋值的方法,即连续赋值语句和过程赋值语句(块&#xff09…

Java数据结构-二叉搜索树

目录 1. 概念2. 二叉搜索树的操作2.1 查找2.2 插入2.3 删除 3. 全部代码 1. 概念 二叉搜索树是特殊的二叉树,也叫二叉排序树,它的特点是:每个结点的左子树上的所有结点的值都小于这个结点的值,右子树上的所有结点的值都大于这个结点的值,另外所有的左子树和右子树也分别为二叉…

详解 Spark 编程之 RDD 依赖关系

一、依赖与血缘关系 依赖:两个相邻 RDD 之间的关系血缘关系:多个连续的 RDD 的依赖由于 RDD 不会保存数据,为了提高容错性,每个 RDD 都会保存自己的血缘关系,一旦某个转换过程出现错误,可以根据血缘关系重新…

随身wifi网络卡顿怎么解决?随身WiFi哪个牌子的最好用?排名第一名的随身WiFi!

对于随身wifi靠不靠谱这个问题,网上一直存在争议。很多人的随身wifi网速不稳定,信号看着满格就是上不了网。关于随身wifi卡顿到底该怎么解决呢? 1.如果是设备网络在一个地方上网速度很快,换一个地方网络就不行了,很可能…

Linux学习笔记(清晰且清爽)

本文首次发布于个人博客 想要获得最佳的阅读体验(无广告且清爽),请访问本篇笔记 Linux安装 关于安装这里就不过多介绍了,安装版本是CentOS 7,详情安装步骤见下述博客在VMware中安装CentOS7(超详细的图文教…