上次写了图像变换-旋转问题,试一试?,后面留了个问题,本来就是随便说说的,留给大家一个探索的机会,刚好碰到最近事情也有点多,没空弄。
不过随着和有点意思同学在后台的不断留言交流,发现留了好多坑了,赶紧抽个时间,先填上一个再说。
下面开始正文。
下面有一个矩3*3的矩阵(你也可以看做二维列表)。
[[1 2 3]
[4 5 6]
[7 8 9]]
问题1:顺时针旋转90度,得到以下矩阵。
[[7 4 1]
[8 5 2]
[9 6 3]]
参考:
import numpy as np
a = np.array(np.arange(1,10)).reshape(3,3)
print(a)
n = len(a)
b = a.copy()
for i in range(n):
for j in range(n):
b[i][j] = a[n-1-j][i] # 顺时针旋转90
# b[i][j] = a[j][n - 1 - i] # 逆时针旋转90
print(b)
说明:这里的解法比较好理解,但是双重循环复杂度比较高,看了网上的解法,有四种,感兴趣的可以自己搜索。
问题2:对矩阵进行镜像操作
[[3 2 1]
[6 5 4]
[9 8 7]]
参考:
import numpy as np
a = np.array(np.arange(1,10)).reshape(3,3)
print(a)
b = a[:,::-1]
print(b)
问题3:上下翻转操作。
[[7 8 9]
[4 5 6]
[1 2 3]]
参考:
import numpy as np
a = np.array(np.arange(1,10)).reshape(3,3)
print(a)
b = a[::-1] # 上下翻转
print(b)
上面仅仅是以数字作为测试,真实图片,只需将a替换成图像矩阵即可,其余不变。
from PIL import Image
import numpy as np
img = Image.open("cat.png")
a = np.asarray(img)
...
上面的图像变换相对来说比较简单,主要就是像素的位置替换了一下。
不过除了上面的,还有一些其它的图像变换,比如图像缩放(放大、缩小),其它角度旋转、平移等各种操作;
这类几何变换,相对于前面提到的变换,尽管还是改同样变了原图像像素点在新图像中的空间位置,但是也引入了一些新的问题。
平移
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
# 平移
def move(image, delta_x,delta_y):
rows,cols = image.shape[:2]
empty_image = np.zeros(image.shape,dtype=np.uint8)
transform=np.array([delta_x,delta_y])
for row in range(rows):
for col in range(cols):
x1,y1 = np.array([row,col])+transform
if x1<rows and x1>=0 and y1<cols and y1 >=0:
empty_image[x1][y1]=image[row][col]
return empty_image
img = Image.open("cat.png")
image = np.asarray(img)
image = move(image,50,250)
plt.imshow(image)
plt.show()
平移比较简单,主要就是矩阵的加法,超出部分删除。
旋转
import numpy as np
import matplotlib.pyplot as plt
from PIL import Image
def rotate(image,angle):
cos = np.math.cos(np.math.radians(angle))
sin = np.math.sin(np.math.radians(angle))
transform = np.array([[cos, -sin],
[sin, cos]])
rows, cols = image.shape[:2]
empty_image = np.zeros(image.shape, dtype=np.uint8)
for row in range(rows):
for col in range(cols):
# 计算旋转后的坐标
x1,y1 = transform.dot(np.array([row,col]))
# 只计算范围之内的
if x1 < rows and x1 >= 0 and y1 < cols and y1 >= 0:
# 通过原图像的坐标计算旋转之后的坐标,并将相应的灰度值传给旋转后的图像
empty_image[int(x1)][int(y1)] = image[row][col]
return empty_image
img = Image.open("cat.png")
image = np.asarray(img)
image = rotate(image,-30)
plt.imshow(image)
plt.show()
旋转可以看做图片上的每个点旋转。
换算后得到下面的公式。
这里是以图片左上角旋转的,超出边界部分删除了。得到的结果出现了一些有规律的噪声,之所以出现这样的问题,是因为通过原图像的坐标计算旋转之后的坐标,并将相应的灰度值传给旋转后的图像。得到的坐标有些是小数,进行了取整操作,这样部分像素并没有对应的像素值。
要实现下面这种效果,首先需要以图像的中心作为中心点,然后采取后向映射的方法——即从旋转后的图像出发,找到对应的原图像的点,然后将原图像中的灰度值传递过来即可,这样旋转后的图像的每个像素肯定可以对应到原图像中的一个点。
具体实现方式可参考这两篇文章,都是一些数学运算,讲的比较清楚了:
https://blog.csdn.net/lkj345/article/details/50555870
https://www.jianshu.com/p/56a717173227
如果需要多次变换(缩放,旋转,平移),需要引入齐次坐标(在微信读书数字图像处理上看到的),通过齐次坐标,不管怎样变换,变换多少次,都可以表示成一连串的矩阵相乘了
例如先放大2倍,然后旋转45度,然后再缩小0.5倍。
而平移用的是矩阵加法,不通用,通过齐次坐标,下面两个结果是一样的,加法变成了乘法。
都是数学,线性代数当年觉得没啥用,现在发现真香,可惜没学好,详细的的为什么要引入齐次坐标,可以看下面这篇,通透:
https://blog.csdn.net/saltriver/article/details/79680364?spm=1001.2014.3001.5501
(全文完)