如何用OpenCV Python的cv.remap实现图像指定区域涂抹变形效果
cv.remap实现区域拉伸/挤压变形的具体逻辑
核心原理说明
cv.remap的本质是坐标重映射,你需要提前生成两个和输出图像尺寸完全一致的浮点数组map_x、map_y:
map_x[y][x]:输出图像中坐标为(x,y)的像素,对应原图像的横坐标值map_y[y][x]:输出图像中坐标为(x,y)的像素,对应原图像的纵坐标值
不需要变形的区域,直接给map_x[y][x] = x、map_y[y][x] = y即可保证像素位置不变。你已经生成的中位线和抛物线之间的区域掩码,可以直接用来圈定需要调整映射规则的范围。
分步实现流程
第一步:准备基础输入参数
提前整理好三个输入:- 原始图像数组
- 你已有的
np.int32类型区域掩码,掩码值为1的位置对应需要变形的区域,其他区域为0 - 每个横坐标x对应的基准直线y值
y_base[x]、抛物线y值y_parabola[x],用于计算区域高度
第二步:初始化映射矩阵
先初始化map_x、map_y默认映射到原坐标,保证非变形区域像素不变:import cv2 as cv import numpy as np h, w = img.shape[:2] map_x = np.zeros((h, w), dtype=np.float32) map_y = np.zeros((h, w), dtype=np.float32) # 给map_x赋值每行的x坐标 for i in range(h): map_x[i, :] = np.arange(w, dtype=np.float32) # 给map_y赋值每列的y坐标 for i in range(w): map_y[:, i] = np.full(h, i, dtype=np.float32)第三步:调整变形区域的映射规则
要实现一侧拉伸、一侧挤压的效果,你可以用归一化距离的幂次变换实现:
假设你要让靠近基准直线的一侧拉伸、靠近抛物线的一侧挤压,先计算区域内任意点(x,y)距离基准线的归一化距离t = (y - y_base[x])/(y_parabola[x] - y_base[x]),给t做幂次调整t_new = t**0.5,幂次小于1时,小t的增长速度快,对应拉伸;大t的增长速度慢,对应挤压,再把映射后的y坐标替换为y_base[x] + t_new * (y_parabola[x] - y_base[x])即可。
示例代码:for x in range(w): region_height = y_parabola[x] - y_base[x] # 跳过高度为0的无效区域 if region_height <= 0: continue # 遍历当前x对应的变形区域y范围 for y in range(y_base[x], y_parabola[x]): if mask[y][x] == 1: t = (y - y_base[x]) / region_height # 幂次调整:小于1是基准线侧拉伸,大于1是抛物线侧拉伸,数值越大效果越明显 t_adjust = t ** 0.5 map_y[y][x] = y_base[x] + t_adjust * region_height # 如果你要实现水平方向的拉伸挤压,调整map_x的值即可,map_y保持默认第四步:调用remap生成结果
直接传入生成好的map_x、map_y即可,插值方式选线性插值就能满足一般需求:res = cv.remap(img, map_x, map_y, cv.INTER_LINEAR, borderMode=cv.BORDER_REPLICATE)
优化建议
上面的嵌套循环执行效率较低,你可以用numpy的广播运算替换循环,处理速度会提升数倍;幂次参数可以根据你的效果需求灵活调整,直到拉伸挤压的程度符合预期。
内容的提问来源于stack exchange,提问作者powoce8826
相关产品推荐
相关产品推荐

