访问图像像素

存储方式

BGR连续存储有助于提升图像扫描速度。

isContinuous()判断是否是连续存储。

颜色空间缩减

仅用这些颜色中具有代表性的很小的部分,就足以达到同样的效果。

将现有颜色空间值除以某个输入值,获得较少的颜色数。

LUT函数:look up table操作

用于批量进行图像元素查找、扫描和操作图像。

使用方法如下:

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

计时函数

两个函数组合起来使用:

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

访问图像中像素的三类方法

要求:减少图像中颜色的数量,比如原来的图像是256种颜色,希望它变成64种颜色,只需将原来颜色除以4(整除)以后再乘以4就可以

  • 方法一,指针访问:C操作符[];
  • 方法二,迭代器iterator
  • 方法三,动态地址计算

方法一

 //---------------------------------【头文件、命名空间包含部分】--------------------------
// 描述:包含程序所使用的头文件和命名空间
//-----------------------------------------------------------------------------------------------
#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); //--------------------------------------【main( )函数】---------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
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());
printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION ); //【4】调用颜色空间缩减函数
colorReduce(srcImage,dstImage,); //【5】计算运行时间并输出
time0 = ((double)getTickCount() - time0)/getTickFrequency();
cout<<"\t此方法运行时间为: "<<time0<<"秒"<<endl; //输出运行时间 //【6】显示效果图
imshow("效果图",dstImage);
waitKey();
} //---------------------------------【colorReduce( )函数】---------------------------------
// 描述:使用【指针访问:C操作符[ ]】方法版的颜色空间缩减函数
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div) //div是要转成多少种颜色的数量
{
//参数准备
outputImage = inputImage.clone(); //拷贝实参到临时变量
int rowNumber = outputImage.rows; //行数
int colNumber = outputImage.cols * outputImage.channels(); //列数 x 通道数=每一行元素的个数 //双重循环,遍历所有的像素值
for(int i = ;i < rowNumber;i++) //行循环
{
uchar* data = outputImage.ptr<uchar>(i); //获取第i行的首地址
for(int j = ;j < colNumber;j++) //列循环
{
// ---------【开始处理每个像素】-------------
data[j] = data[j]/div*div + div/2;
//可以等效使用指针运算从一列移动到下一列,所以还可以这样写:
// *data++ = *data/div*div + div/2;
// ----------【处理结束】---------------------
} //行处理结束
}
}

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

ptr函数可以得到图像任意行的首地址,ptr是模板函数,返回第i行的首地址。

方法二:

获得图像矩阵的begin和end,然后增加迭代从begin到end,将*操作符添加在迭代指针前,即可访问当前指向的内容。

相比用指针直接访问可能出现越界问题,迭代器很安全。

 //-------------------------------------【colorReduce( )函数】-----------------------------
// 描述:使用【迭代器】方法版的颜色空间缩减函数
//----------------------------------------------------------------------------------------------
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)[] = (*it)[]/div*div + div/;
(*it)[] = (*it)[]/div*div + div/;
(*it)[] = (*it)[]/div*div + div/; //三个通道
// ------------------------【处理结束】----------------------------
}
}

方法三:

 //----------------------------------【colorReduce( )函数】-------------------------------
// 描述:使用【动态地址运算配合at】方法版本的颜色空间缩减函数
//----------------------------------------------------------------------------------------------
void colorReduce(Mat& inputImage, Mat& outputImage, int div)
{
//参数准备
outputImage = inputImage.clone(); //拷贝实参到临时变量
int rowNumber = outputImage.rows; //行数
int colNumber = outputImage.cols; //列数 //存取彩色图像像素
for(int i = ;i < rowNumber;i++)
{
for(int j = ;j < colNumber;j++)
{
// ------------------------【开始处理每个像素】--------------------
outputImage.at<Vec3b>(i,j)[0] = outputImage.at<Vec3b>(i,j)[0]/div*div + div/2; //蓝色通道
outputImage.at<Vec3b>(i,j)[] = outputImage.at<Vec3b>(i,j)[]/div*div + div/; //绿色通道
outputImage.at<Vec3b>(i,j)[] = outputImage.at<Vec3b>(i,j)[]/div*div + div/; //红是通道
// -------------------------【处理结束】----------------------------
} // 行处理结束
}
}

Mat类的成员函数at(int x,int y)可以用来存取图像元素。要确保指定的数据类型要和矩阵中的数据类型相符合,因为at方法本身不会对任何数据类型进行转换。

对于一个包含彩色图像的Mat,会返回一个有三个8位数组成的向量。opencv将此类型的向量定义为Vec3b,即有三个unsigned char组成的向量,这也解释了为什么存取彩色图像像素的代码的形式:

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

感兴趣区域:ROI

这个区域是图像分析所关注的重点,圈住这个区域,方便进行进一步的处理。使用ROI指定想读入的目标,可以减少处理时间,提升精度,带来很大便利。

定义ROI区域的方法有两种:

第一种是使用表示矩形区域的Rect。指定矩形的左上角坐标和矩形的weidth和heigth

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

第二种指定ROI的行或列的范围Range。Range是指起始索引到终止索引(不包括终止索引)的一段连续序列:

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

下面代码中,通过一个图像掩模,直接将插入处的像素设置为logo图像的像素值,这样效果会很逼真。

 //-----ROI_AddImage()函数
//利用感兴趣区域ROI实现图像叠加
bool ROI_AddImage()
{
//1、读入图像
Mat srcImagel = imread("doata");
Mat logoImage = imread("dota_logo.jpg");
if(!srcImagel.data)
{
printf("读取错误!\n");
return false;
}
if(!logoImage.data)
{
printf("读取错误!\n");
return false;
} //2、定义一个Mat类型并给其设定ROI区域
Mat imageROI = srcImagel(Rect(,,logoImage.cols,logoImage.rows));
//3、加载掩模(必须是灰度图)
Mat mask = imread("dota_logo.jpg",);
//4、将掩模复制到ROI
logoImage.copyTo(imageROI,mask);
//5、显示结果
namedWindow("test");
imshow("test",srcImagel);
return true;
}

图像掩模一般用来对处理的图像(全部或者局部)进行遮挡,来控制图像处理的区域或处理过程。

掩模一般是小于等于源图像的单通道矩阵,掩模中的值分为两种0和非0。以Mat::copyTo为例,当mask的值不为0,则将源图像拷贝到目标图像,当mask为0,则不进行拷贝,目标图像保持不变。

线性混合操作

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

通过范围0~1之间改变alpha值,来对两幅图像f(x)或两段视频产生时间上的画面叠化效果,就想幻灯片放映和电影制作中的那样,也就是在幻灯片翻页时设置的前后叶缓慢过度叠加效果。

主要用到了addWeighted函数。

计算数组加权和:addWeighted函数

这个函数的作用是计算两个数组(图像阵列)的加权和:

void addWeighted(inputarray src1, double alpha, inputarray arc2, double beta, double gamma, outputarray dst, int dtyoe=-);

(1)需要加权的第一个数组,常常填一个Mat

(2)alpht,第一个数组的权重

(3)src2,第二个数组,和第一个数组拥有相同的尺寸和通道数

(4)beta,第二个数组的权重值

(5)gamma,一个加到权重总和上的标量值

(6)dst,输出的数组,和输入的两个数组有相同的尺寸和通道数

(7)dtype,输出阵列的可选深度,默认-1,。当两个输入数组具有相同的深度时,这个参数设置为-1。

最终的计算结果:

其中I是多维数组的索引值。每个通道都要独立的进行处理。当输出数组的深度为CV_32S时,这个函数就不适用了。

 /---------------------------------【LinearBlending()函数】-------------------------------------
// 函数名:LinearBlending()
// 描述:利用cv::addWeighted()函数实现图像线性混合
//--------------------------------------------------------------------------------------------
bool LinearBlending()
{
//【0】定义一些局部变量
double alphaValue = 0.5;
double betaValue;
Mat srcImage2, srcImage3, dstImage; // 【1】读取图像 ( 两幅图片需为同样的类型和尺寸 )
srcImage2 = imread("mogu.jpg");
srcImage3 = imread("rain.jpg"); if( !srcImage2.data ) { printf("读取srcImage2错误! \n"); return false; }
if( !srcImage3.data ) { printf("读取srcImage3错误! \n"); return false; } // 【2】进行图像混合加权操作
betaValue = ( 1.0 - alphaValue );
addWeighted( srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); // 【3】显示原图窗口
imshow( "<2>线性混合示例窗口【原图】", srcImage2 );
imshow( "<3>线性混合示例窗口【效果图】", dstImage ); return true; }

初级图像混合

 //---------------------------------【头文件、命名空间包含部分】-------------------------------
// 描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream> using namespace cv;
using namespace std; //-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数声明
//-----------------------------------------------------------------------------------------------
bool ROI_AddImage();
bool LinearBlending();
bool ROI_LinearBlending();
void ShowHelpText(); //-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
//system调用dos命令
//color 6f是用来设置dos窗口颜色的;1设置窗口背景颜色,f设置窗口字体颜色。
system("color 6F"); ShowHelpText(); if(ROI_AddImage( ) && LinearBlending( ) && ROI_LinearBlending( ))
{
cout<<endl<<"\n运行成功,得出了需要的图像";
} waitKey();//等待键位操作,按下-> 返回键盘的操作值,否则返回-1
return ;
} //-----------------------------------【ShowHelpText( )函数】----------------------------------
// 描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
void ShowHelpText()
{
//输出欢迎信息和OpenCV版本
printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
printf("\n\n\t\t\t此为本书OpenCV3版的第25个配套示例程序\n");
printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION );
printf("\n\n ----------------------------------------------------------------------------\n");
} //----------------------------------【ROI_AddImage( )函数】----------------------------------
// 函数名:ROI_AddImage()
// 描述:利用感兴趣区域ROI实现图像叠加
//----------------------------------------------------------------------------------------------
bool ROI_AddImage()
{ // 【1】读入图像
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; } // 【2】定义一个Mat类型并给其设定ROI区域
Mat imageROI= srcImage1(Rect(,,logoImage.cols,logoImage.rows)); // 【3】加载掩模(必须是灰度图)
Mat mask= imread("dota_logo.jpg",); //【4】将掩膜拷贝到ROI
logoImage.copyTo(imageROI,mask); // 【5】显示结果
namedWindow("<1>利用ROI实现图像叠加示例窗口");
imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1); return true;
} //---------------------------------【LinearBlending()函数】-------------------------------------
// 函数名:LinearBlending()
// 描述:利用cv::addWeighted()函数实现图像线性混合
//--------------------------------------------------------------------------------------------
bool LinearBlending()
{
//【0】定义一些局部变量
double alphaValue = 0.5;
double betaValue;
Mat srcImage2, srcImage3, dstImage; // 【1】读取图像 ( 两幅图片需为同样的类型和尺寸 )
srcImage2 = imread("mogu.jpg");
srcImage3 = imread("rain.jpg"); if( !srcImage2.data ) { printf("读取srcImage2错误! \n"); return false; }
if( !srcImage3.data ) { printf("读取srcImage3错误! \n"); return false; } // 【2】进行图像混合加权操作
betaValue = ( 1.0 - alphaValue );
addWeighted( srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); // 【3】显示原图窗口
imshow( "<2>线性混合示例窗口【原图】", srcImage2 );
imshow( "<3>线性混合示例窗口【效果图】", dstImage ); return true; } //---------------------------------【ROI_LinearBlending()】-------------------------------------
// 函数名:ROI_LinearBlending()
// 描述:线性混合实现函数,指定区域线性图像混合.利用cv::addWeighted()函数结合定义
// 感兴趣区域ROI,实现自定义区域的线性混合
//--------------------------------------------------------------------------------------------
bool ROI_LinearBlending()
{ //【1】读取图像
Mat srcImage4= imread("dota_pa.jpg",);
Mat logoImage= imread("dota_logo.jpg"); if( !srcImage4.data ) { printf("读取srcImage4错误~! \n"); return false; }
if( !logoImage.data ) { printf("读取logoImage错误~! \n"); return false; } //【2】定义一个Mat类型并给其设定ROI区域
Mat imageROI;
//方法一
imageROI= srcImage4(Rect(,,logoImage.cols,logoImage.rows));
//方法二
//imageROI= srcImage4(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols)); //【3】将logo加到原图上
addWeighted(imageROI,0.5,logoImage,0.3,.,imageROI); //【4】显示结果
imshow("<4>区域线性图像混合示例窗口",srcImage4); return true;
}

分离颜色通道、多通道图像混合

通道分离:split函数

split函数用于将一个多通道数组分离成几个单通道数组,这里的array用语境翻译为数组或者阵列。

两个原型:

 void split(const Mat& src, Mat*mvbegin);
void split(inputarray m, outputarrayofarrays mv);

(1)src、m是需要进行分离的多通道数组

(2)mv是函数的输出数组或者输出的vector容器

 Mat imageBlueChannel;
Mat imageGreenChannel;
Mat imageRedChannel;
vector<Mat> channels; srcImge4 = imread("dota.jpg");
split(srcImage4,channels);//分离色彩通道 //把载入的三通道图像转换到三个单通道图像,放到vector<Mat>类型的channel中
imageBlueChannel = channels.at();
imageGreenChannel = channels.at();
imageRedChannel = channels.at();

通道合并:merge函数

两个原型:

 void merge(const Mat* mv, size_tcount, OutputArray dst);
void merge(inputarrayofarrays mv, outputarray dst);

(1)mv,需要被合并的输入矩阵或vector容器的阵列,这个mv参数中所有矩阵必须有一样的尺寸和深度。

(2)count,当mv为空白的C数组时,代表输入矩阵的个数,这个参数显然必须大于1

(3)dst,输出矩阵,和mv[0]有一样的尺寸,并且通道的数量是矩阵阵列中的通道的总数

merge函数将一些数组合并成一个多通道数组。第i个输入数组的元素被视为mv[i]。C一般用一中的Mat::at()函数对某个通道进行存取,channels.at(0)。

Mat::at()方法返回一个引用到指定的数组元素。注意是引用,相当于两者等价

 //-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//------------------------------------------------------------------------------------------------
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream> //-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-------------------------------------------------------------------------------------------------
using namespace cv;
using namespace std; //-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数声明
//-----------------------------------------------------------------------------------------------
bool MultiChannelBlending();
void ShowHelpText(); //-----------------------------------【main( )函数】------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
system("color 9F"); ShowHelpText( ); if(MultiChannelBlending( ))
{
cout<<endl<<"\n运行成功,得出了需要的图像~! ";
} waitKey();
return ;
} //-----------------------------------【ShowHelpText( )函数】----------------------------------
// 描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
void ShowHelpText()
{
//输出欢迎信息和OpenCV版本
printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
printf("\n\n\t\t\t此为本书OpenCV3版的第26个配套示例程序\n");
printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION );
printf("\n\n ----------------------------------------------------------------------------\n");
} //-----------------------------【MultiChannelBlending( )函数】--------------------------------
// 描述:多通道混合的实现函数
//-----------------------------------------------------------------------------------------------
bool MultiChannelBlending()
{
//【0】定义相关变量
Mat srcImage;
Mat logoImage;
vector<Mat> channels;
Mat imageBlueChannel; //=================【蓝色通道部分】=================
// 描述:多通道混合-蓝色分量部分
//============================================ // 【1】读入图片
logoImage= imread("dota_logo.jpg",);//灰度图是因为,我们是对一个通道进行线性加权的,所以也要将logoimage变成一个通道
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();
//【4】将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageBlueChannel中
addWeighted(
imageBlueChannel(Rect(,,logoImage.cols,logoImage.rows)),//需要加权的第一个数组
1.0,//第一个数组的权重
logoImage,//第二个数组,和第一个数组有相同的尺寸和通道数
0.5//第二个数组的权重
,//gamma,加到权重总和上的标量值
imageBlueChannel(Rect(,,logoImage.cols,logoImage.rows))
); //【5】将三个单通道重新合并成一个三通道
merge(channels,srcImage); //【6】显示效果图
namedWindow(" <1>游戏原画+logo蓝色通道");
imshow(" <1>游戏原画+logo蓝色通道",srcImage); //=================【绿色通道部分】=================
// 描述:多通道混合-绿色分量部分
//============================================ //【0】定义相关变量
Mat imageGreenChannel; //【1】重新读入图片
logoImage= imread("dota_logo.jpg",);
srcImage= imread("dota_jugg.jpg"); if( !logoImage.data ) { printf("读取logoImage错误~! \n"); return false; }
if( !srcImage.data ) { printf("读取srcImage错误~! \n"); return false; } //【2】将一个三通道图像转换成三个单通道图像
split(srcImage,channels);//分离色彩通道 //【3】将原图的绿色通道的引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变
imageGreenChannel= channels.at();
//【4】将原图的绿色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageGreenChannel中
addWeighted(imageGreenChannel(Rect(,,logoImage.cols,logoImage.rows)),1.0,
logoImage,0.5,.,imageGreenChannel(Rect(,,logoImage.cols,logoImage.rows))); //【5】将三个独立的单通道重新合并成一个三通道
merge(channels,srcImage); //【6】显示效果图
namedWindow("<2>游戏原画+logo绿色通道");
imshow("<2>游戏原画+logo绿色通道",srcImage); //=================【红色通道部分】=================
// 描述:多通道混合-红色分量部分
//============================================ //【0】定义相关变量
Mat imageRedChannel; //【1】重新读入图片
logoImage= imread("dota_logo.jpg",);
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】将一个三通道图像转换成三个单通道图像
split(srcImage,channels);//分离色彩通道 //【3】将原图的红色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变
imageRedChannel= channels.at();
//【4】将原图的红色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageRedChannel中
addWeighted(imageRedChannel(Rect(,,logoImage.cols,logoImage.rows)),1.0,
logoImage,0.5,.,imageRedChannel(Rect(,,logoImage.cols,logoImage.rows))); //【5】将三个独立的单通道重新合并成一个三通道
merge(channels,srcImage);

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

图像对比度、亮度调整

一般图像处理算子都是一个函数,接受一个或多个输入图像,并产生输出图像,下面是算子的一般形式:

图像对比度和亮度的操作,属于比较简单的一种--点操作。点操作的特点:仅仅根据输入像素值,来计算相应的输出像素值。这列算子包括亮度(brightness)、对比度(constrast)、颜色校正(colorcorrection)、变换(transformations)。

两种最常用的点操作(点算子)是乘上一个常数(对比度的调节)再加上一个常数(对应亮度的调节),公式如下:

(1)f(x)表示源图像像素;

(2)g(x)表示输出图像像素

(3)a>0,增益,控制对比度

(4)b,偏置,控制亮度

进一步可以改写成:

访问图片中的像素

 //三个for循环执行运算: new_image(i,j) = a*image(i,j) + b
for(int y = ; y < image.rows; y++)
{
for(int x = ; x < image.cols; x++)
{
for(int c = ; c < ,; c++)
{
new_image.at<vector>(y,x)[c] = saturate_cast<uchar>(
(g_nContrastValue*0.01) * (image.at<vector>(y,x)[c]) + g_nBrightValue
)
}
}
}

访问图像的每一个像素,使用的语法为:image.at<vector>(y,x)[c]

因为计算结果可能会超出像素取值范围(溢出),还可能是非整数,所以用saturate_cast对结果进行转换,确保是有效值。saturate_cast模板函数,用于溢出保护,大致原理如下:

 if(data<)
data = ;
else if(data > )
data=;

示例程序:图像对比度、亮度调节

 //--------------------------------------【程序说明】-------------------------------------------
// 程序说明:《OpenCV3编程入门》OpenCV3版书本配套示例程序27
// 程序描述:图像对比度、亮度值调整
// 开发测试所用IDE版本:Visual Studio 2010
// 开发测试所用OpenCV版本: 3.0 beta
// 2014年11月 Created by @浅墨_毛星云
// 2014年12月 Revised by @浅墨_毛星云
//------------------------------------------------------------------------------------------------ //-----------------------------------【头文件包含部分】---------------------------------------
// 描述:包含程序所依赖的头文件
//----------------------------------------------------------------------------------------------
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include "opencv2/imgproc/imgproc.hpp"
#include <iostream> //-----------------------------------【命名空间声明部分】---------------------------------------
// 描述:包含程序所使用的命名空间
//-----------------------------------------------------------------------------------------------
using namespace std;
using namespace cv; //-----------------------------------【全局函数声明部分】--------------------------------------
// 描述:全局函数声明
//-----------------------------------------------------------------------------------------------
static void ContrastAndBright(int, void *);
void ShowHelpText(); //-----------------------------------【全局变量声明部分】--------------------------------------
// 描述:全局变量声明
//-----------------------------------------------------------------------------------------------
int g_nContrastValue; //对比度值
int g_nBrightValue; //亮度值
Mat g_srcImage,g_dstImage;
//-----------------------------------【main( )函数】--------------------------------------------
// 描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )
{
//改变控制台前景色和背景色
system("color 2F"); ShowHelpText();
// 读入用户提供的图像
g_srcImage = imread( "1.jpg");
if( !g_srcImage.data ) { printf("读取g_srcImage图片错误~! \n"); return false; }
//创建和输入图像一样size和type的全零矩阵
g_dstImage = Mat::zeros( g_srcImage.size(), g_srcImage.type() ); //设定对比度和亮度的初值
g_nContrastValue=;
g_nBrightValue=; //创建窗口
namedWindow("【效果图窗口】", ); //创建轨迹条
createTrackbar("对比度:",//轨迹条名字
"【效果图窗口】",//窗口名字
&g_nContrastValue, //对比度当前值
,//对比度最大值
ContrastAndBright//回调函数
);
createTrackbar("亮 度:", "【效果图窗口】",&g_nBrightValue, ,ContrastAndBright ); //调用回调函数,viod* 默认用0
ContrastAndBright(g_nContrastValue,0);//为什么是0???
ContrastAndBright(g_nBrightValue,); //输出一些帮助信息
cout<<endl<<"\t运行成功,请调整滚动条观察图像效果\n\n"
<<"\t按下“q”键时,程序退出\n"; //按下“q”键时,程序退出
while(char(waitKey(1)) != 'q') {}
return ;
} //-----------------------------------【ShowHelpText( )函数】----------------------------------
// 描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
void ShowHelpText()
{
//输出欢迎信息和OpenCV版本
printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
printf("\n\n\t\t\t此为本书OpenCV3版的第27个配套示例程序\n");
printf("\n\n\t\t\t 当前使用的OpenCV版本为:" CV_VERSION );
printf("\n\n ----------------------------------------------------------------------------\n");
} //-----------------------------【ContrastAndBright( )函数】------------------------------------
// 描述:改变图像对比度和亮度值的回调函数
//-----------------------------------------------------------------------------------------------
static void ContrastAndBright(int, void *)
{ // 创建窗口
namedWindow("【原始图窗口】", ); // 三个for循环,执行运算 g_dstImage(i,j) = a*g_srcImage(i,j) + b
for( int y = ; y < g_srcImage.rows; y++ )
{
for( int x = ; x < g_srcImage.cols; x++ )
{
for( int c = ; c < ; c++ )
{
g_dstImage.at<Vec3b>(y,x)[c] = saturate_cast<uchar>( (g_nContrastValue*0.01)*( g_srcImage.at<Vec3b>(y,x)[c] ) + g_nBrightValue );
}
}
} // 显示图像
imshow("【原始图窗口】", g_srcImage);
imshow("【效果图窗口】", g_dstImage);
}

离散傅里叶变换DFT

指傅里叶变换在时域和频域上都呈现离散的形式,将时域信号的采样变换为在离散时间傅里叶变换(DTFT)频域的采样。

对有限长的离散信号做DFT,也应当对其经过周期延拓成周期信号再进行变换。

实际应用中,通常采用快速傅里叶变换来高效计算DFT。

原理

对一张图像使用傅里叶变换就是将它分解成正弦和余弦的和的形式。傅里叶变换的作用实际上就是函数分解的工具。

二维图像傅里叶变换公式:

转换之后的频域值是复数,因此结果使用实数图像+虚数图像,或者幅度图像+相位图像。

实际图像处理过程中,仅仅使用幅度图像,幅度图像包含了原图像几乎所有我们需要的几何信息。如果想通过修改幅度图像或者相位图像来间接修改原空间图像,需要使用逆傅里叶变换得到修改后的空间图像。

频域里面,对于一幅图像,高频部分代表了图像的细节、纹理信息;低频部分代表了图像的轮廓信息。

如果图像受到的噪声位于某个特定的频率范围之内,则可以通过滤波器来恢复原来的图像。

傅里叶变换在图像处理中可以做到图像增强和图像去燥,图像分割之边缘检测、图像特征提取,图像压缩等。

dft函数

dft函数是对一维或二维浮点数数组进行正向或反向离散傅里叶变换。

 void dft(inputarray src, outputarray dst, int flags=, int nonzeroRows=);

(1)src,输入矩阵,实数或虚数

(2)dst,运算结果,尺寸和类型取决于标识符,也就是flags

(3)flags,转换标识符,默认0

(4)nonzeroRows,默认值0。

core组件进阶的更多相关文章

  1. opencv 3 core组件进阶(3 离散傅里叶变换;输入输出XML和YAML文件)

    离散傅里叶变换 #include "opencv2/core/core.hpp" #include "opencv2/imgproc/imgproc.hpp" ...

  2. opencv 3 core组件进阶(2 ROI区域图像叠加&图像混合;分离颜色通道、多通道图像混合;图像对比度,亮度值调整)

    ROI区域图像叠加&图像混合 #include <opencv2/core/core.hpp> #include <opencv2/highgui/highgui.hpp&g ...

  3. opencv 3 core组件进阶(1 访问图像中的像素)

    访问图像像素的三类方法 ·方法一 指针访问:C操作符[ ]; ·方法二 迭代器iterator; ·方法三 动态地址计算. #include <opencv2/core/core.hpp> ...

  4. OpenCV之Core组件进阶

    颜色空间缩减 利用C++类型转换时向下取整操作,实现定义域内颜色缩减.表达式如下 Inew = (Iold/10)*10 简单的颜色空间缩减算法可由以下两步组成: (1)遍历图像矩阵的每个元素 (2) ...

  5. opencv core组件进阶

    1.图像在内存中存储方式,图像矩阵的大小取决于颜色模型,取决于所有的通道数:还有重要的颜色空间缩减的概念:因为如果是RGB的话,使用uchar的话,就有256^3的结合方法.所以要用到颜色缩减的方法, ...

  6. .Net Core组件化视图(部分视图)

    .Net Core组件化视图(部分视图) 1.背景 1.以前我们使用.Net的时候使用部分视图的方式在,.Net Core 中能在单独处理逻辑的部分视图没有了,但是我们还是想使用现在的.Net Cor ...

  7. Vue基础二之全局API、实例属性和全局配置,以及组件进阶(mixins)的详细教程(案列实现,详细图解,附源码)

    本篇文章主要是写Vue.directive().Vue.use()等常用全局API的使用,vm.$props.vm.$options.vm.$slots等实例属性的使用,以及Vue全局配置.组件的mi ...

  8. Django之forms组件进阶

    Django Form表单组件   Form介绍 我们之前在HTML页面中利用form表单向后端提交数据时,都会写一些获取用户输入的标签并且用form标签把它们包起来. 与此同时我们在好多场景下都需要 ...

  9. Vue 爬坑之路(五)—— 组件进阶

    组件(Component)是 Vue.js 最强大的功能之一,之前的文章都只是用到了基本的封装功能,这次将介绍一些更强大的扩展. 一.基本用法 在使用 vue-cli 创建的项目中,组件的创建非常方便 ...

随机推荐

  1. bzoj4823: [Cqoi2017]老C的方块(最小割)

    4823: [Cqoi2017]老C的方块 题目:传送门 题解: 毒瘤题ORZ.... 太菜了看出来是最小割啥边都不会建...狂%大佬强强强   黑白染色?不!是四个色一起染,四层图跑最小割... 很 ...

  2. JAVA设计模式之【建造者模式】

    建造者模式 建造者模式为客户端返回的不是一个简单的产品,而是一个由多个部件组成的复杂产品 角色 Builder抽象建造者 buildPartX getResult ConcreteBuilder具体建 ...

  3. zzuoj--10400--海岛争霸(并查集)

    10400: B.海岛争霸 Time Limit: 2 Sec  Memory Limit: 128 MB Submit: 128  Solved: 46 [Submit][Status][Web B ...

  4. [codeforces 894 E] Ralph and Mushrooms 解题报告 (SCC+拓扑排序+DP)

    题目链接:http://codeforces.com/problemset/problem/894/E 题目大意: $n$个点$m$条边的有向图,每条边有一个权值,可以重复走. 第$i$次走过某条边权 ...

  5. 移动端video播放时不弹出页面层

    移动端视频在播放时会主动弹出页面,有的浏览器不会.对那些会的浏览器进行处理: 直接加上下面三个属性即可,兼容方面就不说了,微信上是很ok的. <video x5-playsinline=&quo ...

  6. VS 2015 C#不能进入断点

    工程\属性\生成页面,去掉优化代码勾选.

  7. echarts中国地图

    echarts中国地图效果图: =================== 需要引入echarts的js文件:(1.echarts.min.js:2.china.js) 下载地址: echarts.min ...

  8. 我的Java历程_写出这个数

    lzJava基础进行中,今天偶然间看到的一个题目: 读入一个自然数n,计算其各位数字之和,用汉语拼音写出和的每一位数字.如下代码: import java.util.*;public class Ma ...

  9. 洛谷P4016 负载平衡问题 费用流

    这道题还是很好的. 考察了选手对网络流的理解. 首先,任意两个相邻点之间的运货量时没有限制的. 我们可以将相邻点之间的流量建为无限大,单位费用设为 1,代表运输一个货物需耗费一个代价. 由于题目要求最 ...

  10. linux常用命令技巧

    原文地址 这篇文章来源于Quroa的一个问答<What are some time-saving tips that every Linux user should know?>—— Li ...