目录
由于最近学OpenCV,需要做个笔记,方便下次复习使用。本文分为以下几个部分:
- 颜色空间衰减
- ROI区域叠加&图像混合
- 分离颜色通道、多通道图像混合
对于多通道图像来说,矩阵中的列会包含多个子列,其子列个数与通道数相等。如下图所示:
可以看到,OpenCV中子列的通道顺序是反过来的——BGR而不是RGB。很多情况下,因为内存足够大,可以实现连续存储,因此,图像中的各行就能一行一行地连接起来,形成一个长行。连续存储有助于提升图像扫描速度,我们可以使用isContinuous()来判断矩阵是否是连续存储的。
颜色空间衰减
若矩阵元素存储的是单通道像素,使用uchar类型,那么就可以存储256个不同值。但若是三通道,那么存储格式的颜色就有一千六百多万总,这可能会对算法性能造成严重影响。
使用颜色空间缩减,可以大大降低复杂度,具体做法是:将现有颜色空间值除以某个输入值,已获得较少的颜色数。比如颜色值0到9可以取值为0,10到19可以取值为10这样。
访问图像中像素三种方法
- 指针访问
- 迭代器iterator
- 动态地址计算
使用指针访问像素
#include <opencv2/core.hpp>
using namespace std;
using namespace cv;
void colorReduce(Mat& inputImage, Mat& outputImage, int div);
int main()
{
Mat img = imread("F:\\11\\xiaohuangren.jpeg");
imshow("原始图像", img);
Mat dstImage;
dstImage.create(img.rows, img.cols, img.type());
//[1]记录起始时间
double time0 = static_cast<double>(getTickCount());
//[2]调用颜色缩减函数
colorReduce(img, dstImage, 32);
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "运行时间" << time0 << "秒" << endl;
imshow("改变后的图像", dstImage);
waitKey(0);
}
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);
for (int j = 0; j < colNumber; j++) {
data[j] = data[j] / div * div + div / 2;
//*data++ = *data/div*div + div/2;
}
}
}
这里div=32,也就是说颜色数256/32=8种颜色。也可以使用8,等等;
运行结果
使用迭代器操作像素
这种方法与STL库的用法类似,使用迭代器的操作访问。
void colorReduce(Mat& inputImage, Mat& outputImage, int div) {
outputImage = inputImage.clone(); //复制实参到临时变量
Mat_<Vec3b>::iterator it = outputImage.begin<Vec3b>(); //vertor,3维像素
Mat_<Vec3b>::iterator itend = outputImage.end<Vec3b>();
for (; it != itend; it++) {
(*it)[0] = (*it)[0] / div * div + div / 2; //访问像素中的通道一,也就是第一种颜色B
(*it)[1] = (*it)[1] / div * div + div / 2;
(*it)[2] = (*it)[2] / div * div + div / 2;
}
}
动态地址计算
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,必须知道图像的数据类型;对于彩色图像,每个像素由三个部分组成:BGR,因此,对于一个包含彩色图像的Mat,会返回一个由三个8位数组成的向量。opencv将此类型的向量定义成Vec3b,即由三个unsigned char组成的向量。
ROI区域叠加&图像混合
在图像处理中,我们常常需要设置感兴趣的区域(ROI),来专注或者简化工作过程。也就是从图像中选取一个图像区域,这个区域是图像分析所关注的重点。我们圈定这个区域,以便进一步处理。
感兴趣区域
Mat srcImage = imread("F:\\11\\xiaohuangren.jpeg");
Mat rect, range;
rect = srcImage(Rect(0, 0, 300, 300));
range = srcImage(Range(0, 300), Range(0, 300));
imshow("修改图像", srcImage);
imshow("Rect", rect);
imshow("Range", range);
运行结果如下:
左上是使用了Rect的构造方法,左下是使用了Range的构造方法。
线性混合操作
线性混合操作是一个典型的二元(两个输入)的像素操作,它们的理论公式如下
g(x) = (1-a)f(x) + ag(x) a是参数,f(x),g(x)表示函数
我们通过在范围0到1改变alpha值,来对两幅图像或视频产生画面重叠效果。
实现方面,主要使用opencv中的addWeighted函数。
实现如下:
int main()
{
Mat srcImage1 = imread("F:\\11\\xiaohuangren.jpeg");
//imshow("原始图像", img);
Mat srcImage2 = imread("F:\\11\\xiaohuangren2.jpg");
Mat dstImage;
addWeighted(srcImage1, 0.5, srcImage2, 0.5, 0.0, dstImage, 0);
imshow("修改图像", dstImage);
waitKey(0);
}
运行结果
我们可以将两种方法结合起来使用。这里就不给出对应的代码了。
分离颜色通道、多通道图像混合
通道分离
spilt函数可以将一个多通道组分离成几个单通道数组。
- void split(const Mat&src,Mat&mvbegin);
- void split(InputArray m,OutputArrayOfArrays mv)
- 第一个参数,InputArray类型的m或Mat类型的引用对象。
- 第二个参数是填函数输出数组或者输出的vertor容器
对应代码
int main()
{
Mat srcImage = imread("F:\\11\\xiaohuangren.jpeg");
Mat channel1, channel2, channel3;
vector<Mat> channel;
split(srcImage, channel);
//这里,将srcImage中的每一通道值输出到vertor容器,而vertor容器是一个Mat类型的,也就是
//该容器类型的第0个元素就是B,第1个元素是G,第2个元素是R
channel1 = channel.at(0); //表示取出srcImage中的蓝色分量
channel2 = channel.at(1);
channel3 = channel.at(2);
imshow("B", channel1);
imshow("G", channel2);
imshow("R", channel3);
waitKey(0);
}
运行结果如下
通道合并
merge()函数是spilt()函数的逆向操作——将多个数组合并成一个多通道的数组。通过组合一些给定的单通道数组,将这些孤立的单通道数组合成一个多通道的数组,从而创建出一个由多个单通道阵列组成的多通道阵列。
- void merge(const Mat& mv,size_tcount,OutputArray dst)
- void merge(InputArrayOfArrays mv,OutputArray dst)
- 第一个参数,填入需要合并的输入矩阵或vertor阵列。
- 第二个参数,count,当mv为一个空白的C数组时,代码输入矩阵的个数,参数需要大于1
- 第三个参数,输出矩阵,并且通道的数量是矩阵阵列中的通道的总数
在spilt的基础上,使用merge,可以恢复原图
Mat dstImage;
merge(channel, dstImage);
imshow("原图", dstImage);
运行结果
总结
由于刚学OpenCV,因此可能会存在一些知识性的错误,希望大家不要见怪。另外,下一篇将是有关
- 对比度、亮度
- 离散傅里叶变换