如何对图像矩形ROI区域实现自然的抛物线曲线形变
抛物线ROI形变调整方案
核心问题排查
- 映射逻辑错误:当前使用正向映射,容易出现像素空洞、重叠问题,应该改为反向映射:遍历输出图像的每个坐标,反向计算对应原图的采样位置,保证每个输出像素都有有效值
- 公式变量匹配错误:你需要的是「垂直中线(ROI水平方向中点)处弯曲最大,向左右两侧衰减」,所以偏移量的核心变量应该是x和ROI水平中点的距离,而非y和ROI垂直中点的距离
- 参数硬编码问题:当前
a和rate_change是固定值,没有和ROI尺寸绑定,不同大小的ROI效果差异极大,需要归一化参数适配任意尺寸ROI - 边界判断语法错误:
if(r.x < nearestNeighborX < r.x + r.width)是C++语法错误,连续比较符不会按预期执行,需改为逻辑与连接的两个判断
调整后的实现逻辑
- 先计算ROI的核心参数:水平中点、垂直中点,最大偏移量按ROI宽度比例设置,适配不同大小ROI
- 用反向映射遍历输出图像ROI范围内的每个(x,y)
- 计算x方向的归一化偏移:偏移量和
(x - 水平中点)的平方成正比,同时可配置y方向上下边缘衰减,保证边缘和原图衔接自然没有断层 - 计算采样的原图坐标,用最近邻插值赋值
修正后代码
#include <iostream> #include <opencv2/opencv.hpp> #include <cmath> using namespace std; using namespace cv; int main() { Mat originalImage = imread("../dog.jpeg",IMREAD_GRAYSCALE); if(originalImage.empty()) { cout << "读取图像失败" << endl; return -1; } Mat outPutImage = originalImage.clone(); // 选择ROI Rect2d r = selectROI("选择ROI后按回车/空格",originalImage); destroyWindow("选择ROI后按回车/空格"); if(r.width == 0 || r.height == 0) { cout << "ROI选择无效" << endl; return -1; } // 核心参数,可调整:max_offset是ROI中线位置的最大像素偏移量,值越大弯曲越明显 double max_offset = r.width * 0.08; // 按ROI宽度比例设置,适配不同大小ROI double x_c = r.x + r.width / 2.0; // ROI水平中线 double y_c = r.y + r.height / 2.0; // ROI垂直中线 // 反向映射:遍历输出图像的每个像素 for(int y = r.y; y < r.y + r.height ; y++){ // y方向衰减系数:上下边缘处为0,垂直中线处为1,保证ROI上下边缘和原图衔接自然 double y_factor = 1.0 - abs(y - y_c) / (r.height / 2.0); y_factor = pow(y_factor, 2); // 加平方让衰减更平滑,不需要衰减可以直接设为1 for(int x = r.x; x < r.x + r.width; x++){ // 计算x方向的偏移:离水平中线越远偏移越大,乘以y方向衰减系数 double delta_x = max_offset * pow( (x - x_c)/(r.width/2.0), 2 ) * y_factor; // 反向映射:输出的(x,y)对应原图的(x + delta_x, y),弯曲方向反过来就把加号改减号 double src_x = x + delta_x; // 边界判断 int nearestX = round(src_x); if(nearestX >= r.x && nearestX < r.x + r.width) { outPutImage.at<uchar>(y, x) = originalImage.at<uchar>(y, nearestX); } else { // 超出范围的保持原图即可 outPutImage.at<uchar>(y, x) = originalImage.at<uchar>(y, x); } } } imshow("形变结果", outPutImage); waitKey(0); imwrite("../result.jpg", outPutImage); return 0; }
参数调整说明
- 调整
max_offset的比例系数即可控制弯曲程度:系数越大弯曲越明显,建议取值范围是r.width * 0.03到r.width * 0.15 - 如果想要弯曲方向反过来,把
double src_x = x + delta_x;里的加号改成减号即可 - 如果不需要y方向的衰减(上下边缘也保持弯曲),直接把
y_factor设为1即可
内容的提问来源于stack exchange,提问作者Roy Amoyal
相关产品推荐
相关产品推荐

