You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 03:07:04