C++ OpenCV制作哈哈镜效果:凸透镜与凹透镜像素映射详解

周予安
周予安
发布时间:2026-10-16 | 5人浏览过
原创

使用C++和OpenCV库,通过像素坐标的数学映射算法,可以轻松在图像上实现凸透镜放大与凹透镜缩小的哈哈镜特效。本文详解核心变换原理与代码实现。

实现原理与准备

哈哈镜效果的本质是图像像素空间的非线性扭曲。在二维坐标系中,我们需要计算输入图像中心点 (cx, cy),以及画布上任意像素点 (x, y) 到中心的相对距离 dx = x - cx 和 dy = y - cy。根据预先设定的变换半径或压缩系数,计算出目标像素在原始图像中的对应位置,并将颜色值提取填充至新的画布上。

第一步:配置开发环境与读取图像

确保已安装 C++ 编译器及 OpenCV 库。主程序流程首先加载本地图片文件进入内存,并检查有效性:

  • 入口与准备:定义 cv::Mat src,调用 imread() 读取图片。
  • 动作:校验 src.empty(),若为空则提示错误并终止。
  • 观察状态:成功加载后,控制台打印提示,内存中存在包含图像数据的矩阵对象。

基础代码结构

int main()
{
    // 读取测试图片
    cv::Mat src = cv::imread("test.jpg");
    if (src.empty()) {
        std::cout << "No Image!" << std::endl;
        return -1;
    }
    
    // 后续处理...return 0;
}

第二步:生成凸透镜(放大)效果

凸透镜特效使图像中心区域的物体看起来被放大。其核心是利用极坐标变换思想,随着距离中心越远,像素偏移量越大。具体计算公式为:
x_new = (dx / 2) * (sqrt(dx² + dy²) / r) + cx
y_new = (dy / 2) * (sqrt(dx² + dy²) / r) + cy

核心逻辑与操作

  1. 初始化画布:创建与原图尺寸相同的零矩阵 canvas 用于存储新图像。
  2. 设定参数:确定中心点 cx, cy 和控制弯曲强度的半径 radius(例如设为 200)。
  3. 遍历绘制:双重循环遍历输出图像每个像素。
    - 计算当前像素相对中心的偏移 dx, dy。
    - 代入上述公式求出新位置 new_x, new_y。
    - 安全检查:必须验证新位置是否在原图边界内。
  4. 像素赋值:将原图在新位置的 BGR 通道颜色拷贝到画布当前位置。

完整函数实现

bool Mirror_Magnify(cv::Mat src)
{
    // 创建画布
    cv::Mat canvas = cv::Mat::zeros(src.size(), src.type());
    
    int cx = src.cols / 2;
    int cy = src.rows / 2;
    int radius = 200; // 决定弯曲程度

    for (int i = 0; i < src.rows; i++) {
        for (int j = 0; j < src.cols; j++) {
            int dx = j - cx;
            int dy = i - cy;
            
            // 计算源图像对应坐标
            int x = (dx / 2) * (std::sqrt(std::pow(dx, 2) + std::pow(dy, 2)) / radius) + cx;
            int y = (dy / 2) * (std::sqrt(std::pow(dx, 2) + std::pow(dy, 2)) / radius) + cy;

            // 边界检查与赋值
            if ((x > 0 && x < src.cols) && (y > 0 && y < src.rows)) {
                cv::Vec3b pixel = src.at<cv::Vec3b>(y, x);
                canvas.at<cv::Vec3b>(i, j)[0] = pixel[0];
                canvas.at<cv::Vec3b>(i, j)[1] = pixel[1];
                canvas.at<cv::Vec3b>(i, j)[2] = pixel[2];
            }
        }
    }
    cv::imshow("Mirror_Magnify", canvas);
    return true;
}

第三步:生成凹透镜(缩小)效果

凹透镜特效使图像看起来向内凹陷收缩。该模式使用了对数缩放的思想,使得边缘像素被剧烈压缩而挤入画面中心:

x = sqrt(sqrt(dx² + dy²)) * compress * cos(atan2(dy, dx)) + cx
y = sqrt(sqrt(dx² + dy²)) * compress * sin(atan2(dy, dx)) + cy

操作要点

  • 引入压缩因子:增加一个 compress 变量(如 12),用于调整形变的剧烈程度。
  • 坐标转换:利用 atan2 获取角度,结合模长进行三角函数映射。
  • 效果呈现:图像中间区域显得更为深远狭窄,四周景物向中心汇聚。

完整函数实现

bool Mirror_Narrow(cv::Mat src)
{
    cv::Mat canvas = cv::Mat::zeros(src.size(), src.type());
    double compress = 12.0; // 压缩强度
    int cx = src.cols / 2;
    int cy = src.rows / 2;

    for (int i = 0; i < src.rows; i++) {
        for (int j = 0; j < src.cols; j++) {
            int dx = j - cx;
            int dy = i - cy;
            
            // 计算极坐标形式的源图像对应坐标
            int x = std::sqrt(std::sqrt(std::pow(dx, 2) + std::pow(dy, 2))) * compress * std::cos(std::atan2(dy, dx)) + cx;
            int y = std::sqrt(std::sqrt(std::pow(dx, 2) + std::pow(dy, 2))) * compress * std::sin(std::atan2(dy, dx)) + cy;

            // 边界检查与赋值
            if ((x > 0 && x < src.cols) && (y > 0 && y < src.rows)) {
                cv::Vec3b pixel = src.at<cv::Vec3b>(y, x);
                canvas.at<cv::Vec3b>(i, j)[0] = pixel[0];
                canvas.at<cv::Vec3b>(i, j)[1] = pixel[1];
                canvas.at<cv::Vec3b>(i, j)[2] = pixel[2];
            }
        }
    }
    cv::imshow("Mirror_Narrow", canvas);
    return true;
}

第四步:运行验证与排错指南

在 Visual Studio 或其他 IDE 中编译代码并运行窗口查看结果。

常见问题排查

  • 画面全黑/空白:通常是因为在赋值时未做 if 越界判断,导致访问了非法内存地址而崩溃,或者越界判定条件写反。确保检查 x > 0 && x < src.cols。
  • 图像闪烁:如果在循环内部频繁调用 imshow 可能导致效率低下。建议将所有效果计算完成后,统一展示或通过按钮触发。
  • 颜色错位:OpenCV 默认通道顺序为 B-G-R (Vec3b),而非 RGB。在处理单通道或多通道分离时需特别注意索引 [0], [1], [2] 的对应关系。

总结

以上便是利用 C++ 配合 OpenCV 制作哈哈镜(包括凸透镜与凹透镜效果)的详细内容。通过理解像素重映射的基本原理,你可以进一步修改 radius 或 compress 等参数来探索更多复杂的图像变形艺术。更多关于 C++ 图像处理的技术资料请关注本站其它相关文章!

本站声明

本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。

Python 相关产品和应用

全部下载

专题列表