opencv学习笔记访问图像中像素的三种方式ROI区域图像叠加和图像混合

Posted 非晚非晚

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了opencv学习笔记访问图像中像素的三种方式ROI区域图像叠加和图像混合相关的知识,希望对你有一定的参考价值。

1. 访问图像中像素的三种方式

任何图像处理算法,都是从操作每个像素开始的。在OpenCV中,提供了三种访问每个像素的方法。

  • 方法1:指针访问:C操作符[]
  • 方法2:迭代器iterator
  • 方法3:动态地址计算

三种访问速度的比较:指针访问 > 迭代器访问 > 动态地址。指针访问速度最快。需要注意的是,OpenCV中的彩色图像不是以RGB的顺序存放的,而是BGR

方法1:指针访问

通过调用函数 Mat::ptr(i) 来得到第i行的首地址地址,然后在行内访问像素;ptr是一个模板函数,它返回第i行的首地址。注意uchar与Vec3b的区别,uchar需要乘以一个通道数

void test01()
{
    //指针的访问
    for (int i = 0; i < Row; i++)
    {
        for (int j = 0; j < Col; j++)
        {
            Scr.ptr<Vec3b>(i)[j][0] = 0;
            //Scr.ptr<uchar>(i)(3*j) = 0; //注意uchar与Vec3b的区别,uchar需要乘以一个通道数
        }
    }
}

方法2:迭代器操作像素

这种方式与STL库的用法类似,创建一个Mat::Iterator对象it,通过it=Mat::begin()迭代器访问首地址,递增迭代器直到it==Mat::end()结束迭代;

void test02()
{
    //迭代器访问,访问的是每一个像素点
    Mat_<Vec3b>::iterator it = Scr.begin<Vec3b>();
    while (it != Scr.end<Vec3b>())
    {
        //(*it)[0] = 0;//蓝色通道置零;BGR存储
        (*it)[1] = 0; //绿色通道置零;
        //(*it)[2] = 0;//红色通道置零;
        it++;
    }
}

方法3:动态地址计算

这种方法是最慢的一种方法,但是比较好理解,使用at函数来得到像素,Mat::at(i,j)为一个像素点的像素值数组,是一个大小为3的数组。从0到2存放了BGR三种颜色的灰度值。

void test03()
{
    //动态访问,访问的是每一个像素点,最慢也是最直接易理解的访问方式
    for (int i = 0; i < Row; i++)
    {
        for (int j = 0; j < Col; j++)
        {
            Scr.at<Vec3b>(i, j)[2] = 0;
        }
    }
}

整体代码示例

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

Mat Scr;
int Row;
int Col;

void test01()
{
    //指针的访问
    for (int i = 0; i < Row; i++)
    {
        for (int j = 0; j < Col; j++)
        {
            Scr.ptr<Vec3b>(i)[j][0] = 0;
        }
    }
}
void test02()
{
    //迭代器访问,访问的是每一个像素点
    Mat_<Vec3b>::iterator it = Scr.begin<Vec3b>();
    while (it != Scr.end<Vec3b>())
    {
        //(*it)[0] = 0;//蓝色通道置零;BGR存储
        (*it)[1] = 0; //绿色通道置零;
        //(*it)[2] = 0;//红色通道置零;
        it++;
    }
}
void test03()
{
    //动态访问,访问的是每一个像素点,最慢也是最直接易理解的访问方式
    for (int i = 0; i < Row; i++)
    {
        for (int j = 0; j < Col; j++)
        {
            Scr.at<Vec3b>(i, j)[2] = 0;
        }
    }
}

int main()
{
    Scr = imread("/home/liqiang/Data/vision/classic/lena.jpg");
    if (Scr.empty())
    {
        printf("%s\\n", "读入图片失败;");
        return 1;
    }
    imshow("原始图像", Scr);

    Row = Scr.rows;           //获取行数目;
    Col = Scr.cols;           //获取列数目;
    int Channel = Scr.channels(); //获取通道数目;
    
    test01();//指针访问
    test02();//迭代器访问
    test03();//动态访问

    imshow("改变后的图像", Scr);
    waitKey(0);
    return 1;
}

输出:

在这里插入图片描述

2. 感兴趣区域ROI和图像叠加

2.1 感兴趣区域ROI

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

定义ROI区域有两种方式:

  • 方法1:使用矩形区域的Rect,指定矩形的左上角坐标(构造函数的前两个参数)和矩形的长宽(构造函数的后两个参数)就可以定义一个矩形区域。
Mat = image;//载入的图片
Mat imageROI = image(Rect(500, 250, logo.cols, logo.rows));//左上角,width,height
  • 方法2:指定感兴趣区域行和列的范围(Range),Range表示从起始索引到终止(不包含)索引的一段连续序列。
imageROI = image(Range(250, 250 + logoImage.rows), Range(200, 200 + logoImage.cols));

注意两种形式,行和列的顺序不同

2.2 图像叠加

下面使用ROI实现图像的叠加操作,实际上就是copy操作。原书采用的掩模的形式,实际上不需要,可以直接拷贝。

#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;

int main()
{
    //【1】读入图像
    Mat srcImage1 = imread("/home/liqiang/Data/vision/classic/lena.jpg");//512*512
    Mat logoImage = imread("/home/liqiang/Data/vision/classic/WindowsLogo.jpg");//240*320
    if (!srcImage1.data)
    {
        printf("读取srcImage1错误~! \\n");
        return false;
    }
    if (!logoImage.data)
    {
        printf("读取logoImage错误~! \\n");
        return false;
    }
    //【2】定义一个Mat类型并给其设定ROI区域
    Mat imageROI = srcImage1(Rect(0, srcImage1.rows-logoImage.rows, logoImage.cols, logoImage.rows));//左下角
   
    //【3】加载掩模(必须是灰度图)
    Mat mask;
     cvtColor(logoImage, mask, COLOR_RGB2GRAY);//用转换函数转换成灰度图

    //【4】将掩膜拷贝到ROI
    //logoImage.copyTo(imageROI, mask);
    logoImage.copyTo(imageROI);//不用掩膜,直接拷贝

    //【5】显示结果
    namedWindow("<1>利用ROI实现图像叠加示例窗口");
    imshow("<1>利用ROI实现图像叠加示例窗口", srcImage1);

    waitKey(0);
    return true;
}

输出效果:
在这里插入图片描述

3. 图像混合(线性混合)

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

g ( x ) = ( 1 − a ) f 1 ( x ) + a f 2 ( x ) g(x) = (1-a)f_1(x)+af_2(x) g(x)=(1a)f1(x)+af2(x)
可以通过alpha在0-1之间的变化,来对两幅图像或者两段视频产生时间上的画面叠化(cross-dissolve)效果,类似于幻灯片和电影制作中的那样,也就是幻灯片翻页或者电影情节过渡时的效果。

实现方面,主要运用了Opencv中的addWeighted函数来实现。它的函数原型如下:

addWeighted(InputArray src1, double alpha, InputArray src2, double beta, double gamma, OutputArray dst, int dtype = -1);
  • src1和src2分别表示两幅图像
  • alpha和beta分别表示两幅图像的权重
  • dst表示输出图像
  • gamma加载到dst的权重
  • dtype表示图像深度,即src1.depth()

dst = src1 * alpha + src2 * beta + gamma

#include <opencv2/opencv.hpp>
using namespace cv;
int main()
{
    Mat src1 = imread("/home/liqiang/Data/vision/classic/LinuxLogo.jpg");
    Mat src2 = imread("/home/liqiang/Data/vision/classic/WindowsLogo.jpg");
    Mat dst;
    double alpha = 0.6;
    addWeighted(src1, alpha, src2, 1 - alpha, 0.0, dst);
    imshow("dst", dst);
    waitKey(0);
    return 0;
}

输出效果:

在这里插入图片描述

还可以结合感兴趣区域和线性混合来实现部分混合,这里就不介绍了,实现起来应该不难。

以上是关于opencv学习笔记访问图像中像素的三种方式ROI区域图像叠加和图像混合的主要内容,如果未能解决你的问题,请参考以下文章

OpenCV学习笔记3基础:图像基本操作

OpenCV学习笔记3基础:图像基本操作

OpenCV学习笔记3基础:图像基本操作

OpenCV学习笔记3基础:图像基本操作

【OPENCV】cv::Mat像素遍历方法比较

opencv之访问图像像素