浅析 空间频率响应 SFR 计算

一、调制传递函数MTF

1. 为何需要调制传递函数

对于光学系统以及成像系统,如何有效评价其成像素质?这里主要包括主观评价以及客观评价。

对于主观评价 ,是通过人眼视觉感知对图像质量的综合判定,包括清晰度、锐度、色彩表现(包括准确性以及饱和度)、动态范围(包括高光与暗部细节以及HDR表现)、噪声以及细节保留、畸变与亮度均匀性以及整体观感与艺术性等。

对于客观评价,这里主要涉及的核心指标:分辨率锐度(对比度)

分辨率

其中分辨率是成像系统区分物体细节的能力,它通常用每毫米线对表示 ( lp /mm)(其中线对是一条黑线和一条白线的序列)。这种每毫米的线对度量也称之为频率。频率的倒数产生两条解析线之间以毫米为单位的间距。对于所有成像元件,对于分辨率板成像时,完美的线条边缘会在一定程度上变得模糊。高分辨率图像是由于模糊最小而表现出大量细节图像。相反,低分辨率图像缺乏细节。如下图所示:

对比度是衡量光学系统传递物体明暗差异能力的关键指标,直接关系到成像的清晰度和细节还原能力。

考虑通过将最大值分配给白条并将零值分配给黑条来标准化条目标的强度。 绘制这些值会产生方波,从中可以更容易地看出对比度的概念。在数学上,对比度使用如下公式来计算:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> % C o n t r a s t = [ I m a x − I m i n I m a x + I m i n ] \%Contrast = [\frac{I_{max} - I_{min}}{I_{max} + I_{min}}] </math>%Contrast=[Imax+IminImax−Imin]

那如何衡量这两者的关系,这里就需要调制传递函数。

2. 何为调制传递函数MTF

对于光学设计人员图比较光学系统的性能时,常用的衡量标准是调制传递函数(MTF)Modulation Transfer Function (以下简称MTF)。它所描述的是光学系统在像方"还原"物方的能力,具体是指调制度随空间频率变化的函数称为调制度传递函数。

对于接触过信号与系统这么课的小伙伴来说,一看到"传递函数"大概就知道了它所描述的是系统传递信息的能力。成像过程可以看做是物方到像方经由成像镜头传递信息的过程,MTF越高,意味着信息折损越少,如果MTF=1,那就意味着信息被完全传递到了像方;而如果MTF很低甚至MTF=0,就意味着只有很少,甚至完全没有信息到达像方。从这个角度我们就可以直观地感受MTF所描述的对象了。

传递函数最开始是为了说明镜头的能力。在各个摄像头镜头中经常采用MTF描述镜头的MTF曲线,表明镜头的能力。这些曲线是通过理想的测试环境下尽量减少其它系统对镜头的解析力的衰减的情况下测试得出的。但是其实MTF也可以涵盖对整个成像系统的解析力评价。MTF的值越接近于1,说明镜头的成像效果越好。

镜头的MTF

对于玩镜头的摄影党同学网上购买镜头时可以看到如下的MTF图,不过这里MTF图与光学设计中的MTF图会存在不一样(下面会阐述),因为这个时候横坐标又变了。在摄影镜头中,横轴代表的是半像高,例如全画幅镜头通常就是20mm多一点,而典型的频率会给出10lp/mm和30lp/mm或者15lp/mm和45lp/mm。换言之,镜头MTF图并没有给出所有空间频率下的性能,而是选择了两个典型频率来进行展示,MTF图的横轴被用来展示不同像场位置的MTF性能。不过我们这里重点介绍光学设计中的MTF,具体细节这里不再表述。

在镜头的MTF图中,还会给出一根实线和一根虚线,通常实线代表的是"子午方向",也就是tangential或者meridional,虚线代表的是"弧矢方向",也就是sagittal。由于在这两个方向上的像差有所不同,因此MTF也是不同,简单的理解就是一个横着的黑白条纹和一个竖着的黑白条纹经过镜头成像后可能会有不一样的对比度。

空间频率响应SFR

典型的MTF如下图所示,描述的是随着空间频率变换对比度的变化。

其中这里很重要的概念就是横坐标空间频率 ,提到频率大家想到的就是不默认的"时间频率 <math xmlns="http://www.w3.org/1998/Math/MathML"> f f </math>f"(以 <math xmlns="http://www.w3.org/1998/Math/MathML"> H z Hz </math>Hz为单位),描述了在时域中与周期 <math xmlns="http://www.w3.org/1998/Math/MathML"> T T </math>T(以秒为单位)之间的关系,表示 <math xmlns="http://www.w3.org/1998/Math/MathML"> f = 1 / T f=1/T </math>f=1/T 。那何为空间频率?从字面意思来看就是描述在空间中的周期关系,具体就是一维空间频率 <math xmlns="http://www.w3.org/1998/Math/MathML"> v v </math>v与该维中距离 <math xmlns="http://www.w3.org/1998/Math/MathML"> x x </math>x之间的关系:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> v = 1 x v = \frac{1}{x} </math>v=x1

空间频率的单位必须与其相应的空间单位对应,因此如果以毫米为单位测量距离,则空间频率以每毫米的周期数或每毫米的线对数( <math xmlns="http://www.w3.org/1998/Math/MathML"> l p m lpm </math>lpm 或 <math xmlns="http://www.w3.org/1998/Math/MathML"> l p / m m lp/mm </math>lp/mm)表示,后者通常用于光学测量。

从物理意义来看,对于日常见到的图像,其中总是包含着一些低频的信息,例如物体的轮廓、大面积的光影变化、色彩的分布等,也包含着一些高频的信息,例如物体的细节、细小的文字等。低频的信息在画面的不同位置处变化是比较缓慢地,就像下面左图所示的一样,是比较宽大的黑白条纹;高频的信息则在非常小的尺度内就有很明显的变化,例如图片中的文字往往只在数十个像素内就包含了诸多笔画,这就像下面右图的细小黑白条纹一样。

看完横轴之后,我们来看看纵轴,MTF的纵轴被称为对比度(contrast),上面已经解释这里不再赘述。

而MTF通常跟空间频率的关系比较密切,会测出不同空间频率下的MTF,绘制成一条曲线。而SFR(spatial frequency response)表示空间频率响应,表示的也是相机的解像能力,在这个层面上,MTF与SFR是一样的意思。

SFR是 spatial frequency response (SFR) 主要是用于测量随着空间频率的线条增加对单一影像的所造成影响。简言之SFR就是MTF的另外一种测试方法。这种测试方法在很大程度上精简了测试流程。SFR的最终计算是希望得到MTF曲线。SFR的计算方法和MTF虽然不同但是在结果上是基本一致的

频域与空域- SFR与PSF

对于从数学以及工程角度理解MTF而言,这里不得不提及频域空域

对于研究图像处理或者光学系统而言,频域和空域这两个概念是相当有用的。空域是我们日常所看到的世界或者图像,图像的灰度值与空间坐标构成映射关系;而频域我们并不能直接看到,但是可以通过一些数学工具(傅里叶变换)去研究,在频域中,图像的空间频率及其幅值构成了映射关系。

MTF是在频域描述一个光学系统还原物方信息的能力,也就是镜头对不同空间频率的正弦条纹还原能力如何。这个过程可以用下述公式简述:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> F i m a g e = F o b j e c t ⋅ S F R F_{image} = F_{object} \cdot SFR </math>Fimage=Fobject⋅SFR

而如果是在空域,那我们就可以用到PSF,也就是点扩散函数(point spread function, PSF) 来描述。点扩散函数所描述的就是镜头将一个理想物点进行成像得到的结果。这个过程可以用下述公式简述:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f i m a g e = f o b j e c t ∗ P S F f_{image} = f_{object} \ast PSF </math>fimage=fobject∗PSF

其中 ∗ 代表的是卷积运算。

那何为点扩展函数该如何表示?这里就引申出点扩展函数PSF、线扩散函数(Line Spread Function, LSF)以及边沿扩散函数(Edge Spread Function, ESF)。为理解MTF的推导过程,这里重点介绍下。

点扩散函数PSF

点扩散函数(point spread function (PSF 以下均使用PSF缩写)是描述光学系统对理想点光源成像后光强分布的数学函数。其核心意义在于量化系统对点光源的模糊程度,是评估成像系统分辨率、像差和衍射效应的关键参数。

当一个理想点光源(数学上可用δ函数表示)通过光学系统时,由于衍射和像差的影响,其成像会扩展为一个弥散斑(如艾里斑),这种光强分布即为PSF。物理意义上,PSF是光学系统的脉冲响应函数,描述了一个成像系统对一个点光源(物体)的响应,反映了系统对空间细节的传递能力。PSF越窄,分辨率越高;反之,模糊越明显。

上图水平面坐标x,y为像面空间坐标,z轴为像面该点处亮度。由上图我们不难发现点扩散函数的切面图具有如下特性:

在相干光照明时,一个点物在像面上造成的强度分布即为点扩散函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> h ( x , y ) h(x,y) </math>h(x,y) 。由于像是中心对称的,因此,通过一条过中心的狭缝观察像斑,我们便可以得到如上图b所示的强度分布曲线 <math xmlns="http://www.w3.org/1998/Math/MathML"> h ( x i ) , 作为沿 h(xi) ,作为沿 </math>h(xi),作为沿xi$的点扩散函数。

线扩展函数LSF

线扩散函数顾名思义,是由线光源产生,当然你也可以认为是由一排点光源叠加产生。如下图所示:

如果用亮狭缝或一个亮线通过光学系统成像,取直线像的长度方向为 <math xmlns="http://www.w3.org/1998/Math/MathML"> y i y_i </math>yi,则沿 <math xmlns="http://www.w3.org/1998/Math/MathML"> x i x_i </math>xi方向的光强分布 <math xmlns="http://www.w3.org/1998/Math/MathML"> L ( x i ) L(x_i) </math>L(xi) 就叫做线扩散函数,线扩散函数是由点扩散函数累积而成。

定义:LSF描述了一个理想线光源(如无限细的亮线)经过光学系统后,在像平面上沿垂直方向的强度分布。

物理意义:反映系统对线光源的模糊程度,是PSF在一维方向的投影。

数学表达式
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> L S F ( x ) LSF(x) </math>LSF(x)

从数学关系上,LSF可以通过对PSF沿某一方向(通常是垂直于线光源的方向)积分得到:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> L S F ( x ) = ∫ − ∞ ∞ P S F ( x , y ) d y LSF(x) = \int_{-\infty}^{\infty}PSF(x,y)dy </math>LSF(x)=∫−∞∞PSF(x,y)dy

边沿扩展函数ESF

有了对线扩散函数理解的基础,请试想一下,假设用一与狭缝方向平行的刀片放置在像平面上。开始时刀片完全挡住狭缝像,逐渐沿 <math xmlns="http://www.w3.org/1998/Math/MathML"> x i x_i </math>xi方向移动刀片,刀片后的探测器接收到的全部光信号与刀片位置关系可用下图的阴影变化表示。光通量随 <math xmlns="http://www.w3.org/1998/Math/MathML"> x i x_i </math>xi的变化称边缘扩散函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> E ( x i ) E(x_i) </math>E(xi) ,与线扩散函数关系为:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> E S F ( x ) = ∫ − ∞ x L S F ( x ) d x ESF(x) = \int_{-\infty}^{x}LSF(x)dx </math>ESF(x)=∫−∞xLSF(x)dx

对照上图,所谓探测器接收到的总光通量就是图中阴影部分的面积,它是对线扩散函数左半区间内的无穷积分【 <math xmlns="http://www.w3.org/1998/Math/MathML"> x i x_i </math>xi的右侧被刀片挡住,光无法通过】,也即是该处的边缘扩散函数值。因此对线扩散函数积分以后我们有类似如下图的边缘扩散函数:

但如果我们的实景是如下的样子呢?假设我们的面光源是中心区域亮,两侧区域逐渐变暗。

这时候我们可以把面中的每一条竖线当成一个假想的线光源,于是我们可以抽象出如下图所示,无限个线光源产生的线扩散函数叠加的像面情形。

MTF与PSF、LSF、ESF关系

当获取点光源像的亮度分布函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> P S F ( X , Y ) PSF(X,Y) </math>PSF(X,Y) 后,对其进行二维傅里叶变换即可得 <math xmlns="http://www.w3.org/1998/Math/MathML"> M T F ( u , v ) MTF (u,v) </math>MTF(u,v) 。因此,从理论上讲,从 <math xmlns="http://www.w3.org/1998/Math/MathML"> P S F PSF </math>PSF也是获取 <math xmlns="http://www.w3.org/1998/Math/MathML"> M T F MTF </math>MTF的一个方法。但是,在实际的应用中,由于地面点光源强度很弱,此方法一般较少采用。相对于PSF来说,LSF的能量得到了一定程度的加强。

SFR计算MTF(通过斜边) 就是通过ESF来得到LSF,然后进行FFT得到MTF各个频率的值的。这几者之间的关系如下图。

从数学角度推导,具体可见清晰度和分辨率:调制传递函数 MTF 及相关概念、ISO12233测量方法,这里不再推导。

二、如何测量MTF

1. 测量PSF

从上面的空域与时域关系来看,测量 MTF 的一种方法是直接测量摄像头捕获的 PSF 在实践中,这种方法相当困难。因为为了使成像的点光源尽可能小,其强度也会下降。这会使测量的信噪比非常低,从而引入大的误差。 此外,PSF 由摄像头阵列进行采样,可能会在测量中引入混叠,并且还会大大限制分辨率。

2. 黑白条纹图

对于MTF也可以使用正弦的黑白条纹图来获取,但是现实中制作这样的正弦图表更具挑战性。不过可以尝试一种更简单的变体,在暗区和亮区之间有明显的过渡,如下图所示:

可以通过傅里叶级数以谐波函数的形式表示具有锐利边缘的条形图案
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> f ( x , y ) = a + b 4 π ∑ n = 0 N ( − 1 ) n cos ⁡ ( 2 π ( 2 n + 1 ) k x ) 2 n + 1 f(x, y) = a + b \frac{4}{\pi} \sum_{n=0}^{N} \frac{(-1)^n \cos\left(2\pi(2n+1)kx\right)}{2n+1} </math>f(x,y)=a+bπ4n=0∑N2n+1(−1)ncos(2π(2n+1)kx)

如果我们对这种扩展应用与上述正弦模式采用相同的数学处理,我们会发现传递函数将由一个新量给出,即对比度传递函数 (contrast transfer function, CTF)。 CTF、c( ) 和 MTF 之间的关系是 (Coltman, 1954)
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> c ( v ) = 4 π ( m ( v ) − m ( 3 v ) 3 + m ( 5 v ) 5 − ...   ) c(v) = \frac{4}{\pi} \left( m(v) - \frac{m(3v)}{3} + \frac{m(5v)}{5} - \dots \right) </math>c(v)=π4(m(v)−3m(3v)+5m(5v)−...)

需要注意的是,级数项中的符号无规律。 然而,转换后可以发现,符号交替正负:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> m ( v ) = π 4 ( c ( v ) + c ( 3 v ) 3 − c ( 5 v ) 5 + ...   ) m(v) = \frac{\pi}{4} \left( c(v) + \frac{c(3v)}{3} - \frac{c(5v)}{5} + \dots \right) </math>m(v)=4π(c(v)+3c(3v)−5c(5v)+...)

在 CTF 和 MTF 之间转换显然有些麻烦,因为原则上计算中必须包括无限项的谐波。 然而,如上所述,光学系统有一个截止频率,高于该频率,MTF 为零。 因此,在实践中,计算中使用有限项。 此外,空间频率越高,需要的项就越少。

3. ISO2233标准

ISO12233 标准 (ISO, 2014) 描述了两种通过测量空间频率响应 (SFR) 来估计 MTF 的方法。MTF 和 SFR 之间的区别是为了强调这样一个事实,即摄像头的测量频率响应可能会根据测量条件而改变,例如 反映在用于执行测量的测试图表中。 因此,在实践中,不能使用单个 MTF 曲线准确地表征数码相机。 标准中描述了两种 SFR 方法:

  • 基于斜边的 SFR (E-SFR)
  • 基于正弦西门子星形测试图的 SFR (S-SFR)

边缘SFR

上述表明无法直接通过测量摄像头捕获的PSF来测量SFR,那如何有效测量PSF?通过PSF以及LSF的关系,是否可以获取线性扩展函数的积分ESF来反推计算,这里可以采用锐边 目标来解决,比如刀口,而不是点光源。理想的锐边模型就是垂直刀口模型,如下图所示:

该模型可以表示为阶跃函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> u ( t ) u(t) </math>u(t),如下定义:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> Θ ( x ) = { 0 , x ≤ 0 1 , x > 0 \Theta(x) = \begin{cases} 0, & x \le 0 \\ 1, & x > 0 \end{cases} </math>Θ(x)={0,1,x≤0x>0

则理想的锐边与摄像头的PSF的卷积公式如下:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> I edge ( x , y ) = ∫ − ∞ ∞ ∫ − ∞ ∞ Θ ( x ′ ) h ( x − x ′ , y − y ′ )   d x ′ d y ′ = ∫ − ∞ ∞ Θ ( x − x ′ ) ℓ ( x ′ )   d x ′ = ∫ − ∞ x ℓ ( x ′ )   d x ′ = [ e ( x ′ ) ] − ∞ x = e ( x ) \begin{aligned} I_{\text{edge}}(x, y) &= \int_{-\infty}^{\infty} \int_{-\infty}^{\infty} \Theta(x') h(x - x', y - y') \, dx' dy' \\ &= \int_{-\infty}^{\infty} \Theta(x - x') \ell(x') \, dx' \\ &= \int_{-\infty}^{x} \ell(x') \, dx' = \left[ e(x') \right]_{-\infty}^{x} = e(x) \end{aligned} </math>Iedge(x,y)=∫−∞∞∫−∞∞Θ(x′)h(x−x′,y−y′)dx′dy′=∫−∞∞Θ(x−x′)ℓ(x′)dx′=∫−∞xℓ(x′)dx′=[e(x′)]−∞x=e(x)

上述 <math xmlns="http://www.w3.org/1998/Math/MathML"> I e d g e ( x , y ) Iedge(x,y) </math>Iedge(x,y) 为实际摄像系统获取的像素函数,即ESF边沿扩展函数。那LSF线性扩展函数则是ESF边沿扩展函数的导数,如下
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ℓ ( x ) = d e ( x ) d x \ell(x) = \frac{de(x)}{dx} </math>ℓ(x)=dxde(x)

因此,捕获锐利边缘的图像,对边缘的横截面求导数,然后进行傅里叶变换可以获得 SFR。相比MTF计算方式,边缘SFR计算具有如下优势:

  • 简单,通过单次拍摄即可提取全频域信息,无需扫描多个频率的靶标。
  • 高噪声比 ,锐边靶标的黑白对比区域(如金属刀口或印刷靶标)能提供高对比度信号,降低噪声对ESF提取的影响。
  • 兼容性强,适用于各类成像系统(相机、显微镜、望远镜等),无需定制复杂靶标
  • 计算效率高,通过分析单一边缘的ESF,可快速推导出LSF和MTF,无需复杂的多频拟合。

如下图显示了边缘SFR测量方法原理过程:

通过相对于像素阵列倾斜边缘来进一步改善 (Reichenbach 等人,1991;Fischer 和 Holm,1994)。即在几乎垂直于边缘的方向上的稍微偏移扫描,见上图(a)。通过扫描图像中每一行的边缘剖面,然后根据相对于其他边缘剖面的位置偏移对剖面进行位移,然后将所得剖面相互叠加,获得过采样边缘剖面。通过这种方式,可以对 SFR 进行更准确的评估。 在 ISO12233 标准(ISO,2014)中,由此获得的边缘轮廓样本在 1/4 像素宽的 bin 内进行平均。 因此,完成了四倍过采样(Williams,1998;Burns,2000;Williams 和 Burns,2001;Burns 和 Williams,2002;Williams,2003)。

综上所述,ISO 12233中计算边缘SFR的算法如下:

  1. 对于边缘图像中的每一行,估计边缘的位置。
  2. 对于边缘位置,使用线性回归估计边缘方向和位置。
  3. 根据通过计算其在上一步获得的直线上的位置找到的位置对每条直线进行置换。
  4. 将每条位移线放在一起,形成一个过采样的边缘轮廓。
  5. 大小为 1/4 像素的 bin 内的平均边缘值,并在生成的网格上重新采样边缘轮廓。
  6. 计算超采样边缘的导数,通过边缘函数与有限差分滤波器的卷积得到线扩散函数。
  7. 计算LSF的离散傅里叶变换。SFR是傅里叶变换的绝对值。

因此,SFR不需要拍摄不同的空间频率下的线对。它只需要一个黑白的斜边(刀口)即可换算出约略相等于所有空间频率下的MTF。

正弦调制西门子星SFR

作为测量边缘 SFR 的一种替代方法,可以直接测量测试图表中的调制,该测试图表具有不同空间频率的正弦调制星爆图案。 进行这种测量的一种优雅方法是使用正弦调制的西门子星形图案(Loebich 等人,2007 年),如下图所示。这种方法 (S-SFR) 在 ISO 12233 中也被描述为测量 SFR 的替代方法。 与倾斜边缘 SFR (E-SFR) 相比,在某些情况下,使用正弦变化特征而不是锐利边缘可以获得不同的结果。这可能是由于用于颜色插值、降噪和 锐化等可能会以不同的方式处理这些特征而不同。

与采用正弦或条形图案特征的其他图表相比,使用这种特殊的星爆图案测试图表,可以在图像的较小部分内适应广泛的空间频率 。 此外,还可以测量不同方向的SFR,从而自然地能够评估例如切向和径向方向的SFR

具体的测量算法在此不表述,关键是理解上述的测量方法的意义。

三、计算SFR

1、计算SFR过程

这里以斜边的SFR举例说明,具体的算法过程如下:

具体的计算过程如下:

  1. 获取垂直斜边的ROI
  2. 进行数据的归一化
  3. 计算图像每一行的像素矩心
  4. 对每行的矩心使用最小二乘法进行线性拟合,获得一条关于矩心的直线
  5. 重新定位ROI,获得ESF
  6. 对获得的ESF进行四倍超采样
  7. 通过差分运算获得LSF
  8. 对LSF应用汉明窗
  9. 进行DFT运算

2、详细过程解析

1)获取垂直边缘的ROI

这里水平和垂直的Edge只是为了计算图像在水平方向和垂直方向的解析力,与算法本身无关,因为水平的Edge会被进行90°旋转后作为输入,然后计算SFR的值。具体的边缘检测算法可以参考 三种常用边缘检测算法,主要三种算法:

  • Canny算法是一种经典的边缘检测算法,它在图像中寻找梯度的极大值来检测边缘,并通过非极大值抑制和双阈值处理来提高检测结果的准确性和稳定性。它通常能够较好地检测出图像中的边缘。Canny算法对噪声有较好的抑制效果。

    在实际应用中,可以根据具体场景和要求调整高斯平滑的标准差以及高低阈值的设定,以获得最佳的边缘检测结果。

  • Sobel算法 是一种经典的图像边缘检测算法,用于在图像中检测边缘。它是基于一种离散的差分算子,通过计算图像像素灰度值的梯度来识别边缘。Sobel算子分为水平方向和垂直方向两个部分,分别用Sx和Sy表示。这两个算子都是3x3的矩阵,用于对图像进行卷积运算。但是对噪声比较明敏感。

  • Prewitt算法与Sobel算法类似,都是基于差分算子进行边缘检测。同时,该算法易受到噪声的影响。

2)进行数据的归一化

在Sensor获得图像之后,呈现出来的图像由于要符合人眼的感觉,会对图像像素进行伽马变换,使得其变成非线性的像素数据,从而使图像的显示更加符合人眼的感受。所以,当我们要进行sensor的成像解析力分析时,要先将图像处理成没有经过伽马变换前的。

一般sensor会对raw图像进行一个2.2的gamma变换,若我们想恢复原始图像时,我们只需要进行一个1/2.2的gamma变换即可。

3)计算图像每一行的像素矩心

这一步的操作其实是为了计算出边缘的位置。具体讲来就是,我们会将图片中的每一行像素都计算具体的矩心位置。这样讲可能比较抽象,我们直接来看图吧。
可以看到,其实每一行像素的矩心计算出来的结果,其实就是在黑白分界线的附近。矩心的计算公式如下:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> d t 1 = ∑ ( Img [ i + 1 ] − Img [ i ] ) d t = ∑ [ ( Img [ i + 1 ] − Img [ i ] ) ⋅ i ] \begin{aligned} dt1 &= \sum \left( \text{Img}[i+1] - \text{Img}[i] \right) \\ dt &= \sum \left[ \left( \text{Img}[i+1] - \text{Img}[i] \right) \cdot i \right] \end{aligned} </math>dt1dt=∑(Img[i+1]−Img[i])=∑[(Img[i+1]−Img[i])⋅i]

矩心对应位置:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ∣ s h i f t [ i ] ∣ = d t d t 1 |shift[i]| = \frac{dt}{dt1} </math>∣shift[i]∣=dt1dt

shift[i] 就是对应的第i行的矩心位置。

c 复制代码
/*****************************************************************************/
unsigned short locate_centroids(double *farea, double *temp, 
        double *shifts,
        unsigned short size_x, unsigned short size_y,
        double *offset) {
  unsigned long i, j;
  double dt, dt1, dt2;
​
  /* Compute the first difference on each line. Interpolate to find the 
     centroid of the first derivatives. */
  for (j = 0; j < size_y; j++) {
    dt = 0.0;
    dt1 = 0.0;
    for (i = 0; i < size_x-1; i++) {
      dt2 = farea[(j*(long)size_x)+(i+1)] - farea[(j*(long)size_x)+i]; 
      dt += dt2 * (double)i;
      dt1 += dt2;
    }
    shifts[j]=dt/dt1;
  }
​
  /* check again to be sure we aren't too close to an edge on the corners. 
     If the black to white transition is closer than 2 pixels from either 
     side of the data box, return an error of 5; the calling program will 
     display an error message (the same one as if there were not a difference 
     between the left and right sides of the box ) */
  if (shifts[size_y-1] < 2  || size_x - shifts[size_y-1] < 2) {
    fprintf(stderr,"** WARNING: Edge comes too close to the ROI corners.\n");
    return 5;
  }
//防止矩心过于靠近图像的边界
  if (shifts[0] < 2 || size_x - shifts[0] < 2){
    fprintf(stderr,"** WARNING: Edge comes too close to the ROI corners.\n");
    return 5;
  }
​
  /* Reference rows to the vertical centre of the data box */
  j = size_y/2;
  dt = shifts[j];
  for (i = 0; i < size_y; i++) {
    temp[i] = (double)i - (double)j;
    shifts[i] -= dt;
  }
  *offset = dt;
  return 0;
}
​

4)对每行的矩心使用最小二乘法进行线性拟合,获得一条关于矩心的直线

这个没啥好说的,其实就是根据你计算出来的那么多个矩心的点,然后获取分界边缘的直线表达式。

我们知道用的是最小二乘法就可以了。最小二乘法公式如下:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> B = ∑ ( x [ i ] − ∑ x l e n ) ⋅ y [ i ] ∑ ( x [ i ] − ∑ x l e n ) 2 B = \sum \left( x[i] - \frac{\sum x}{len} \right) \cdot \frac{y[i]}{\sum \left( x[i] - \frac{\sum x}{len} \right)^2} </math>B=∑(x[i]−len∑x)⋅∑(x[i]−len∑x)2y[i]
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> A = ∑ y − ∑ x ⋅ B l e n A = \frac{\sum y - \sum x \cdot B}{len} </math>A=len∑y−∑x⋅B

最后获得的其实就是上面的图中那条红色的线,略微有差距,可是基本上可以说是完全一致的了。

c 复制代码
unsigned short fit(unsigned long ndata, double *x, double *y, double *b, 
       double *a, double *R2, double *avar, double *bvar)
{
  unsigned long i;
  double t,sxoss,syoss,sx=0.0,sy=0.0,st2=0.0;
  double ss,sst,sigdat,chi2,siga,sigb;
 
  *b=0.0;
  for ( i=0; i < ndata; i++ ) {
    sx += x[i];//x的叠加
    sy += y[i];//y的叠加
  }
  //求平均值
  ss=(double)ndata;
  sxoss=sx/ss;   
  syoss=sy/ss;
  for ( i=0; i < ndata; i++ ) {
    t = x[i] - sxoss;  //
    st2 += t*t; //方差
    *b += t * y[i];  
  }
  *b /= st2;         /* slope  *///斜率
  *a =(sy-sx*(*b))/ss; /* intercept *///截距
  siga=sqrt((1.0+sx*sx/(ss*st2))/ss);
  sigb=sqrt(1.0/st2);
  chi2=0.0;
  sst=0.0;
  for (i=0; i < ndata; i++) {
    chi2 += SQR( y[i] - (*a) - (*b) * x[i]); 
    sst += SQR( y[i] - syoss); 
  }
  sigdat=sqrt(chi2/(ndata-2));
  siga *= sigdat;
  sigb *= sigdat;
  *R2 = 1.0 - chi2/sst;//拟合程度
  *avar = siga;
  *bvar = sigb;
  return 0;
}

5)重新定位ROI,获得ESF

转换坐标轴,将坐标轴转换到计算出来的矩心直线上。如图所示:

c 复制代码
  /* Start image at new location, so that same row is center */
  center_row = *nrows/2;
  start_row = center_row - size_y/2;
  farea_old = farea;
  farea = farea + start_row*size_x;
  /* On center row how much shift to get edge centered in row. */
  /* offset = 0.;  Original code effectively used this (no centering)*/
  if(user_angle)
    offset = *off - size_x/2;
  else
    offset = offset1 + 0.5 + offset2  - size_x/2; 
​
  *off = offset;
  if(version & ROUND || version & DER3)
    offset += 0.125;
​
  if (err != 0)     {   /* Slopes are bad.  But send back enough
         data, so a diagnostic image has a chance. */
    *pcnt2 = 2*size_x;  /* Ignore derivative peak */
    return 4; 
  }
​
  /* reference the temp and shifts values to the new y centre */
  /* Instead of using the values in shifts, synthesize new ones based on 
     the best fit line. */
  col = size_y/2;
  for (i=0; i < size_y; i++) {
    shifts[i] = (*slope) * (double)(i-col) + offset;
  }

6)对获得的ESF进行四倍超采样

我们将每一行中X轴坐标相等的像素值累加起来,然后求均值后得到下面第一行的数组。

这里是将整张图片的像素转换为一个长度为rows的数组,然后进行4倍的扩增。然后其他没有像素的地方,是进行一个向前寻找非0的像素值进行替换,这样就获得了ESF。

这里补充下为啥使用四倍超采样

在光学成像系统的性能评估中,SFR(Spatial Frequency Response)ESF(Edge Spread Function) 的测量常采用 四倍超采样(4× Oversampling) ,其核心目的是 提高测量精度,尤其是避免因传感器像素离散化导致的误差。

基本原理:通过倾斜边缘(如ISO 12233标准推荐的 Slanted Edge 方法),使边缘跨越多个像素,再利用插值算法重建更高分辨率的ESF(边缘扩散函数),最终计算LSF(线扩散函数)和SFR。

数学本质

  • 原始图像采样率为1像素(奈奎斯特频率 fN =1/(2p ),p为像素间距)。
  • 四倍超采样后,等效采样间隔为 p/4,分辨率提升4倍。

为什么需要四倍超采样?

(1) 避免像素混叠(Aliasing)

  • 问题 :若边缘完全水平或垂直,ESF只能在整数像素位置采样,导致 欠采样

  • 后果

    • 高频信息丢失(MTF曲线在奈奎斯特频率附近失真)。
    • 无法准确评估系统的真实分辨率。

(2) 提高ESF/LSF的精度

  • 倾斜边缘:边缘与像素网格成小角度(建议2°~5°),使单像素行可覆盖多个亚像素位置的ESF数据。
  • 插值重建:通过多项式拟合或重心法,将ESF采样点密度提升4倍,更精确地计算LSF(微分ESF得到)。
c 复制代码
//进行超采样,生成长度为size_x*ALPHA(4)的当行图像(ESF),保存在AveEdge中
unsigned short bin_to_regular_xgrid(unsigned short alpha,//alpha->指的是超取样的倍数
            double *edgex, double *Signal, 
            double *AveEdge, long *counts,
            unsigned short size_x,
            unsigned short size_y)
{
  long i, j, k,bin_number;
  long bin_len;
  int nzeros;
 
  bin_len = size_x * alpha;  //扩大四倍
 
  for (i=0; i<bin_len; i++) {
    AveEdge[i] = 0;
    counts[i] = 0;
  }
  for (i=0; i<(size_x*(long)size_y); i++) {
    bin_number = (long)floor((double)alpha*edgex[i]);//向下取整
    if (bin_number >= 0) {
      if (bin_number <= (bin_len - 1) ) {
        AveEdge[bin_number] = AveEdge[bin_number] + Signal[i];//把每一行的距离边缘x轴一样远的信号相加
        counts[bin_number] = (counts[bin_number])+1;//记录下对应位置有多少个信号相加
      }
    }
  }
 
  nzeros = 0;
  for (i=0; i<bin_len; i++) {
    j = 0;
    k = 1;
    //感觉写的有点复杂
    if (counts[i] == 0) {
      nzeros++; //记录有多少个位置是空的,即没有信号
      //K的作用:因为这里的信号为0,找到后面第一个不为零的值,赋给当前这个零
      if (i == 0) {
        while (!j) { //当j==0时,表示此处的信号为0
          if (counts[i+k] != 0) {//第一行的元素
      AveEdge[i] = AveEdge[i+k]/((double) counts[i+k]);
            j = 1;//充当flag。。。为啥不用布尔类型
    }
          else k++;//直到找到第一个不为零的数字才会停止
  }
      } else {
        while (!j && ((i-k) >= 0) ) { //j==0&&i-k>=0  j==0说明 counts[i]是0  i-k>0说明  k在i前面,找前面不为零的数值赋给AveEdge[i]
          if ( counts[i-k] != 0) {
      AveEdge[i] = AveEdge[i-k];   /* Don't divide by counts since it already happened in previous iteration */
      j = 1;
    } 
    else k++;
  }
        if ( (i-k) < 0 ) {//k>i,其实联合上面那段,就是:此处的counts[i]累加次数为零,所以AveEdge[i]也就是0,所以要找到附近一个不为零的值赋给AveEdge[i]
    k = 1;
    while (!j) {
      if (counts[i+k] != 0) {
        AveEdge[i] = AveEdge[i+k]/((double) counts[i+k]);
        j = 1;
      } 
      else k++;
    }
  }
      }
    } 
    else 
      AveEdge[i] = (AveEdge[i])/ ((double) counts[i]);//如果此处不为零,直接就求个平均值
  }
 
  if (nzeros > 0) {//提示信息
    fprintf(stderr, "\nWARNING: %d Zero counts found during projection binning.\n", nzeros);
    fprintf(stderr, "The edge angle may be large, or you may need more lines of data.\n\n");
  }
  return nzeros;
}

7)通过差分运算获得LSF

这不用多说,就是得到的ESF数组元素进行差分,获得LSF,即进行微分运算。以下是函数公式:
<math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> d t = ∑ ( A v e [ i + 1 ] − A v e [ i ] ) ⋅ i d t 1 = ∑ ( A v e [ i + 1 ] − A v e [ i ] ) c e n t r o i d = ∑ ( A v e [ i + 1 ] − A v e [ i ] ) ⋅ i ∑ ( A v e [ i + 1 ] − A v e [ i ] ) ⋅ i L S F [ i ] = A v e [ i + 1 ] − A v e [ i ] ( 差分后返回数组 LSF ) \begin{aligned} dt &= \sum \left( Ave[i+1] - Ave[i] \right) \cdot i \\ dt1 &= \sum \left( Ave[i+1] - Ave[i] \right) \\ centroid &= \sum \left( Ave[i+1] - Ave[i] \right) \cdot \frac{i}{\sum \left( Ave[i+1] - Ave[i] \right)} \cdot i \\ LSF[i] &= Ave[i+1] - Ave[i] \quad (\text{差分后返回数组 LSF}) \end{aligned} </math>dtdt1centroidLSF[i]=∑(Ave[i+1]−Ave[i])⋅i=∑(Ave[i+1]−Ave[i])=∑(Ave[i+1]−Ave[i])⋅∑(Ave[i+1]−Ave[i])i⋅i=Ave[i+1]−Ave[i](差分后返回数组 LSF)

补充:这里使用的是Centroid 差分运算,Centroid 差分运算 是一种高精度数值微分方法,用于减少噪声影响并提高导数的计算准确性。

Centroid(质心)差分是一种 加权中心差分,通过计算局部数据的质心偏移来估计导数,相比普通中心差分具有更强的抗噪能力。其核心步骤:

  1. 对 ESF 数据进行局部拟合(通常用多项式或高斯函数)。
  2. 计算拟合曲线的质心位置(一阶矩)。
  3. 用质心偏移量近似导数,得到 LSF。
c 复制代码
void calculate_derivative(unsigned int len, double *AveTmp, double *AveEdge,
        double *centroid,int separation)
{
  unsigned long i;
  double dt, dt1;
  dt = 0.0;
  dt1 = 0.0;
  for (i=0; i< len; i++) 
    AveTmp[i] = AveEdge[i];
​
  for (i=1; i< len-separation; i++) {
    /* Not wasting time with division by 2 since constant factors don't 
       change SFR computation */
    AveEdge[i] = (AveTmp[i+separation]-AveTmp[i-1]);  
    if (separation == 1)
      AveEdge[i] /= 2.0;
    dt += AveEdge[i] * (double)i;
    dt1 += AveEdge[i];
  }
  *centroid = dt/dt1;
  AveEdge[0] = AveEdge[1];
  if (separation == 1) AveEdge[len-1] = AveEdge[len-2];
}

8)对LSF应用汉明窗

对上面的LSF数组进行汉明窗处理,这一步主要也是看公式,如下:

<math xmlns="http://www.w3.org/1998/Math/MathML"> s f r c = 0.54 − 0.46 ⋅ cos ⁡ ( π ⋅ r o w s ⋅ 2 r o w s ⋅ 2 ) sfrc = 0.54 - 0.46 \cdot \cos\left( \frac{\pi \cdot rows \cdot 2}{rows \cdot 2} \right) </math>sfrc=0.54−0.46⋅cos(rows⋅2π⋅rows⋅2) (注:这是汉宁窗的系数,0.540.46 是固定参数) <math xmlns="http://www.w3.org/1998/Math/MathML"> A v e E d g e [ i ] = L S F [ i ] ⋅ s f r c AveEdge[i] = LSF[i] \cdot sfrc </math>AveEdge[i]=LSF[i]⋅sfrc

在计算SFR(空间频率响应)时,对线扩散函数(LSF)进行傅里叶变换前加汉明窗(Hamming Window) 的目的是为了减少频谱泄漏(Spectral Leakage),从而提高频率分析的准确性。

c 复制代码
void apply_hamming_window(  unsigned short alpha,
                            unsigned int oldlen,  //size_x*4
                          unsigned short newxwidth, //size_x
        double *AveEdge, long *pcnt2)
{
  long i,j,k, begin, end, edge_offset;
  double sfrc;
 
  /* Shift the AvgEdge[i] vector to centre the lsf in the transform window */
  // 将Avgedge移到中心位置, 两边由于移动造成的空位由0补齐
  edge_offset = (*pcnt2) - (oldlen/2);//不能理解为什么一定要反着计算,pcnt2肯定是小于oldlen/2吧。。
  if (edge_offset != 0) {                                                   //cer=6
    if (edge_offset < 0 ) {  //这里根据分析的话,应该edge_offset只会小于0啊。。     ↓
      for (i=oldlen-1; i > -edge_offset-1; i--)            //   [l l l l l l l max l l l l l]
                  AveEdge[i] = (AveEdge[i+edge_offset]);   //    ↑     ↑        ↑
      for (i=0; i < -edge_offset; i++)                     //   left center=3 right
                  AveEdge[i] = 0.00; /* last operation */    //offset = center-cer=-3
    } else {                                               //            cer=6
                                                           //              ↓
      for (i=0; i < oldlen-edge_offset; i++)               // [0 0 0 l l l l l l l max l l]
                  AveEdge[i] = (AveEdge[i+edge_offset]);   //  ↑           ↑        ↑
      for (i=oldlen-edge_offset; i < oldlen; i++)          //            center=3
      AveEdge[i] = 0.00;
    }
  }
  /* Multiply the LSF data by a Hamming window of width NEWXWIDTH*alpha */
  //将begin和end两侧的值用0填充,但是感觉没啥用
  begin = (oldlen/2)-(newxwidth*alpha/2);//上下看来,begin只会等于0,因为oldlen=bin_len, bin_len=size_x*alpha == newxwidth*alpha
  if (begin < 0) begin = 0;
  end = (oldlen/2)+(newxwidth*alpha/2);
  if (end > oldlen )  end = oldlen;
  for (i=0; i< begin; i++) 
    AveEdge[i] = 0.0;
  for (i=end; i< oldlen; i++) 
    AveEdge[i] = 0.0;
 
  // 给begin和end之间的数据加上汉明窗
  // 汉明窗 W(n,α ) = (1 -α ) - α cos(2*PI*n/(N-1)) ,(0≤n≤N-1)
  // 一般情况下,α取0.46
  // 下面计算方法等于窗发生了平移(故符号发生了变化), 结果是一样的
  for (i=begin,j = -newxwidth*alpha/2; i < end; i++,j++) {
    sfrc = 0.54 + 0.46*cos( (M_PI*(double)j)/(newxwidth*alpha/2) );
    AveEdge[i] = (AveEdge[i])*sfrc; 
  }
  //将lsfbegin的位置左移到index0的位置
  //但在代码中应该是不会起作用的,
  if (begin != 0) /* Shift LSF to begin at index 0 (rather than begin) */
    for (k=0, i=begin; k<newxwidth*alpha; i++,k++) 
      AveEdge[k] = AveEdge[i];
}

9)进行DFT运算

这里实际就是离散FFT计算LSF来获取SFR。

c 复制代码
unsigned short ftwos(long number, double dx, double *lsf, 
         long ns, double ds, double *sfr)
{
  double a, b, twopi, g;
  long i,j;
 
    //                n-1              k
    // DFT ==> X[k] = Σ  x[n]e^(-j2π - n)
    //                n=0              N
 
  twopi = 2.0 * M_PI;
  for (j = 0; j < ns; j++){//ns=1/2*bin_len  前一半
    g = twopi * dx * ds * (double)j;
    for (i = 0, a = 0, b = 0; i < number; i++) { 
      a += lsf[i] * cos(g * (double)(i));
      b += lsf[i] * sin(g * (double)(i)); 
    }
    sfr[j] = sqrt(a * a + b * b); 
  }
  return 0;
}

3. ISO 12233:1999标准实现

具体代码可见github.com/RayXie29/SF...。其核心的SFR计算实现如下:

c 复制代码
int SFRCalculation(cv::Mat &ROI, double gamma)
{
  if (ROI.empty())
  {
    std::cerr << "Open the ROI image error" << std::endl;
    return 0;
  }
​
  int height = ROI.rows, width = ROI.cols;
  //Do the gamma decoding to eliminate the gamma encoded by camera device 
  // 伽马校正解码,消除相机成像管线中的非线性伽马编码,恢复线性光强响应
  de_Gamma(ROI, gamma);
  int i, j;
​
  double slope = 0, intercept = 0;
  
  //Center centroid offset
  double CCoffset = 0;
  std::vector<double> y_shifts(height);
  
  ////Calculate the shifts between Centroids and Centroid of image center 
  /* 质心定位,通过计算每行像素的质心偏移,确定倾斜边缘的位置
  1. 逐行扫描倾斜边缘区域
  2. 计算每行的质心位置(边缘中心)
  3. 记录质心相对于图像中心的偏移量(y_shifts)
  4. 计算整体中心偏移量(CCoffset)
  */
  std::vector<double> Cen_Shifts = CentroidFind(ROI, y_shifts, &CCoffset);
  if (Cen_Shifts.empty()) { 
    std::cout << "center shift empty!!!" << std::endl;
    return 0; 
  }
​
  //simple linear regression for slanted edge fitting
  // 边缘角度拟合,简单线性回归(Simple Linear Regression)拟合质心偏移与行号的斜率
  SLR(Cen_Shifts, y_shifts, &intercept, &slope);
​
  //Truncate the number of rows of data to largest slope cycle which will have an integer number of full phase rotations
  // 数据截断优化,根据斜率计算最大完整周期数,消除相位不连续带来的频谱泄漏
  ReduceRows(slope, &height);
​
  //update the CCoffset to the offset between original mid point of image and reference mid point we calculated
  CCoffset = CCoffset + 0.5 + intercept - width / 2;
​
  //Mapping the pixel value of original image into a sampling data which the length is 4 times of original image width
  //This step is for concentrating the amount of change of original pixel values
  int SamplingLen = width * 4;
  /* 过采样处理,提高边缘定位精度至亚像素级,流程:
  1. 沿倾斜边缘投影像素值
  2. 使用双线性插值生成4倍超采样数据
  */
  std::vector<double> OverSamplingData = OverSampling(ROI, slope, CCoffset, height, width, &SamplingLen);
​
  //Using hamming window to filter the ripple signal of two side of data
  // 加窗滤波,应用汉明窗抑制频谱泄漏,减少FFT后的旁瓣震荡,提升MTF曲线的平滑度
  OverSamplingData = HammingWindows(OverSamplingData, SamplingLen);
​
  //decrete four transform
  // 离散傅里叶变换,实现DFT计算,生成频域响应
  DFT(OverSamplingData, SamplingLen);
  width = int(SamplingLen / 4);
  double maxData = 0;
  for (i = 0; i < SamplingLen; ++i)
  {
    if (OverSamplingData[i] != 0)
    {
      maxData = OverSamplingData[i];
      break;
    }
  }
​
  //将归一化后的频响数据写入CSV文件,横轴为归一化空间频率(0~Nyquist频率),纵轴为对比度保留比例
  for (int i = 0; i < SamplingLen; ++i) { OverSamplingData[i] /= maxData; }
  std::fstream mtf_file("./ref/mtf.csv", std::ios::out);
  
  for (i = 0; i <= width; ++i)
  {
    double frequency = (double)i / width;
    mtf_file << frequency << "," << OverSamplingData[i] << "\n";
  }
​
  mtf_file.close();
  
  return 1;
}

示例演示图像,包括原始raw图、截取图以及roi区域图,如下:

经过SFR计算后拟合曲线图如下所示:

4. ISO 12233 2017版

如下图所示是基于边界的SFR算法所用的Test Chart(ISO 12233 2017版):

具体的算法流程图如下:

具体如下:

  1. 选取ROI区域

    ROI区域的选取如下图红色边框区域所示,选择的ROI区域由黑白两部分组成,中间为分界线。分界线与的ROI区域的上下边框交接点距离左右边框不得小于两个像素(其实就是所选取的ROI区域中的分界线不能太过倾斜),此外,ROI区域亮度调制比不得小于20%(其实就是黑白对比明显)。

  2. 进行数据归一化

    数据归一化处理通过逆光电转换函数(OECF)/逆Gamma校正对非线性的图像编码值(若为8 位图像,即灰度值0 − 255)进行逆向线性化处理,将其还原为一个类CCD原始光电采样信号的序列。
    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> φ ( p , r ) = a ⋅ O E C F [ D N red ] + b ⋅ O E C F [ D N green ] + c ⋅ O E C F [ D N blue ] \varphi(p,r) = a \cdot OECF[DN_{\text{red}}] + b \cdot OECF[DN_{\text{green}}] + c \cdot OECF[DN_{\text{blue}}] </math>φ(p,r)=a⋅OECF[DNred]+b⋅OECF[DNgreen]+c⋅OECF[DNblue]

    其中 <math xmlns="http://www.w3.org/1998/Math/MathML"> D N red , D N green , D N blue DN_{\text{red}},\ DN_{\text{green}},\ DN_{\text{blue}} </math>DNred, DNgreen, DNblue分别为各个通道的数字输出, <math xmlns="http://www.w3.org/1998/Math/MathML"> φ ( p , r ) φ(p,r) </math>φ(p,r) 为CCD上第r行,第 <math xmlns="http://www.w3.org/1998/Math/MathML"> p p </math>p列原始光电采样信号, <math xmlns="http://www.w3.org/1998/Math/MathML"> a a </math>a, <math xmlns="http://www.w3.org/1998/Math/MathML"> b b </math>b, <math xmlns="http://www.w3.org/1998/Math/MathML"> c c </math>c为各个通道的加权值

  3. 计算每一行的矩心

    在ISO 12233中通过如下公式进行计算:
    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> C ( r ) = ∑ p = 1 P − 1 p ⋅ [ ϕ ( p + 1 , r ) − ϕ ( p , r ) ] ∑ p = 1 P − 1 [ ϕ ( p + 1 , r ) − ϕ ( p , r ) ] − 0.5 C(r) = \frac{\sum_{p=1}^{P-1} p \cdot \left[ \phi(p+1, r) - \phi(p, r) \right]}{\sum_{p=1}^{P-1} \left[ \phi(p+1, r) - \phi(p, r) \right]} - 0.5 </math>C(r)=∑p=1P−1[ϕ(p+1,r)−ϕ(p,r)]∑p=1P−1p⋅[ϕ(p+1,r)−ϕ(p,r)]−0.5

    其中, <math xmlns="http://www.w3.org/1998/Math/MathML"> P P </math>P为ROI区域的总列数, <math xmlns="http://www.w3.org/1998/Math/MathML"> C ( r ) C(r) </math>C(r) 为第 <math xmlns="http://www.w3.org/1998/Math/MathML"> r r </math>r行的矩心位置

  4. 对矩心进行直线拟合

    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> m = ⌊ Δ r Δ C ( r ) ⌋ m = \left\lfloor \frac{\Delta r}{\Delta C(r)} \right\rfloor </math>m=⌊ΔC(r)Δr⌋

    这个公式的意思就是求相邻两行之间的矩心的斜率,然后对所有斜率求均值m,最后获得函数:
    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> S ( r ) = R 2 − r m S(r) = \frac{\frac{R}{2} - r}{m} </math>S(r)=m2R−r

    其中 <math xmlns="http://www.w3.org/1998/Math/MathML"> R R </math>R是总ROI区域的总行数, <math xmlns="http://www.w3.org/1998/Math/MathML"> S ( r ) S(r) </math>S(r)并不是拟合的直线,但是在后面和其他公式结合起来之后起到了相当于矩心直线的作用。

  5. 获得边缘扩散函数ESF

    在ISO 12233中公式如下:
    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> ESF ⁡ ′ ( j ) = ∑ r = 1 R ∑ p = 1 P ϕ ( p , r ) ⋅ α ( p , r , j ) ∑ r = 1 R ∑ p = 1 P α ( p , r , j ) \operatorname{ESF}'(j) = \frac{\sum_{r=1}^{R} \sum_{p=1}^{P} \phi(p, r) \cdot \alpha(p, r, j)}{\sum_{r=1}^{R} \sum_{p=1}^{P} \alpha(p, r, j)} </math>ESF′(j)=∑r=1R∑p=1Pα(p,r,j)∑r=1R∑p=1Pϕ(p,r)⋅α(p,r,j)

    其中函数 <math xmlns="http://www.w3.org/1998/Math/MathML"> α ( p , r , j ) α(p,r, j) </math>α(p,r,j) 作用是将上述ROI光电采集信号归并到边缘扩散函数ESF中,其定义如下
    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> α ( p , r , j ) = { 1 , − 0.125 ≤ [ p − S ( r ) − j ] < 0.125 0 , otherwise \alpha(p, r, j) = \begin{cases} 1, & -0.125 \leq [p - S(r) - j] < 0.125 \\ 0, & \text{otherwise} \end{cases} </math>α(p,r,j)={1,0,−0.125≤[p−S(r)−j]<0.125otherwise

    其中 <math xmlns="http://www.w3.org/1998/Math/MathML"> j j </math>j为整数,其实也就是边缘扩散函数的下标

  6. 四倍超采样

    在获得边缘扩散函数的过程中采用的离散距离单位缩小为0.25个像素,我们将边缘扩散函数的横坐标乘以4倍,然后将没有函数值的横坐标位置,向前寻找非零的函数值进行替换。这样将离散距离单位归为1,但它代表的还0.25个像素,而这也就是我们说的四倍倍超采样。

  7. 生成线性扩散序列LSF

    在ISO 12233中公式如下:
    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> LSF ⁡ W ′ ( j ) = W ( j ) ESF ⁡ ′ ( j + 1 ) − ESF ⁡ ′ ( j − 1 ) 2 , for j = 2 , ... , N − 1 \operatorname{LSF}'_W(j) = W(j) \frac{\operatorname{ESF}'(j+1) - \operatorname{ESF}'(j-1)}{2}, \quad \text{for } j = 2, \dots, N-1 </math>LSFW′(j)=W(j)2ESF′(j+1)−ESF′(j−1),for j=2,...,N−1

    其中
    <math xmlns="http://www.w3.org/1998/Math/MathML" display="block"> W ( j ) = 0.54 + 0.46 cos ⁡ [ 2 π ( j − 2 X ) 4 X ] W(j) = 0.54 + 0.46 \cos\left[ \frac{2\pi(j - 2X)}{4X} \right] </math>W(j)=0.54+0.46cos[4X2π(j−2X)]

    其实线性扩散序列LSF就是边缘扩散函数ESF均值滤波后再进行微分,在此基础上通过汉明窗滤波去除噪声。

  8. 归一化离散傅里叶变换

    在ISO 12233中公式如下:

    <math xmlns="http://www.w3.org/1998/Math/MathML"> e − SFR ⁡ ( k ) = D ( k ) ∣ ∑ j = 1 N LSF ⁡ W ′ ( j ) e − i 2 π k j / N ∑ j = 1 N LSF ⁡ W ′ ( j ) ∣ , for k = 0 , 1 , 2 , ... , N / 2 , or ( N + 1 ) / 2 if N is odd e-\operatorname{SFR}(k) = D(k) \left| \frac{\sum_{j=1}^{N} \operatorname{LSF}'W(j) e^{-i 2\pi k j / N}}{\sum{j=1}^{N} \operatorname{LSF}'_W(j)} \right|, \quad \text{for } k = 0, 1, 2, \dots, N/2, \text{ or } (N+1)/2 \text{ if } N \text{ is odd} </math>e−SFR(k)=D(k) ∑j=1NLSFW′(j)∑j=1NLSFW′(j)e−i2πkj/N ,for k=0,1,2,...,N/2, or (N+1)/2 if N is odd

    即对上述线性扩散序列应用归一化离散傅里叶变换即可过得最后的结果。

5. ISO 12233:2023标准

ISO 12233:2023标准中引入的新的基于边缘的空间频率响应(e-SFR)特征,即斜星,标志着该标准的重大变化。与之前使用的斜方形相比,这一特征提供了四个额外的边缘方向,使得除了从垂直和水平边缘得到的SFR之外,还能够测量矢状和切向的空间频率响应(SFR)。

如下图分别为(从左到右)2017版斜方形和2023版四周期斜星形,如下图所示:

四、参考资料

  1. 光学设计漫谈-调制传递函数MTF
  2. 调制传递函数(MTF)简介
  3. 清晰度和分辨率:调制传递函数 MTF 及相关概念、ISO12233测量方法
  4. SFR算法详解(三)------浅析关于SFR的一些物理意义及原理
  5. SFR 原理分析 代码
  6. 相机光学传递函数MTF
  7. 图像解析力算法---SFR(Spatial Frequency Response)概念理解
  8. 镜头光学指标介绍----清晰度SFR/MTF
  9. 图像解析力算法---SFR(Spatial Frequency Response)原理分析(一)
  10. 图像解析力算法---SFR(Spatial Frequency Response)原理分析(二)
  11. SFR解析算法 - SFR_Calculation (C语言)
  12. 图像传感器与信号处理------SFR算法/ISO 12233解读
  13. 图像分辨率测试ISO12233 - 2017中文翻译
  14. imatest关于ISO 12233:2023标准中eSFR斜星检测与分析方法
相关推荐
树下水月1 小时前
PHP 一种改良版的雪花算法
算法·php·dreamweaver
一只数据集2 小时前
全尺寸人形机器人灵巧手力觉触觉数据集-2908条ROSbag数据覆盖14大应用场景深度解析
大数据·人工智能·算法·机器人
罗西的思考3 小时前
【GUI-Agent】阿里通义MAI-UI 代码阅读(2)--- 实现
人工智能·算法·机器学习
刀法如飞4 小时前
TypeScript 数组去重的 20 种实现方式,哪一种你还不知道?
前端·javascript·算法
sali-tec5 小时前
C# 基于OpenCv的视觉工作流-章66-直线夹角
图像处理·人工智能·opencv·算法·计算机视觉
AC赳赳老秦5 小时前
接口测试自动化:用 OpenClaw 对接 Postman,实现批量回归测试、测试报告自动生成与推送
java·人工智能·python·算法·elasticsearch·deepseek·openclaw
_风满楼5 小时前
TDD实战-会议室冲突检测的红绿重构循环
前端·javascript·算法
pq2176 小时前
java实现遗传算法
算法
木井巳6 小时前
【递归算法】单词搜索
java·算法·leetcode·决策树·深度优先