Radon 变换原理和应用_radon变换-程序员宅基地

技术标签: 数学相关  计算机视觉  边缘检测  图像处理  数学  

Radon 变换原理和应用

前言: 承接 Hough 变换,从简单的直线检测说起,推广到 Radon 变换。介绍了 Radon 变换的基本原理和应用。主要是Hough直线检测的拓展,深度较浅。

上启自:详解 Hough 变换

1. 从直线检测说起

1.1 基本问题

详解 Hough 变换 中,对直线检测折腾这么久,已经是个成熟的”直线检测者”了,所以基本问题就不从点分析,直接从图出发。即

如何检测下图(a)中的直线?

在这里插入图片描述

当然可以使用 Hough 变换,如直接套用那篇博文中自编的代码就可以得到结果,展示如下:
在这里插入图片描述

再简单概述下原理:即,将原空间中的点变换到参数空间,每个原空间的点对应参数空间一条直线。原空间中有N个点就对应参数空间中N个直线。参数空间中直线相交就是表明对应原空间的点共线。参数空间中交点重复次数(亮度)越多,就表明原空间中共线的点越多。这些点连起来就是要求的直线。

注:如果使用 Hough变换 博文中的代码,将 θ \theta θ 范围修改下,其实不必 [ 0 , 2 π ] [0,2\pi] [0,2π],修改为 [ 0 , π ] [0,\pi] [0,π] 即可。

1.2 另辟蹊径

检测之前一般都是进行边缘提取,我们的目标是检测 图(b) 边缘二值图中是否存在直线,以及,如果存在,表示出这条直线。

先抛弃 Hough 变换的思路,考虑另外想法。

(1) 第一种想法:考虑从不同方向把 (b) 图拍扁(术语叫投影)。比如从两个角度(下图 A 1 , A 2 A1,A2 A1,A2 )拍扁(投影)它,示意如下:

在这里插入图片描述

这里使用两个方向 ( α = 0 \alpha=0 α=0​​​ 和 α = 45 ° \alpha=45° α=45°​​​​ )的光线 A 1 , A 2 A1,A2 A1,A2​​​ 对边缘进行投影。假设投影到投影面的位置点的数量越多,投影结果越亮。那么图中,灰色箭头标志的地方会非常亮。因为整条直线的像素点都会投影到该处。

不仅如此,事实上,考察所有投影方向,即 α ∈ [ 0 , π ] \alpha\in [0,\pi] α[0,π]​ (注意:因为不分正负,所以 [ 0 , π ] [0,\pi] [0,π]​ 和 [ π , 2 π ] \pi,2\pi] π,2π]​ 结果是一样的),最亮的点依旧会是图示中灰色标志的部分。这是直线的特点。

所以,是否我们可以考察所有方向下,对边缘图像进行投影,找到最亮的点(或者达到规定亮度阈值的点,比如存在多条直线)和此时对应的光线方向。 那这条直线不就检测出来了,直线方程不就确定了吗?

先不着急编程实践,再思考另一种想法。

(2)第二种想法:Hough 变换中,有提到可以用 θ \theta θ d d d​ 表示一条直线,如下图示意(不懂为何,可以参考 Hough 变换博文):

在这里插入图片描述

其中, d d d 为圆心到直线距离, θ \theta θ 是垂线与 x x x 轴夹角。那么这一想法就是使用 穷举法 的思想,即

因为 θ \theta θ 是有范围的,同时因为在图像中检测直线,所以 d d d 也是有范围,最大为图像的斜对角距离。于是,我们就考察“所有”的 θ \theta θ d d d ,这里的”所有“取决于你的精度,即 θ \theta θ 以及 d d d​​ 各自的离散值取值间隔。来表示图像空间内”所有“的直线。

表示出”所有“直线后,下面我们要做的就是,将直线一条一条的与原图相匹配。记录该直线的点与边缘图中点重叠的个数,把该个数记为该线与图的匹配度。比如取所有线的其中四条,作个示意:

在这里插入图片描述

其中, L 1 , L 2 L1,L2 L1,L2 L 3 , L 4 L3,L4 L3,L4 故意取了 θ \theta θ 相同,但 d d d​ 不同所表示的直线(图中L1,L2没有标角度和距离,怕线看起来太乱了)。上面所说的匹配度即该条直线与边缘图中像素重叠数,如 L 1 L1 L1 与 边缘线没有像素重叠,匹配度就为0, L 4 L4 L4同理。其中这举例的四条直线中,匹配度最高的是 L 2 L2 L2​。因为它与边缘图中包含直线的所有像素都重合。

不仅如此,事实上,考察所有 θ \theta θ,距离 d d d 所表示的直线,匹配度数值最高的依旧会是 L 2 L2 L2

说到这,你可能会发现,其实这就是所提到的第一种想法的另种思路。只是这里的 L 1 , L 2 , ⋯ L1,L2,\cdots L1,L2,​​ 就是第一种想法中的 一条 光线,如 第二种想法里的 L 2 L2 L2​ 直线其实就是 第一种想法里 A 2 A2 A2​​​ 方向下,最亮点对应的那一条光线。匹配度也就对应于第一种想法里的亮度,也就是重合度。

唯一的区别和需要注意的是,两个想法中的所用角度表示方法不同(但属于无伤大雅的东西):前者是光线的倾角,后者是光线垂线的倾角

1.3 编程实现

基于以上想法,编程,进行直线检测:

注:这里使用想法二中的倾角 θ \theta θ​​,这一倾角的好处和直线表示方法,参看Hough变换博文。

直接给出结果,通过 θ \theta θ d d d 确定的直线方程为
d = x c o s θ + y s i n θ d = xcos\theta+ysin\theta d=xcosθ+ysinθ

完整代码如下:

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

# 读取图片
imgPath = "C:\\Users\\zhangwei156\\Desktop\\figure\\waterm.jpeg"
grayImg = cv2.imread(imgPath, 0)
    
# 提取边缘
edgeImg = cv2.Canny(grayImg, 300, 500)

H, W = edgeImg.shape

# 精度
theta_div = 500
d_div = 500
theta_max = np.pi
dMax = np.floor(np.sqrt(H**2 + W**2))

# 离散取值
theta = np.linspace(0, np.pi, theta_div)[:-1]
d = np.linspace(-dMax, dMax, d_div)

# 分辨率
theta_res = np.pi/(theta_div-1)
d_res = 2*dMax/(d_div-1)

# 记录 亮度/匹配度/叠加次数 的模板
forceImg = np.zeros((theta_div, d_div), np.uint16)

mesh = np.meshgrid(theta, d)
for _t, _d in zip(mesh[0].flatten(), mesh[1].flatten()):
    # 分母为 0 情况
    if _t == 0:
        continue
        y = np.arange(0, H)
        x = y * 0 + 1
    else:
        x = np.arange(W)
        y = ((_d-x*np.cos(_t))/np.sin(_t)).astype(np.int64) # theta 和 d 确定的直线
    pixel = 2  # 上下容错像素范围
    # y轴上下容错像素
    for i in range(1, pixel+1):
        x = np.hstack((x,x,x))
        y = np.hstack((y-i,y,y+i))
    # 剔除 y < 0 和 y > H 也即图像外的点
    ind = np.where( (y>=0) & (y<H) )
    if ind[0].shape[0] == 0:
        continue
    y = y[ind[0]]
    x = x[ind[0]]
    index = np.where(edgeImg[y, x]!=0 )[0]
    
    # 取值
    tt = int(_t/theta_res)
    dd = int((_d+dMax)/d_res)
    # 亮度值
    forceImg[tt, dd] = index.shape[0]

# 结果
plt.imshow(forceImg)
plt.show()

# 检查结果正确与否的代码
# 最亮处所代表的 theta 和 d 值
O = np.where(forceImg == np.max(forceImg))
# theta 和 d 的真实值
theta_ = O[0]*theta_res
d_ = O[1]*d_res - dMax
# 在原图中显示检测到的直线
for _o in (zip(theta_, d_)):
    x_ = np.arange(W)
    y_ = ((_o[1]-x_*np.cos(_o[0]))/np.sin(_o[0]))
    ind_ = np.where((y_>0) & (y_<H))
    x_ = x_[ind_[0]]
    y_ = y_[ind_[0]]

plt.imshow(grayImg)    
plt.plot(x_,y_,color = "r")
plt.show()

结果展示:

在这里插入图片描述

其中, f o r c e I m g forceImg forceImg 中最亮点对应的实际 ( θ , d ) = ( 1.561 , 181.383 ) (\theta, d) = (1.561, 181.383) (θ,d)=(1.561,181.383)​​ ,即 θ = 89.46 ° ; d = 181.4 \theta = 89.46° ; d=181.4 θ=89.46°;d=181.4 。看来垂线还不是纯竖直的,有一点点偏。

此时,你会有个很“吃惊”的发现。

对比一下前面展示的使用 Hough 变换方法时结果,两者 f o r c e I m g forceImg forceImg 图是一致的!

想象一下整个过程,实际上,针对上述问题,它就是 Hough 变换的解决方式。它就是 Hough 变换。

换句话说,想法一,想法二以及之前的 Hough 直线检测想法都是同源的!

区别在于:Hough 变换中使用 θ , d \theta,d θ,d​ 穷举的是过每个边缘点的所有直线,而以上两个想法通过 θ , d \theta,d θ,d​ 穷举的是图像空间内所有直线。但 Hough 变换有基于先验知识,换句话说,Hough变换更精准。

所以之后用 Hough 检测直线时,也可以使用 randon 变换函数(上面这个就是最基础的randon变换,就是后面要引申的本文主题,但这里不得不先提下)。

2. Radon 变换

2.1 回顾

回顾上述过程,从某个方向对图像投影,一个特定的 θ , d \theta,d θ,d 确定某条唯一的光线,投影结果为一个固定值,即亮度(累加度)。也即 ( θ , d ) ⟶ 亮 度 (\theta,d) \longrightarrow 亮度 (θ,d) ,这是个函数对应关系。

亮度的计算是光线对应图像上的像素值累加。上例的特殊之处在于,边缘图为二值图,即边缘部分像素值为 1,黑色部分像素值为 0。

想象若图像像素值为任意值,事实上也可以累加。推广到更一般情况,即不再如图像像素那般是一格格的、离散的,而是连续的。那求和其实就是求线积分。

把问题推广到这种程度,事实上就是要展示的 Radon 变换内容。

2.2 Radon 变换

如下图所示,不再是简单的离散像素,而是连续的函数值 f ( x , y ) f(x,y) f(x,y),我们暂且称之为”密度“。一条由 θ , d \theta,d θ,d 确定的唯一光线,经过 f ( x , y ) f(x,y) f(x,y) 物体,得到亮度值为 R R R 的投影。

在这里插入图片描述

根据以上的思路,此时亮度值应该是 L L L 线积分,即
R = ∫ L f ( x , y ) d s R = \int_L f(x,y)ds R=Lf(x,y)ds
且光线 L L L​ 可以由 θ , d \theta,d θ,d​ 唯一确定,即
L ( θ , d ) :    d = x cos ⁡ θ + y sin ⁡ θ L(\theta,d):\ \ d = x\cos\theta + y\sin\theta L(θ,d):  d=xcosθ+ysinθ
也就有
R ( θ , d ) = ∫ L ( θ , d ) f ( x , y ) d s R(\theta,d) = \int_{L(\theta,d)} f(x,y)ds R(θ,d)=L(θ,d)f(x,y)ds
该式子也就是一个全新的函数对应关系,表示的是给定自变量 θ , d \theta,d θ,d​ ,会有与之对应的函数值 R ( θ , d ) R(\theta,d) R(θ,d)​​ ,该值的物理意义为亮度(或者说是叠加度、衰减后的值、匹配度等等)。而 θ , d \theta,d θ,d​ 为连续的,因此将该函数用图像画出来就是对应于前面 f o r c e I m g forceImg forceImg​ 的样子,即横、纵坐标为 θ , d \theta,d θ,d​,亮度大小为 R R R​。更一般情况是对应一个三维空间, x , y x,y x,y​ 轴为 θ , d \theta,d θ,d​ , z z z​ 轴对应为 R R R​。

下面就是对积分的计算,该部分为数学中线积分求解问题,不详述。

法一:直接求解

当给定 θ , d \theta,d θ,d 时,此时 R 为
R = ∫ y = d 1 − x cos ⁡ θ 1 sin ⁡ θ 1 f ( x , y ) d s R = \int_{y = \frac{d_1-x \cos\theta_1}{\sin\theta_1}}f(x,y)ds R=y=sinθ1d1xcosθ1f(x,y)ds
y y y 替换一下,高等数学中的线积分求解问题。这是一种思路。

法二 δ \delta δ 函数
δ \delta δ​ 函数是一个广义函数。简单形式如下:
δ ( t ) = { 0 , t ≠ 0 1 , t = 0 \delta(t) = \left\{ \begin{aligned} 0, &\quad t\ne 0\\ 1, &\quad t=0 \end{aligned} \right. δ(t)={ 0,1,t=0t=0
结合上述直线方程 L ( θ , d ) :   d − x c o s θ − y s i n θ = 0 L(\theta,d):\ d-xcos\theta-ysin\theta = 0 L(θ,d): dxcosθysinθ=0, 则有
δ ( d − x c o s θ − y s i n θ ) = { 0 , d − x c o s θ − y s i n θ ≠ 0 1 , d − x c o s θ − y s i n θ = 0 \delta(d-xcos\theta-ysin\theta) = \left\{ \begin{aligned} 0, &\quad d-xcos\theta-ysin\theta\ne 0\\ 1, &\quad d-xcos\theta-ysin\theta=0 \end{aligned} \right. δ(dxcosθysinθ)={ 0,1,dxcosθysinθ=0dxcosθysinθ=0
最后,radon 变换方程写为:
R ( θ , d ) = ∬ f ( x , y ) ⋅ δ ( d − x c o s θ + y sin ⁡ θ ) d x d y R(\theta,d) = \iint f(x,y)\cdot \delta(d-xcos\theta+y\sin\theta)dxdy R(θ,d)=f(x,y)δ(dxcosθ+ysinθ)dxdy
当给定 θ , d \theta,d θ,d​ 时,同样带入计算。

可以总结一下,Radon 变换就是对应 ( θ , d ) (\theta,d) (θ,d) 确定的光线与空间中密度函数的积分。

2.3 Radon 逆变换

想象一下,如果我从不同角度对物体进行照射,得到了每个方向下的投影,即所有 θ , d \theta,d θ,d 对应的 R R R 值。那么反过来,已知 R ( θ , d ) R(\theta,d) R(θ,d) ,是否也就唯一确定 f ( x , y ) f(x,y) f(x,y) 的情况。

至于如何已知 R ( θ , d ) R(\theta,d) R(θ,d) 反求 f ( x , y ) f(x,y) f(x,y)​ ,现在用不到,挖坑,之后用到再探究其方法。

3. 应用

前面已经提到了一个应用:直线检测(同 Hough 检测一致)

还有个经典应用,即 CT 断层成像的重建。

CT 原理其实就是借助 x 射线照射组织器官,不同组织器官对 x 的射线衰减程度(或系数;对应于上面的 f ( x , y ) f(x,y) f(x,y) )不同,一条 x 射线穿过一系列组织器官后,经衰减后,到达采集设备,得到一个衰减后的强度值(对应于上面的 R R R)。

我们采集得到一系列的 R R R 情况,就可以通过 r a d o n radon radon 逆变换得到衰减信息,即组织器官信息。

换句话说, r a d o n radon radon 变换可用于三维重建,且是三维重建研究方向的helloworld

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/deepsprings/article/details/119321544

智能推荐

JavaScript学习笔记_curry函数未定义-程序员宅基地

文章浏览阅读343次。五种原始的变量类型1.Undefined--未定义类型 例:var v;2.String -- ' '或" "3.Boolean4.Number5.Null--空类型 例: var v=null;Number中:NaN -- not a number非数本身是一个数字,但是它和任何数字都不相等,代表非数,它和自己都不相等判断是不是NaN不能用=_curry函数未定义

兑换码编码方案实践_优惠券编码规则-程序员宅基地

文章浏览阅读1.2w次,点赞2次,收藏17次。兑换码编码设计当前各个业务系统,只要涉及到产品销售,就离不开大大小小的运营活动需求,其中最普遍的就是兑换码需求,无论是线下活动或者是线上活动,都能起到良好的宣传效果。兑换码:由一系列字符组成,每一个兑换码对应系统中的一组信息,可以是优惠信息(优惠券),也可以是相关奖品信息。在实际的运营活动中,要求兑换码是唯一的,每一个兑换码对应一个优惠信息,而且需求量往往比较大(实际上的需求只有预期_优惠券编码规则

c语言周林答案,C语言程序设计实训教程教学课件作者周林ch04结构化程序设计课件.ppt...-程序员宅基地

文章浏览阅读45次。C语言程序设计实训教程教学课件作者周林ch04结构化程序设计课件.ppt* * 4.1 选择结构程序设计 4.2 循环结构程序设计 4.3 辅助控制语句 第四章 结构化程序设计 4.1 选择结构程序设计 在现实生活中,需要进行判断和选择的情况是很多的: 如果你在家,我去拜访你 如果考试不及格,要补考 如果遇到红灯,要停车等待 第四章 结构化程序设计 在现实生活中,需要进行判断和选择的情况..._在现实生活中遇到过条件判断的问

幻数使用说明_ioctl-number.txt幻数说明-程序员宅基地

文章浏览阅读999次。幻数使用说明 在驱动程序中实现的ioctl函数体内,实际上是有一个switch{case}结构,每一个case对应一个命令码,做出一些相应的操作。怎么实现这些操作,这是每一个程序员自己的事情。 因为设备都是特定的,这里也没法说。关键在于怎样组织命令码,因为在ioctl中命令码是唯一联系用户程序命令和驱动程序支持的途径 。 命令码的组织是有一些讲究的,因为我们一定要做到命令和设备是一一对应的,利_ioctl-number.txt幻数说明

ORB-SLAM3 + VScode:检测到 #include 错误。请更新 includePath。已为此翻译单元禁用波浪曲线_orb-slam3 include <system.h> 报错-程序员宅基地

文章浏览阅读399次。键盘按下“Shift+Ctrl+p” 输入: C++Configurations,选择JSON界面做如下改动:1.首先把 “/usr/include”,放在最前2.查看C++路径,终端输入gcc -v -E -x c++ - /usr/include/c++/5 /usr/include/x86_64-linux-gnu/c++/5 /usr/include/c++/5/backward /usr/lib/gcc/x86_64-linux-gnu/5/include /usr/local/_orb-slam3 include 报错

「Sqlserver」数据分析师有理由爱Sqlserver之十-Sqlserver自动化篇-程序员宅基地

文章浏览阅读129次。本系列的最后一篇,因未有精力写更多的入门教程,上篇已经抛出书单,有兴趣的朋友可阅读好书来成长,此系列主讲有理由爱Sqlserver的论证性文章,希望读者们看完后,可自行做出判断,Sqlserver是否真的合适自己,目的已达成。渴望自动化及使用场景笔者所最能接触到的群体为Excel、PowerBI用户群体,在Excel中,我们知道可以使用VBA、VSTO来给Excel带来自动化操作..._sqlsever 数据分析

随便推点

智慧校园智慧教育大数据平台(教育大脑)项目建设方案PPT_高校智慧大脑-程序员宅基地

文章浏览阅读294次,点赞6次,收藏4次。教育智脑)建立学校的全连接中台,对学校运营过程中的数据进行处理和标准化管理,挖掘数据的价值。能:一、原先孤立的系统聚合到一个统一的平台,实现单点登录,统一身份认证,方便管理;三、数据共享,盘活了教育大数据资源,通过对外提供数。的方式构建教育的通用服务能力平台,支撑教育核心服务能力的沉淀和共享。物联网将学校的各要素(人、机、料、法、环、测)全面互联,数据实时。智慧校园解决方案,赋能教学、管理和服务升级,智慧教育体系,该数据平台具有以下几大功。教育大数据平台底座:教育智脑。教育大数据平台,以中国联通。_高校智慧大脑

编程5大算法总结--概念加实例_算法概念实例-程序员宅基地

文章浏览阅读9.5k次,点赞2次,收藏27次。分治法,动态规划法,贪心算法这三者之间有类似之处,比如都需要将问题划分为一个个子问题,然后通过解决这些子问题来解决最终问题。但其实这三者之间的区别还是蛮大的。贪心是则可看成是链式结构回溯和分支界限为穷举式的搜索,其思想的差异是深度优先和广度优先一:分治算法一、基本概念在计算机科学中,分治法是一种很重要的算法。字面上的解释是“分而治之”,就是把一个复杂的问题分成两_算法概念实例

随笔—醒悟篇之考研调剂_考研调剂抑郁-程序员宅基地

文章浏览阅读5.6k次。考研篇emmmmm,这是我随笔篇章的第二更,原本计划是在中秋放假期间写好的,但是放假的时候被安排写一下单例模式,做了俩机试题目,还刷了下PAT的东西,emmmmm,最主要的还是因为我浪的很开心,没空出时间来写写东西。  距离我考研结束已经快两年了,距离今年的考研还有90天左右。  趁着这个机会回忆一下青春,这一篇会写的比较有趣,好玩,纯粹是为了记录一下当年考研中发生的有趣的事。  首先介绍..._考研调剂抑郁

SpringMVC_class org.springframework.web.filter.characterenco-程序员宅基地

文章浏览阅读438次。SpringMVC文章目录SpringMVC1、SpringMVC简介1.1 什么是MVC1.2 什么是SpringMVC1.3 SpringMVC的特点2、HelloWorld2.1 开发环境2.2 创建maven工程a>添加web模块b>打包方式:warc>引入依赖2.3 配置web.xml2.4 创建请求控制器2.5 创建SpringMVC的配置文件2.6 测试Helloworld2.7 总结3、@RequestMapping注解3.1 @RequestMapping注解的功能3._class org.springframework.web.filter.characterencodingfilter is not a jakart

gdb: Don‘t know how to run. Try “help target“._don't know how to run. try "help target".-程序员宅基地

文章浏览阅读4.9k次。gdb 远程调试的一个问题:Don't know how to run. Try "help target".它在抱怨不知道怎么跑,目标是什么. 你需要为它指定target remote 或target extended-remote例如:target extended-remote 192.168.1.136:1234指明target 是某IP的某端口完整示例如下:targ..._don't know how to run. try "help target".

c语言程序设计教程 郭浩志,C语言程序设计教程答案杨路明郭浩志-程序员宅基地

文章浏览阅读85次。习题 11、算法描述主要是用两种基本方法:第一是自然语言描述,第二是使用专用工具进行算法描述2、c 语言程序的结构如下:1、c 语言程序由函数组成,每个程序必须具有一个 main 函数作为程序的主控函数。2、“/*“与“*/“之间的内容构成 c 语言程序的注释部分。3、用预处理命令#include 可以包含有关文件的信息。4、大小写字母在 c 语言中是有区别的。5、除 main 函数和标准库函数以..._c语言语法0x1e