opencv 学习: 04 通过ROI处理图片局部数据,以添加水印为例

展示如何访问和修改由 cv::Mat 表示的图像的像素值。

以及在图片中定义一个 ROI (region of interest)

ROI (Region of interest)

有时,我们只需要对图片的某个区域进行处理。这就需要定义一个 ROI 来达成这个目的。

假设,想要将一个小图片,比如添加logo水印,拷贝到一个大的图片上去。

此时我们可以在被拷贝的大图上定义一个ROI,这个ROI就决定了logo水印将被粘贴的位置。

使用 cv::Rect 定义ROI:

cpp 复制代码
// logo 将被放置在 图片的右下角
cv::Rect roi = cv::Rect(image.cols - logo.cols,// ROI coordinates x
			image.rows - logo.rows,// y
			logo.cols,//width
			logo.rows); //

然后,可以使用 该 ROI ,在目标图片的 Mat 对象上,得到一个对应 ROI 的 Mat 对象。image_roi 指向目标图片,并且使用roi对象记录了对应的区域。目标图片mat引用计数会增加。

cpp 复制代码
//简单说就是利用ROI ,在目标image上定义一个新的 Mat
cv::Mat image_roi(image,roi);

image_roi 就相当于 目标图片上对应区域的映射,对其的操作,将会作用于该目标图片。最后其实就是直接赋值拷贝即可。

cpp 复制代码
logo.copyTo(image_roi); // 将logo图片复制到roiImage中

完整代码:

cpp 复制代码
#include <iostream>
#include <opencv2/opencv.hpp> 


int main(int argc, char* argv[]) 
{

    // 检查命令行参数
    if (argc != 4) {
        std::cerr << "Usage: " << argv[0] << " <input_image> <logo_image> <output_image>" << std::endl;
        return -1;
    }

  
    // 读取输入图像 imread 完成对文件的读取,图片数据的解码,存放为 Mat 对象 这三个功能
    cv::Mat inputImage = cv::imread(argv[1]);
    if (inputImage.empty()) {
        std::cerr << "Error: Could not load image " << argv[1] << std::endl;
        return -1;
    }

    cv::Mat logo = cv::imread(argv[2]);
    if (logo.empty()) {
        std::cerr << "Error: Could not load logo image " << argv[2] << std::endl;
        return -1;
    }

     std::cout << "[1] Input Image... "<< std::endl;
    // 显示原始图片
    cv::namedWindow("Input Image", cv::WINDOW_NORMAL);
    // 被显示的图片会经过一定的预处理,CV_16U和CV_32S 的像素值类型会被除以256.浮点型像素也会将 在 0.0 - 1.0之间的数据显示,小于 0 的显示黑色,大于1显示为白色。
    cv::imshow("Input Image", inputImage);
    cv::waitKey(0);

    std::cout << "[2] Logo Image... "<< std::endl;

    // 显示logo图片
    cv::namedWindow("Logo Image", cv::WINDOW_NORMAL);
    // 被显示的图片会经过一定的预处理,CV_16U和CV_32S 的像素值类型会被除以256.浮点型像素也会将 在 0.0 - 1.0之间的数据显示,小于 0 的显示黑色,大于1显示为白色。
    cv::imshow("Logo Image", logo);
    cv::waitKey(0);

  
    std::cout << "[3] ROI... "<< std::endl;
    // 注意 roi x y width height 的 值不能为负,否则抛出异常
    cv::Rect roi = cv::Rect(inputImage.cols - logo.cols,
                            inputImage.rows - logo.rows,
                            logo.cols,
                            logo.rows);
  
    //cv::Mat roiImage = inputImage(roi);
    cv::Mat image_roi(inputImage,roi);
    logo.copyTo(image_roi); // 将logo图片复制到roiImage中
  
    //这样其实是不行的,相当于将 image_roi 的数据指向了logo。
    //而不是像 copyTo 一样复制数据
    //image_roi = logo;


    // 显示结果图片
    std::cout << "[4] Output Image... "<< std::endl;
    cv::namedWindow("Output Image", cv::WINDOW_NORMAL);
    // 被显示的图片会经过一定的预处理,CV_16U和CV_32S 的像素值类型会被除以256.浮点型像素也会将 在 0.0 - 1.0之间的数据显示,小于 0 的显示黑色,大于1显示为白色。
    cv::imshow("Output Image", inputImage);
    cv::waitKey(0);

    //保存
    std::cout << "[5] Save Image... "<< std::endl;
    if (!cv::imwrite(argv[3], inputImage)) {
        std::cerr << "Error: Could not save image to " << argv[3] << std::endl;
        return -1;
    }
  
    std::cout << "Input image: " << argv[1] << std::endl;
    std::cout << "Output image: " << argv[2] << std::endl;
  
    cv::destroyAllWindows();
    return 0;
}

效果如下:水印被放置在最右下角。

相关推荐
Madison-No78 分钟前
搭建项目测试环境
linux·运维·服务器·python
吴声子夜歌1 小时前
Shell编程实例——编写安全的shell脚本(二)
linux·运维·shell
xiaoye-duck3 小时前
《Linux 网络编程》深入理解 IP 协议(二):网段划分、私有 IP 与 NAT 地址转换
linux·网络·ip
汉克老师3 小时前
GESP2026年9月认证C++六级( 第三部分编程题(1、数组划分))精讲
c++·gesp·小学生·学c++编程
千里码aicood3 小时前
基于PLC与机器视觉的智能分拣控制系统设计
c++·自动化·plc
zx_741484813 小时前
【Linux入门】Shell 函数、正则表达式与文本处理:cut 与 awk
linux·运维·正则表达式
漂流瓶jz4 小时前
UVA-1442 洞穴 题解答案代码 算法竞赛入门经典第二版
c++·算法·题解·aoapc·算法竞赛入门经典·uva
Zenova EdgeOS4 小时前
Linux dmesg 工业边缘实战:内核日志过滤、持久化与故障定位
linux·运维·边缘计算·工业边缘·dmesg·内核日志
朽棘不雕4 小时前
进一步了解模板
c++
金士顿4 小时前
System.Text.Json Source Generator 深度解析:为什么 Native AOT 不喜欢反射
linux·asp.net core·arm64·net·native aot