如何用Python OpenCV实现无损失的图像平移等变换功能
解决OpenCV图像变换无损失的问题
问题根源
你当前的实现每次平移都会直接修改copy_img,且使用cv.warpAffine时输出尺寸固定为原图像的(cols, rows),导致移出画布的像素被永久截断。后续反向平移时,丢失的部分无法恢复——因为操作对象已经是被截断后的图像了。旋转、剪切等其他仿射变换也会出现同类问题。
解决方案
核心思路是保留原始图像,累积变换参数,每次基于原始图像生成完整的变换结果,同时动态调整输出画布的尺寸,确保所有变换后的像素都能被容纳:
- 全程保留原始图像不修改,用变量记录累积的变换参数(比如总平移量);
- 每次变换时,基于原始图像和累积参数计算结果;
- 计算能容纳所有变换后像素的画布尺寸,避免截断。
修改后的代码
import cv2 as cv import numpy as np # 加载原始图像,全程不修改 img = cv.imread('tekapo.bmp') if img is None: print("file not found") exit() orig_rows, orig_cols, ch = img.shape # 初始变换参数:累积平移量 total_dx = 0 total_dy = 0 # 初始中心点(基于原始图像) c = (int(orig_cols/2), int(orig_rows/2)) def draw_line(input_img, center): c_img = input_img.copy() x, y = center # 基于当前显示图像的尺寸画辅助线 cv.line(c_img, center, (input_img.shape[1], y), (0, 0, 255), 1) cv.line(c_img, center, (x, input_img.shape[0]), (0, 255, 0), 1) cv.circle(c_img, center, 5, (0, 0, 255), -1) return c_img def imshow(input_img, center): c_img = input_img.copy() cv.imshow('C_img', draw_line(c_img, center)) def click_mouse(e, x, y, d, p): global c if e == cv.EVENT_LBUTTONDOWN: # 将当前显示图像的点击坐标转换回原始图像坐标 orig_x = x - total_dx - (abs(total_dx) if total_dx < 0 else 0) orig_y = y - total_dy - (abs(total_dy) if total_dy < 0 else 0) c = (int(orig_x), int(orig_y)) update_display() def translate(dx, dy): global total_dx, total_dy total_dx += dx total_dy += dy update_display() def update_display(): # 计算能容纳平移后所有像素的画布尺寸 new_cols = orig_cols + abs(total_dx) new_rows = orig_rows + abs(total_dy) # 调整平移矩阵,确保图像在新画布中完整显示 offset_x = abs(total_dx) if total_dx < 0 else 0 offset_y = abs(total_dy) if total_dy < 0 else 0 M = np.float32([[1, 0, total_dx + offset_x], [0, 1, total_dy + offset_y]]) # 生成完整的变换图像 transformed_img = cv.warpAffine(img, M, (new_cols, new_rows)) # 计算当前显示图像中的中心点位置 display_center = (c[0] + total_dx + offset_x, c[1] + total_dy + offset_y) imshow(transformed_img, display_center) return transformed_img # 初始化显示 update_display() cv.setMouseCallback('C_img', click_mouse) while True: key = cv.waitKey() if key == ord('t'): # 右下方平移 translate(5, 5) elif key == ord('e'): # 左上方平移 translate(-5, -5) elif key == 27: # ESC退出 print(f"原始图像中心点坐标:{c}") break cv.destroyAllWindows()
关键说明
- 全程保留
img作为原始图像,所有变换都基于它生成结果,不会丢失任何像素; - 用
total_dx和total_dy累积平移量,每次变换只更新参数,不修改图像; update_display()函数会根据累积的平移量动态计算画布尺寸,确保所有像素都能显示;- 鼠标点击坐标会转换回原始图像坐标,避免画布偏移导致中心点记录错误。
这种思路同样可以扩展到旋转、剪切等其他仿射变换:只需要累积对应的变换矩阵,每次基于原始图像和累积矩阵生成结果,并计算合适的输出画布尺寸即可。
内容的提问来源于stack exchange,提问作者KIM.H.S
相关产品推荐
相关产品推荐

