基于python的数字图像处理--学习笔记(二)
基于python的简单图像矩阵变换:
使用opencv-python读取图片文件,并使用numpy和math等库对图片进行对称反转、旋转、平移、剪切等操作。
当使用cv2.imread
读入一个图片后,完全可以将读入的图片转换为nuparray矩阵,所有对图片灰度(色度)的变化,都可以表示为是对矩阵中像素位置对应的数据的变换操作。
也即,图像变换的本质是将像素点的坐标通过某一种函数关系,所有对图像的操作都是对像素矩阵的操作。在本次内容中,还并未涉及灰度值的变换,主要是图像像素坐标的迁移如平移或旋转。
在平移旋转中,变换后的图像和原图像的像素是一一对应的;而对于剪切等变换,会有原图像上多个像素对应变换后图像像素的情况。由此,即可引出,图像映射的前向和后向区分问题。
向前映射和向后映射:
假设变换前图像为I(x,y),变换后图像为I’(x’,y’),则变换前后的图像之间存在下列关系
由原图像像素表示出变换后的像素,以原图每个像素为基准计算被它影响的新图像素。
即为前向映射
由变换后的图像像素还原出原图像像素,以新图每个像素为基准计算影响它的原图像素。
即为后向映射
在这种情况下,我们知道输出图像上整数点位置(x’,y’)在变换前位于输入图像上的位置(x,y),一般来说这是个非整数点位置,利用其周围整数点位置的输入图像像素值进行插值,就得到了该点的像素值。我们遍历输出图像,经过坐标变换、插值两步操作,我们就能将其像素值一个个地计算出来,因此向后映射又叫图像填充映射。
前向映射可能会导致多对一的情况,也会出现目标点没有像素,但是原图像有的问题。工程上多使用反向映射,避免上述问题,同时减少计算量。但反向映射需要提前知道反变换,在变换复杂的场合,其反变换会很难求得。
图像旋转:
终于写到正题了,开始结合python实现对图像的简单变换。
前文说过,图像变换就是矩阵变换:图像旋转也即矩阵旋转。(x’,y’)表示一个点经过旋转后的新位置,(x,y)表示未旋转前的原始位置,θ为旋转角度,编程中以弧度为单位。
注意:计算多个点的旋转,需要将每个点位置分别代入公式计算。
-
由以上方法,可写出旋转代码。但运行后会发现有诸多问题,这里先按下不表。
import cv2 as cv import numpy as np import math def rotate(img, angle): height, width, _ = img.shape res = np.zeros((height, width, 3), np.uint8) #生成三通道原图片大小的变换后图片模板 anglePi = angle * np.pi / 180.0 for i in range(height):#y for j in range(width):#x # y = round(j * np.cos(anglePi) - i * np.sin(anglePi)) 前向映射 # x = round(j * np.sin(anglePi) + i * np.cos(anglePi)) # 后向映射,下面为逆矩阵 srcY = (j * np.cos(anglePi) + i * np.sin(anglePi)) srcX = (-j * np.sin(anglePi) + i * np.cos(anglePi)) # 后向映射+双线性插值 x = math.floor(srcX) y = math.floor(srcY) u = srcX - x v = srcY - y if 0 <= x <= 255 and 0 <= y <= 255: res[i, j] = (1 - u) * (1 - v) * img[x, y] + u * (1 - v) * img[x + 1, y] + (1 - u) * v * img[x, y + 1] + u * v * img[x + 1, y + 1] # res[i, j] = img[x, y] # res[x, y] = img[i, j] print(res.shape) return res if __name__ == '__main__': img = cv.imread("pic1.jpg") cv.imshow("rotated img"