opencv笔记(1)图像的简单处理
图片读取,保存与展示
首先需要引用opencv
import cv2
读取文件用imread
img = cv2.imread("fileaddress\file")
然后保存文件用
cv2.imwrite("fileaddress\file",file)
这里file表示程序中要保存的文件。
读取文件时可以在函数中加各种参数,比如读取图片时读取灰度图片并且尺寸缩减为原图片的\(1/2\)。
cv.imread("fileaddress\file",cv2.IMREAD_GRAYSCALE_2)
展示图片时用imshow
cv2.imshow('title',file)
cv2.waitKey()
cv2.destroyAllWindows()
这里的file还是上文的file,表示之前读取的文件,通常在imshow函数后面需要搭配一个waitkey函数,否则可能卡出,原因待查。
读取的图片文件(file)有许多参数,比如形状和大小,形状反映维度,大小表示像素数。
例子
import cv2
img = cv2.imread("D:\salut.jpg")
print(img.size,img.shape)
cv2.imshow('salut',img)
cv2.waitKey()
cv2.destroyAllWindows()
图像的色彩空间处理
色彩空间是指一个色彩模型和其映射函数。色彩模型通过一组数字来描述颜色(RGB,YCrCb),映射函数则将色彩模型的数字映射到绝对色彩空间中去,绝对色彩空间就是我们经常看到的色彩空间。
OpenCv中有多种色彩转换函数,通过cv2.cvtColor转换,这个函数中包含的参数都以COLOR开头,可以通过
[x for x in dir(cv2) if x.startswith("COLOR")]
找到。
比如将礼炮一号的图片转化为灰度图片。
原图
我们读取并将图片转化为灰度图片
img = cv2.imread("D:\salut.jpg")
grey_img=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
cv2.imwrite("grey_salut.jpg",grey_img)
我们可以看到灰度图片的色彩空间只有一个频道。
图像的几何变换
仿射函数
不管是平移还是旋转,opencv进行这一系列操作都要基于warpAffine函数,顾名思义,warpaffine就是仿射弯曲函数,函数参数为一个仿射变换矩阵。
warpAffine(...)
warpAffine(src, M, dsize[, dst[, flags[, borderMode[, borderValue]]]]) -> dst
src表示输入图像,代表仿射矩阵,dsize表示输出图像,flags表示插值算法标识。
譬如对于图像\(A\),经过仿射变化后得到图像\(B\),其仿射变化可以用公式表示为
\[ B=AM+b \]
考虑对任意二维向量\(v\)存在\(v=(x,y)^t\)。对\(v\)进行仿射变化映射到同样的二维空间中,则有
\[ u=\begin{pmatrix}a_1 & c_1\\a_2 & c_2\end{pmatrix}\cdot\begin{pmatrix}x\\y\end{pmatrix}+\begin{pmatrix}b1\\b2\end{pmatrix}=\begin{pmatrix}a_1 & c_1 & b_1\\a_2 & c_2 & b_2\end{pmatrix}\times\begin{pmatrix}x\\y\\1\end{pmatrix} \]
因此用\(2\times3\)矩阵表示仿射变化。
平移变换
譬如我们对图像进行平移操作,则此是矩阵为\(\bigl( \begin{smallmatrix} 1 & 0 & x_t \\ 0 & 1 & y_t\end{smallmatrix} \bigr)\),其中\(x_t\)表示横向移动,y_t表示纵向移动。
按照如下代码
t_matrix=np.float32([[1,0,100],[0,1,100]])
num_col,num_row=img.shape[:2]
salut_t=cv2.warpAffine(img,t_matrix,(num_row,num_col))
则得到图像
上述图像因为进行了移动,但输出图像大小不变,就看起来像是被裁切了,因此我们改变输出参数,可以得到完整的被平移后的图像。
t_matrix=np.float32([[1,0,100],[0,1,100]])
num_col,num_row=img.shape[:2]
salut_t=cv2.warpAffine(img,t_matrix,(num_row+200,num_col+200))
则得到
原图尺寸为\((616, 800, 3)\),生成的新图为\((816, 1000, 3)\)。
旋转变换
至于旋转变化,我们先只考虑纯粹旋转,此时\(b\)为零向量,而仿射矩阵则为
\[ M=\begin{pmatrix} cos\theta & -sin\theta \\ sin\theta & cos\theta \\ \end{pmatrix} \]
其中\(\theta\)是变化后的度数。
进行旋转变化时可以用cv2自带的函数getRotationMatrix2D。
getRotationMatrix2D(center, angle, scale) -> retval
center为原图像旋转中心,angle为旋转角度,scale为缩放系数。函数进行如下计算
\[ \begin{bmatrix} \alpha & \beta & (1- \alpha ) \cdot \texttt{center.x} - \beta \cdot \texttt{center.y} \\ - \beta & \alpha & \beta \cdot \texttt{center.x} + (1- \alpha ) \cdot \texttt{center.y} \end{bmatrix} \]
其中
\[ \begin{array}{l} \alpha = \texttt{scale} \cdot \cos \texttt{angle} , \\ \beta = \texttt{scale} \cdot \sin \texttt{angle} \end{array} \]
我们输入一个以最左上端点为旋转中心,顺时针\(30°\),无缩放的矩阵,如下所示
t_matrix=cv2.getRotationMatrix2D((0,0),30,1)
salut_rotate=cv2.warpAffine(img,t_matrix,(num_col,num_row))
则得到下图
缩放变换
同样的,考虑缩放变换,我们也先用warpAffine函数。考虑有如下矩阵
\[ M=\begin{pmatrix} 2 & 0 & 0 \\ 0 & 2 &0 \\ \end{pmatrix} \]
上述矩阵将图像放大为原来的两倍,我们这里用最近邻插值,即某个点的值取离其最近点的。
t_matrix=np.float32([[2,0,0],[0,2,0]])
img_t=cv2.warpAffine(img,t_matrix,(num_row*2,num_col*2),flags=cv2.INTER_NEAREST)
得到下图
此图的尺寸为\((1600,1232,3)\)。
除了warpAffine,还可以直接用resize函数来缩放。resize函数是这样的
resize(src, dsize[, dst[, fx[, fy[, interpolation]]]]) -> dst
参数src是输入图片,dsize是大小,dst表示输出的图像,fx和fy分别表示水平和垂直方向的缩放系数,interpolation表示插值算法,同之前的flag参数。
依然取上面的例子,此时我们将图片缩小为原来的一半,有两种表达方式
img_resize=cv2.resize(img,(400,308),interpolation=cv2.INTER_LINEAR)
或者
img_resize=cv2.resize(img,None,fx=0.5,fy=0.5,interpolation=cv2.INTER_LINEAR)
两种结果是一样的。
输出图片为
仿射变换
仿射变换是对一个向量空间进行一此线性变换再进行一次平移,以变换到另一个向量空间的操作,我们之前做的操作基本都是仿射变换。
仿射变换也可以用其次方程组表示为
\[ u=\begin{pmatrix}x'\\y'\\1\end{pmatrix}=\begin{pmatrix}a_1 & c_1 & b_1\\a_2 & c_2 & b_2\\0 & 0 & 1\end{pmatrix}\times\begin{pmatrix}x\\y\\1\end{pmatrix} \]
在opencv中,考虑三角形经过仿射变换依然是三角形这一特点,我们可以通过给出两个向量空间的三组点,用getAffineTransform函数来求出仿射变换的矩阵,然后放到warpAffine中进行操作。
getAffineTransform(src, dst) -> retval
这个函数输入参数src(source)为原向量空间的三个点坐标,dst(destination)为要变换成的向量空间的三个点坐标。
我们对上述例子进行如此操作。
src_points=np.float32([[0,0],[0,799],[615,0]])
dst_points=np.float32([[0,0],[615,0],[0,799]])
matrix_t=cv2.getAffineTransform(src_points,dst_points)
m=cv2.warpAffine(img,matrix_t,(500,900))
得到如下图片
射影变换
我们给出一个光源,然后对一张图片进行投影后得到的新图片就是经过射影变换得到的图片。射影变换保证了图片的单映性。同上述的一样,我们需要求出射影变换所需要的矩阵,而求射影变换矩阵的函数为函数getPerspectiveTransform。
getPerspectiveTransform(src, dst[, solveMethod]) -> retval
其中输入参数src(source)为原向量空间的四个点坐标,dst(destination)为要变换成的向量空间的四个点坐标。
\[ \begin{bmatrix} x'_i \\ y'_i \\ t_i \end{bmatrix} = \texttt{map_matrix} \cdot \begin{bmatrix} x_i \\ y_i \\ 1 \end{bmatrix}\\dst(i)=(x'_i,y'_i), src(i)=(x_i, y_i), i=0,1,2,3 \]
我们需要四个点是因为射影平面具有如下性质
- 给定任意两个不同的点,恰有一条线会重合这两个点。
- 给定任意两条不同的线,恰有一个点会重合这两条线。
- 存在四个点,使得没有线可以重合两个以上的这些点。
还需要注意此时我们用的是warpPerspective函数,不是warpAffine函数。
于是我们进行下述操作
src_points = np.float32([[0,0], [cols-1,0], [0,rows-1], [cols-1,rows-1]])
dst_points = np.float32([[0,0], [cols-1,0], [int(0.33*cols),rows-1],[int(0.66*cols),rows-1]])
projective_matrix = cv2.getPerspectiveTransform(src_points, dst_points)
im = cv2.warpPerspective(img, projective_matrix, (cols,rows))
我们将图像的下面的两个顶点分别投影到底边的\(1/3\)和\(2/3\)处,得到了如下图片
本节内容主要参考OpenCV with Python By Example,作者Prateek Joshi。