人工智能之图像预处理
人工智能之图像预处理
使用库:OpenCV-python
1 图像镜像翻转
在OpenCV中,镜像翻转是以图像的中心作为原点,可沿水平和垂直方向进行翻转,方向由参数flipCode的大小决定
-
cv2.flip(img,flipCode)
-
参数flipCode
- flipCode=0:垂直翻转
- flipCode>0: 水平翻转
- flipCode<0: 水平翻转+垂直翻转
import cv2 as cv # 读取图片 img1=cv.imread(r"./images/cat1.png") # 图片路径转为自己的路径 # =0:垂直翻转,图片像素点沿x轴翻转 img2=cv.flip(img1,flipCode=0) cv.imshow("img2",img2) # >0 : 水平翻转,图片像素点沿y轴翻转 img3=cv.flip(img1,1) cv.imshow("img3",img3) # <0: 水平垂直翻转,水平翻转和垂直翻转的结合 img4=cv.flip(img1,-1) cv.imshow("img4",img4) cv.waitKey(0) cv.destroyAllWindows()
2 图像仿射变换
仿射变换是一种保持了点之间相对距离不变的线性变换
-
基本性质
- 保持直线
- 保持平行
- 比例不变
- 不保持角度和长度
-
基本原理
通过某种映射将点(x,y)(x,y)(x,y) 映射到新点(x′,y′)(x',y')(x′,y′)。而这种映射通常使用矩阵形式[x′y′]=[abtxcdty]⋅[xy1]\begin{bmatrix} x'\\ y'\\ \end{bmatrix}=\begin{bmatrix} a & b & t_x \\c & d & t_y\\\end{bmatrix}·\begin{bmatrix} x \\ y \\ 1 \\ \end{bmatrix}[x′y′]=[acbdtxty]⋅xy1.
[abtxcdty]\begin{bmatrix} a & b & t_x \\c & d & t_y\\\end{bmatrix}[acbdtxty]这部分矩阵被称为变换矩阵,其中,a,b,c,d四个参数用于控制图像的旋转、缩放和剪切;tx,tyt_x,t_ytx,ty两个参数用于控制图像在平面上的移动量。原点坐标被扩展成齐次坐标形式以便于同时处理线性变换和平移
-
实现仿射变换
调用**cv2.warpAffine(img,M,dsize)**函数
参数设置
- img: 仿射变换的图像
- M:2x3的变换矩阵,类型是np.float32
- dsize:(width,height) 设置仿射变换后图像的大小
2.1 仿射变换–旋转
可将图像绕着某个点旋转一定的角度。
cv2.getRotationMatrix2D(center,angle,scale)函数
- center:旋转中心
- angle: 旋转角度
- scale:缩放比例
- 返回值:返回2x3变换矩阵
import cv2 as cv
# 读取
img = cv.imread(r"./images/cat1.png")
img = cv.resize(img, (810, 800))
cv.imshow("img", img)
# 得到变换矩阵
M = cv.getRotationMatrix2D((img.shape[1]/2, img.shape[0]/2), 45, 1)
# 调用仿射变换函数,实现旋转
img_rotate = cv.warpAffine(img, M, (810,800)) # 810为宽,800为高
cv.imshow("img_rotate", img_rotate)
cv.waitKey(0)
cv.destroyAllWindows()
2.2 仿射变换–平移
可将图像中的每个点沿着某个方向移动一定的距离。因为是平移,所以变换矩阵[abtxcdty]\begin{bmatrix} a & b & t_x \\c & d & t_y\\\end{bmatrix}[acbdtxty]中的a、d=1,b、c=0
变换矩阵为:
[10tx01ty]\begin{bmatrix} 1 & 0 & t_x \\0 & 1 & t_y\\\end{bmatrix}[1001txty]
import cv2 as cv
import numpy as np
# 读取
img = cv.imread(r"./images/cat1.png")
img = cv.resize(img, (480, 480))
cv.imshow("img", img)
# 获取高宽
h,w = img.shape[:2]
# 设置平移的变换矩阵
M = np.float32([[1,0,100],[0,1,50]])
# 调用函数实现仿射变换
img_move = cv.warpAffine(img, M, (w,h))
cv.imshow("img_move", img_move)
cv.waitKey(0)
cv.destroyAllWindows()
2.3 仿射变换–缩放
可任意缩放图片的大小。因为是缩放,所以变换矩阵[abtxcdty]\begin{bmatrix} a & b & t_x \\c & d & t_y\\\end{bmatrix}[acbdtxty]中的a、d为缩放因子,b,c,tx,ty=0b,c,t_x,t_y=0b,c,tx,ty=0
变换矩阵为:
[sx000sy0]\begin{bmatrix} s_x & 0 & 0\\0 & s_y & 0\\\end{bmatrix}[sx00sy00]
import cv2 as cv
import numpy as np
# 读取
img = cv.imread(r"./images/cat1.png")
img = cv.resize(img, (480, 480))
cv.imshow("img", img)
# 获取图像尺寸
h,w = img.shape[:2]
sx=0.5 # X轴缩放因子
sy=0.5 # Y轴缩放因子
# 设置变换矩阵
M = np.float32([[sx,0,0],[0,sy,0]])
# 调用函数实现仿射变换
img_move = cv.warpAffine(img, M, (w,h))
cv.imshow("img_move", img_move)
cv.waitKey(0)
cv.destroyAllWindows()
2.4 仿射变换–剪切
OpenCV中剪切不同日常的剪切,它是将对象的形状改变为斜平行四边形,使其面积不变,但在显示上却缺少部分。假如沿X轴剪切,即固定图像X轴的长度,沿X轴拉伸图像;假如沿Y轴剪切,即固定图像Y轴的长度,沿Y轴拉伸图像。
因为是剪切,所以变换矩阵[abtxcdty]\begin{bmatrix} a & b & t_x \\c & d & t_y\\\end{bmatrix}[acbdtxty]中的a、d=1,b,c为剪切因子,tx,ty=0b,c为剪切因子,t_x,t_y=0b,c为剪切因子,tx,ty=0
变换矩阵为:
[1shy0shx10]\begin{bmatrix} 1 & sh_y & 0\\sh_x& 1 & 0\\\end{bmatrix}[1shxshy100]其中shy为X轴的剪切因子,shx为Y轴的剪切因子sh_y为X轴的剪切因子,sh_x为Y轴的剪切因子shy为X轴的剪切因子,shx为Y轴的剪切因子
import cv2 as cv
import numpy as np
# 读取
img = cv.imread(r"./images/cat1.png")
img = cv.resize(img, (480, 480))
cv.imshow("img", img)
# 获取图像尺寸
h,w = img.shape[:2]
shx=0.5 # y轴剪切因子
shy=0.5 # X轴剪切因子
# 设置变换矩阵
M = np.float32([[1,shy,0],[shx,1,0]])
# 调用函数实现仿射变换
img_move = cv.warpAffine(img, M, (800,800))
cv.imshow("img_move", img_move)
cv.waitKey(0)
cv.destroyAllWindows()
3插值方法
当图像经过仿射变换后,由于像素之间的间隔不一致从而导致信息丢失和图像质量下降的问题。当我们对图像进行仿射变换等操作时,需要在新的像素位置上计算出对应的像素值,而插值算法的作用就是根据已知原图像的像素值来推测未知位置的像素值。
在图像处理和计算机图形学中,插值(Interpolation)是一种通过已知数据点之间的推断或估计来获取新数据点的方法。它在图像处理中常用于处理图像的放大、缩小、旋转、变形等操作,以及处理图像中的像素值。在实际中我们是通过原图像去推测目标图像的像素值,但在应用时则是反过来。
目标点和原图像点坐标计算公式:
原图像x点=目标图点x∗原图像宽度目标图像宽度原图像x点=目标图点x*{\frac{原图像宽度}{目标图像宽度}}原图像x点=目标图点x∗目标图像宽度原图像宽度
原图像y点=目标图点y∗原图像高度目标图像高度原图像y点=目标图点y*{\frac{原图像高度}{目标图像高度}}原图像y点=目标图点y∗目标图像高度原图像高度
例如:目标图点为(2,3)(2,3)(2,3),高宽为3x6,原图为高宽为3x3则:
原图点x=2∗36=1原图点x=2*{\frac{3}{6}}=1原图点x=2∗63=1
原图点y=3∗33=1原图点y=3*{\frac{3}{3}}=1原图点y=3∗33=1
根据计算结果,目标图点为(2,3)(2,3)(2,3)的像素值等于原图像中点(1,1)(1,1)(1,1)的像素值
3.1最近邻插值
CV2.INTER_NEAREST
最近邻插值的原则是:目标像素点的像素值与经过上述公式计算出来的对应的像素点的像素值相同,如出现小数部分需要进行向下取整。
3.2 双线性插值
CV2.INTER_LINEAR
双线性插值是一种图像缩放、旋转或平移时进行像素值估计的插值方法。当需要对图像进行变换时,特别是尺寸变化时,原始图像的某些像素坐标可能不再是新图像中的整数位置,也就是说,当计算出来的原图点坐标为非整数时,这时就需要使用双线性插值算法来确定这些非整数坐标的像素值。
双线性插值的工作原理是这样的:
- 设目标图像上坐标为
(x', y'),通过上述公式计算得到在原图像上对应的p点坐标为(x, y)。 - 在原图像上找到四个最接近
(x, y)的像素点,通常记作 p1(x1,y1),p2(x2,y1),p3(x1,y2),p4(x2,y2)p_1(x_1,y_1),p_2(x_2,y_1),p_3(x_1,y_2),p_4(x_2,y_2)p1(x1,y1),p2(x2,y1),p3(x1,y2),p4(x2,y2),它们构成一个2x2的邻域矩阵。
p1(x1,y1)p_1(x_1,y_1)p1(x1,y1) R1(x,y1)R_1(x,y_1)R1(x,y1) p2(x2,y1)p_2(x_2,y_1)p2(x2,y1)
p(x,y)p(x,y)p(x,y)
p3(x1,y2)p_3(x_1,y_2)p3(x1,y2) R2(x,y2)R_2(x,y_2)R2(x,y2) p4(x2,y2)p_4(x_2,y_2)p4(x2,y2)
分别在水平方向和垂直方向上做线性插值:
水平方向做线性插值则有R1和R2的像素值为:f(R1)≈(x−x1)x2−x1f(p2)+(x2−x)x2−x1f(p1),f(R2)≈(x−x1)x2−x1f(p4)+(x2−x)x2−x1f(p3)R_1和R_2的像素值为:f(R_1)\approx {\frac{(x-x_1)}{x_2-x_1}f(p_2)+{\frac{(x_2-x)}{x_2-x_1}}}f(p1),f(R_2)\approx {\frac{(x-x_1)}{x_2-x_1}f(p_4)+{\frac{(x_2-x)}{x_2-x_1}}}f(p3)R1和R2的像素值为:f(R1)≈x2−x1(x−x1)f(p2)+x2−x1(x2−x)f(p1),f(R2)≈x2−x1(x−x1)f(p4)+x2−x1(x2−x)f(p3)
垂直方向做线性插值则有点p的像素值为:f(p)≈y−y1y2−y1f(R1)+y2−yy2−y1f(R2)点p的像素值为:f(p)\approx {\frac{y-y_1}{y_2-y_1}}f(R_1)+{\frac{y_2-y}{y_2-y_1}}f(R_2)点p的像素值为:f(p)≈y2−y1y−y1f(R1)+y2−y1y2−yf(R2)
- 综合上述两次线性插值的结果,得到最终位于
(x', y')处的新像素的估计值。
注意:先垂直线性插值后水平插值结果也一样
存在问题:
-
采用不同的坐标系时产生的结果是不一样的,而且无论我们采用什么坐标系,最左侧和最右侧(最上侧和最下侧)的点是“不公平的”。
-
参与计算的点相对都往右下偏移会产生相对的位置信息损失
因此,在OpenCV中,为了解决这两个问题,将公式进行了优化,如下所示:
原图像x点=(目标图点x+0.5)∗原图像宽度目标图像宽度−0.5
原图像x点=(目标图点x+0.5)*{\frac{原图像宽度}{目标图像宽度}}-0.5
原图像x点=(目标图点x+0.5)∗目标图像宽度原图像宽度−0.5
原图像y点=(目标图点y+0.5)∗原图像宽度目标图像宽度−0.5 原图像y点=(目标图点y+0.5)*{\frac{原图像宽度}{目标图像宽度}}-0.5 原图像y点=(目标图点y+0.5)∗目标图像宽度原图像宽度−0.5
使用该公式计算出原图中的对应坐标后再进行插值计算,就不会出现上面的情况了。
3.3像素区域插值
cv2.INTER_AREA
像素区域插值主要分两种情况,缩小图像和放大图像的工作原理并不相同。
- 缩小图像 : 对一个区域内的像素值取平均值。
- 放大图像
- 整数倍 与最近邻插值类似
- 非整数倍 调用双线性插值法
3.4 双三次插值
cv2.INTER_CUBIC
与双线性插值法相同,该方法也是通过映射,在映射点的邻域内通过加权来得到放大图像中的像素值。不同的是,双三次插值法需要原图像中近邻的16个点来加权,也就是4x4的网格。
第一步:找出目标像素(x′,y′)(x',y')(x′,y′)在原图像中对应的像素(x+u,y+v)(x+u,y+v)(x+u,y+v)
第二步:根据原图像距离像素(x+u,y+v)(x+u,y+v)(x+u,y+v)最近的16个像素点作为计算目标图像(x′,y′)(x',y')(x′,y′)处像素值的参数,利用BiCubic基函数求出16个像素点的权重,目标图像素(x′,y′)(x',y')(x′,y′)的值就等于16个像素点的加权叠加。
BiCubic基函数(一维函数)也就是双三次插值的权重函数,它决定了如何根据距离对周围像素进行加权平均。
$$
w(d) =
\begin{cases}
(a + 2)d^3 - (a + 3)d^2 + 1 & , 0 \leq d < 1 \
ad^3 - 5ad^2 + 8ad - 4a & , 1 \leq d < 2 \
0 & , d \geq 2
\end{cases}
$$
a一般取-0.5或-0.75,用于控制插值函数的形状。
d代表的是原像素点与某个像素点之间的相对距离,dyd_ydy、dxd_xdx
我们要做的就是将上面的16个点相较于p点的位置距离算出来,获取16像素所对应的权重W(d)W(d)W(d)。然而BiCubic函数是一维的,所以我们需要将像素点的行与列分开计算,比如a00这个点,我们需要将dxd_xdx带入BiCubic函数中,计算a00点对于P点的x方向的权重,然后将dyd_ydy带入BiCubic函数中,计算a00点对于P点的y方向的权重,其他像素点也是这样的计算过程,最终我们就可以得到P所对应的目标图像B在(X,Y)处的像素值为:
B(X,Y)=∑i=03∑j=03aij×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+u,y+v)(x+u,y+v) ,其中 x、yx、yx、y 表示整数部分,u、v表示小数部分。那么我们取坐标的整数部分作为参考点,也就是(x,y)(x,y)(x,y),小数部分表示目标像素相对于参考点的偏移量。
比如说目标点的坐标为(3.7,2.4)(3.7,2.4)(3.7,2.4),那么我们的参考点就是(3,2)(3,2)(3,2)。
目标部分相对于参考点的偏移量:
bh=vb_h=vbh=v,bw=ub_w=ubw=u
然后我们才需要来计算相邻像素和目标像素的距离:
比如我们的P点在行上的 四个像素(ii=−1,0,1,2)(ii=-1,0,1,2)(ii=−1,0,1,2),对应的距离就是:dh=∣ii−bh∣d_h=|ii-b_h|dh=∣ii−bh∣
ii=−1ii=-1ii=−1时: dh=∣−1−0.7∣=1.7d_h=|-1-0.7|=1.7dh=∣−1−0.7∣=1.7 wi=−0.0315w_i=-0.0315wi=−0.0315
ii=0ii=0ii=0时: dh=∣0−0.7∣=0.7d_h=|0-0.7|=0.7dh=∣0−0.7∣=0.7 wi=0.2895w_i=0.2895wi=0.2895
ii=1ii=1ii=1时: dh=∣1−0.7∣=0.3d_h=|1-0.7|=0.3dh=∣1−0.7∣=0.3 wi=0.8155w_i=0.8155wi=0.8155
ii=−1ii=-1ii=−1时: dh=∣2−0.7∣=1.3d_h=|2-0.7|=1.3dh=∣2−0.7∣=1.3 wi=−0.0735w_i=-0.0735wi=−0.0735
在列上的 四个像素(jj=−1,0,1,2)(jj=-1,0,1,2)(jj=−1,0,1,2),对应的距离就是:dw=∣ii−bw∣d_w=|ii-b_w|dw=∣ii−bw∣
jj=−1jj=-1jj=−1时: dw=∣−1−0.4∣=1.4d_w=|-1-0.4|=1.4dw=∣−1−0.4∣=1.4 wj=−0.072w_j=-0.072wj=−0.072
jj=0jj=0jj=0时: dw=∣0−0.4∣=0.4d_w=|0-0.4|=0.4dw=∣0−0.4∣=0.4 wj=0.696w_j=0.696wj=0.696
jj=1jj=1jj=1时: dw=∣1−0.4∣=0.6d_w=|1-0.4|=0.6dw=∣1−0.4∣=0.6 wj=0.424w_j=0.424wj=0.424
jj=−1jj=-1jj=−1时: dw=∣2−0.4∣=1.6d_w=|2-0.4|=1.6dw=∣2−0.4∣=1.6 wj=−0.048w_j=-0.048wj=−0.048
最后利用我们的加权求和算出像素值:
公式
B(X,Y)=∑i=03∑j=03aij×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)
B(X,Y)=(a00∗wi0∗wj0)+(a01∗wi0∗wj1)+(a02∗wi0∗wj2)+(a03∗wi0∗wj3)+(a10∗wi1∗wj0)+(a11∗wi1∗wj1)+(a12∗wi1∗wj2)+(a13∗wi1∗wj3)+(a20∗wi2∗wj0)+(a21∗wi2∗wj1)+(a22∗wi2∗wj2)+(a23∗wi2∗wj3)+(a30∗wi3∗wj0)+(a31∗wi3∗wj1)+(a22∗wi3∗wj2)+(a33∗wi3∗wj3)
B(X,Y)
=(a_{00}*w_{i0}*w_{j0})+(a_{01}*w_{i0}*w_{j1})+(a_{02}*w_{i0}*w_{j2})+(a_{03}*w_{i0}*w_{j3})
+(a_{10}*w_{i1}*w_{j0})+(a_{11}*w_{i1}*w_{j1})+(a_{12}*w_{i1}*w_{j2})+(a_{13}*w_{i1}*w_{j3})
+(a_{20}*w_{i2}*w_{j0})+(a_{21}*w_{i2}*w_{j1})+(a_{22}*w_{i2}*w_{j2})+(a_{23}*w_{i2}*w_{j3})
+(a_{30}*w_{i3}*w_{j0})+(a_{31}*w_{i3}*w_{j1})+(a_{22}*w_{i3}*w_{j2})+(a_{33}*w_{i3}*w_{j3})
B(X,Y)=(a00∗wi0∗wj0)+(a01∗wi0∗wj1)+(a02∗wi0∗wj2)+(a03∗wi0∗wj3)+(a10∗wi1∗wj0)+(a11∗wi1∗wj1)+(a12∗wi1∗wj2)+(a13∗wi1∗wj3)+(a20∗wi2∗wj0)+(a21∗wi2∗wj1)+(a22∗wi2∗wj2)+(a23∗wi2∗wj3)+(a30∗wi3∗wj0)+(a31∗wi3∗wj1)+(a22∗wi3∗wj2)+(a33∗wi3∗wj3)
3.4 Lanczos 差值
cv2.INTER_LANCZOS4
Lanczos插值方法与双三次插值的思想是一样的,不同的就是其需要的原图像周围的像素点的范围变成了8*8,并且不再使用BiCubic函数来计算权重,而是换了一个公式计算权重。
要求出目标图像中每一个像素点(X,Y)的值,必须先找出像素(X,Y)在原图像中对应的像素(x+u,y+v),其中x、y表示整数部分,u、v表示小数部分,再根据原图像距离像素(x+u,y+v)最近的64个像素点作为计算目标图像(X,Y)处像素值的参数,利用权重函数求出64个像素点的权重,图B像素(x+u,y+v)的值就等于64个像素点的加权叠加。
$$
L(x)=\begin{cases}
1 & x=0\
\frac{a\sin(\pi x)\sin(\frac{\pi x}{a})}{\pi{2}x{2}}&,-a\leq x\leq a,x\neq0\
0&,\text{其他}
\end{cases}
$$
其中a通常取2或者3,当a=2时,该算法适用于图像缩小。a=3时,该算法适用于图像放大。
与双三次插值一样,这里也需要将像素点分行和列分别带入计算权重值,其他像素点也是这样的计算过程,最终我们就可以得到所对应的目标图像在(X,Y)处的像素值为:
S(x,y)=∑i=[x]−a+1[x]+a∑j=[y]−a+1[y]+asijL(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]表示对坐标值向下取整,通过该方法就可以计算出新的图像中所有的像素点的像素值。
3.6 总结
| 最近邻插值 | 计算速度最块 | 可能会导致图像出现锯齿状边缘和失真,效果较差 |
|---|---|---|
| 双线性插值 | 计算速度慢 | 效果大幅度的提高,适用于大多数场景 |
| 双三次插值、Lanczos插值 | 计算速度都很慢 | 效果好。 |
在OpenCV中,关于插值方法默认选择的都是双线性插值,且一般情况下双线性插值已经能满足大部分需求。
import cv2 as cv
# 读取
img = cv.imread(r"./images/cat1.png")
img = cv.resize(img, (810, 800))
cv.imshow("img", img)
# 获取旋转矩阵,旋转中心为整数,旋转角度为,缩放比例
M = cv.getRotationMatrix2D((img.shape[1]/2, img.shape[0]/2), 45, 1)
# 仿射变换函数,实现旋转
img_rotate = cv.warpAffine(img, M, (810,800)) # 810为宽,800为高
cv.imshow("img_rotate", img_rotate)
img_near = cv.warpAffine(img, M, (810,800), flags=cv.INTER_NEAREST) # 最近邻 速度快,精确较低
img_area = cv.warpAffine(img, M, (810,800), flags=cv.INTER_AREA) # 像素区域
img_linear = cv.warpAffine(img, M, (810,800), flags=cv.INTER_LINEAR) # 双线性插值
img_cubic = cv.warpAffine(img, M, (810,800), flags=cv.INTER_CUBIC) # 双三次插值
img_lanczos = cv.warpAffine(img, M, (810,800), flags=cv.INTER_LANCZOS4) # lanczos4插值
cv.waitKey(0)
cv.destroyAllWindows()
4. 边缘填充
在经过仿射变化后,新图某些像素点没有在原图找到对应的像素点,此时默认将像素值(0,0,0)填充上去。通过边缘填充,可以选择不同的填充方式。
4.1边界复制(BORDER_REPLICATE)
边界复制会将边界处的像素值进行复制,然后作为边界填充的像素值,如下图所示,可以看到四周的像素值都一样。
4.2边界反射(BORDER_REFLECT)
4.3边界反射101(BORDER_REFLECT_101)
边界反射不同的是,不再反射边缘的像素点,如下图所示。
4.4边界常数(BORDER_CONSTANT)
当选择边界常数时,还要指定常数值是多少,默认的填充常数值为0,如下图所示。
new_img=cv.warpAffine(img,M,(w,h),cv.INTER_LANCZOS4,borderMode=cv.BORDER_CONSTANT,borderValue=(0,0,255))
4.5 边界包裹(BORDER_WRAP)
new_img=cv.warpAffine(img,M,(w,h),cv.INTER_LANCZOS4,borderMode=cv.BORDER_WRAP)
边缘填充
import cv2 as cv
# 读取
img = cv.imread(r"./images/cat1.png")
img = cv.resize(img, (810, 800))
# 获取旋转矩阵,旋转中心为整数,旋转角度为,缩放比例
M = cv.getRotationMatrix2D((img.shape[1]/2, img.shape[0]/2), 45, 1)
# 仿射变换函数,实现旋转
img_rotate = cv.warpAffine(img, M, (810,800)) # 810为宽,800为高
img_near = cv.warpAffine(img, M, (810,800), flags=cv.INTER_NEAREST,borderMode=cv.BORDER_REPLICATE)
img_linear = cv.warpAffine(img, M, (810,800), flags=cv.INTER_LINEAR,borderMode=cv.BORDER_REFLECT)
img_cubic = cv.warpAffine(img, M, (810,800), flags=cv.INTER_CUBIC,borderMode=cv.BORDER_REFLECT_101)
img_lanczos = cv.warpAffine(img, M, (810,800), flags=cv.INTER_LANCZOS4,borderMode=cv.BORDER_CONSTANT,borderValue=(0,0,255))
new_img=cv.warpAffine(img,M,(810,800),flags=cv.INTER_LANCZOS4,borderMode=cv.BORDER_WRAP)
5图像矫正(透视变换)
透视变换是把一个图像投影到一个新的视平面的过程,在现实世界中,我们观察到的物体在视觉上会受到透视效果的影响,即远处的物体看起来会比近处的物体小。透视投影是指将三维空间中的物体投影到二维平面上的过程,这个过程会导致物体在图像中出现形变和透视畸变。透视变换可以通过数学模型来校正这种透视畸变,使得图像中的物体看起来更符合我们的直观感受。通俗的讲,透视变换的作用其实就是改变一下图像里的目标物体的被观察的视角。
![]() | ![]() |
|---|
- M=getPerspectiveTransform(src,dst)
- src:原图像上需要进行透视变化的四个点的坐标,这四个点用于定义一个原图中的四边形区域
- dst:透视变换后,src的四个点在新目标图像的四个新坐标。
- cv2.warpPerspective(src, M, dsize, flags, borderMode)
透视变换
import cv2 as cv
import numpy as np
# 读图
img = cv.imread("./images/3.png")
h,w = img.shape[:2]
# 定义点坐标 原图左上(184,100),右上(509,142),右下(494,337),左下(120,291)
pts1 = np.float32([[184,100], [509,142],[494,337],[120,291]])
# 新图点坐标
pts2 = np.float32([[0,0],[w,0],[w,h],[0,h]])
M = cv.getPerspectiveTransform(pts1,pts2)
img_perspective = cv.warpPerspective(img,M,(w,h),cv.INTER_LINEAR,cv.BORDER_WRAP)
cv.imshow("img", img)
cv.imshow("img1", img_perspective)
cv.waitKey(0)
cv.destroyAllWindows()
6图像色彩空间转换
OpenCV中,图像色彩空间转换是一个非常基础且重要的操作,就是将图像从一种颜色表示形式转换为另一种表示形式的过程。通过将图像从一个色彩空间转换到另一个色彩空间,可以更好地进行特定类型的图像处理和分析任务。常见的颜色空间包括RGB、HSV、YUV等。
-
色彩空间转换的作用
-
提高图像处理效果
-
节省计算资源
-
6.1 RGB颜色空间
在图像处理中,最常见的就是RGB颜色空间。RGB颜色空间是我们接触最多的颜色空间,是一种用于表示和显示彩色图像的一种颜色模型。RGB代表红色(Red)、绿色(Green)和蓝色(Blue),这三种颜色通过不同强度的光的组合来创建其他颜色,广泛应用于我们的生活中,比如电视、电脑显示屏以及上面实验中所介绍的RGB彩色图。RGB颜色空间可以产生大约1600万种颜色,几乎包括了世界上的所有颜色,也就是说可以使用RGB颜色空间来生成任意一种颜色。
注意:在OpenCV中,颜色是以BGR的方式进行存储的,而不是RGB,这也是上面红色的像素值是(0,0,255)而不是(255,0,0)的原因。
6.2 颜色加法
cv.add()
使用OpenCV的cv.add()函数把两幅图像相加,或者可以简单地通过numpy操作添加两个图像,如res = img1 + img2。两个图像应该具有相同的大小和类型。
OpenCV加法和Numpy加法之间存在差异。OpenCV的加法是饱和操作,而Numpy添加是模运算。
6.3 颜色加权加法
cv2.addWeighted(src1,alpha,src2,deta,gamma)
src1、src2:输入图像。alpha、beta:两张图象权重。gamma:亮度调整值。gamma > 0,图像会变亮。gamma < 0,图像会变暗。gamma = 0,则没有额外的亮度调整。
** 颜色加法**
import cv2 as cv
import numpy as np
x=np.uint8([[250]])
y=np.uint8([[10]])
pig = cv.imread("./images/pig.png")
cao = cv.imread("./images/cao.png")
# numpy直接相加
# dst=x+y # 对255取模,取余 4
dst1=pig+cao
cv.imshow("pig",dst1)
# 颜色加法 cv.add()
# print(cv.add(x,y)) # 输出:[ [ 255] ]
img_add = cv.add(pig, cao)
cv.imshow("img_add", img_add)
# 颜色加权加法
img_add_alpha = cv.addWeighted(pig,0.35,cao,0.65,10)
cv.imshow("img_add_alpha", img_add_alpha)
cv.waitKey(0)
cv.destroyAllWindows()
6.4 HSV颜色空间
HSV颜色空间指的是HSV颜色模型,这是一种与RGB颜色模型并列的颜色空间表示法。RGB颜色模型使用红、绿、蓝三原色的强度来表示颜色,是一种加色法模型,即颜色的混合是添加三原色的强度。而HSV颜色空间使用色调(Hue)、饱和度(Saturation)和亮度(Value)三个参数来表示颜色,色调H表示颜色的种类,如红色、绿色、蓝色等;饱和度表示颜色的纯度或强度,如红色越纯,饱和度就越高;亮度表示颜色的明暗程度,如黑色比白色亮度低。
色调H:
使用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°,蓝色为240°。它们的补色是:黄色为60°,青色为180°,紫色为300°。通过改变H的值,可以选择不同的颜色
饱和度S:
饱和度S表示颜色接近光谱色的程度。一种颜色可以看成是某种光谱色与白色混合的结果。其中光谱色所占的比例越大,颜色接近光谱色的程度就越高,颜色的饱和度就越高。饱和度越高,颜色就越深而艳,光谱色的白光成分为0,饱和度达到最高。通常取值范围为0%~100%,其中0%表示灰色或无色,100%表示纯色,通过调整饱和度的值,可以使颜色变得更加鲜艳或者更加灰暗。
明度V:
明度表示颜色明亮的程度,对于光源色,明度值与发光体的光亮度有关;对于物体色,此值和物体的透射比或反射比有关。通常取值范围为0%(黑)到100%(白),通过调整明度的值,可以使颜色变得更亮或者更暗。
一般对颜色空间的图像进行有效处理都是在HSV空间进行的,然后对于基本色中对应的HSV分量需要给定一个严格的范围,下面是通过实验计算的模糊范围(准确的范围在网上都没有给出)。
H: 0— 180
S: 0— 255
V: 0— 255
此处把部分红色归为紫色范围:
为什么有了RGB颜色空间我们还是需要转换成HSV颜色空间来进行图像处理呢?
- 符合人类对颜色的感知方式:人类对颜色的感知是基于色调、饱和度和亮度三个维度的,而HSV颜色空间恰好就是通过这三个维度来描述颜色的。因此,使用HSV空间处理图像可以更直观地调整颜色和进行色彩平衡等操作,更符合人类的感知习惯。
- 颜色调整更加直观:在HSV颜色空间中,色调、饱和度和亮度的调整都是直观的,而在RGB颜色空间中调整颜色不那么直观。例如,在RGB空间中要调整红色系的颜色,需要同时调整R、G、B三个通道的数值,而在HSV空间中只需要调整色调和饱和度即可。
- 降维处理有利于计算:在图像处理中,降维处理可以减少计算的复杂性和计算量。HSV颜色空间相对于RGB颜色空间,减少了两个维度(红、绿、蓝),这有利于进行一些计算和处理任务,比如色彩分割、匹配等。
因此,在进行图片颜色识别时,我们会将RGB图像转换到HSV颜色空间,然后根据颜色区间来识别目标颜色。
6.5 RGB转Gray(灰度)
cv2.cvtColor是OpenCV中的一个函数,用于图像颜色空间的转换。可以将一个图像从一个颜色空间转换为另一个颜色空间,比如从RGB到灰度图,或者从RGB到HSV的转换等。
cv2.cvtColor(img,code)
img:输入图像,可以是一个Numpy数组绘着一个OpenCV的Mat对象Mat是一个核心的数据结构,主要用于存储图像和矩阵数据。在 Python 中使用 OpenCV 时,通常直接处理的是 NumPy 数组,cv2模块自动将Mat对象转换为 NumPy 数组。二者之间的转换是透明且自动完成的。例如,当你使用cv2.imread()函数读取图像时,返回的是一个 NumPy 数组,但在C++中则是Mat对象。
code:指定转换的类型,可以使用预定义的转换代码。- 例如
cv2.COLOR_RGB2GRAY表示从rgb到灰度图像的转换。
- 例如
例子:颜色转换
import cv2 as cv
# 读取图片
img1=cv.imread(r"./images/cat1.png")
# 转为灰度图
gray=cv.cvtColor(img1,cv.COLOR_BGR2GRAY)
cv.imshow("gray",gray)
# 转HSV
hsv=cv.cvtColor(img1,cv.COLOR_BGR2HSV)
cv.imshow("hsv",hsv)
cv.waitKey(0)
cv.destroyAllWindows()
DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。
更多推荐





所有评论(0)