如何用Python变换矩阵裁剪经旋转缩放平移后的无黑边图像
解决图像对齐后黑边裁剪问题
要去除平移、旋转、缩放组合变换后产生的黑边,核心是追踪原图像所有顶点经过完整变换后的位置,找到包裹所有有效内容的最小矩形,以此作为裁剪区域。以下是具体实现方案:
关键原理
图像对齐过程包含平移、旋转、缩放三个连续变换,我们可以将这三个变换合并为一个总变换矩阵,然后用这个矩阵计算原图像四个顶点(左上角、右上角、右下角、左下角)变换后的坐标。通过这些坐标的极值(最小/最大x、y),就能确定有效内容的边界,从而精准裁剪掉黑边。
修改后的完整代码
import numpy as np import cv2 import pandas as pd import math # 补充缺失的math模块导入 def alignFromReferenceImage(image1, imgname1, image2, imgname2): df = pd.read_csv("./image_metadata.csv", delimiter=';') # 优化参考点查找逻辑:直接匹配id列,避免isin的误匹配 img1_id = imgname1.split('.')[0] filter_data = df[df['id'] == img1_id].iloc[0] x1_s, y1_s = filter_data['x_s'], filter_data['y_s'] x1_a, y1_a = filter_data['x_a'], filter_data['y_a'] img2_id = imgname2.split('.')[0] filter_data2 = df[df['id'] == img2_id].iloc[0] x2_s, y2_s = filter_data2['x_s'], filter_data2['y_s'] x2_a, y2_a = filter_data2['x_a'], filter_data2['y_a'] # 1. 计算平移变换矩阵 tx = x2_s - x1_s ty = y2_s - y1_s rows1, cols1 = image1.shape[:2] rows2, cols2 = image2.shape[:2] # 获取原图像2的尺寸 M_trans = np.float32([[1, 0, -tx], [0, 1, -ty]]) # 2. 计算旋转缩放变换矩阵 d1 = math.sqrt((x1_a - x1_s)**2 + (y1_a - y1_s)**2) d2 = math.sqrt((x2_a - x2_s)**2 + (y2_a - y2_s)**2) scale = d1 / d2 dx1 = x1_a - x1_s dy1 = -(y1_a - y1_s) alpha1 = math.degrees(math.atan2(dy1, dx1)) alpha1 = alpha1 if alpha1 >= 0 else alpha1 + 360 dx2 = x2_a - x2_s dy2 = -(y2_a - y2_s) alpha2 = math.degrees(math.atan2(dy2, dx2)) alpha2 = alpha2 if alpha2 >= 0 else alpha2 + 360 ang = alpha1 - alpha2 centre = (x1_s, y1_s) M_rot_scale = cv2.getRotationMatrix2D(centre, ang, scale) # 3. 合并变换矩阵:先平移,再旋转缩放,总矩阵为 M_rot_scale @ M_trans(齐次坐标下) # 补充平移矩阵的齐次维度,方便矩阵乘法 M_trans_homo = np.vstack([M_trans, [0, 0, 1]]) M_rot_scale_homo = np.vstack([M_rot_scale, [0, 0, 1]]) M_total = M_rot_scale_homo @ M_trans_homo M_total = M_total[:2, :] # 转回2x3的OpenCV变换矩阵格式 # 4. 计算原图像2四个顶点变换后的坐标 # 原图像2的四个顶点(齐次坐标) vertices = np.float32([ [0, 0, 1], [cols2 - 1, 0, 1], [cols2 - 1, rows2 - 1, 1], [0, rows2 - 1, 1] ]) # 应用总变换 transformed_vertices = (M_total @ vertices.T).T # 提取x和y坐标 xs = transformed_vertices[:, 0] ys = transformed_vertices[:, 1] # 5. 确定裁剪边界:取坐标的极值,确保在图像范围内 min_x = max(int(np.floor(xs.min())), 0) max_x = min(int(np.ceil(xs.max())), cols1 - 1) min_y = max(int(np.floor(ys.min())), 0) max_y = min(int(np.ceil(ys.max())), rows1 - 1) # 6. 执行变换和裁剪 aligned_image = cv2.warpAffine(image2, M_total, (cols1, rows1)) cropped_image = aligned_image[min_y:max_y+1, min_x:max_x+1] return cropped_image
代码说明
- 变换矩阵合并:将平移、旋转缩放的变换矩阵合并为一个,确保顶点变换计算的准确性,避免两次独立变换带来的误差。
- 顶点追踪:通过计算原图像四个顶点的变换后位置,精准定位有效内容的边界,这比仅依赖旋转场景的裁剪逻辑更通用,覆盖了平移和缩放的影响。
- 边界修正:裁剪边界会被限制在目标图像(image1)的尺寸范围内,避免出现超出图像的索引错误。
内容的提问来源于stack exchange,提问作者Neel
相关产品推荐
相关产品推荐

