OpenCV开发笔记(九):OpenCV区域图像(ROI)和整体、局部图像混合

若该文为原创文章,未经允许不得转载
原博主博客地址:https://blog.csdn.net/qq21497936
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/101351444

目录

前言

Demo

感兴趣区域:ROI

选择指定区域

测试代码

测试效果

图像混合(整体混合)

线性混合

计算数组加权和:addWeighted()函数

测试代码

测试效果

图像混合(局部混合)

测试代码

测试效果

Demo源码

工程模板:对应版本号v1.6.0


 

OpenCV开发专栏

OpenCV开发笔记(〇):使用mingw530_32编译openCV3.4.1源码,搭建Qt5.9.3的openCV开发环境

OpenCV开发笔记(一):OpenCV介绍、编译

OpenCV开发笔记(二):cvui交互界面

OpenCV开发笔记(三):OpenCV图像的概念和基本操作

OpenCV开发笔记(四):OpenCV图片和视频数据的读取与存储

OpenCV开发笔记(五):OpenCV读取与操作摄像头

OpenCV开发笔记(六):OpenCV基础数据结构、颜色转换函数和颜色空间

OpenCV开发笔记(七):OpenCV基础图形绘制

OpenCV开发笔记(八):OpenCV常用操作之计时、缩放、旋转、镜像

OpenCV开发笔记(九):OpenCV区域图像(ROI)和整体、局部图像混合

OpenCV开发笔记(十):OpenCV图像颜色通道分离和图像颜色多通道混合

持续补充中…

 

    OpenCV开发笔记(九):OpenCV区域图像(ROI)和整体、局部图像混合

前言

       图像操作常常需要获取指定区域的图像,对于OpenCV提供了ROI(感兴趣区域)支持;

       图像的叠加和混合也是常用的操作之一。

 

Demo

  

感兴趣区域:ROI

       在图像处理领域中,专业名词感兴趣的区域其实就是选取指定区域的图像

选择指定区域

    图像中提取感兴趣区域(Region of interest)有两种方法:

  • 方法一:使用构造函数
//创建宽度为 320,高度为 240 的 3 通道图像
Mat img(Size(320, 240), CV_8UC3);
//roi 是表示 img 中 Rect(10, 10, 100, 100)区域的对象
Mat roi(img, Rect(10, 10, 100, 100));
  • 方法二:使用括号运算符
Mat roi2 = img(Rect(10, 10, 100, 100));
// 用括号运算符
Mat roi3 = img(Range(10, 100), Range(10, 100)); 
// 用构造函数
Mat roi4(img, Range(10, 100), Range(10, 100));

测试代码

cv::Mat srcMat;
QString fileName = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
srcMat = cv::imread(fileName.toStdString());
if(!srcMat.data)
{
    qDebug() << __FILE__ << __LINE__ << "Failed to load image:" << fileName;
    return;
}
cv::imshow("orgin mat", srcMat);
cv::Mat roiMat = srcMat(cv::Range(srcMat.rows/2 - 50, srcMat.rows/2 + 50),
                        cv::Range(srcMat.cols/2 - 30, srcMat.cols/2 + 30));
cv::imshow("roi mat", roiMat);
cv::waitKey(0);

测试效果

 

图像混合(整体混合)

       图像混合有多种方式。

线性混合

       线性混合操作是一种典型的二元(两个输入)的像素操作,它的理论公式如下:

       G(x) = (1-a)F1(x) + aF2(x)

       a代表透明度的值(0.0~1.0)对两幅图像(F1和F2)活两段视频产生时间上的画面叠化效果,前面页面切换至后面页面的一个切换过程。

       线性混合过程主要使用addWeighted函数。

计算数组加权和:addWeighted()函数

       这个函数的作用是计算两个mat的加权和,当然读者可以自己遍历像素计算。该函数的原型如下:

CV_EXPORTS_W void addWeighted(InputArray src1, 
                              double alpha,
                              InputArray src2,
                              double beta, 
                              double gamma, 
                              OutputArray dst, 
                              int dtype = -1);
  • 参数一:输入图像1,mat类型
  • 参数二:alpha值,表示图像1的权重
  • 参数三:输入图像2,mat类型
  • 参数四:beta值,表示图像2的权重
  • 参数五:gamma值,一个加到权重总和上的标量值
  • 参数六:输出图像dst,mat类型,它与输入的两个mat尺寸和通道相同
  • 参数七:dtype,输出陈列的可选深度,默认值-1,当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。

       以上函数的计算公式如下:

       dst = src1[I] * alpha + src2[I] * beta + gamma

       其中I为元素位置的索引值,当遇到多通道mat的时候,每个通道都需要独立的进行处理。

       注意:当输出mat的深度为CV_32S时,这个函数不使用,会导致内存溢出或者算出的结果错误。

测试代码

// 测试线性混合: 正向 与 x轴翻转后的线性混合
cv::Mat mat1;
cv::Mat mat2;
cv::Mat mat3;
QString fileName1 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
QString fileName2 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.bmp";
mat1 = cv::imread(fileName1.toStdString());
mat2 = cv::imread(fileName2.toStdString());
if(!mat1.data || !mat2.data)
{
    qDebug() << __FILE__ << __LINE__
             << "Failed to load image:" << fileName1 << "or" << fileName2;
    return;
}
// 对mat2进行缩放,缩放至mat1的大小,否则会报错
cv::resize(mat2, mat2, cv::Size(mat1.cols, mat1.rows));
// 然后进行混合
double a = 0.0;
while(true)
{
    cv::addWeighted(mat1, a, mat2, (1.0-a), 0.0, mat3);
    cv::imshow("mat3", mat3);
    int key = cv::waitKey(0);
    if(key == 27)
    {
        break;
    }else if(key == '1')
    {
        a += 0.05;
        if(a >= 1.0)
        {
            a = 1.0;
        }
    }else if(key == '2')
    {
        a -= 0.05;
        if(a < 0.0)
        {
            a = 0.0;
        }
    }
}

测试效果

 

图像混合(局部混合)

       原理和方法参照图像混合(整体混合)。

       局部混合可以理解对感兴趣的区域进行混合,这里特别要注意一下:

  • 使用 mat1 = mat2,其实mat1是mat2的副本(同一段数据);
  • 使用 mat1 = mat2(roi),其实mat1是mat2上roi区域的副本(同一段数据);
  • 使用 mat1 = mat2.clone(),mat1才是mat2的拷贝副本(两段相同数据);
  • 使用 mat1 = mat2(roi).clone(),mat1才是mat2上roi区域的的拷贝副本(两段相同数据);

测试代码

// 测试线性混合: 正向 与 x轴翻转后的线性混合
cv::Mat mat1;
cv::Mat mat2;
cv::Mat mat3;
QString fileName1 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
QString fileName2 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.bmp";
mat1 = cv::imread(fileName1.toStdString());
mat2 = cv::imread(fileName2.toStdString());
if(!mat1.data || !mat2.data)
{
    qDebug() << __FILE__ << __LINE__
             << "Failed to load image:" << fileName1 << "or" << fileName2;
    return;
}
// 对mat2进行缩放,缩放至mat1的1/4的大小
cv::resize(mat2, mat2, cv::Size(mat1.cols/2, mat1.rows/2));
double a = 0.0;
while(true)
{
    // mat4只是mat1的副本
//        cv::Mat mat4 = mat1;
    cv::Mat mat4 = mat1.clone();
    qDebug() << __FILE__ << __LINE__
             << mat1.cols/2 - mat1.cols/4
             << mat1.cols/2 + mat1.cols/4
             << mat1.rows/2 - mat1.rows/4
             << mat1.rows/2 + mat1.rows/4;
    cv::Mat mat5 = mat4(cv::Range(mat1.rows/2 - mat1.rows/4, mat1.rows/2 + mat1.rows/4),
                        cv::Range(mat1.cols/2 - mat1.cols/4, mat1.cols/2 + mat1.cols/4));
    qDebug() <<__FILE__<<__LINE__;
    cv::addWeighted(mat5, a, mat2, (1.0-a), 0.0, mat5);
    qDebug() <<__FILE__<<__LINE__;
    cv::imshow("mat4", mat4);
    int key = cv::waitKey(0);
    if(key == 27)
    {
        break;
    }else if(key == '1')
    {
        a += 0.05;
        if(a >= 1.0)
        {
            a = 1.0;
        }
    }else if(key == '2')
    {
        a -= 0.05;
        if(a < 0.0)
        {
            a = 0.0;
        }
    }
}

测试效果

 

Demo源码

void OpenCVManager::testROIAndBlend()
{
#define TEST_ROI        (0)     // roi
#define TEST_BLEND      (0)     // 图像加权混合(全部区域)
#define TEST_BLEND_ROI  (1)     // 图像加权混合(roi区域)

#if TEST_ROI
    // 测试提取
    cv::Mat srcMat;
    QString fileName = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
    srcMat = cv::imread(fileName.toStdString());
    if(!srcMat.data)
    {
        qDebug() << __FILE__ << __LINE__ << "Failed to load image:" << fileName;
        return;
    }
    cv::imshow("orgin mat", srcMat);
    cv::Mat roiMat = srcMat(cv::Range(srcMat.rows/2 - 50, srcMat.rows/2 + 50),
                            cv::Range(srcMat.cols/2 - 30, srcMat.cols/2 + 30));
    cv::imshow("roi mat", roiMat);
    cv::waitKey(0);
#endif

#if TEST_BLEND
    // 测试线性混合: 正向 与 x轴翻转后的线性混合
    cv::Mat mat1;
    cv::Mat mat2;
    cv::Mat mat3;
    QString fileName1 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
    QString fileName2 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.bmp";
    mat1 = cv::imread(fileName1.toStdString());
    mat2 = cv::imread(fileName2.toStdString());
    if(!mat1.data || !mat2.data)
    {
        qDebug() << __FILE__ << __LINE__
                 << "Failed to load image:" << fileName1 << "or" << fileName2;
        return;
    }
    // 对mat2进行缩放,缩放至mat1的大小,否则会报错
    cv::resize(mat2, mat2, cv::Size(mat1.cols, mat1.rows));
    // 然后进行混合
    double a = 0.0;
    while(true)
    {
        cv::addWeighted(mat1, a, mat2, (1.0-a), 0.0, mat3);
        cv::imshow("mat3", mat3);
        int key = cv::waitKey(0);
        if(key == 27)
        {
            break;
        }else if(key == '1')
        {
            a += 0.05;
            if(a >= 1.0)
            {
                a = 1.0;
            }
        }else if(key == '2')
        {
            a -= 0.05;
            if(a < 0.0)
            {
                a = 0.0;
            }
        }
    }
#endif

#if TEST_BLEND_ROI
    // 测试线性混合: 正向 与 x轴翻转后的线性混合
    cv::Mat mat1;
    cv::Mat mat2;
    cv::Mat mat3;
    QString fileName1 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.jpg";
    QString fileName2 = "D:/qtProject/openCVDemo/openCVDemo/modules/openCVManager/images/1.bmp";
    mat1 = cv::imread(fileName1.toStdString());
    mat2 = cv::imread(fileName2.toStdString());
    if(!mat1.data || !mat2.data)
    {
        qDebug() << __FILE__ << __LINE__
                 << "Failed to load image:" << fileName1 << "or" << fileName2;
        return;
    }
    // 对mat2进行缩放,缩放至mat1的1/4的大小
    cv::resize(mat2, mat2, cv::Size(mat1.cols/2, mat1.rows/2));
    double a = 0.0;
    while(true)
    {
        // mat4只是mat1的副本
    //        cv::Mat mat4 = mat1;
        cv::Mat mat4 = mat1.clone();
        qDebug() << __FILE__ << __LINE__
                 << mat1.cols/2 - mat1.cols/4
                 << mat1.cols/2 + mat1.cols/4
                 << mat1.rows/2 - mat1.rows/4
                 << mat1.rows/2 + mat1.rows/4;
        cv::Mat mat5 = mat4(cv::Range(mat1.rows/2 - mat1.rows/4, mat1.rows/2 + mat1.rows/4),
                            cv::Range(mat1.cols/2 - mat1.cols/4, mat1.cols/2 + mat1.cols/4));
        qDebug() <<__FILE__<<__LINE__;
        cv::addWeighted(mat5, a, mat2, (1.0-a), 0.0, mat5);
        qDebug() <<__FILE__<<__LINE__;
        cv::imshow("mat4", mat4);
        int key = cv::waitKey(0);
        if(key == 27)
        {
            break;
        }else if(key == '1')
        {
            a += 0.05;
            if(a >= 1.0)
            {
                a = 1.0;
            }
        }else if(key == '2')
        {
            a -= 0.05;
            if(a < 0.0)
            {
                a = 0.0;
            }
        }
    }
#endif
}

 

工程模板:对应版本号v1.6.0

       对应版本号v1.6.0

 

原博主博客地址:https://blog.csdn.net/qq21497936
本文章博客地址:https://blog.csdn.net/qq21497936/article/details/101351444

发布了227 篇原创文章 · 获赞 237 · 访问量 40万+

猜你喜欢

转载自blog.csdn.net/qq21497936/article/details/101351444