基于C++的opencv(七)core组件

一、访问图像中的像素

1.1.图像在内存中的存储方式

opencv中子列的通道顺序是反过来的——BGR而不是RGB。很多情况下,因为内存足够大,可实现连续存储,因此,图像中的各行就能一行一行地连接起来,形成一个长行。连续存储有助于提升图像扫描速度,我们可以使用isContinuous()来判断矩阵是否是连续存储的。

1.2.颜色空间缩减

若矩阵元素存储的是单通道像素,使用c或c++的无符号字符类型,那么像素可有256个不同值。

颜色空间缩减(color space reduction)的做法是:将现有颜色空间值除以某个输入值,以获得较少的颜色数。

简单的颜色空间缩减算法可由下面两步组成:
1.遍历图像矩阵的每一个像素;
2.对像素应用上述公式。

1.3.LUT函数:Look up table操作

//首先建立一个mat型用于查表

Mat lookUpTable(1,256,CV_8U);
uchar*p=lookUpTable.data;
for(int i=0;i<256;++i)
	p[i]=table[i];
//然后我们调用函数(I是输入,J是输出)
for(int i=0;i<times;++i)
	LUT(I,lookUpTable,J)

1.4.计时函数

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

double time0=static_cast<double>(getTickCount());//记录起始时间
//进行图像处理操作
time0=((double)getTickCount()-timeo)/getTickFrequency();
cout<<"此方法运行时间为:“<<time0<<"秒”<<endl;//输出运行时间

1.5.访问图像中像素二点三种方法

opencv中三种访问每个像素地方法:
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()
{
   
	Mat srcImage=imread("1.jpg");
	imshow("原图",srcImage);

	Mat dstImage;
	dstImage.create(srcImage.rows,srcImage.cols,srcImage.type());//效果图的大小、类型与原图片相同
	//记录起始时间
	double time0=static_cast<double>(getTickCount());
	colorReduce(srcImage,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();

	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;
		}
	}
}

Mat类有若干成员函数可以获取图像的属性。公有成员变量cols和rows给出了图像的宽和高,而成员函数channels()用于返回图像的通道数。灰度图的通道数为1,彩色图的通道数为3.

int colNumber=outputImage.cols*outputImage.channels();

Mat类提供了ptr函数可以得到图像任意行的首地址。ptr是一个模板函数,他返回第i行的首地址。

uchar *data=outputImage.ptr<uchar>(i);

【二】用迭代器操作像素
在迭代法中,所需要做的仅仅是获得图像矩阵的begin和end,然后增加迭代直至从begin到end。将*操作符添加在迭代指针前,即可访问当前指向二点内容。

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;
	}
}

【三】动态地址计算
使用动态地址运算配合at方法。

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;
		}
	}
}

Mat类中的cols和rows给出了图像的宽和高。而成员函数at(int x,int y)可以用来存取图像元素,但是必须在编译期知道图像的数据类型。一定要确保指定的数据类型要和矩阵中的数据类型相符合,因为at方法本身不会对任何数据类型进行转换。

对于彩色图像,每个像素由三个部分构成:蓝色通道、绿色通道和红色通道(BGR)。对于一个包含彩色图像的Mat,会返回一个由三个8位数组成的向量,opencv将此类型的向量定义为Vec3b,即由三个unsigned char组成的向量。

image.at<Vec3b>(j,i)[channel]=value;

其中,索引值channel标明了颜色通道号。

二、ROI区域图像叠加&图像混合

2.1.感兴趣区域:ROI

感兴趣区域(ROI,region of interest)

定义ROI区域的两种方法:
1.使用矩形区域的Rect。它指定矩形的左上角坐标(构造函数的前两个参数)和矩形的长宽(构造函数的后两个参数)以定义一个矩形区域;

Mat imageROI;
imageROI=image(Rect(500,250,logo.cols,logo.rows));

2.指定感兴趣行或列的范围(Range)。Range是指从起始索引到终止索引(不包含终止索引)的一系列连续序列。cRange可以用来定义Range。

imageROI=image(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));

利用ROI将一幅画加到另一幅图的指定位置。通过一个图像淹没(mask),直接将插入处的像素设置为logo图像额像素值。

bool ROI_AddImage()
{
   
	Mat srcImage1=imread("dota_pa.jpg");
	Mat logoImage=imread("dota_logo.jpg");
	if(!srcImage1.data)
	{
   
		printf("读取srcImage1错误~! \n");
		return false;
	}
	if(!logoImage.data)
	{
   
		printf("读取logoImage错误~! \n");
		return false;
	}
	Mat imageROI=srcImage1(Rect(200,250,logoImage.cols,logoImage.rows));
	//加载掩膜(必须是灰度图)
	Mat mask=imread("dota_logo.jpg",0);
	//将掩膜复制到ROI
	logoImage.copyTo(imageROI,mask);

	namedWindow("<1>利用ROI实现图像叠加示例窗口");
	imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1);

	return true:
}

先载入两张jpg图片,然后定义一个Mat类型的imageROI,并使用Rect设置其感兴趣区域为srcImage1中的一块区域,将imageROI和srcImage1关联起来。

2.2.线性混合操作

线性混合操作是一种典型的二元(两个输入)的像素操作。
主要运用OpenCV中addWeighted函数。

2.3.计算数组加权和:addWeighted()函数

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

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

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

其中I是多维数组元素的索引值。当输出数组的深度位CV_32S时,这个函数不适用,这时候会内存溢出或者算出的结果不对。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值