文章目录
一、图像预处理
1 图像翻转(图像镜像旋转)
在OpenCV中,图片的镜像旋转是以图像的中心为原点进行镜像翻转的。
cv2.flip(img,flipcode)
参数
- img: 要翻转的图像
- flipcode: 指定翻转类型的标志
- flipcode=0: 垂直翻转,图片像素点沿x轴往下翻转
- flipcode=1: 水平翻转,图片像素点沿y轴往左翻转
- flipcode=-1: 水平垂直翻转,水平翻转和垂直翻转的结合
2 图像仿射变换
仿射变换(Affine Transformation)是一种线性变换,保持了点之间的相对距离不变。
仿射变换的基本性质
- 保持直线
- 保持平行
- 比例不变性
- 不保持角度和长度
常见的仿射变换类型
- 旋转:绕着某个点或轴旋转一定角度。
- 平移:仅改变物体的位置,不改变其形状和大小。
- 缩放:改变物体的大小。
- 剪切:使物体发生倾斜变形。
仿射变换的基本原理
线性变换
二维空间中,图像点坐标为(x,y),仿射变换的目标是将这些点映射到新的位置 (x’, y’)。
为了实现这种映射,通常会使用一个矩阵乘法的形式:
(类似于y=kx+b)
a,b,c,d是线性变换部分的系数,控制旋转、缩放和剪切。
t_x,t_y 是平移部分的系数,控制图像在平面上的移动。
- 输入点的坐标被扩展为齐次坐标形式[x,y,1],以便能够同时处理线性变换和平移
- 输入点的坐标被扩展为齐次坐标形式[x,y,1],以便能够同时处理线性变换和平移
cv2.warpAffine()函数
仿射变换函数
cv2.warpAffine(img,M,dsize)
img:输入图像。
M:2x3的变换矩阵,类型为
np.float32
。下面介绍如何产生dsize:输出图像的尺寸,形式为
(width,height)
。
2.1 图像旋转
旋转图像可以将图像绕着某个点旋转一定的角度。
cv2.getRotationMatrix2D()函数
获取旋转矩阵
M = cv2.getRotationMatrix2D(center,angle,scale)
- center:旋转中心点的坐标,格式为
(x,y)
。 - angle:旋转角度,单位为度,正值表示逆时针旋转负值表示顺时针旋转。
- scale:缩放比例,若设为1,则不缩放。
- 返回值:M,2x3的旋转矩阵。
- center:旋转中心点的坐标,格式为
示例:
import cv2 as cv
import numpy as np
# 读取图片
cat = cv.imread("./imgs/cat1.png")
h, w, _ = cat.shape
center = (w // 2, h // 2) # 获取中心点
angle = 45 # 旋转角度
scale = 0.6 # 缩放
# 获取旋转矩阵
M = cv.getRotationMatrix2D(center, angle, scale)
# 旋转图片
cat2 = cv.warpAffine(cat, M, (w, h))
cv.imshow("cat", cat)
cv.imshow("cat2", cat2)
cv.waitKey(0)
cv.destroyAllWindows()
2.2 图像平移
移操作可以将图像中的每个点沿着某个方向移动一定的距离。
假设我们有一个点 P ( x , y ) P(x,y) P(x,y),希望将其沿x轴方向平移 t x t_x tx*个单位,沿y轴方向平移 t y t_y ty个单位到新的位置 P ′ ( x ′ , y ′ ) P′(x′,y′) P′(x′,y′),那么平移公式如下:
x ′ = x + t x x′=x+tx x′=x+tx
y ′ = y + t y y′=y+ty y′=y+ty
在矩阵形式下,该变换可以表示为:
这里的 t x t_x tx和 t y t_y ty分别代表在x轴和y轴上的平移量。
示例:自己手动生成M矩阵
# 读取图片
cat = cv.imread("./imgs/cat1.png")
h, w, _ = cat.shape
# 获取平移矩阵---50是x轴平移像素点,60是y轴平移像素点
M = np.float32([[1, 0, 50], [0, 1, 60]])
# 平移后的图片
cat2 = cv.warpAffine(cat, M, (w, h))
2.3 图像缩放
缩放操作可以改变图片的大小。
假设要把图像的宽高分别缩放为0.5和0.8,那么对应的缩放因子sx=0.5,sy=0.8。
点 P ( x , y ) P(x,y) P(x,y)对应到新的位置 P ′ ( x ′ , y ′ ) P'(x',y') P′(x′,y′),缩放公式为:
x ′ = s x ∗ x x′=s_x*x x′=sx∗x
y ′ = s y ∗ y y′=s_y*y y′=sy∗y
在矩阵形式下,该变换可以表示为:
相较于图像旋转中只能等比例的缩放,图像缩放更加灵活,可以在指定方向上进行缩放。
sx和sy分别表示在x轴和y轴方向上的缩放因子。
- 示例:自己手动生成M矩阵
# 读取图片
cat = cv.imread("./imgs/cat1.png")
h, w, _ = cat.shape
sx = 0.5
sy = 0.6
# 获取缩放矩阵---sx是行的缩放系数,sy是列的缩放系数
M = np.float32([[sx, 0, 0], [0, sy, 0]])
# 缩放后的图片---w,h长,宽
cat2 = cv.warpAffine(cat, M, (w, h))
2.4 图像剪切
剪切操作可以改变图形的形状,以便其在某个方向上倾斜,它将对象的形状改变为斜边平行四边形,而不改变其面积。
想象我们手上有一张矩形纸片,如果你固定纸片的一边,并沿着另一边施加一个平行于该边的力,这张纸片就会变形为一个平行四边形。这就是剪切变换的一个直观解释。
对于二维空间中的点 P ( x , y ) P(x,y) P(x,y),对他进行剪切变换:
沿x轴剪切: x ′ = x + s h y ∗ y x'=x+sh_y*y x′=x+shy∗y y ′ = y y'=y y′=y
沿y轴剪切: x ′ = x x'=x x′=x y ′ = s h x ∗ x + y y'=sh_x*x+y y′=shx∗x+y
当需要同时沿两个方向进行剪切时, x ′ = x + s h y ∗ y x'=x+sh_y*y x′=x+shy∗y , y ′ = s h x ∗ x + y y'=sh_x*x+y y′=shx∗x+y
在矩阵形式下,该变换可以表示为:
来一个图理解一下:
shy和shx分别对应沿x轴和y轴方向上的剪切因子。
可以理解为,x不变,y偏移
- 示例:自己手动生成M矩阵
shy*y决定竖直方向的偏移量,shx*x决定水平方向的偏移量
图片最右下角发生的拉扯,其他点的变化是为了保持长和宽不变
import cv2 as cv
import numpy as np
# 读取图片
cat = cv.imread("./imgs/cat1.png")
h, w, _ = cat.shape
shy = 0.5
shx = 0.6
# 获取平移矩阵---shy影响竖直方向的偏移量,shx影响水平方向的偏移量
M = np.float32([[1, shy, 0], [shx, 1, 0]])
# 缩放后的图片---w,h长,宽
cat2 = cv.warpAffine(cat, M, (w, h))
3 插值方法
在图像处理中常用于处理图像的放大、缩小、旋转、变形等操作,以及处理图像中的像素值。
当我们对图像进行缩放或旋转等操作时,需要在新的像素位置上计算出对应的像素值,而插值算法的作用就是根据已知的像素值来推测未知位置的像素值。
3.1 最近邻插值
多处理整数,遇到小数要取整。
- 在获得变化后图像的方法中添加参数
flags=cv2.INTER_NEAREST
new_img1=cv.warpAffine(img,M,(w,h),flags=cv.INTER_NEAREST)
变大后的图像点取原图像的哪个坐标点:计算公式:
s r c X = d s t X ∗ s r c W i d t h d s t W i d t h s r c X=d s t X*{\frac{s r c Width}{d s t Width}} srcX=dstX∗dstWidthsrcWidth
s r c Y = d s t Y ∗ s r c H e i g h t d s t H e i g h t s r cY=d s t Y*{\frac{s r c H e i g h t}{d s t H e i g h t}} srcY=dstY∗dstHeightsrcHeight
- dstX:目标图像中某点的x坐标,
- dstY:目标图像中某点的 y y y坐标,
- srcWidth:原图的宽度,
- dstWidth:目标图像的宽度;
- srcHeight:原图的高度,
- dstHeight:目标图像的高度。
- 而srcX和srcY:目标图像中的某点对应的原图中的点的x和y的坐标。
示例:
一张图的第一行像素点的值分别为: 10 10 20 30 40 放大一倍后 新图像的第一行的第3个像素点的值是多少?
待求新点坐标:(2,0)
带公式求原点坐标
- s r c x = 2 ∗ ( 5 10 ) = 1 s r c x=2*({\frac{5}{10}})=1 srcx=2∗(105)=1
- s r c y = 0 ∗ ( 5 10 ) = 0 s r c y=0*({\frac{5}{10}})=0 srcy=0∗(105)=0
取点(1,0)的像素值10
3.2 双线性插值(常用)
处理小数坐标
- 在获得变化后图像的方法中添加参数
flags=CV2.INTER_LINEAR
原理: 4乘4的图像 变成6乘6的图像 那么目标图像的(3,3)点的像素是原图中(1.8333,1.8333)的像素颜色,但是坐标必须是整数 它周围有四个像素点 该取谁呢? 按照到各自的距离比例
来分配颜色值
- 示例:
比如我们根据上述公式计算出了新图像中的某点所对应的原图像的点P,其周围的点分别为Q12、Q22、Q11、Q21, 要插值的P点不在其周围点的连线上,这时候就需要用到双线性插值了。首先延申P点得到P和Q11、Q21的交点R1与P和Q12、Q22的交点R2,如下图所示:
然后根据Q11、Q21得到R1的插值,根据Q12、Q22得到R2的插值,然后根据R1、R2得到P的插值即可,这就是双线性插值。以下是计算过程:
首先计算R1和R2的插值:
f ( R 1 ) ≈ x 2 − x x 2 − x 1 f ( Q 11 ) + x − x 1 x 2 − x 1 f ( Q 21 ) f(R_{1})\approx\frac{x_{2}-x}{x_{2}-x_{1}}f(Q_{11})+\frac{x-x_{1}}{x_{2}-x_{1}}f(Q_{21}) f(R1)≈x2−x1x2−xf(Q11)+x2−x1x−x1f(Q21)
f ( R 2 ) ≈ x 2 − x x 2 − x 1 f ( Q 12 ) + x − x 1 x 2 − x 1 f ( Q 22 ) f(R_{2})\approx\frac{x_{2}-x}{x_{2}-x_{1}}f(Q_{12})+\frac{x-x_{1}}{x_{2}-x_{1}}f(Q_{22}) f(R2)≈x2−x1x2−xf(Q12)+x2−x1x−x1f(Q22)
然后根据R1和R2计算P的插值:
f ( P ) ≈ y 2 − y y 2 − y 1 f ( R 1 ) + y − y 1 y 2 − y 1 f ( R 2 ) f(P)\approx{\frac{y_{2}-y}{y_{2}-y_{1}}}f(R_{1})+{\frac{y-y_{1}}{y_{2}-y_{1}}}f(R_{2}) f(P)≈y2−y1y2−yf(R1)+y2−y1y−y1f(R2)
这样就得到了P点的插值。注意此处如果先在y方向插值、再在x方向插值,其结果与按照上述顺序双线性插值的结果是一样的。
- 两个问题:
- 坐标原点的取值不同,会导致结果不同
- 原点位置的坐标,一般是直接相同
- 远离原点的位置,总会进行插值计算
- 中心位置的像素容易偏离
- 如下图,变化后的坐标(2,2)对应原坐标(1.2,1.2)
- 计算的像素会偏离原中心(1,1)
- 解决方法:
因此,在OpenCV中,为了解决这两个问题,将公式**(计算新图点对应的原图点)**进行了优化,如下所示:
s r c X = ( d s t X + 0.5 ) ∗ s r c W i d t h d s t W i d t h − 0.5 s r c X=(d s t X+0.5)*{\frac{s r c W i d t h}{d s t W i d t h}}-0.5 srcX=(dstX+0.5)∗dstWidthsrcWidth−0.5
s r c Y = ( d s t Y + 0.5 ) ∗ s r c H e i g h t d s t H e i g h t − 0.5 s r c Y=(d s t Y+0.5)\ast{\frac{s r c H e i g h t}{d s t H e i g h t}}-0.5 srcY=(dstY+0.5)∗dstHeightsrcHeight−0.5
使用该公式计算出原图中的对应坐标后再进行插值计算,就不会出现上面的情况了。
3.3 像素区域插值–一般缩小使用
- 在获得变化后图像的方法中添加参数
flags=cv2.INTER_AREA
像素区域插值主要分两种情况,缩小图像和放大图像的工作原理并不相同。
当使用像素区域插值方法进行缩小图像时,它就会变成一个均值滤波器(滤波器其实就是一个卷积核,这里只做简单了解,后面实验中会介绍),其工作原理可以理解为对一个区域内的像素值取平均值。
当使用像素区域插值方法进行放大图像时
- 如果图像放大的比例是整数倍,那么其工作原理与最近邻插值类似;
- 如果放大的比例不是整数倍,那么就会调用双线性插值进行放大。
其中目标像素点与原图像的像素点的对应公式如下所示:
s r c X = d s t X ∗ s r c W i d t h d s t W i d t h s r c X=d s t X*{\frac{s r c W i d t h}{d s t W i d t h}} srcX=dstX∗dstWidthsrcWidth
s r c Y = d s t Y ∗ s r c H e i g h t d s t H e i g h t s r c Y=d s t Y*{\frac{s r c H e i g h t}{d s t H e i g h t}} srcY=dstY∗dstHeightsrcHeight
3.4 双三次插值
cv2.INTER_CUBIC
双三次插值法需要原图像中近邻的16个点来加权,也就是4x4的网格。
目标像素点与原图像的像素点的对应公式如下所示:
s r c X = d s t X ∗ s r c W i d t h d s t W i d t h s r c X=d s t X*{\frac{s r c W i d t h}{d s t W i d t h}} srcX=dstX∗dstWidthsrcWidth
s r c Y = d s t Y ∗ s r c H e i g h t d s t H e i g h t s r c Y=d s t Y*{\frac{s r c H e i g h t}{d s t H e i g h t}} srcY=dstY∗dstHeightsrcHeight
下面我们举例说明,假设原图像A大小为m*n,缩放后的目标图像B的大小为M*N。其中A的每一个像素点是已知的,B是未知的,我们想要求出目标图像B中每一个像素点(X,Y)的值,必须先找出像素(X,Y)在原图像A中对应的像素(x,y),再根据原图像A距离像素(x,y)最近的16个像素点作为计算目标图像B(X,Y)处像素值的参数,利用BiCubic基函数求出16个像素点的权重,图B像素(x,y)的值就等于16个像素点的加权叠加。
BiCubic基函数也就是双三次插值的权重函数,它决定了如何根据距离对周围像素进行加权平均。
假如下图中的P点就是目标图像B在(X,Y)处根据上述公式计算出的对应于原图像A中的位置,P的坐标位置会出现小数部分,所以我们假设P点的坐标为(x+u,y+v),其中x、y表示整数部分,u、v表示小数部分,那么我们就可以得到其周围的最近的16个像素的位置,我们用a(i,j)(i,j=0,1,2,3)来表示,如下图所示。
然后给出BiCubic函数:
a
一般取-0.5或-0.75,用于控制插值函数的形状。
d
代表的是目标像素点与某个像素点之间的相对距离,d_h、d_w
我们要做的就是将上面的16个点相较于p点的位置距离算出来,获取16像素所对应的权重W(d)。然而BiCubic函数是一维的,所以我们需要将像素点的行与列分开计算,比如a00这个点,我们需要将d_x带入BiCubic函数中,计算a00点对于P点的x方向的权重,然后将d_y带入BiCubic函数中,计算a00点对于P点的y方向的权重,其他像素点也是这样的计算过程,最终我们就可以得到P所对应的目标图像B在(X,Y)处的像素值为:
B ( X , Y ) = ∑ i = 0 3 ∑ j = 0 3 a i j × W ( i ) × W ( j ) B(X,Y)=\sum_{i=0}^{3}\sum_{j=0}^{3}a_{i j}\times W_{(i)}\times W_{(j)} B(X,Y)=i=0∑3j=0∑3aij×W(i)×W(j)
依此办法我们就可以得到目标图像中所有的像素点的像素值。
刚刚我们说拿到了目标点的坐标为 (x+u,y+v) ,其中 x、y 表示整数部分,u、v表示小数部分。那么我们取坐标的整数部分作为参考点,也就是(x,y),小数部分表示目标像素相对于参考点的偏移量。
3.5 Lanczos插值
cv2.INTER_LANCZOS4
Lanczos插值方法与双三次插值的思想是一样的,不同的就是其需要的原图像周围的像素点的范围变成了8*8,并且不再使用BiCubic函数来计算权重,而是换了一个公式计算权重。
首先还是目标像素点与原图像的像素点的对应公式如下所示:
s r c X = d s t X ∗ s r c W i d t h d s t W i d t h s r c X=d s t X*{\frac{s r c W i d t h}{d s t W i d t h}} srcX=dstX∗dstWidthsrcWidth
s r c Y = d s t Y ∗ s r c H e i g h t d s t H e i g h t s r c Y=d s t Y*{\frac{s r c H e i g h t}{d s t H e i g h t}} srcY=dstY∗dstHeightsrcHeight
下面我们举例说明,假设原图像A大小为m*n,缩放后的目标图像B的大小为M*N。其中A的每一个像素点是已知的,B是未知的,我们想要求出目标图像B中每一个像素点(X,Y)的值,必须先找出像素(X,Y)在原图像A中对应的像素(x,y),再根据原图像A距离像素(x,y)最近的64个像素点作为计算目标图像B(X,Y)处像素值的参数,利用权重函数求出64个像素点的权重,图B像素(x,y)的值就等于64个像素点的加权叠加。
假如下图中的P点就是目标图像B在(X,Y)处根据上述公式计算出的对应于原图像A中的位置,P的坐标位置会出现小数部分,所以我们假设P点的坐标为(x+u,y+v),其中x、y表示整数部分,u、v表示小数部分,那么我们就可以得到其周围的最近的64个像素的位置,我们用a(i,j)(i,j=0,1,2,3,4,5,6,7)来表示,如下图所示。
然后给出权重公式:
其中a通常取2或者3,当a=2时,该算法适用于图像缩小。a=3时,该算法适用于图像放大。
与双三次插值一样,这里也需要将像素点分行和列分别带入计算权重值,其他像素点也是这样的计算过程,最终我们就可以得到P所对应的目标图像B在(X,Y)处的像素值为:
S ( x , y ) = ∑ i = [ x ] − a + 1 [ x ] + a ∑ j = [ y ] − a + 1 [ y ] + a s i j L ( x − i ) L ( y − j ) S(x,y)=\sum_{i=[x]-a+1}^{[x]+a}\sum_{j=[y]-a+1}^{[y]+a}s_{i j}L(x-i)L(y-j) S(x,y)=i=[x]−a+1∑[x]+aj=[y]−a+1∑[y]+asijL(x−i)L(y−j)
其中 [ x ] [x] [x]、 [ y ] [y] [y]表示对坐标值向下取整,通过该方法就可以计算出新的图像中所有的像素点的像素值。