数字图像处理-基础
还是按照以往的阅读方式,先分析整章的框架,再逐点分析。本章从人类视觉系统剖析;光、电磁波的成像特点;数字图像的生成;图像基本操作;像素间基本关系以及本书的数学工具几方面进行展开。即图像的来源、获取方式与处理方式的介绍。
根据光线沿直线传播的原理以及凸透镜折射原理,物体对光的反射通过晶状体在视网膜上呈现倒立缩小的图像。人眼的光感受器有两类:锥状体和杆状体。锥状体对颜色敏感;杆状体没有色彩感觉,对低照明度敏感。
彩色光源的质量有三种:发光强度,光通量和亮度。发光强度是从光源流出的能量总量。光通量用流明数(lm)来度量,用于表示观察者从光源感受到的能量。亮度是光感知的主观描绘方式,无法用于度量。通常场景的明暗程度多采用光通量来评估。
没有颜色的光称为单色光或无色光。单色光的唯一属性是它的强度或大小。因为感知单色光的强度从黑色到灰色变化,最后到白色,灰度级一词通常用来表示单色光的强度。
同样,电磁波图像的获取也是通过评估其能量强度实现的。
其本质是通过传感器获取能量值将其转化为电压,从而实现量化效果,为图像构建提供数据。
传感器获取方式有:
采样可以理解为将图像沿某一与坐标轴平行的数据等比例分割成若干份,而将每一份样本的幅值映射成数值的形式可以理解为量化。采样与量化后的数据更便于存储,
下图为提取图片第800行和第500列的像素值的变化情况。不难发现,以列提取,天空的渐亮而像素值也逐渐变高,在灯带处到达顶峰;地面突然变漆黑,像素值呈现断崖式下降。
贴一波代码:
image = Image.open("img/image.jpg") # 替换为你的图像文件路径
image_gray = image.convert("L") # 转换为灰度图像
row_index = 800 # 要获取的行索引
column_index = 500 # 要获取的列索引
image_array = np.array(image)
image_array[row_index, :] = (255,0,0) #将行高亮为红色
image_array[: ,column_index] = (255,0,0) #将列高亮为红色
new_image = Image.fromarray(image_array)
row_pixels = list(image_gray.getdata())[row_index * image_gray.width : (row_index + 1) * image_gray.width]
column_pixels = [image_gray.getpixel((column_index, y)) for y in range(image_gray.height)]
令 f ( s , t ) f(s,t) f(s,t)表示一幅具有两个连续变量 s s s和 t t t的连续图像函数,通过取样和量化,可以把该函数转换成数字图像。假设把这幅连续图像取样为一个二维阵列 f ( x , y ) f(x,y) f(x,y),该阵列包含有 M M M行和 N N N列,其中 ( x , y ) (x,y) (x,y)是离散坐标。通常图像在任何坐标 ( x , y ) (x,y) (x,y)处的值记为 f ( x , y ) f(x,y) f(x,y),其中 x x x和 y y y都是整数。由一幅图像的坐标张成的实平面部分称为空间域, x x x和 y y y称为空间变量或空间坐标。
数值阵列通常用于处理和算法开发。以公式形式,可将一个 M × N M×N M×N的数值阵列表示为:
f ( x , y ) = [ f ( 0 , 0 ) f ( 0 , 1 ) … f ( 0 , N − 1 ) f ( 1 , 0 ) f ( 1 , 1 ) … f ( 1 , N − 1 ) … … f ( M − 1 , 0 ) f ( M − 1 , 1 ) … f ( M − 1 , N − 1 ) ] f(x, y)=\left[\begin{array}{lclc} f(0,0) & f(0,1) & \ldots & f(0, N-1) \\ f(1,0) & f(1,1) & \ldots & f(1, N-1) \\ & \ldots \ldots & & \\ f(M-1,0) & f(M-1,1) & \ldots & f(M-1, N-1) \end{array}\right] f(x,y)= f(0,0)f(1,0)f(M−1,0)f(0,1)f(1,1)……f(M−1,1)………f(0,N−1)f(1,N−1)f(M−1,N−1)
根据公式中的坐标可以很清楚的了解到数字图像的原点位于左上角,其中正x轴向下延伸,正y轴向右延伸。这样表示的好处基于:许多图像显示(譬如电视显示屏)扫描都是从左上角开始的,然后依次向下移动一行。更重要的事实是矩阵的第一个元素按照惯例应在阵列的左上角,这种表示方式使用的是我们熟悉的标准右手笛卡尔坐标系统。
同样采用上图,将其每个坐标的像素值大小定义为z轴的值(此处采用red通道的值),进而构建三维图像如下:
贴个代码:
image = o3d.io.read_image("img/image.jpg")
pixels = np.asarray(image)
point_cloud = o3d.geometry.PointCloud()
height, width, _ = pixels.shape
points = []
for i in range(height):
for j in range(width):
points.append([i, j, pixels[i, j, 0]]) # 使用R通道的像素值作为高度
point_cloud.points = o3d.utility.Vector3dVector(points)
o3d.visualization.draw_geometries([point_cloud])
量化时每个像素的值会定义为灰度级数跨越范围 [ 0 , L − 1 ] [0,L-1] [0,L−1]中的整数。而灰度跨越的值域有一个非正式的别名:动态范围。动态范围定义为系统中最大可度量灰度与最小可检测灰度之比。上限取决于饱和度,下限取决于噪声。一幅图像中最高和最低灰度级间的灰度差定义为对比度。
出于存储和量化硬件的考虑,灰度级数通常会采用2的整数幂,即 L = 2 k L=2^{k} L=2k,这也意味着每个像素需要占用 k k kbit的存储。一张图像需要占用 M × N × k M×N×k M×N×kbit的容量。拥有 L = 2 k L=2^{k} L=2k灰度级的图像也被称为“K比特图像”。
空间分辨率与灰度分辨率,个人理解是采样与量化的产物。
空间分辨率是图像中可辨别的最小细节的度量。而图像分辨率是单位距离内可分辨的最大线对数量。线对数量是指在单位距离内能够分辨的最大特征线对数目。每个线对由一条明线和一条暗线组成,它们之间有明显的对比。线对数量越高,表示图像能够显示更多的细节和较小的特征。这与图像分辨率有关,因为较高的图像分辨率意味着图像中有更多的像素,可以更好地捕捉和表示细节。
灰度分辨率是指在灰度级中可分辨的最小变化。灰度中可分辨的真实变化不仅受噪声和饱和度的影响,也受人类感知能力的影响。
最近邻算法通过计算新图坐标点 ( X d s t , Y d s t ) (X_{dst},Y_{dst}) (Xdst,Ydst)在原图中对应坐标 ( X s r c , Y s r c ) (X_{src},Y_{src}) (Xsrc,Ysrc),从而获取新坐标点对应的像素值。
X s r c = X d s t × ( W i d t h s r c W i d t h d s t ) , Y s r c = Y d s t × ( H e i g h t s r c H e i g h t d s t ) X_{src} = X_{dst}\times(\frac{Width_{src}}{Width_{dst}}),\\ Y_{src} = Y_{dst}\times(\frac{Height_{src}}{Height_{dst}}) Xsrc=Xdst×(WidthdstWidthsrc),Ysrc=Ydst×(HeightdstHeightsrc)
将图像放大1.5倍效果如下:
新图中目标位置为 ( x , y ) (x, y) (x,y),四个邻近像素为 ( x 1 , y 1 ) (x_1, y_1) (x1,y1), ( x 1 , y 2 ) (x_1, y_2) (x1,y2), ( x 2 , y 1 ) (x_2, y_1) (x2,y1)和 ( x 2 , y 2 ) (x_2, y_2) (x2,y2),对应的像素值为 p 11 p_{11} p11, p 12 p_{12} p12, p 21 p_{21} p21和 p 22 p_{22} p22。目标位置与邻近像素的关系如下:
(x1, y1) ------Q1------ (x1, y2)
| | |
| (x, y) |
| | |
(x2, y1) ------Q2------ (x2, y2)
首先设定 ( x , y ) (x,y) (x,y)在该区域水平和垂直方向上的相对位置 α \alpha α与 β \beta β:
α = y − y 1 y 2 − y 1 , β = x − x 1 x 2 − x 1 , \alpha= \frac{y-y_{1}}{y_{2}-y_{1}},\\\beta=\frac{x-x_{1}}{x_{2}-x_{1}}, \\ α=y2−y1y−y1,β=x2−x1x−x1,
然后计算 Q 1 Q_{1} Q1与 Q 2 Q_{2} Q2的像素值:
Q 1 = α p 11 + ( 1 − α ) p 12 , Q 2 = α p 21 + ( 1 − α ) p 22 Q_{1}=\alpha p_{11}+(1-\alpha)p_{12}, \\Q_{2}=\alpha p_{21}+(1-\alpha)p_{22} Q1=αp11+(1−α)p12,Q2=αp21+(1−α)p22
有了 Q 1 Q_{1} Q1与 Q 2 Q_{2} Q2的像素值后就可以计算目标点的像素值:
f ( x , y ) = β Q 1 + ( 1 − β ) Q 2 f(x,y)=\beta Q_{1}+(1-\beta) Q_{2} f(x,y)=βQ1+(1−β)Q2
整理得计算公式如下:
f ( x , y ) = α β p 11 + α ( 1 − β ) p 12 + ( 1 − α ) β p 21 + ( 1 − α ) ( 1 − β ) p 22 f(x, y) = \alpha\beta p_{11} + \alpha(1 - \beta)p_{12} + (1 - \alpha)\beta p_{21} + (1 - \alpha)(1 - \beta)p_{22} f(x,y)=αβp11+α(1−β)p12+(1−α)βp21+(1−α)(1−β)p22
当邻近像素没有值的时候,例如在边缘情况下,通常会采用边界处理策略来确定缺失像素的值。常见的边界处理策略有以下几种:
该方法通过计算16个最近邻点的加权值进而计算当前点的像素值,其公式如下:
f ( x , y ) = ∑ i = 0 3 ∑ j = 0 3 α i j x i y j f(x, y) = \sum_{i=0}^{3}\sum_{j=0}^{3}\alpha_{ij}x^{i}y^{j} f(x,y)=i=0∑3j=0∑3αijxiyj
详细过程可以看这篇论文:Cubic convolution interpolation for digital image processing
简单观察会发现双三次内插与双线性内插效果似乎一致,实际上像素值会有一些不同,而且双线性插值其实是三次内插的特例形式。
符号: 坐标:
[[a,b,c], [[(x-1,y-1),(x-1,y),(x-1,y+1)],
[d,e,f], [ (x,y-1) , (x,y) , (x,y+1) ],
[g,h,i]] [(x+1,y-1),(x+1,y),(x+1,y+1)]]
在如上例子中,bh,df分别是e的垂直和水平的相邻像素,记作 N 4 ( p ) N_{4}(p) N4(p)。acgi是e的对角相邻像素,记作 N D ( p ) N_{D}(p) ND(p)。这八个点称为e的8邻域,记作 N 8 ( p ) N_{8}(p) N8(p)。
将灰度值范围取一个子集 V V V作为邻接像素。则邻接有以下三种定义:
4邻接:如果q在集合 N 4 ( p ) N_{4}(p) N4(p)中,则具有 V V V中数值的两个像素p和q是4邻接的。
8邻接:如果q在集合 N 8 ( p ) N_{8}(p) N8(p)中,则具有V中数值的两个像素p和q是8邻接的。
m邻接(混合邻接):以下情形可以认定p和q是m邻接的
存在一条路线从一个像素到另一个像素,并且该路线上任意相邻两点均为邻接,则称这两个点是连通的。
若从一个像素出发能找到一条通路回到该点,则称这条通路是闭合通路。
令S是图像中的一个像素子集。如果 S的全部像素之间存在一个通路,则可以说两个像素p和q 在S中是连通的。对于S中的任何像素p,S中连通到该像素的像素集称为 S的连通分量。如果S仅有一个连通分量,则集合 S称为连通集。
令R是图像中的一个像素子集。如果R是连通集,则称R为一个区城。如果两个区域联合形成一个连通集,则区域尽 R i R_{i} Ri和 R j R_{j} Rj,称为邻接区域。不邻接的区域称为不连接区域。
假设一幅图有 K K K个不连接的区域,且他们都不接触图像的边界。令 R u R_{u} Ru为 K K K个区域的并集,令 R u c R_{u}^{c} Ruc为其补集,则称 R u R_{u} Ru中的所有点为图像的前景, R u c R_{u}^{c} Ruc中的所有点为图像的背景。
区域 R R R中与 R R R的补集相邻的点称为称为 R R R的边界(边缘或轮廓)。
像素 p ( x , y ) p(x,y) p(x,y)与 q ( s , t ) q(s,t) q(s,t)的欧式距离,即直角坐标系下两点间的距离:
D e ( p , q ) = [ ( x − s ) 2 + ( y − t ) 2 ] 0.5 D_{e}(p,q)=[(x-s)^{2}+(y-t)^{2}]^{0.5} De(p,q)=[(x−s)2+(y−t)2]0.5
可以理解为通路步数的计算:
D 4 ( p , q ) = ∣ x − s ∣ + ∣ y − t ∣ D_{4}(p,q)=|x-s|+|y-t| D4(p,q)=∣x−s∣+∣y−t∣
可以理解为以 p p p点为中心的矩形填充层数:
D 8 ( p , q ) = m a x ( ∣ x − s ∣ , ∣ y − t ∣ ) D_{8}(p,q)=max(|x-s|,|y-t|) D8(p,q)=max(∣x−s∣,∣y−t∣)
欧式距离给出的结果比较准确,但计算时要进行平方和开方运算,计算量大。 城市距离和棋盘距离均为非欧式距离, 计算量小,但有一定的误差。
阵列相乘:
[ a b c d ] ⋅ [ x y z w ] = [ a x b y c z d w ] \begin{bmatrix} a & b \\ c & d \\ \end{bmatrix} \cdot \begin{bmatrix} x & y \\ z & w \\ \end{bmatrix}= \begin{bmatrix} ax & by \\ cz & dw \\ \end{bmatrix} [acbd]⋅[xzyw]=[axczbydw]
矩阵相乘:
[ a b c d ] ⋅ [ x y z w ] = [ a x + b z a y + b w c x + d z c y + d w ] \begin{bmatrix} a & b \\ c & d \\ \end{bmatrix} \cdot \begin{bmatrix} x & y \\ z & w \\ \end{bmatrix}= \begin{bmatrix} ax + bz & ay + bw \\ cx + dz & cy + dw \\ \end{bmatrix} [acbd]⋅[xzyw]=[ax+bzcx+dzay+bwcy+dw]
将算子定义为 H H H,若图像 f ( x , y ) f(x,y) f(x,y)经过算子 H H H,得到 g ( x , y ) g(x,y) g(x,y):
H [ f ( x , y ) ] = g ( x , y ) H[f(x,y)]=g(x,y) H[f(x,y)]=g(x,y)
且满足:
H [ a i f i ( x , y ) + a j f j ( x , y ) ] = a i H [ f i ( x , y ) ] + a j H [ f j ( x , y ) ] = a i g i ( x , y ) + a j g j ( x , y ) H[a_{i}f_{i}(x,y)+a_{j}f_{j}(x,y)]=a_{i}H[f_{i}(x,y)]+a_{j}H[f_{j}(x,y)]=a_{i}g_{i}(x,y)+a_{j}g_{j}(x,y) H[aifi(x,y)+ajfj(x,y)]=aiH[fi(x,y)]+ajH[fj(x,y)]=aigi(x,y)+ajgj(x,y)
则称之为线性操作,否则为非线性操作。
图像间的算术操作属于阵列操作,
s ( x , y ) = f ( x , y ) + g ( x , y ) d ( x , y ) = f ( x , y ) − g ( x , y ) p ( x , y ) = f ( x , y ) × g ( x , y ) v ( x , y ) = f ( x , y ) ÷ g ( x , y ) s(x,y)=f(x,y)+g(x,y)\\ d(x,y)=f(x,y)-g(x,y)\\ p(x,y)=f(x,y)\times g(x,y)\\ v(x,y)=f(x,y)\div g(x,y) s(x,y)=f(x,y)+g(x,y)d(x,y)=f(x,y)−g(x,y)p(x,y)=f(x,y)×g(x,y)v(x,y)=f(x,y)÷g(x,y)
先搞两张图片:
试试乘法:(这个效果不是很好,只能在英文部分略微看到一点黄色)
乘除法的效果都不是很好,究其原因是由于数值乘除后处于图像可表示范围的边界,对比度低,可视化效果较差。
集合以及逻辑操作其实在高中就已经频繁接触,这里不多做赘述,直接上图片:
result_and = cv2.bitwise_and(image1, image2)
result_or = cv2.bitwise_or(image1, image2)
result_not1 = cv2.bitwise_not(image1)
result_not2 = cv2.bitwise_not(image2)
result_xor = cv2.bitwise_xor(image1, image2)
result_xnor = cv2.bitwise_not(result_xor)
空间操作可以分为三大类:1、单像素操作;2、邻域操作;3、几何空间变换。
关于单像素操作,文中提及的方案为:改变像素的灰度值,但没有具体展开,会在下一章提及,敬请期待吧。
关于邻域操作,可以理解为对某一点的邻域内的所有点执行某一函数得到的值,作为该点的新值。卷积和池化操作就属于邻域操作。
关于几何空间变换,书中公式有部分错误,此处参见wiki仿射变换
1.恒等变换
仿射矩阵 T T T: [ 1 0 0 0 1 0 0 0 1 ] \begin{bmatrix} 1 & 0 & 0 \\0 & 1 & 0 \\0 & 0 & 1 \\ \end{bmatrix} 100010001 ,对应的坐标公式为: [ x y 1 ] = [ 1 0 0 0 1 0 0 0 1 ] [ v w 1 ] = [ v w 1 ] \begin{bmatrix} x \\ y\\ 1 \\ \end{bmatrix}=\begin{bmatrix} 1 & 0 & 0 \\ 0 & 1 & 0 \\ 0 & 0 & 1 \\ \end{bmatrix}\begin{bmatrix}v\\ w \\1\end{bmatrix}=\begin{bmatrix}v\\ w \\1\end{bmatrix} xy1 = 100010001 vw1 = vw1
2.尺度变换,通俗来讲就是缩放
仿射矩阵 T T T: [ c x 0 0 0 c y 0 0 0 1 ] \begin{bmatrix} c_x & 0 & 0 \\ 0 & c_y & 0 \\ 0 & 0 & 1 \\ \end{bmatrix} cx000cy0001 ,对应的坐标公式为: [ x y 1 ] = [ c x 0 0 0 c y 0 0 0 1 ] [ v w 1 ] = [ s x v s y w 1 ] \begin{bmatrix} x \\y\\1 \\ \end{bmatrix}=\begin{bmatrix} c_x & 0 & 0 \\ 0 & c_y & 0 \\ 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix}v\\ w \\1\end{bmatrix}=\begin{bmatrix} s_xv \\s_yw\\1 \\ \end{bmatrix} xy1 = cx000cy0001 vw1 = sxvsyw1
3.旋转变换
仿射矩阵 T T T: [ cos ( θ ) − sin ( θ ) 0 sin ( θ ) cos ( θ ) 0 0 0 1 ] \begin{bmatrix} \cos(\theta) & -\sin(\theta) & 0 \\ \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix} cos(θ)sin(θ)0−sin(θ)cos(θ)0001 ,
对应的坐标公式为: [ x y 1 ] = [ cos ( θ ) − sin ( θ ) 0 sin ( θ ) cos ( θ ) 0 0 0 1 ] [ v w 1 ] = [ v cos ( θ ) − w sin ( θ ) v sin ( θ ) + w cos ( θ ) 1 ] \begin{bmatrix} x \\y\\1 \\ \end{bmatrix}=\begin{bmatrix} \cos(\theta) & -\sin(\theta) & 0 \\ \sin(\theta) & \cos(\theta) & 0 \\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix}v\\ w \\1\end{bmatrix}=\begin{bmatrix}v\cos(\theta)-w\sin(\theta)\\ v\sin(\theta)+w\cos(\theta) \\1\end{bmatrix} xy1 = cos(θ)sin(θ)0−sin(θ)cos(θ)0001 vw1 = vcos(θ)−wsin(θ)vsin(θ)+wcos(θ)1
4:平移变换
仿射矩阵 T T T: [ 1 0 t x 0 1 t y 0 0 1 ] \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \\ \end{bmatrix} 100010txty1 ,对应的坐标公式为: [ x y 1 ] = [ 1 0 t x 0 1 t y 0 0 1 ] [ v w 1 ] = [ v + t x w + t y 1 ] \begin{bmatrix} x \\y\\1 \\ \end{bmatrix} = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \\ \end{bmatrix}\begin{bmatrix}v\\ w \\1\end{bmatrix} = \begin{bmatrix}v+t_x\\ w+t_y \\1\end{bmatrix} xy1 = 100010txty1 vw1 = v+txw+ty1
5.偏移变换
仿射矩阵 T T T: [ 1 tan ( θ x ) 0 tan ( θ y ) 1 0 0 0 1 ] \begin{bmatrix} 1 & \tan(\theta_x) & 0 \\ \tan(\theta_y) & 1 & 0 \\ 0 & 0 & 1 \\ \end{bmatrix} 1tan(θy)0tan(θx)10001 ,
对应的坐标公式为: [ x y 1 ] = [ 1 tan ( θ x ) 0 tan ( θ y ) 1 0 0 0 1 ] [ v w 1 ] = [ v + t a n ( θ x ) w t a n ( θ y ) v + w 1 ] \begin{bmatrix} x \\ y \\ 1 \\ \end{bmatrix}= \begin{bmatrix} 1 & \tan(\theta_x) & 0 \\ \tan(\theta_y) & 1 & 0 \\ 0 & 0 & 1 \\ \end{bmatrix} \begin{bmatrix} v \\ w \\ 1 \\ \end{bmatrix}=\begin{bmatrix} v+tan(\theta_x)w \\ tan(\theta_y)v+w \\ 1 \\ \end{bmatrix} xy1 = 1tan(θy)0tan(θx)10001 vw1 = v+tan(θx)wtan(θy)v+w1
来个中心旋转变换:
h, w = original_img.shape[:2]
#用于实现围绕中心点旋转
trans_mat = np.array([[1,0,int(h/2)],
[0,1,int(w/2)],
[0,0,1]])
trans_mat_inv = np.linalg.inv(trans_mat)
angle = 45 # 旋转角度为90度
angle_rad = np.deg2rad(angle) # 将角度转换为弧度
#旋转矩阵
Tr = np.array([[np.cos(angle_rad), -np.sin(angle_rad), 0],
[np.sin(angle_rad), np.cos(angle_rad), 0],
[0, 0, 1]])
Tr_inv = np.linalg.inv(Tr)
out_img = np.zeros_like(original_img)
for x in range(h):
for y in range(w):
pos = np.dot(np.dot(np.dot(trans_mat,Tr),trans_mat_inv),np.array([[x],[y],[1]]))
x1, y1 = int(pos[0]), int(pos[1])
if 0 <= x1 < h and 0 <= y1 < w:
out_img[x, y] = original_img[x1, y1]
而当放大的时候,由仿射矩阵计算出来的坐标并不能完全覆盖整张图片。这个时候前面学习的内插就派上了用途:
#仅放大
out_img = np.zeros_like(original_img)
Ts = np.array([[2, 0, 0],
[0, 1.5, 0],
[0, 0, 1]])
trans_mat = np.array([[1,0,int(h/2)],
[0,1,int(w/2)],
[0,0,1]])
trans_mat_inv = np.linalg.inv(trans_mat)
#找原图像在新图像上的对应点,将像素填充进去
for x in range(original_img.shape[0]):
for y in range(original_img.shape[1]):
pos = np.dot(np.dot(np.dot(trans_mat,Ts),trans_mat_inv),np.array([x,y,1]))
x1, y1 = int(pos[0]), int(pos[1])
if 0 <= x1 < out_img.shape[0] and 0 <= y1 < out_img.shape[1]:
out_img[x1, y1] = original_img[x, y]
#最近邻插值
out_img = np.zeros_like(original_img)
Ts = np.array([[2, 0, 0],
[0, 1.5, 0],
[0, 0, 1]])
trans_mat = np.array([[1,0,int(h/2)],
[0,1,int(w/2)],
[0,0,1]])
trans_mat_inv = np.linalg.inv(trans_mat)
Ts_inv = np.linalg.inv(Ts)
#找新图像在原图像上的近似坐标,用其填充当前位置
for x in range(out_img.shape[0]):
for y in range(out_img.shape[1]):
pos = np.dot(np.dot(np.dot(trans_mat,Ts_inv),trans_mat_inv),np.array([x,y,1]))
x1, y1 = int(pos[0]), int(pos[1])
if 0 <= x1 < original_img.shape[0] and 0 <= y1 < original_img.shape[1]:
out_img[x, y] = original_img[x1, y1]
如果你还记得线性代数,又恰好能看懂我的代码,那你很容易发现这两个过程其实就是等式两边同乘以仿射矩阵的逆。
通常RGB图像在每个像素上包含红绿蓝三个分量,而多光谱图片包含n个分量。这就构成了三维向量(n维向量)的情况。那么两个像素点的像素向量的欧式距离的计算方式如下:
D ( z , a ) = [ ( z − a ) ⊺ ( z − a ) ] 0.5 = [ ( z 1 − a 1 ) 2 + ( z 2 − a 2 ) 2 + … … + ( z n − a n ) 2 ] 0.5 D(z,a)=[(z-a)^{\intercal}(z-a)]^{0.5}=[(z_1-a_1)^2+(z_2-a_2)^2+……+(z_n-a_n)^2]^{0.5} D(z,a)=[(z−a)⊺(z−a)]0.5=[(z1−a1)2+(z2−a2)2+……+(zn−an)2]0.5
注意,这里是指像素向量的欧氏距离,与之前提到的坐标距离有所差异。
参见7.5的代码,虽然我们用的是彩色图像,但是写法方面只需考虑坐标,而省略了向量的计算。也表明了向量计算的支持使得算法的开发与实现更加便捷。关于向量的计算后续还会详细提及。
二维线性变换 T T T的通式可表示为:
T ( u , v ) = ∑ x = 0 M − 1 ∑ y = 0 N − 1 f ( x , y ) r ( x , y , u , v ) T(u,v)=\sum^{M-1}_{x=0}\sum^{N-1}_{y=0}f(x,y)r(x,y,u,v) T(u,v)=x=0∑M−1y=0∑N−1f(x,y)r(x,y,u,v)
其中 r ( x , y , u , v ) r(x,y,u,v) r(x,y,u,v)是正变换核, u u u和 v v v是变换变量。而反变换的通式为:
f ( x , y ) = ∑ x = 0 M − 1 ∑ y = 0 N − 1 T ( u , v ) s ( x , y , u , v ) f(x,y)=\sum^{M-1}_{x=0}\sum^{N-1}_{y=0}T(u,v)s(x,y,u,v) f(x,y)=x=0∑M−1y=0∑N−1T(u,v)s(x,y,u,v)
那么 s ( x , y , u , v ) s(x,y,u,v) s(x,y,u,v)就成为反变换核。
关于这部分的用法在第四章会有详细推导过程。
令 z i , i = 1 , 2 , … … L − 1 z_{i},i=1,2,……L-1 zi,i=1,2,……L−1表示图像中所有可能出现的灰度值。那么通过计算 z k z_k zk在图像中出现的次数 n k n_k nk就能够估算 z k z_k zk出现的概率:
p ( z k ) = n k M N p(z_k)=\frac{n_k}{MN} p(zk)=MNnk
那么每个灰度值的概率和必然为1。均值、方差均与概率论中计算公式类似,此处不过多叙述,主要是基本概念的介绍。
本章介绍了很多图像处理的基础概念与基础计算方式,其功效如何,且看下回分解。