图像数字化——图像变换:旋转、平移(不调用库函数)
图像变换:旋转、平移
一、图像旋转实现
数学原理:
图像旋转可以通过一个标准的旋转矩阵来实现。给定一个原始坐标 (x,y)(x, y)(x,y),我们可以通过旋转矩阵将其映射到新坐标 (x′,y′)(x', y')(x′,y′)。
旋转矩阵:
旋转变换的基本形式是通过一个角度 θ\thetaθ 来确定旋转矩阵。对于二维空间中的点 (x,y)(x, y)(x,y),旋转矩阵形式为:
[x′y′]=[cosθ−sinθsinθcosθ][xy] \begin{bmatrix} x' \\ y' \end{bmatrix} = \begin{bmatrix} \cos\theta & -\sin\theta \\ \sin\theta & \cos\theta \end{bmatrix} \begin{bmatrix} x \\ y \end{bmatrix} [x′y′]=[cosθsinθ−sinθcosθ][xy]
其中:
- x′x'x′ 和 y′y'y′ 是旋转后的新坐标。
- θ\thetaθ 是旋转角度,顺时针为正方向。
- xxx 和 yyy 是原始坐标。
为了进行旋转,我们只需要将原坐标 (x,y)(x, y)(x,y) 通过上述旋转矩阵进行转换。可以理解为,矩阵中的元素 cosθ\cos\thetacosθ 和 sinθ\sin\thetasinθ 调整了坐标系的方向,使得坐标点沿着指定的角度旋转。
1.1 计算旋转后画布的尺寸
为了避免旋转后图像被截断,我们需要重新计算旋转后的图像尺寸。旋转后的图像宽度 W′W'W′ 和高度 H′H'H′ 可以通过以下公式计算:
W′=W∣cosθ∣+H∣sinθ∣H′=W∣sinθ∣+H∣cosθ∣ W' = W |\cos\theta| + H |\sin\theta| \\ H' = W |\sin\theta| + H |\cos\theta| W′=W∣cosθ∣+H∣sinθ∣H′=W∣sinθ∣+H∣cosθ∣
其中:
- WWW 和 HHH 分别是原图的宽度和高度。
- W′W'W′ 和 H′H'H′ 是旋转后图像的宽度和高度。
- θ\thetaθ 是旋转角度。
这两个公式的意义在于,通过将原图的宽度和高度与旋转角度的三角函数相乘,得到新的尺寸,以确保旋转后的图像不会被裁切。
1.2 逆向映射像素
旋转后的图像需要从原图中逆向映射像素,即我们通过新的图像坐标 (x′,y′)(x', y')(x′,y′) 找到对应的原图坐标 (x,y)(x, y)(x,y)。逆向映射的公式如下:
[xy]=[cosθsinθ−sinθcosθ][x′−cx′y′−cy′]+[cxcy] \begin{bmatrix} x \\ y \end{bmatrix} = \begin{bmatrix} \cos\theta & \sin\theta \\ -\sin\theta & \cos\theta \end{bmatrix} \begin{bmatrix} x' - c_x' \\ y' - c_y' \end{bmatrix} + \begin{bmatrix} c_x \\ c_y \end{bmatrix} [xy]=[cosθ−sinθsinθcosθ][x′−cx′y′−cy′]+[cxcy]
其中:
- (x,y)(x, y)(x,y) 是原图中的像素坐标。
- (x′,y′)(x', y')(x′,y′) 是旋转后图像中的像素坐标。
- (cx,cy)(c_x, c_y)(cx,cy) 和 (cx′,cy′)(c_x', c_y')(cx′,cy′) 分别是原图和旋转后图像的中心坐标。
这一步的目的是计算旋转后图像中每个像素在原图中的对应位置,进而对每个像素进行赋值。
1.3 双线性插值
由于旋转后的图像坐标 (x′,y′)(x', y')(x′,y′) 通常是浮动的,所以我们需要对原图中的像素值进行插值计算,常用的方法是双线性插值。双线性插值公式如下:
假设我们已经得到了逆向映射的原图坐标 (xorig,yorig)(x_{\text{orig}}, y_{\text{orig}})(xorig,yorig),并且该坐标位置在原图上处于四个相邻像素的范围内。我们使用四个相邻像素的值来计算新像素值:
I(x′,y′)=(1−dx)(1−dy)I(x1,y1)+dx(1−dy)I(x2,y1)+(1−dx)dyI(x1,y2)+dxdyI(x2,y2) I(x', y') = (1 - dx)(1 - dy) I(x_1, y_1) + dx(1 - dy) I(x_2, y_1) + (1 - dx) dy I(x_1, y_2) + dx dy I(x_2, y_2) I(x′,y′)=(1−dx)(1−dy)I(x1,y1)+dx(1−dy)I(x2,y1)+(1−dx)dyI(x1,y2)+dxdyI(x2,y2)
其中:
- (x1,y1),(x2,y1),(x1,y2),(x2,y2)(x_1, y_1), (x_2, y_1), (x_1, y_2), (x_2, y_2)(x1,y1),(x2,y1),(x1,y2),(x2,y2) 是相邻的四个像素位置。
- dxdxdx 和 dydydy 是 (xorig,yorig)(x_{\text{orig}}, y_{\text{orig}})(xorig,yorig) 相对四个像素的距离。
这种插值方式能够有效平滑图像,减少旋转带来的锯齿现象。
二、图像平移实现
数学原理:
图像平移是指将图像中的每个像素点按照指定的距离 Δx\Delta xΔx 和 Δy\Delta yΔy 在水平和垂直方向上进行移动。平移变换的数学表达式为:
{x′=x+Δxy′=y+Δy \begin{cases} x' = x + \Delta x \\ y' = y + \Delta y \end{cases} {x′=x+Δxy′=y+Δy
其中:
- (x,y)(x, y)(x,y) 是原图中的像素坐标。
- (x′,y′)(x', y')(x′,y′) 是平移后图像中的像素坐标。
- Δx\Delta xΔx 和 Δy\Delta yΔy 是水平和垂直方向上的平移距离。
2.1 平移的步骤
- 创建空白画布:根据平移量生成一个新的画布,尺寸为原图尺寸。
- 计算每个像素的新坐标:通过平移公式 (x′,y′)=(x+Δx,y+Δy)(x', y') = (x + \Delta x, y + \Delta y)(x′,y′)=(x+Δx,y+Δy) 计算每个像素的平移后坐标。
- 边界检查:确保新的坐标在新画布的有效范围内,如果坐标越界,则跳过该像素。
- 复制像素值:将每个像素从原图复制到新图的位置。
平移操作不会影响图像的结构,仅仅是移动图像的位置。
三、关键参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
angle_deg | float | 旋转角度(顺时针为正) |
dx, dy | int | 平移量(像素单位) |
cos_val, sin_val | float | 旋转矩阵的元素(cosθ\cos\thetacosθ 和 sinθ\sin\thetasinθ) |
cx, cy | int | 图像中心坐标 |
new_cx, new_cy | int | 旋转后图像中心坐标 |
四、性能优化提示
-
预先计算旋转矩阵的逆矩阵:旋转矩阵的逆矩阵计算量较大,可以在旋转开始时预先计算好,避免重复计算。
-
Numpy向量化操作替代循环:通过利用Numpy的广播机制,可以将像素的计算过程向量化,从而加速图像的变换过程,避免逐个像素的循环操作。
-
边界像素的特殊处理:在处理旋转和平移时,图像边界的像素可能会超出画布的范围。通过对边界像素进行特殊处理,减少不必要的判断和计算,可以提升效率。对于旋转,可以使用填充法(如边界填充或者镜像填充);对于平移,可以根据平移量直接跳过无效的像素位置。
五、代码实现
这个代码示例实现了图像的旋转和平移功能,接下来会分块详细解释每个部分的实现原理。
1. 导入必要的库
import numpy as np
from PIL import Image
import matplotlib.pyplot as plt
import math
numpy:用于处理图像数据的数组操作。PIL:Python Imaging Library,用于加载和处理图像。matplotlib.pyplot:用于显示图像的可视化。math:提供数学函数,例如三角函数,帮助进行图像旋转。
2. 手动实现图像旋转
def manual_rotate(image_array, angle_deg):
"""
手动实现图像旋转(不使用OpenCV)
:param image_array: 输入图像数组 (H,W,C)
:param angle_deg: 旋转角度(度)
:return: 旋转后的图像数组
"""
- 该函数将图像数组作为输入,并接受一个旋转角度(单位为度)。
- 图像旋转采用逆向映射方式,即将旋转后的每个像素点逆向映射到原图上。
旋转计算过程
-
角度转换为弧度
angle_rad = math.radians(angle_deg)旋转角度是用度表示的,需要转换为弧度进行计算。
-
计算旋转后的画布尺寸
cos_val = math.cos(angle_rad) sin_val = math.sin(angle_rad) new_w = int(w * abs(cos_val) + h * abs(sin_val)) new_h = int(w * abs(sin_val) + h * abs(cos_val))计算旋转后的图像宽度
new_w和高度new_h,确保画布能容纳整个旋转后的图像。 -
创建新画布
rotated = np.zeros((new_h, new_w, 3), dtype=np.uint8)创建一个新的空白画布,大小为旋转后的画布尺寸,初始化为零。
-
逆向映射
x_orig = (x - new_cx) * cos_val + (y - new_cy) * sin_val + cx y_orig = -(x - new_cx) * sin_val + (y - new_cy) * cos_val + cy对于旋转后的每个像素点
(x, y),通过逆向映射公式计算在原图中的对应位置(x_orig, y_orig)。 -
双线性插值
# 双线性插值 if 0 <= x_orig < w-1 and 0 <= y_orig < h-1: x1, y1 = int(x_orig), int(y_orig) x2, y2 = min(x1 + 1, w - 1), min(y1 + 1, h - 1) dx = x_orig - x1 dy = y_orig - y1 # 对每个通道进行插值 for c in range(3): val = (1-dx)*(1-dy)*image_array[y1,x1,c] + \ dx*(1-dy)*image_array[y1,x2,c] + \ (1-dx)*dy*image_array[y2,x1,c] + \ dx*dy*image_array[y2,x2,c] rotated[y,x,c] = int(val)使用双线性插值来计算每个像素点的值,这样可以避免旋转过程中产生锯齿状的效果。根据四个邻近像素的值,计算出新像素点的插值。
3. 手动实现图像平移
def manual_translate(image_array, dx, dy):
"""
手动实现图像平移(不使用OpenCV)
:param image_array: 输入图像数组 (H,W,C)
:param dx: 水平平移量(像素)
:param dy: 垂直平移量(像素)
:return: 平移后的图像数组
"""
- 该函数将图像数组作为输入,并接受水平和垂直平移量(单位为像素)。
平移计算过程
-
创建空白画布
translated = np.zeros_like(image_array)创建一个与原图像同样大小的空白画布。
-
计算平移后的坐标
x_new = x - dx y_new = y - dy对于每个像素
(x, y),根据平移量dx和dy计算平移后的新坐标(x_new, y_new)。 -
边界检查
if 0 <= x_new < w and 0 <= y_new < h: translated[y,x] = image_array[y_new, x_new]检查平移后的新坐标是否在图像范围内。如果在范围内,将对应的像素值复制到新图像中。
4. 显示图像对比
def display_comparison(original, transformed, title):
"""显示对比结果"""
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(original)
plt.title('Original Image')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(transformed)
plt.title(title)
plt.axis('off')
plt.tight_layout()
plt.show()
- 使用
matplotlib显示原图和变换后的图像,便于对比效果。
5. 主程序
if __name__ == "__main__":
try:
# 读取测试图像
img = Image.open('imori.jpg')
img_array = np.array(img)
# 旋转示例(45度)
rotated_img = manual_rotate(img_array, 45)
display_comparison(img_array, rotated_img, 'Rotated 45°')
# 平移示例(x=50, y=30)
translated_img = manual_translate(img_array, 50, 30)
display_comparison(img_array, translated_img, 'Translated (50,30)')
except FileNotFoundError:
print("错误:找不到图像文件 'imori.jpg'")
except Exception as e:
print(f"发生错误:{str(e)}")
- 主程序加载测试图像
imori.jpg,然后展示旋转和平移的效果。 - 使用
try...except结构来处理文件未找到和其他错误。
6. 总结
- 旋转:通过计算旋转矩阵,并使用逆向映射和双线性插值处理旋转后的像素点。
- 平移:直接通过计算每个像素的平移位置,并进行边界检查,确保图像内容的正确迁移。
以上代码在没有依赖 OpenCV 等库的情况下,手动实现了图像的旋转和平移功能,适合了解图像变换原理的学习者使用。
7.结果
展示相应旋转,平移后的结果图。



浙公网安备 33010602011771号