OpenCV3学习笔记core组件进阶

目录

由于最近学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,因此可能会存在一些知识性的错误,希望大家不要见怪。另外,下一篇将是有关

  • 对比度、亮度
  • 离散傅里叶变换


  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值