opencv学习(5)图像像素的访问、颜色通道的分离和融合

代码都是源于毛星云的《opencv3.0编程入门》

1、计时函数
getTickCount()和getTickFrequency()函数;
getTickCount()函数返回CPU自某个事件以来走过的时钟周期;
getTickFrequency()函数 返回CPU一秒所走的周期数;

事例:

double time0=static_cast< double>(getTickCount()); //记录起始时间
// 事件的处理操作
time0=((double)getTickCount()-time0)/getTickFrequency();
cout<<"此方法的运行时间为:"<< time0<<"秒"<< endl;

2、访问图像像素中的三类方法:
1)【方法一】指针访问:C操作符[ ];
2)【方法二】迭代器iterator;
3)【方法三】动态地址计算;

主程序代码:

#include <opencv2/core/core.hpp> 
#include <opencv2/highgui/highgui.hpp> 
#include <iostream> 
using namespace std;  
using namespace cv;  

void colorReduce(Mat& inputImage, Mat& outputImage, int div);

int main( )  
{  
    //【1】创建原始图并显示
    Mat srcImage = imread("1.jpg");  
    imshow("原始图像",srcImage);  

    //【2】按原始图的参数规格来创建创建效果图
    Mat dstImage;
    dstImage.create(srcImage.rows,srcImage.cols,srcImage.type());//效果图的大小、类型与原图片相同 

    //【3】记录起始时间
    double time0 = static_cast<double>(getTickCount());  

    //【4】调用颜色空间缩减函数
    colorReduce(srcImage,dstImage,32);  

    //【5】计算运行时间并输出
    time0 = ((double)getTickCount() - time0)/getTickFrequency();
    cout<<"\t此方法运行时间为: "<<time0<<"秒"<<endl;  //输出运行时间

    //【6】显示效果图
    imshow("效果图",dstImage);  
    waitKey(0);  
}  

【方法一】指针访问:C操作符[ ];
速度最快,但有点抽象;

void colorReduce(Mat& inputImage, Mat& outputImage, int div)  
{  
    //参数准备
    outputImage = inputImage.clone();  //拷贝实参到临时变量
    int rowNumber = outputImage.rows;  //行数
    int colNumber = outputImage.cols*outputImage.channels();  //列数 x 通道数=每一行元素的个数

    //双重循环,遍历所有的像素值
    for(int i = 0;i < rowNumber;i++)  //行循环
    {  
        uchar* data = outputImage.ptr<uchar>(i);  //获取第i行的首地址
        for(int j = 0;j < colNumber;j++)   //列循环
        {   
            // ---------【开始处理每个像素】------------- 
            data[j] = data[j]/div*div + div/2;  
            // ----------【处理结束】---------------------
        }  //行处理结束
    }  
}  

Mat类中:公有成员变量cols和rows给出了图像的宽和高;成员函数channels()用于返回图像的通道数;
每行的像素个数:`int colNumber=outputImage.cols*outputImage.channels();
Mat 类提供了ptr函数可以得到图像任意行的首地址。ptr是一个模板函数,返回第i行的首地址;
unchar* data =outputImage.ptr< uchar>(i);//获取第i行的首地址;

【方法二】迭代器iterator;

void colorReduce(Mat& inputImage, Mat& outputImage, int div)  
{  
    //参数准备
    outputImage = inputImage.clone();  //拷贝实参到临时变量
    //获取迭代器
    Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>();  //初始位置的迭代器
    Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();  //终止位置的迭代器

    //存取彩色图像像素
    for(;it != itend;++it)  
    {  
        // ------------------------【开始处理每个像素】--------------------
        (*it)[0] = (*it)[0]/div*div + div/2;  
        (*it)[1] = (*it)[1]/div*div + div/2;  
        (*it)[2] = (*it)[2]/div*div + div/2;  
        // ------------------------【处理结束】----------------------------
    }  
}  

在迭代器中我们需要的仅仅是获取图像矩阵的begin和end,将*操作符添加在迭代器指针前,即可访问当前指向的内容;

3)【方法三】动态地址计算;

void colorReduce(Mat& inputImage, Mat& outputImage, int div)  
{  
    //参数准备
    outputImage = inputImage.clone();  //拷贝实参到临时变量
    int rowNumber = outputImage.rows;  //行数
    int colNumber = outputImage.cols;  //列数

    //存取彩色图像像素
    for(int i = 0;i < rowNumber;i++)  
    {  
        for(int j = 0;j < colNumber;j++)  
        {   
            // ------------------------【开始处理每个像素】--------------------
            outputImage.at<Vec3b>(i,j)[0] =  outputImage.at<Vec3b>(i,j)[0]/div*div + div/2;  //蓝色通道
            outputImage.at<Vec3b>(i,j)[1] =  outputImage.at<Vec3b>(i,j)[1]/div*div + div/2;  //绿色通道
            outputImage.at<Vec3b>(i,j)[2] =  outputImage.at<Vec3b>(i,j)[2]/div*div + div/2;  //红是通道
            // -------------------------【处理结束】----------------------------
        }  // 行处理结束 
    }  
}  

动态地址运算配合at方法的colorReduce函数;

3、分离颜色通道、多通道图像的混合
1)通道分离:split( )函数;
split( )函数的C++版本有两个原型;分别是:
void split(const Mat& src,Mat* mvbegin);
void split(InputArray m,OutputArrayOfArrays mv);
第一个参数,InputArray类型的m或者const Mat&类型的src,是需要进行分离的多通道数组;
第二个参数,OutputArrayOfArrays类型的mv,填函数的输出数组或者输出的vector容器。
我们一起看一个示例。

vector<Mat> channels;
Mat imageBlueChannel;
Mat imageGreenChannel;
Mat imageRedChannel;
srcImage4= imread("dota.jpg");
// 把一个 3 通道图像转换成 3 个单通道图像
split(srcImage4,channels);//分离色彩通道
imageBlueChannel = channels.at(0);
imageGreenChannel = channels.at(1);
imageRedChannel = channels.at(2);

上面的代码先做了相关的类型声明,然后把载入的 3 通道图像转换成 3 个单通道图像,放到vector类型的 channels 中,接着进行引用赋值。
根据 OpenCV 的 BGR 色彩空间(Blue、Green、Red,蓝绿红),其中 channels.at(0)就表示引用取出 channels 中的蓝色分量,channels.at(1)就表示引用取出 channels 中的绿色色分量,channels.at(2)就表示引用取出 channels 中的红色分量。
将一个多通道数组分离成几个单通道数组的 split()函数的内容大概就是以上这些了,下面我们来看一下和它关系密切的 merge()函数。

2)通道合并:merge( )函数;
依然是一个示例,综合了 split()函数和 merge()函数的使用,先将图像的通道进行拆分,再将通道合并。具体代码如下所示。

//定义一些 Mat 对象
Mat srcImage=imread("1.jpg");
Mat imageBlueChannel, imageGreenChannel, imageRedChannel,mergeImage;
//定义 Mat 向量容器保存拆分后的数据
vector<Mat> channels;
//通道的拆分
split(srcImage,channels);
//提取三色的通道的数据
imageBlueChannel = channels.at(0);
imageGreenChannel = channels.at(1);
imageRedChannel = channels.at(2);
//对拆分的通道数据合并
merge(channels,mergeImage);
//显示最终的合并效果
imshow("mergeImage",mergeImage);

一对做相反操作的 split()函数和 merge()函数和用法就是这些。另外提一点,如果我们需要从多通道数组中提取出特定的单通道数组,或者说实现一些复杂的通道组合,可以使用 mixChannels()函数。

以其中的蓝色通道为例:

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>

using namespace cv;
using namespace std;
int main()
{
    Mat srcImage;
    Mat logoImage;
    vector<Mat> channels;

    Mat  imageBlueChannel;
    Mat  imageGreenChannel;
    Mat  imageRedChannel;

    //=================【蓝色通道部分】=================
    // 【1】读入图片
    logoImage = imread("dota_logo.jpg", 0);
    srcImage = imread("dota_jugg.jpg");

    if (!logoImage.data) { printf("Oh,no,读取logoImage错误~! \n"); return false; }
    if (!srcImage.data)  { printf("Oh,no,读取srcImage错误~! \n"); return false; }

    //【2】把一个3通道图像转换成3个单通道图像
    split(srcImage, channels);//分离色彩通道

    //【3】将原图的蓝色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变
    imageBlueChannel = channels.at(0);
    //【4】将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageBlueChannel中。简单讲就是原图的蓝色通道加上了logo替换了原始的蓝色通道。
    addWeighted(imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0,
        logoImage, 0.5, 0, imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows)));

    //【5】将三个单通道重新合并成一个三通道
    merge(channels,srcImage);

    //【6】显示效果图
    namedWindow(" <1>游戏原画+logo蓝色通道");
    imshow(" <1>游戏原画+logo蓝色通道", srcImage);
    waitKey(0);
    return 0;
}

你可能感兴趣的:(opencv)