zoukankan      html  css  js  c++  java
  • 学习 opencv---(3) ROI 区域图像叠加&初级图像混合

       在这篇文章里,我们一起学习了在OpenCV中如何定义感兴趣区域ROI,如何使用addWeighted函数进行图像混合操作,以及将ROI和addWeighted函数结合起来使用,对指定区域进行图像混合操作。

        

    一、设定感兴趣区域——ROI(region of interest)

        在图像处理领域,我们常常要设置感兴趣区域(ROI,region of interest),来专注或者简化我们的工作过程,也就是从图像中选择的一个图像区域,这个区域是我们图像分析关注的重点。我们圈定这个区域,以便进行进一步处理。而且,使用ROI指定我们想读入的目标,可以减少处理时间,增加精度,给图像处理带来不小的便利。

          

       

             ROI区域定义的两种方法

            定义ROI区域有俩种方法,第一种是使用 cv::Rect ,顾名思义,cv::Rect 表示一个矩形区域。指定矩形的左上角坐标(构造函数的前俩个参数) 和矩形的长宽(构函数的后俩个参数)就可以定义一个矩形区域

    1 //定义一个Mat类型并给其设定ROI区域
    2 Mat imageROI;
    3 
    4 //方法一
    5 imageROI=image(Rect(500,250,logo.cols,logo.rows));

        

       另一种定义ROI的方式是指定感兴趣行或列的范围Range.Range 是指从起始索引到终止索引(不包括终止索引)的一连段连续序列

      cv::Range 可以用来定义Range .如果使用cv::Range 定义ROI ,那么前列定义ROI 的代码可以重写为:

    1 //方法二
    2 imageROI = srcImage3((Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));

          好了,下面我们来看一个实例,显示如何利用ROI将一幅图加到另一幅图的指定位置。大家如果需要拷贝如下的函数中的代码直接运行的话,自己建一个基于console的程序,然后把函数体中的内容拷贝到main函数中,然后找两幅大小合适的图片,加入到工程目录下,并和代码中读取的文件名一致即可。

          在下面的代码中,我们通过一个图像掩膜(mask),直接将插入处的像素设置为logo图像的像素值,这样效果会很赞很逼真

     1 /*------------------------------------------------------
     2      函数名:ROI_AddImage()
     3      描述: 利用感兴趣区域ROI实现图像叠加(把图像叠加到ROI区域中,而不是俩张图的直接叠加)
     4 -------------------------------------------------------*/
     5 
     6 #include <opencv2/core/core.hpp>
     7 #include <opencv2/highgui/highgui.hpp>
     8 
     9 using namespace cv;
    10 
    11 bool ROI_AddImage()
    12 {
    13     
    14     //【1】读入图像
    15     Mat srcImage1 = imread("dota_pa.jpg");
    16     Mat logoImage = imread("dota_logo.jpg");
    17     
    18     if (!srcImage1.data)
    19     {
    20         printf("fuck, read the picture is wrong!!! 
    ");
    21         return false;
    22     }
    23 
    24     if (!logoImage.data)
    25     {
    26         printf("fuck, read the picture is wrong!!! 
    ");
    27         return false;
    28     }
    29 
    30     //【2】定义一个Mat类型并给其设定ROI区域
    31     Mat imageROI = srcImage1(Rect(200,250,logoImage.cols ,logoImage.rows));
    32 
    33     //【3】加载掩膜
    34     Mat mask = imread("dota_logo.jpg",0);
    35 
    36     //【4】将掩膜拷贝到ROI
    37     logoImage.copyTo(imageROI,mask);
    38 
    39     //【5】显示结果
    40     namedWindow("1 利用ROI实现图像叠加示例窗口");
    41     imshow("1 利用ROI实现图像叠加示例窗口",srcImage1);
    42 
    43     return true;
    44 }

         这个函数首先是载入了两张jpg图片到srcImage1和logoImage中,然后定义了一个Mat类型的imageROI,并使用cv::Rect设置其感兴趣区域为srcImage1中的一块区域,将imageROI和srcImage1关联起来。接着定义了一个Mat类型的的mask并读入dota_logo.jpg,顺势使用Mat:: copyTo把mask中的内容拷贝到imageROI中,于是就得到了最终的效果图,namedWindow和imshow配合使用,显示出最终的结果。

         

          二、初级图像混合——线性混合操作

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

                    

         

        如果看过我之前写的游戏编程Alpha混合那篇文章的朋友们应该有些熟悉,其实他们是差不多的:

         【Visual C++】游戏开发五十五浅墨 DirectX教程二十二水乳交融的美学:alpha混合技术

          我们通过在范围0到1之间改变alpha值(α)值,来对两幅图像(f0(x)和f1(x))或两段视频(同样为(f0(x)和f1(x))产生时间上的画面叠化(cross-dissolve)效果,就像幻灯片放映和电影制作中的那样。即在幻灯片翻页时设置的前后页缓慢过渡叠加效果,以及电影情节过渡时经常出现的画面叠加效果。

         实现方面,我们主要用了Opencv中AddWeighted函数,我们来全面的了解一下它:

         

            addWeighted函数

         这个函数的作用是,计算俩个数组(图像阵列)的加权和,原型如下:

    1 void addweighted (InputArray src1 ,double alpha, InputArray src2 ,double beta, double gamma, OutputArray dst ,int dtype = -1);

           

    • 第一个参数,InputArray类型的src1,表示需要加权的第一个数组,常常填一个Mat。
    • 第二个参数,alpha,表示第一个数组的权重
    • 第三个参数,src2,表示第二个数组,它需要和第一个数组拥有相同的尺寸和通道数。
    • 第四个参数,beta,表示第二个数组的权重值。
    • 第五个参数,dst,输出的数组,它和输入的两个数组拥有相同的尺寸和通道数。
    • 第六个参数,gamma,一个加到权重总和上的标量值。看下面的式子自然会理解。
    • 第七个参数,dtype,输出阵列的可选深度,有默认值-1。;当两个输入数组具有相同的深度时,这个参数设置为-1(默认值),即等同于src1.depth()。

           如果用数学公式来表达,addWeighted函数计算如下两个数组(src1和src2)的加权和,得到结果输出给第四个参数。即addWeighted函数的作用可以被表示为为如下的矩阵表达式为:

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

      其中的I,是多维数组元素的索引值。而且,在遇到多通道数组的时候,每个通道都需要独立地进行处理。另外需要注意的是,当输出数组的深度为CV_32S时,这个函数就不适用了,这时候就会内存溢出或者算出的结果压根不对。

      

           理论和函数的讲解就是上面这些,接着我们来看代码实例,以融会贯通。 

     1 bool LinerBlending()
     2 {
     3     //【0】定义一些局部变量
     4     double alphaValue = 0.5;     //这些值感觉不懂
     5     double betaValue;
     6     Mat srcImage2, srcImage3, dstImage;
     7 
     8     //【1】读取图像(两幅图需为同样的类型和尺寸)
     9     srcImage2 = imread("mogu.jpg");
    10     srcImage3 = imread("rain.jpg");
    11 
    12     if (!srcImage2.data)
    13     {
    14         printf("你妹,读取srcImage2错误!!!  
    "); 
    15         return false;
    16     }
    17     if (!srcImage3.data)
    18     {
    19         printf("你妹,读取srcImage3错误!!!  
    ");
    20         return false;
    21     }
    22 
    23 
    24     //【2】做图像混合加权操作
    25     betaValue = (1.0 - alphaValue );
    26     addWeighted(srcImage2,alphaValue ,srcImage3 ,betaValue, 0.0,dstImage );
    27 
    28     //【3】创建并显示原图窗口
    29     namedWindow("2 线性混合示例窗口【原图】 by hehhe",1);
    30     imshow("2 线性混合示例窗口【原图】 by hehhe", srcImage2);
    31 
    32     //【4】创建并显示效果图窗口
    33     namedWindow("3 线性混合示例窗口【效果图】 by hehhe", 1);
    34     imshow("3 线性混合示例窗口【效果图】 by hehhe",dstImage);
    35 
    36     return true;
    37 
    38 }

           

          

                三、综合示例

           在前面分别介绍的设定感兴趣区域ROI和使用addWeighted 函数进行线性混合的基础上,我们还将他们俩者中和起来使用,也就是先指定ROI,并用addWeighted 函数对我们指定的ROI区域进行混合操作,我们将其封装在了一个名为ROI_LinerBlending 的函数中。。。。。

        

      1 #include <opencv2/core/core.hpp>
      2 #include <opencv2/highgui/highgui.hpp>
      3 #include <iostream>
      4 
      5 using namespace cv;
      6 using namespace std;
      7 
      8 bool ROI_AddImage();
      9 bool LinerBlending();
     10 bool ROI_LinerBlending();
     11 
     12 
     13 
     14 /*--------------------------------------------------------------------------------------------------
     15  【1】    函数名:ROI_AddImage()
     16           描述: 利用感兴趣区域ROI实现图像叠加(把图像叠加到ROI区域中,而不是俩张图的直接叠加)
     17 ----------------------------------------------------------------------------------------------------*/
     18 
     19 
     20 bool ROI_AddImage()
     21 {
     22     
     23     //【1】读入图像
     24     Mat srcImage1 = imread("dota_pa.jpg");
     25     Mat logoImage = imread("dota_logo.jpg");
     26     
     27     if (!srcImage1.data)
     28     {
     29         printf("fuck, read the picture is wrong!!! 
    ");
     30         return false;
     31     }
     32 
     33     if (!logoImage.data)
     34     {
     35         printf("fuck, read the picture is wrong!!! 
    ");
     36         return false;
     37     }
     38 
     39     //【2】定义一个Mat类型并给其设定ROI区域
     40     Mat imageROI = srcImage1(Rect(200,250,logoImage.cols ,logoImage.rows));
     41 
     42     //【3】加载掩膜
     43     Mat mask = imread("dota_logo.jpg",0);
     44 
     45     //【4】将掩膜拷贝到ROI
     46     logoImage.copyTo(imageROI,mask);
     47 
     48     //【5】显示结果
     49     namedWindow("1 利用ROI实现图像叠加示例窗口");
     50     imshow("1 利用ROI实现图像叠加示例窗口",srcImage1);
     51 
     52     //waitKey();
     53 
     54     return true;
     55 }
     56 
     57 
     58 
     59 /*----------------------------------------------------
     60    【2】  函数名:LinerBlending
     61           描述:利用cv::addWeighted() 函数实现图像线性混合
     62 -------------------------------------------------------*/
     63 
     64 bool LinerBlending()
     65 {
     66     //【0】定义一些局部变量
     67     double alphaValue = 0.5;     //这些值感觉不懂
     68     double betaValue;
     69     Mat srcImage2, srcImage3, dstImage;
     70 
     71     //【1】读取图像(两幅图需为同样的类型和尺寸)
     72     srcImage2 = imread("mogu.jpg");
     73     srcImage3 = imread("rain.jpg");
     74 
     75     if (!srcImage2.data)
     76     {
     77         printf("你妹,读取srcImage2错误!!!  
    "); 
     78         return false;
     79     }
     80     if (!srcImage3.data)
     81     {
     82         printf("你妹,读取srcImage3错误!!!  
    ");
     83         return false;
     84     }
     85 
     86 
     87     //【2】做图像混合加权操作
     88     betaValue = (1.0 - alphaValue );
     89     addWeighted(srcImage2,alphaValue ,srcImage3 ,betaValue, 0.0,dstImage );
     90 
     91     //【3】创建并显示原图窗口
     92     namedWindow("2 线性混合示例窗口【原图】 by hehhe",1);
     93     imshow("2 线性混合示例窗口【原图】 by hehhe", srcImage2);
     94 
     95     //【4】创建并显示效果图窗口
     96     namedWindow("3 线性混合示例窗口【效果图】 by hehhe", 1);
     97     imshow("3 线性混合示例窗口【效果图】 by hehhe",dstImage);
     98 
     99     //waitKey();
    100 
    101     return true;
    102 
    103 }
    104 
    105 
    106 
    107 
    108 /*---------------------------------------------------------------------------------------------
    109    【3】  函数名:ROI_LinerBlending
    110           描述: 线性混合实现函数,指定区域线性图像混合,利用cv::addWeighted ()结合定义
    111                  感兴趣区域ROI,实现自定义区域的线性混合
    112 -----------------------------------------------------------------------------------------------*/
    113 bool ROI_LinerBlending()
    114 {
    115     //【1】读取图像
    116     Mat srcImage4 = imread("dota_pa.jpg",1);
    117     Mat logoImage = imread("dota_logo.jpg");
    118 
    119     if (!srcImage4.data)
    120     {
    121         printf("你妹,读取srcImage4错误!!!  
    ");
    122         return false;
    123     }
    124     if (!logoImage.data)
    125     {
    126         printf("你妹,读取srcImage错误!!!  
    ");
    127         return false;
    128     }
    129 
    130 
    131     //【2】定义一个Mat类型并给其设定ROI区域
    132     Mat imageROI;
    133     imageROI = srcImage4(Rect(200,250,logoImage.cols,logoImage.rows));
    134 
    135     //【3】将logo 加到原图上
    136     addWeighted(imageROI,0.5,logoImage,0.3,0,imageROI);
    137 
    138     //【4】显示结果
    139     namedWindow("4 区域线性图像混合示例窗口 by hehheh");
    140     imshow("4 区域线性图像混合示例窗口 by hehheh",srcImage4);
    141 
    142     return true;
    143 
    144 }
    145 
    146 
    147 /*------------------------------------------------------
    148   【4】  main函数
    149          描述:控制台程序的入口函数,我们的程序从这里开始
    150 ----------------------------------------------------------*/
    151 
    152 int main()
    153 {
    154     system("color 10");   //控制cmd 窗口的背景颜色
    155 
    156     if (ROI_AddImage() && LinerBlending() && ROI_LinerBlending())
    157     {
    158         cout << "嗯,好了,得出了想要的图像" << endl;
    159     }
    160 
    161     waitKey();
    162     return 0;
    163 }

     

     嗯,本篇文章到这里就基本结束了。。。。。。。。。。。。。。

  • 相关阅读:
    数据库设计
    vs2013怎么删除代码前的小箭头
    win 7系统自带的截图工具在哪里?如何使用?
    SQL Server不允许保存更改
    多个分组中取每个分组中最新的一条数据
    批量向数据库多张表导入数据的实现
    判断字符串是只是数字
    Mac下查看端口占用情况
    Mac上使用Docker Desktop安装Kubernetes
    关于Lombok框架子类继承时EqualsAndHashCode注解的callSuper取值的思考
  • 原文地址:https://www.cnblogs.com/wyuzl/p/6209143.html
Copyright © 2011-2022 走看看