图像仿射变换及其逆变换【Python实现】
详细步骤及示例代码
假设我们有8x8个点及其变换后的坐标,下面的代码展示了如何根据这些点还原放射变换矩阵:
# -*- coding: utf-8 -*-
'''
@Time : 2024/09/06 16:43:10
@Author : wydxry
'''
import numpy as np
import cv2
from PIL import Image
import matplotlib.pyplot as plt
def generate_grid_points(image_size, grid_size):
"""
生成均匀分布的网格点坐标
"""
x = np.linspace(0, image_size[0] - 1, grid_size[0])
y = np.linspace(0, image_size[1] - 1, grid_size[1])
xv, yv = np.meshgrid(x, y)
return np.vstack([xv.ravel(), yv.ravel()]).T
def random_affine_transform_matrix():
"""
生成一个随机的放射变换矩阵
"""
theta = np.random.uniform(-0.5, 0.5) # 旋转角度
tx = np.random.uniform(-50, 50) # 平移 x
ty = np.random.uniform(-50, 50) # 平移 y
matrix = np.array([
[np.cos(theta), -np.sin(theta), tx],
[np.sin(theta), np.cos(theta), ty],
[0, 0, 1]
])
return matrix
def apply_affine_transform(image, matrix):
"""
应用放射变换到图像
"""
rows, cols = image.shape[:2]
return cv2.warpAffine(image, matrix[:2], (cols, rows))
def inverse_affine_transform(matrix):
"""
计算放射变换矩阵的逆矩阵
"""
print("matrix shape: ", matrix.shape)
inverse_matrix = np.linalg.inv(matrix)
return inverse_matrix
def apply_inverse_affine_transform(image, matrix):
"""
应用逆放射变换到图像
"""
inverse_matrix = inverse_affine_transform(matrix)
print(inverse_matrix)
rows, cols = image.shape[:2]
return cv2.warpAffine(image, inverse_matrix[:2], (cols, rows))
def apply_inverse_affine_transform_to_points(points, matrix):
"""
应用逆放射变换到点坐标
"""
inverse_matrix = inverse_affine_transform(matrix)
ones = np.ones((points.shape[0], 1))
homogeneous_points = np.hstack([points, ones])
original_points = (inverse_matrix @ homogeneous_points.T).T
return original_points[:, :2]
def estimate_affine_transform(original_points, transformed_points):
"""
根据原始点和变换后的点估计放射变换矩阵
"""
assert original_points.shape == transformed_points.shape
assert original_points.shape[0] >= 3 # 至少需要三个点来计算放射变换
# 生成方程组的矩阵
A = []
B = []
for (x, y), (x_prime, y_prime) in zip(original_points, transformed_points):
A.append([x, y, 1, 0, 0, 0])
A.append([0, 0, 0, x, y, 1])
B.append(x_prime)
B.append(y_prime)
A = np.array(A)
B = np.array(B)
# 求解线性方程组
X = np.linalg.lstsq(A, B, rcond=None)[0]
# 构造放射变换矩阵
matrix = np.array([
[X[0], X[1], X[2]],
[X[3], X[4], X[5]],
[0, 0, 1]
])
return matrix
def apply_affine_transform(image, matrix):
"""
应用放射变换到图像
"""
rows, cols = image.shape[:2]
return cv2.warpAffine(image, matrix[:2], (cols, rows))
def apply_affine_transform_to_points(points, matrix):
"""
应用放射变换到点坐标
"""
ones = np.ones((points.shape[0], 1))
homogeneous_points = np.hstack([points, ones])
transformed_points = (matrix @ homogeneous_points.T).T
return transformed_points[:, :2]
# 生成均匀分布的8x8坐标点
image_size = (2048, 2048)
grid_size = (8, 8)
# 读取图像
image_path = '1.jpg'
image = np.array(Image.open(image_path).resize(image_size))
# 随机生成放射变换矩阵
affine_matrix = random_affine_transform_matrix()
print("Affine Transform Matrix:\n", affine_matrix)
# 应用变换到图像
transformed_image = apply_affine_transform(image, affine_matrix)
# 假设原始点和变换后的点
original_points = generate_grid_points(image_size, grid_size)
transformed_points = apply_affine_transform_to_points(original_points, affine_matrix)
# 估计放射变换矩阵
estimated_affine_matrix = estimate_affine_transform(original_points, transformed_points)
print("Estimated Affine Transform Matrix:\n", estimated_affine_matrix)
# 验证:应用估计的矩阵到图像
restored_image = apply_affine_transform(image, estimated_affine_matrix)
# 显示结果
fig, ax = plt.subplots(1, 3, figsize=(18, 6))
# 显示原始图像和坐标
ax[0].imshow(image)
ax[0].scatter(original_points[:, 0], original_points[:, 1], color='red', label='Original Points')
ax[0].set_title('Original Image with Points')
# 显示变换后的图像和新坐标
ax[1].imshow(transformed_image)
ax[1].scatter(transformed_points[:, 0], transformed_points[:, 1], color='blue', label='Transformed Points')
ax[1].set_title('Transformed Image with Points')
# 显示估计的变换后的图像
ax[2].imshow(restored_image)
ax[2].scatter(transformed_points[:, 0], transformed_points[:, 1], color='green', label='Estimated Points')
ax[2].set_title('Restored Image with Estimated Points')
plt.show()
plt.savefig("result.jpg")
# 显示结果
fig, ax = plt.subplots(1, 3, figsize=(18, 6))
# 显示原始图像
ax[0].imshow(transformed_image, cmap='gray')
ax[0].set_title('Original Image')
ax[0].axis('off')
# 显示变换后的图像
ax[1].imshow(restored_image, cmap='gray')
ax[1].set_title('Transformed Image')
ax[1].axis('off')
# 显示差异图像
ax[2].imshow(restored_image - transformed_image, cmap='gray')
ax[2].set_title('Difference Image')
ax[2].axis('off')
plt.show()
plt.savefig("compare.jpg")
实验结果
解释
-
估计放射变换矩阵:
estimate_affine_transform
函数根据原始点和变换后点的对应关系,建立方程组并求解,从而估计放射变换矩阵。- 方程组的建立基于放射变换的线性关系
-
应用变换:
apply_affine_transform
将估计的放射变换矩阵应用于图像。apply_affine_transform_to_points
将估计的放射变换矩阵应用于点坐标。
-
验证结果:
- 显示原始图像、变换后的图像和应用估计的变换后的图像,并标记相应的点坐标。
注意事项
- 至少需要三个非共线的点来计算放射变换矩阵,但在实际应用中,使用更多点可以提高估计的精度。
- 确保你的环境中已经安装了
numpy
、cv2
(OpenCV)、PIL
和matplotlib
。