700字范文,内容丰富有趣,生活中的好帮手!
700字范文 > 图像的几何变换—平移 旋转 镜像 缩放 剪切(原理+调用函数+像素操作)

图像的几何变换—平移 旋转 镜像 缩放 剪切(原理+调用函数+像素操作)

时间:2019-11-27 20:40:09

相关推荐

图像的几何变换—平移 旋转 镜像 缩放 剪切(原理+调用函数+像素操作)

目录

一、平移1.调用函数(平移矩阵)2.像素操作(遍历赋值) 二、旋转1.调用函数(旋转矩阵)2.像素操作(反向映射) 三、镜像1.调用函数(镜像矩阵)2.像素操作(反向映射) 四、缩放1.缩小(八邻域均值)2.放大(双线性插值) 五、剪切索引切片

一、平移

1.调用函数(平移矩阵)

图像平移后的坐标:

[ x ′ y ′ 1 ] = [ 1 0 Δ x 0 1 Δ y 0 0 1 ] ⋅ [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} =\begin{bmatrix} 1&0&\Delta x \\ 0&1&\Delta y \\ 0&0&1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} ⎣ ⎡​x′y′1​⎦ ⎤​=⎣ ⎡​100​010​ΔxΔy1​⎦ ⎤​⋅⎣ ⎡​xy1​⎦ ⎤​

{ x ′ = x + Δ x y ′ = y + Δ y \begin{cases} x' =x + \Delta x \\ y' =y + \Delta y \end{cases} {x′=x+Δxy′=y+Δy​

warpAffine函数用法参考这里:warpAffine函数

程序实现及运行结果:

import cv2import matplotlib.pyplot as pltimport numpy as np# 读取图像img1 = cv2.imread("flower.jpeg")# 图像平移h, w = img1.shape[:2]M = np.float32([[1, 0, 100], [0, 1, 50]]) # 平移矩阵,y方向向下平移50,x方向向右平移100dst = cv2.warpAffine(img1, M, (w, h))# 图像显示fig, axes = plt.subplots(1, 2, figsize=(10, 8), dpi=100)axes[0].imshow(img1[:, :, ::-1])axes[0].set_title("original")axes[1].imshow(dst[:, :, ::-1])axes[1].set_title("after translation")plt.show()

2.像素操作(遍历赋值)

算法思想很简单,首先将所有像素点沿x方向向右平移100,一行一行的进行处理,将(0,100)处的彩色值赋值给(0,0)处,(0,101)处的彩色值赋值给(0,1)处,然后把第0行所有列(521列)处理完。之后再处理下一行。同理沿着y方向向下平移50也是差不多这个思想。

至于这个从520到0也是有讲究的,因为我们为了避免移动过程发生覆盖而数据丢失,所以需要倒序处理。打个比方,第0行,假如我们从0列到520列,那么100列之后的值就会被覆盖,这样我们就不能正确将原来100列之后的初始值进行移动(赋值),而我们从第520列处理就可以完美的避免这个问题。

程序实现及运行结果:

import cv2import matplotlib.pyplot as pltimg1 = cv2.imread('flower.jpeg')img2 = img1.copy()# x方向向右平移100for j in range(521):for i in range(520,-1,-1):if i<100:img2[j, i] = 0else:img2[j, i] = img2[j, i-100]# y方向向下平移50for u in range(521):for v in range(520,-1,-1):if v<50:img2[v, u] = 0else:img2[v, u] = img2[v-50, u]# 图像显示fig, axes = plt.subplots(1, 2, figsize=(10, 8), dpi=100)axes[0].imshow(img1[:, :, ::-1])axes[0].set_title("original")axes[1].imshow(img2[:, :, ::-1])axes[1].set_title("after translation")plt.show()

二、旋转

1.调用函数(旋转矩阵)

极坐标:

{ x = r ⋅ c o s α y = r ⋅ s i n α \begin{cases} x =r \cdot cos\alpha \\ y =r \cdot sin\alpha \end{cases} {x=r⋅cosαy=r⋅sinα​

{ x ′ = r ⋅ c o s ( α + θ ) = r ⋅ c o s α ⋅ c o s θ − r ⋅ s i n α ⋅ s i n θ = x ⋅ c o s θ − y ⋅ s i n θ y ′ = r ⋅ s i n ( α + θ ) = r ⋅ s i n α ⋅ c o s θ + r ⋅ c o s α ⋅ s i n θ = x ⋅ s i n θ + y ⋅ c o s θ \begin{cases} x' =r \cdot cos(\alpha+\theta)\\ \hspace{0.4cm} =r \cdot cos\alpha \cdot cos\theta - r \cdot sin\alpha \cdot sin\theta\\ \hspace{0.4cm} =x \cdot cos\theta - y \cdot sin\theta\\ \\ y' =r \cdot sin(\alpha+\theta)\\ \hspace{0.4cm} =r \cdot sin\alpha \cdot cos\theta + r \cdot cos\alpha \cdot sin\theta\\ \hspace{0.4cm} =x \cdot sin\theta + y \cdot cos\theta\\ \end{cases} ⎩ ⎨ ⎧​x′=r⋅cos(α+θ)=r⋅cosα⋅cosθ−r⋅sinα⋅sinθ=x⋅cosθ−y⋅sinθy′=r⋅sin(α+θ)=r⋅sinα⋅cosθ+r⋅cosα⋅sinθ=x⋅sinθ+y⋅cosθ​

直角坐标:

[ x ′ y ′ 1 ] = [ c o s θ − s i n θ 0 s i n θ c o s θ 0 0 0 1 ] ⋅ [ x y 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} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} ⎣ ⎡​x′y′1​⎦ ⎤​=⎣ ⎡​cosθsinθ0​−sinθcosθ0​001​⎦ ⎤​⋅⎣ ⎡​xy1​⎦ ⎤​

{ x ′ = x ⋅ c o s θ − y ⋅ s i n θ y ′ = x ⋅ s i n θ + y ⋅ c o s θ \begin{cases} x' =x \cdot cos\theta - y \cdot sin\theta\\ y' =x \cdot sin\theta + y \cdot cos\theta \end{cases} {x′=x⋅cosθ−y⋅sinθy′=x⋅sinθ+y⋅cosθ​

程序实现及运行结果:

import cv2import matplotlib.pyplot as pltimport numpy as npimport math# 读取图像img1 = cv2.imread("blossom.jpeg")# 图像旋转h, w = img1.shape[:2]M = np.float32([[math.cos(math.pi/4), -math.sin(math.pi/4), 0], [math.sin(math.pi/4), math.cos(math.pi/4), 0]]) # 顺时针旋转45°dst = cv2.warpAffine(img1, M, (w, h))# 图像显示fig, axes = plt.subplots(1, 2, figsize=(10, 8), dpi=100)axes[0].imshow(img1[:, :, ::-1])axes[0].set_title("original")axes[1].imshow(dst[:, :, ::-1])axes[1].set_title("after rotation")plt.show()

2.像素操作(反向映射)

此程序运用了一种反向映射的思想,这样可以确保旋转后的位置坐标都有彩色值,我们根据旋转矩阵的逆,可以找到旋转后的图像坐标所对应原图像的彩色值,从而对其进行赋值即可

其中我们需要注意两个问题,一是我们旋转后的坐标位置所对应原图像的坐标可能是越界的(找不到坐标与其对应);二是我们求的坐标向量可能是负数,需要舍去。

程序实现及运行结果:

import cv2import matplotlib.pyplot as pltimport numpy as npimport math# 读取图像img1 = cv2.imread("blossom.jpeg")img2 = img1.copy()# 图像旋转(反向映射)RM1 = np.float32([[math.cos(math.pi/4), math.sin(math.pi/4), 0], [-math.sin(math.pi/4), math.cos(math.pi/4), 0], [0, 0, 1]]) # 计算出的旋转矩阵RM2 = np.linalg.inv(RM1) # 求解旋转矩阵的逆for i in range(521):for j in range(521):D = np.dot(RM2, [[i], [j], [1]]) # 旋转后的图像坐标位置 相对应的 原图像坐标位置if int(D[0])>=521 or int(D[1])>=521: # 旋转后的图像坐标 相对应的 原图像坐标位置 越界img2[i, j] = 0elif int(D[0]) < 0 or int(D[1]) < 0: # 旋转后的图像坐标 相对应的 原图像坐标位置 负值img2[i, j] = 0else:img2[i, j] = img1[int(D[0]), int(D[1])]# 图像显示fig, axes = plt.subplots(1, 2, figsize=(10, 8), dpi=100)axes[0].imshow(img1[:, :, ::-1])axes[0].set_title("original")axes[1].imshow(img2[:, :, ::-1])axes[1].set_title("after rotation")plt.show()

三、镜像

水平镜像:

{ x ′ = w i d t h − 1 − x y ′ = y \begin{cases} x' =width-1-x\\ y' =y \end{cases} {x′=width−1−xy′=y​

[ x ′ y ′ 1 ] = [ − 1 0 w i d t h − 1 0 1 0 0 0 1 ] ⋅ [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} =\begin{bmatrix} -1&0&width-1 \\ 0&1&0 \\ 0&0&1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} ⎣ ⎡​x′y′1​⎦ ⎤​=⎣ ⎡​−100​010​width−101​⎦ ⎤​⋅⎣ ⎡​xy1​⎦ ⎤​

垂直镜像:

{ x ′ = x y ′ = h e i g h t − 1 − y \begin{cases} x' =x\\ y' =height-1-y \end{cases} {x′=xy′=height−1−y​

[ x ′ y ′ 1 ] = [ 1 0 0 0 − 1 h e i g h t − 1 0 0 1 ] ⋅ [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} =\begin{bmatrix} 1&0&0 \\ 0&-1&height-1 \\ 0&0&1 \end{bmatrix} \cdot \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} ⎣ ⎡​x′y′1​⎦ ⎤​=⎣ ⎡​100​0−10​0height−11​⎦ ⎤​⋅⎣ ⎡​xy1​⎦ ⎤​

1.调用函数(镜像矩阵)

import cv2import matplotlib.pyplot as pltimport numpy as np# 读取图像img1 = cv2.imread("technology building.jpeg")# 图像水平镜像h, w = img1.shape[:2]M = np.float32([[-1, 0, 520], [0, 1, 0]])dst1 = cv2.warpAffine(img1, M, (w, h))# 图像垂直镜像h, w = img1.shape[:2]M = np.float32([[1, 0, 0], [0, -1, 520]])dst2 = cv2.warpAffine(img1, M, (w, h))# 图像显示fig, axes = plt.subplots(1, 3, figsize=(80, 10), dpi=100)axes[0].imshow(img1[:, :, ::-1])axes[0].set_title("original")axes[1].imshow(dst1[:, :, ::-1])axes[1].set_title("after horizontal-mirror")axes[2].imshow(dst2[:, :, ::-1])axes[2].set_title("after vertical-mirror")plt.show()

2.像素操作(反向映射)

import cv2import matplotlib.pyplot as pltimport numpy as np# 读取图像img1 = cv2.imread("technology building.jpeg")img2 = img1.copy()img3 = img1.copy()# 图像水平镜像RM1 = np.float32([[1, 0, 0], [0, -1, 520], [0, 0, 1]]) # 计算出的旋转矩阵RM2 = np.linalg.inv(RM1) # 求解旋转矩阵的逆for i in range(521):for j in range(521):D = np.dot(RM2, [[i], [j], [1]]) # 旋转后的图像坐标位置 相对应的 原图像坐标位置if int(D[0])>=521 or int(D[1])>=521: # 旋转后的图像坐标 相对应的 原图像坐标位置 越界img2[i, j] = 0elif int(D[0]) < 0 or int(D[1]) < 0: # 旋转后的图像坐标 相对应的 原图像坐标位置 负值img2[i, j] = 0else:img2[i, j] = img1[int(D[0]), int(D[1])]# 图像垂直镜像RM3 = np.float32([[-1, 0, 520], [0, 1, 0], [0, 0, 1]]) # 计算出的旋转矩阵RM4 = np.linalg.inv(RM3) # 求解旋转矩阵的逆for i in range(521):for j in range(521):D = np.dot(RM4, [[i], [j], [1]]) # 旋转后的图像坐标位置 相对应的 原图像坐标位置if int(D[0])>=521 or int(D[1])>=521: # 旋转后的图像坐标 相对应的 原图像坐标位置 越界img3[i, j] = 0elif int(D[0]) < 0 or int(D[1]) < 0: # 旋转后的图像坐标 相对应的 原图像坐标位置 负值img3[i, j] = 0else:img3[i, j] = img1[int(D[0]), int(D[1])]# 图像显示fig, axes = plt.subplots(1, 3, figsize=(80, 10), dpi=100)axes[0].imshow(img1[:, :, ::-1])axes[0].set_title("original")axes[1].imshow(img2[:, :, ::-1])axes[1].set_title("after horizontal-mirror")axes[2].imshow(img3[:, :, ::-1])axes[2].set_title("after vertical-mirror")plt.show()

四、缩放

1.缩小(八邻域均值)

算法思想很简单,就是我们遍历原图像的每一个像素,原图像(1080×1437)的每一个像素与自己所在的八邻域(9个像素值)取平均值,然后将像素放进模板(360×479)中形成新的图像。相当于我们用一个3×3的卷积核对原图像进行卷积操作

一共需要注意两个问题,一是我们先对边界元素扩充一行一列,这样原图像边界也可以很好的进行卷积操作而不越界;二是我们在进行求和会超出255而越界(每个通道像素值0-255,比如我们用230+230,不会得到460,而是会变成460-255-1=204),如果不处理,就会让我们的缩小图像变成黑色,所以我这里是先将所有值缩小10倍相加再放大10呗,当然你也可以选择使用其它方式。

程序实现及运行结果:

import cv2# 读取图像img1 = cv2.imread("city.jpeg")img3 = cv2.imread("img3.png") # 制作一个模板# 图像缩小img2 = cv2.copyMakeBorder(img1, 1, 1, 1, 1, cv2.BORDER_REFLECT) # 上下左右各扩充1,复制最近像素# 索引列表(位置坐标)list1 = list(range(1, 1080, 3))list2 = list(range(360))list3 = list(range(1, 1435, 3))list4 = list(range(479))# 一个像素点自身与八邻域取均值for i,u in zip(list1, list2):for j,v in zip(list3, list4):# 防止越界img3[u, v] = ((img2[i,j]/10 + img2[i-1,j-1]/10 + img2[i-1,j]/10 + img2[i,j+1]/10 + img2[i,j-1]/10 + img2[i,j+1]/10 + img2[i+1,j-1]/10 + img2[i+1,j]/10 + img2[i+1,j+1]/10) / 9)*10# 保存图像cv2.imwrite('shrink.jpeg',img3)

original:

shrink:

2.放大(双线性插值)

单线性插值:

y − y 0 x − x 0 = y 1 − y 0 x 1 − x 0 \boxed {\frac {y-y_0} {x-x_0}=\frac {y_1-y_0} {x_1-x_0}} x−x0​y−y0​​=x1​−x0​y1​−y0​​​

y = x 1 − x x 1 − x 0 ⋅ y 0 + x − x 0 x 1 − x 0 ⋅ y 1 = d 1 d ⋅ y 0 + d 0 d ⋅ y 1 \boxed {y = \frac {x_1-x} {x_1-x_0} \cdot y_0+\frac {x-x_0} {x_1-x_0} \cdot y_1}={ \frac {d_1} {d} \cdot y_0+\frac {d_0} {d} \cdot y_1} y=x1​−x0​x1​−x​⋅y0​+x1​−x0​x−x0​​⋅y1​​=dd1​​⋅y0​+dd0​​⋅y1​

这个公式表示的结果就是:x1和x0分别到x的距离作为权重,用于对y0和y1的加权。d0/d > d1/d,而y1到y < y0到y 说明y1比重更大,所以乘以d0/d。

双线性插值:

①x方向的单线性插值去分别计算R1、R2的像素值:

f ( x , y 1 ) = x 2 − x x 2 − x 1 ⋅ f ( Q 11 ) + x − x 0 x 1 − x 0 ⋅ f ( Q 21 ) \boxed {f(x,y_1) = \frac {x_2-x} {x_2-x_1} \cdot f(Q_{11})+\frac {x-x_0} {x_1-x_0} \cdot f(Q_{21})} f(x,y1​)=x2​−x1​x2​−x​⋅f(Q11​)+x1​−x0​x−x0​​⋅f(Q21​)​

f ( x , y 2 ) = x 2 − x x 2 − x 1 ⋅ f ( Q 12 ) + x − x 0 x 1 − x 0 ⋅ f ( Q 22 ) \boxed {f(x,y_2)= \frac {x_2-x} {x_2-x_1} \cdot f(Q_{12})+\frac {x-x_0} {x_1-x_0} \cdot f(Q_{22})} f(x,y2​)=x2​−x1​x2​−x​⋅f(Q12​)+x1​−x0​x−x0​​⋅f(Q22​)​

②y方向的单线性插值计算P点的像素值:

f ( x , y ) ≈ y 2 − y y 2 − y 1 ⋅ f ( x , y 1 ) + y − y 1 y 2 − y 1 ⋅ f ( x , y 2 ) \boxed {f(x,y) \approx \frac {y_2-y} {y_2-y_1} \cdot f(x,y_1)+\frac {y-y_1} {y_2-y_1} \cdot f(x,y_2)} f(x,y)≈y2​−y1​y2​−y​⋅f(x,y1​)+y2​−y1​y−y1​​⋅f(x,y2​)​

算法设计:

假设我们取原图像的四个像素(如图所示15、16行,20、21列)(黑圈和红圈),像素值分别为P1、P2、P3、P4。

以我们需要插值在(14.3,20.3)为例子,根据双线性插值原理计算:

Q12 = 0.3×P2 + 0.7×P1

Q34 = 0.3×P4 + 0.7×P3

PQ = 0.3×Q34 + 0.7×Q12

这样我们就可以得到绿色三角的像素值,根据每个插值的像素和原图像四个像素的距离得出它们各自的系数(权重),并能得到中间的(黄蓝紫黄)的像素值。

最后,我们再把这四个像进行相应的外扩(如第三块图所示),就可以把原来2×2的图像放大成一幅4×4的图像。(继承了原图像的4个像素值,利用双线性插值得到了新的12个像素值)

程序实现及运行结果:

import cv2import matplotlib.pyplot as plt# 定义一个类(双线性插值计算)class bilinear_interpolation:P1 = P2 = P3 = P4 = 0# 计算图中绿色三角像素值def green(self):Q12_green = 0.3 * self.P2 + 0.7 * self.P1Q34_green = 0.3 * self.P4 + 0.7 * self.P3PQ_green = 0.3 * Q34_green + 0.7 * Q12_greenreturn PQ_green# 计算图中蓝色三角像素值def blue(self):Q12_blue = 0.6 * self.P2 + 0.4 * self.P1Q34_blue = 0.6 * self.P4 + 0.4 * self.P3PQ_blue = 0.3 * Q34_blue + 0.7 * Q12_bluereturn PQ_blue# 计算图中紫色三角像素值def purple(self):Q12_purple = 0.3 * self.P2 + 0.7 * self.P1Q34_purple = 0.3 * self.P4 + 0.7 * self.P3PQ_purple = 0.6 * Q34_purple + 0.4 * Q12_purplereturn PQ_purple# 计算图中黄色三角像素值def yellow(self):Q12_yellow = 0.6 * self.P2 + 0.4 * self.P1Q34_yellow = 0.6 * self.P4 + 0.4 * self.P3PQ_yellow= 0.6 * Q34_yellow + 0.4 * Q12_yellowreturn PQ_yellowBI = bilinear_interpolation()# 读取图像img1 = cv2.imread("sunset.jpg")img2 = cv2.imread("img4.png") # 制作一个模板# 索引列表(位置坐标)list1 = list(range(0, 322, 2))list2 = list(range(0, 644, 4))list3 = list(range(0, 500, 2))list4 = list(range(0, 1000, 4))# 放大图像(双线性插值)for i,u in zip(list1, list2):for j,v in zip(list3, list4):img2[u, v], img2[u, v+3], img2[u+3, v], img2[u+3, v+3] = img1[i, j], img1[i, j+1], img1[i+1, j], img1[i+1, j+1] # 原图像未更改像素值BI.P1 = img1[i, j]; BI.P2 = img1[i, j + 1]; BI.P3 = img1[i + 1, j]; BI.P4 = img1[i + 1, j + 1]img2[u, v+1] = img2[u+1, v] = img2[u+1, v+1] =BI.green()img2[u, v+2] = img2[u+1, v+3] = img2[u+1, v+2] =BI.blue()img2[u+2, v] = img2[u+3, v+1] = img2[u+2, v+1] =BI.purple()img2[u+3, v+2] = img2[u+2, v+3] = img2[u+2, v+2] =BI.yellow()# 保存图像cv2.imwrite('enlarged.jpeg',img2)

original:

enlarged:

五、剪切

索引切片

程序实现及运行结果:

import cv2import matplotlib.pyplot as plt# 读取图像img1 = cv2.imread("podoid.jpeg")print(img1.shape) # (1290, 1080, 3)# 图像剪切img2 = img1[420:800, 300:800]print(img2.shape) # (380, 500, 3)# 图像显示fig, axes = plt.subplots(1, 2, figsize=(10, 8), dpi=100)axes[0].imshow(img1[:, :, ::-1])axes[0].set_title("original")axes[1].imshow(img2[:, :, ::-1])axes[1].set_title("shear")plt.show()

本文代码已开源,欢迎大家进行二次开发:/xiaolong_ROS/Graphics-Processing-and-Machine-Vision

如有错误或者不足之处,欢迎大家留言指正!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。