【Opencv3学习笔记 3】常用数据结构以及图像操作函数

文章目录

    • 0. 颜色空间转换
    • 1. 简单图形绘制:
      • 椭圆
      • 实心圆
      • 凹多边形
      • 线
      • 整体绘制代码:
    • 2. 图像操作
      • 2.1 图像在内存中的存储方式:
          • 补充小知识点:
          • 尝试:
      • 2.2 颜色空间缩减
        • 计时函数
      • 2.3 访问图像中像素的三种方式
      • 2.4 ROI 区域图像叠加
      • 2.5 线性混合操作
      • 2.6 分离颜色通道、多通道图像混合
        • 2.6.1 通道分离:split()
        • 2.6.2 通道合并:merge()
      • 2.6 图像对比度、亮度调整
      • 2.6 离散傅里叶变换
        • 2.6.1 dft()函数
        • 2.6.2 示例程序:离散傅里叶变换
    • 3. 输入输出XML和YAML文件

0. 颜色空间转换

//---------------------------------【头文件、命名空间包含部分】----------------------------
//		描述:包含程序所使用的头文件和命名空间
//-------------------------------------------------------------------------------------------------
#include "opencv2/imgproc/imgproc.hpp"
#include "opencv2/highgui/highgui.hpp"
#include 
#include 

using namespace cv;


int main(int argc, const char** argv)
{	
	// 1. 载入图片
	Mat srcImage = imread("1.jpg", 1), dstImage;
	
	// 2. 转换颜色空间
	cvtColor(srcImage, dstImage, COLOR_BGR2Lab);

	// 3. 显示效果图
	imshow("效果图", dstImage);

	// 4. 保持显示
	waitKey();

	return 0;
}

1. 简单图形绘制:

椭圆

调用Opencv中的elipse函数


//-------------------------------【DrawEllipse( )函数】--------------------------------
//		描述:自定义的绘制函数,实现了绘制不同角度、相同尺寸的椭圆
//-----------------------------------------------------------------------------------------
void DrawEllipse(Mat img, double angle)
{
	int thickness = 2;
	int lineType = 8;

	ellipse(img,
		Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2),
		Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16),
		angle,
		0,
		360,
		Scalar(255, 129, 0),
		thickness,
		lineType);
}

实心圆

调用circle函数,圆心由点center定义,颜色为Scalar(0,0,255),按BGR的格式为红色,线粗为thickness = -1,因此是实心圆。

//-----------------------------------【DrawFilledCircle( )函数】---------------------------
//		描述:自定义的绘制函数,实现了实心圆的绘制
//-----------------------------------------------------------------------------------------
void DrawFilledCircle(Mat img, Point center)
{
	int thickness = -1;
	int lineType = 8;

	circle(img,
		center,
		WINDOW_WIDTH / 32,
		Scalar(0, 0, 255),
		thickness,
		lineType);
}

凹多边形

调用fillPoly函数,顶点集为ppt,顶点数量为npt,颜色为Scalar(255,255,255)白色


//-----------------------------------【DrawPolygon( )函数】--------------------------
//		描述:自定义的绘制函数,实现了凹多边形的绘制
//--------------------------------------------------------------------------------------
void DrawPolygon(Mat img)
{
	int lineType = 8;

	//创建一些点
	Point rookPoints[1][20];
	rookPoints[0][0] = Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
	rookPoints[0][1] = Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
	rookPoints[0][2] = Point(3 * WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][3] = Point(11 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][4] = Point(19 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][5] = Point(3 * WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][6] = Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][7] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][8] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][9] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][10] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][11] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][12] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][13] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][14] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][16] = Point(WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][17] = Point(13 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][18] = Point(5 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);

	const Point* ppt[1] = { rookPoints[0] };
	int npt[] = { 20 };

	fillPoly(img,
		ppt,
		npt,
		1,
		Scalar(255, 255, 255),
		lineType);
}

线

调用line函数,从点start到点end的直线,粗细为thickness = 2,8联通(lineType = 8)

//-----------------------------------【DrawLine( )函数】--------------------------
//		描述:自定义的绘制函数,实现了线的绘制
//---------------------------------------------------------------------------------
void DrawLine(Mat img, Point start, Point end)
{
	int thickness = 2;
	int lineType = 8;
	line(img,
		start,
		end,
		Scalar(0, 0, 0),
		thickness,
		lineType);
}

整体绘制代码:

//---------------------------------【头文件、命名空间包含部分】----------------------------
//          描述:包含程序所使用的头文件和命名空间
//------------------------------------------------------------------------------------------------
#include 
#include 
using namespace cv;

//此程序对于OpenCV3版需要额外包含头文件:
#include 



//-----------------------------------【宏定义部分】-------------------------------------------- 
//		描述:定义一些辅助宏 
//------------------------------------------------------------------------------------------------ 
#define WINDOW_NAME1 "【绘制图1】"        //为窗口标题定义的宏 
#define WINDOW_NAME2 "【绘制图2】"        //为窗口标题定义的宏 
#define WINDOW_WIDTH 600//定义窗口大小的宏



//--------------------------------【全局函数声明部分】-------------------------------------
//		描述:全局函数声明
//-----------------------------------------------------------------------------------------------
void DrawEllipse(Mat img, double angle);//绘制椭圆
void DrawFilledCircle(Mat img, Point center);//绘制圆
void DrawPolygon(Mat img);//绘制多边形
void DrawLine(Mat img, Point start, Point end);//绘制线段



//-----------------------------------【ShowHelpText( )函数】----------------------------------
//          描述:输出一些帮助信息
//----------------------------------------------------------------------------------------------
void ShowHelpText()
{
	//输出欢迎信息和OpenCV版本
	printf("\n\n\t\t\t非常感谢购买《OpenCV3编程入门》一书!\n");
	printf("\n\n\t\t\t此为本书OpenCV3版的第20个配套示例程序\n");
	printf("\n\n\t\t\t   当前使用的OpenCV版本为:" CV_VERSION);
	printf("\n\n  ----------------------------------------------------------------------------\n");
}




//---------------------------------------【main( )函数】--------------------------------------
//		描述:控制台应用程序的入口函数,我们的程序从这里开始执行
//-----------------------------------------------------------------------------------------------
int main(void)
{

	// 创建空白的Mat图像
	Mat atomImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);
	Mat rookImage = Mat::zeros(WINDOW_WIDTH, WINDOW_WIDTH, CV_8UC3);

	ShowHelpText();
	// ---------------------<1>绘制化学中的原子示例图------------------------

	//【1.1】先绘制出椭圆
	DrawEllipse(atomImage, 90);
	DrawEllipse(atomImage, 0);
	DrawEllipse(atomImage, 45);
	DrawEllipse(atomImage, -45);

	//【1.2】再绘制圆心
	DrawFilledCircle(atomImage, Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2));

	// ----------------------------<2>绘制组合图-----------------------------
	//【2.1】先绘制出椭圆
	DrawPolygon(rookImage);

	// 【2.2】绘制矩形
	rectangle(rookImage,
		Point(0, 7 * WINDOW_WIDTH / 8),
		Point(WINDOW_WIDTH, WINDOW_WIDTH),
		Scalar(0, 255, 255),
		-1,
		8);

	// 【2.3】绘制一些线段
	DrawLine(rookImage, Point(0, 15 * WINDOW_WIDTH / 16), Point(WINDOW_WIDTH, 15 * WINDOW_WIDTH / 16));
	DrawLine(rookImage, Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8), Point(WINDOW_WIDTH / 4, WINDOW_WIDTH));
	DrawLine(rookImage, Point(WINDOW_WIDTH / 2, 7 * WINDOW_WIDTH / 8), Point(WINDOW_WIDTH / 2, WINDOW_WIDTH));
	DrawLine(rookImage, Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8), Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH));

	// ---------------------------<3>显示绘制出的图像------------------------
	imshow(WINDOW_NAME1, atomImage);
	moveWindow(WINDOW_NAME1, 0, 200);
	imshow(WINDOW_NAME2, rookImage);
	moveWindow(WINDOW_NAME2, WINDOW_WIDTH, 200);

	waitKey(0);
	return(0);
}



//-------------------------------【DrawEllipse( )函数】--------------------------------
//		描述:自定义的绘制函数,实现了绘制不同角度、相同尺寸的椭圆
//-----------------------------------------------------------------------------------------
void DrawEllipse(Mat img, double angle)
{
	int thickness = 2;
	int lineType = 8;

	ellipse(img,
		Point(WINDOW_WIDTH / 2, WINDOW_WIDTH / 2),
		Size(WINDOW_WIDTH / 4, WINDOW_WIDTH / 16),
		angle,
		0,
		360,
		Scalar(255, 129, 0),
		thickness,
		lineType);
}


//-----------------------------------【DrawFilledCircle( )函数】---------------------------
//		描述:自定义的绘制函数,实现了实心圆的绘制
//-----------------------------------------------------------------------------------------
void DrawFilledCircle(Mat img, Point center)
{
	int thickness = -1;
	int lineType = 8;

	circle(img,
		center,
		WINDOW_WIDTH / 32,
		Scalar(0, 0, 255),
		thickness,
		lineType);
}


//-----------------------------------【DrawPolygon( )函数】--------------------------
//		描述:自定义的绘制函数,实现了凹多边形的绘制
//--------------------------------------------------------------------------------------
void DrawPolygon(Mat img)
{
	int lineType = 8;

	//创建一些点
	Point rookPoints[1][20];
	rookPoints[0][0] = Point(WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
	rookPoints[0][1] = Point(3 * WINDOW_WIDTH / 4, 7 * WINDOW_WIDTH / 8);
	rookPoints[0][2] = Point(3 * WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][3] = Point(11 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][4] = Point(19 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][5] = Point(3 * WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][6] = Point(3 * WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][7] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][8] = Point(26 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][9] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][10] = Point(22 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][11] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][12] = Point(18 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][13] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 4);
	rookPoints[0][14] = Point(14 * WINDOW_WIDTH / 40, WINDOW_WIDTH / 8);
	rookPoints[0][15] = Point(WINDOW_WIDTH / 4, WINDOW_WIDTH / 8);
	rookPoints[0][16] = Point(WINDOW_WIDTH / 4, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][17] = Point(13 * WINDOW_WIDTH / 32, 3 * WINDOW_WIDTH / 8);
	rookPoints[0][18] = Point(5 * WINDOW_WIDTH / 16, 13 * WINDOW_WIDTH / 16);
	rookPoints[0][19] = Point(WINDOW_WIDTH / 4, 13 * WINDOW_WIDTH / 16);

	const Point* ppt[1] = { rookPoints[0] };
	int npt[] = { 20 };

	fillPoly(img,
		ppt,
		npt,
		1,
		Scalar(255, 255, 255),
		lineType);
}


//-----------------------------------【DrawLine( )函数】--------------------------
//		描述:自定义的绘制函数,实现了线的绘制
//---------------------------------------------------------------------------------
void DrawLine(Mat img, Point start, Point end)
{
	int thickness = 2;
	int lineType = 8;
	line(img,
		start,
		end,
		Scalar(0, 0, 0),
		thickness,
		lineType);
}

效果图:
【Opencv3学习笔记 3】常用数据结构以及图像操作函数_第1张图片

2. 图像操作

2.1 图像在内存中的存储方式:

图像矩阵的大小取决于所用的颜色模型(通道数)
这里我们来做个实验。首先我们利用构造函数创建一个Mat对象

补充小知识点:

public Mat(int rows, int cols, MatType type, IntPtr data, long step = 0)
type的规则:
【Opencv3学习笔记 3】常用数据结构以及图像操作函数_第2张图片

尝试:

我们创建一个3*3的Mat

int main(void)
{
	Mat a(3, 3, CV_8UC3, Scalar(0,0,255));
	cout << a;
	return 0;
}

结果如下:
在这里插入图片描述
可以发现,对于多通道图像来说,矩阵的列会包含多个子列,每个子列个数和通道数相等。
其中子列的通道顺序是反过来的——BGR而不是RGB。
很多时候,内存可以实现连续存储,这样有助于图像扫描速度,我们可以用isContinuous()来判断矩阵是否是连续存储的。

2.2 颜色空间缩减

做法:将现有颜色空间值除以某个输入值,以获得较少的颜色数(“做减法”),比如0-9映射到新值0,10-19映射到10。
这样就可以利用C++中的自动截余做到。
比如:Iold = 14;Inew = (Iold/10) * 10 = 10;
处理255种像素时,我们可以打表,免去每次的计算过程

int divideWith = 10;
uchar table[256];
for(int i = 0;i<256;i++)
{
	table[i] = divideWith * (i/divideWith );
}

这样就可以直接取值p[j] = table[p[i]];
我们还可以用Opencv提供的lookUpTable函数LUT来操作。

计时函数

getTickCount()getTickFrequency()
组合使用:

double time0 = static_cast<double>(getTickCount());   //记录起始时间
	//  .....操作
time0 = ((double)getTickCount() - time0) / getTickFrequency();
cout << "运行时间为" << time0 << "秒" << endl;

2.3 访问图像中像素的三种方式

  • 方法一 指针访问:
    操作符[]
    实例:
//---------------------------------【colorReduce( )函数】---------------------------------
//          描述:使用【指针访问: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;  
			// ----------【处理结束】---------------------
		}  //行处理结束
	}  
}  
  • 方法二 迭代器iterator:
    相比于指针访问,迭代器是非常安全的方法。
    实例:
//-------------------------------------【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)[0] = (*it)[0]/div*div + div/2;  
		(*it)[1] = (*it)[1]/div*div + div/2;  
		(*it)[2] = (*it)[2]/div*div + div/2;  
		// ------------------------【处理结束】----------------------------
	}  
}  

  • 方法三 动态地址计算:
    配合at方法的colorReduce函数的代码:

//----------------------------------【colorReduce( )函数】-------------------------------
//          描述:使用【动态地址运算配合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;  //红是通道
			// -------------------------【处理结束】----------------------------
		}  // 行处理结束     
	}  
}  

2.4 ROI 区域图像叠加

定义ROI有两种方法:

  1. 使用Rect,指定左上坐标以及长宽
Mat imageROI;
imageROI = image(Rect(500,250,logo.cols,logo.rows));
  1. 指定行或列的范围(Range)
Mat imageROI;
imageROI = image(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));

示例:
利用ROI实现图像叠加,通过一个图像掩膜(mask),直接将插入处的像素设置为logo的像素值,这样效果会很逼真。
【Opencv3学习笔记 3】常用数据结构以及图像操作函数_第3张图片

【Opencv3学习笔记 3】常用数据结构以及图像操作函数_第4张图片

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(200,250,logoImage.cols,logoImage.rows));

	// 【3】加载掩模(必须是灰度图)
	Mat mask= imread("dota_logo.jpg",0);

	//【4】将掩膜拷贝到ROI
	logoImage.copyTo(imageROI,mask);

	// 【5】显示结果
	namedWindow("<1>利用ROI实现图像叠加示例窗口");
	imshow("<1>利用ROI实现图像叠加示例窗口",srcImage1);

	return true;
}

【Opencv3学习笔记 3】常用数据结构以及图像操作函数_第5张图片
这里的copyTo()函数:
作用是把mask和image重叠以后把mask中像素值为0(black)的点对应的image中的点变为透明,而保留其他点。
logo的边界的黑带残缺,而且原本是logo的图像区域显示还是imageROI的图像。即ROI区域在叠加以后的像素值为0(black)的点被保留为原图像的像素点。
image.copyTo(imageROI,mask),mask作为一个掩模板,如果在某个像素点(i, j)其值为1(只看第一通道,所以mask单通道即可),则把image.at(i, j)处的值直接赋给imageROI.at(i, j),如果其值为0,则imageROI.at(i, j)处保留其原始像素值。这样能够使得图标边缘的颜色和原图保持一致。
参考这篇博文可以更好的理解一些基础概念

2.5 线性混合操作

线性混合操作是一种典型的二元(两个输入)的像素操作,它的理论公式如下:g(x)=(1-a)f1(x)+af2(x),我们通过在范围0到1之间改变alpha值,来对两幅图像 f0(x)f1(x) 或者两段视频(同样为 f0(x)f1(x))产生时间上的画面叠化(cross-dissolve)。
就像幻灯片放映和电影制作中的那样,也就是在幻灯片翻页时设置的前后页缓慢过渡叠加效果,以及电影情节过渡时经常出现的画面叠加效果。实现方面,主要运用了OpenCV 中 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;
}

【Opencv3学习笔记 3】常用数据结构以及图像操作函数_第6张图片

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

2.6.1 通道分离:split()

int main()
{
	Mat srcImage;
	Mat imageROI;
	vector<Mat> channels;
	srcImage = imread("1.jpg");


	// 把一个3通道的图像转换成3个单通道图像
	split(srcImage, channels);  //分离色彩通道
	imageROI = channels.at(0);
	imshow("分离",imageROI);
	waitKey();
	return 0;
}

【Opencv3学习笔记 3】常用数据结构以及图像操作函数_第7张图片

2.6.2 通道合并:merge()

int main()
{
	Mat srcImage;
	Mat imageROI;
	vector<Mat> channels;
	srcImage = imread("1.jpg");


	// 把一个3通道的图像转换成3个单通道图像
	split(srcImage, channels);  //分离色彩通道
	imageROI = channels.at(0);
	imshow("分离",imageROI);
	merge(channels,srcImage);    //将三个单通道重新合并成一个三通道
	imshow("合并",srcImage);
	waitKey();
	return 0;
}

【Opencv3学习笔记 3】常用数据结构以及图像操作函数_第8张图片

2.6 图像对比度、亮度调整

int main()
{
	int g_nContrastValue; //对比度值
	int g_nBrightValue;  //亮度值
	Mat g_srcImage, g_dstImage;
	//创建窗口
	namedWindow("【效果图窗口】", 1);
	//设定对比度和亮度的初值
	g_nContrastValue = 80;
	g_nBrightValue = 80;
	// 读入用户提供的图像
	g_srcImage = imread("1.jpg");
	if (!g_srcImage.data) { printf("读取g_srcImage图片错误~! \n"); return false; }
	g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type());
	// 三个for循环,执行运算 g_dstImage(i,j) = a*g_srcImage(i,j) + b
	for (int y = 0; y < g_srcImage.rows; y++)
	{
		for (int x = 0; x < g_srcImage.cols; x++)
		{
			for (int c = 0; c < 3; 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);
}

2.6 离散傅里叶变换

计算以及显示傅里叶变换后的幅度图像
频域里,高频部分代表了图像的细节纹理等信息;低频部分代表了图像的轮廓信息。

2.6.1 dft()函数

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

2.6.2 示例程序:离散傅里叶变换

利用dft函数计算两个二维实矩阵卷积

int main( )
{

	//【1】以灰度模式读取原始图像并显示
	Mat srcImage = imread("1.jpg", 0);
	if(!srcImage.data ) { printf("读取图片错误,请确定目录下是否有imread函数指定图片存在~! \n"); return false; } 
	imshow("原始图像" , srcImage);   

	ShowHelpText();

	//【2】将输入图像延扩到最佳的尺寸,边界用0补充
	int m = getOptimalDFTSize( srcImage.rows );
	int n = getOptimalDFTSize( srcImage.cols ); 
	//将添加的像素初始化为0.
	Mat padded;  
	copyMakeBorder(srcImage, padded, 0, m - srcImage.rows, 0, n - srcImage.cols, BORDER_CONSTANT, Scalar::all(0));

	//【3】为傅立叶变换的结果(实部和虚部)分配存储空间。
	//将planes数组组合合并成一个多通道的数组complexI
	Mat planes[] = {Mat_<float>(padded), Mat::zeros(padded.size(), CV_32F)};
	Mat complexI;
	merge(planes, 2, complexI);         

	//【4】进行就地离散傅里叶变换
	dft(complexI, complexI);           

	//【5】将复数转换为幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))
	split(complexI, planes); // 将多通道数组complexI分离成几个单通道数组,planes[0] = Re(DFT(I), planes[1] = Im(DFT(I))
	magnitude(planes[0], planes[1], planes[0]);// planes[0] = magnitude  
	Mat magnitudeImage = planes[0];

	//【6】进行对数尺度(logarithmic scale)缩放
	magnitudeImage += Scalar::all(1);
	log(magnitudeImage, magnitudeImage);//求自然对数

	//【7】剪切和重分布幅度图象限
	//若有奇数行或奇数列,进行频谱裁剪      
	magnitudeImage = magnitudeImage(Rect(0, 0, magnitudeImage.cols & -2, magnitudeImage.rows & -2));
	//重新排列傅立叶图像中的象限,使得原点位于图像中心  
	int cx = magnitudeImage.cols/2;
	int cy = magnitudeImage.rows/2;
	Mat q0(magnitudeImage, Rect(0, 0, cx, cy));   // ROI区域的左上
	Mat q1(magnitudeImage, Rect(cx, 0, cx, cy));  // ROI区域的右上
	Mat q2(magnitudeImage, Rect(0, cy, cx, cy));  // ROI区域的左下
	Mat q3(magnitudeImage, Rect(cx, cy, cx, cy)); // ROI区域的右下
	//交换象限(左上与右下进行交换)
	Mat tmp;                           
	q0.copyTo(tmp);
	q3.copyTo(q0);
	tmp.copyTo(q3);
	//交换象限(右上与左下进行交换)
	q1.copyTo(tmp);                 
	q2.copyTo(q1);
	tmp.copyTo(q2);

	//【8】归一化,用0到1之间的浮点值将矩阵变换为可视的图像格式
	//此句代码的OpenCV2版为:
	//normalize(magnitudeImage, magnitudeImage, 0, 1, CV_MINMAX); 
	//此句代码的OpenCV3版为:
	normalize(magnitudeImage, magnitudeImage, 0, 1, NORM_MINMAX); 

	//【9】显示效果图
	imshow("频谱幅值", magnitudeImage);    
	waitKey();

	return 0;
}

效果:

3. 输入输出XML和YAML文件

写入与读取的步骤:

  1. 实例化一个FileStorage类对象,用默认带参数的构造函数完成初始化,或者用FileStorage::open()成员函数辅助初始化
  2. 使用流操作符<<进行文件写入操作,或者>>进行文件读取操作,类似C++
  3. 使用FileStorage::release()函数析构FileStorage类对象,关闭文件

注意:

  • 对于vector结构的输入和输出,要在第一个元素前加上[,在最后一个元素后加上]
  • 而对于map结构的操作,使用的符号是{}
  • 对于一连串的node,可以使用FileNodeIterator结构
    例如
// string
fs << "strings" << "[" << "image1.jpg" << "image2.jpg" << "image3.jpg";
fs << "]";

// map
fs << "Mapping" << "{" << "one" << 1 << "two" << 2;
fs << "}";

// 遍历
FileNode n = fs["strings"];
FileNodeIterator it = n.begin(), it_end = n.end();
for(; it != it_end;it++)
{
	cout<<(string)*it<<endl;
}

写入代码

#include "opencv2/opencv.hpp"  
#include   
using namespace cv;  

//-----------------------------------【main( )函数】--------------------------------------------
//	描述:控制台应用程序的入口函数,我们的程序从这里开始
//-----------------------------------------------------------------------------------------------
int main( )  
{  
	//改变console字体颜色
	system("color 5F"); 

	ShowHelpText();

	//初始化
	FileStorage fs("test.yaml", FileStorage::WRITE);  

	//开始文件写入
	fs << "frameCount" << 5;  
	time_t rawtime; time(&rawtime);  
	fs << "calibrationDate" << asctime(localtime(&rawtime));  
	Mat cameraMatrix = (Mat_<double>(3,3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1);  
	Mat distCoeffs = (Mat_<double>(5,1) << 0.1, 0.01, -0.001, 0, 0);  
	fs << "cameraMatrix" << cameraMatrix << "distCoeffs" << distCoeffs;  
	fs << "features" << "[";  
	for( int i = 0; i < 3; i++ )  
	{  
		int x = rand() % 640;  
		int y = rand() % 480;  
		uchar lbp = rand() % 256;  

		fs << "{:" << "x" << x << "y" << y << "lbp" << "[:";  
		for( int j = 0; j < 8; j++ )  
			fs << ((lbp >> j) & 1);  
		fs << "]" << "}";  
	}  
	fs << "]";  
	fs.release();  

	printf("\n文件读写完毕,请在工程目录下查看生成的文件~");
	getchar();

	return 0;  
}  

读取代码

#include "opencv2/opencv.hpp"  
#include   
using namespace cv;  
using namespace std;  

int main( )  
{  
	//改变console字体颜色
	system("color 6F"); 

	//初始化
	FileStorage fs2("test.yaml", FileStorage::READ);  

	// 第一种方法,对FileNode操作
	int frameCount = (int)fs2["frameCount"];  

	std::string date;  
	// 第二种方法,使用FileNode运算符> > 
	fs2["calibrationDate"] >> date;  

	Mat cameraMatrix2, distCoeffs2;  
	fs2["cameraMatrix"] >> cameraMatrix2;  
	fs2["distCoeffs"] >> distCoeffs2;  

	cout << "frameCount: " << frameCount << endl  
		<< "calibration date: " << date << endl  
		<< "camera matrix: " << cameraMatrix2 << endl  
		<< "distortion coeffs: " << distCoeffs2 << endl;  

	FileNode features = fs2["features"];  
	FileNodeIterator it = features.begin(), it_end = features.end();  
	int idx = 0;  
	std::vector<uchar> lbpval;  

	//使用FileNodeIterator遍历序列
	for( ; it != it_end; ++it, idx++ )  
	{  
		cout << "feature #" << idx << ": ";  
		cout << "x=" << (int)(*it)["x"] << ", y=" << (int)(*it)["y"] << ", lbp: (";  
		// 我们也可以使用使用filenode >> std::vector操作符很容易的读数值阵列
		(*it)["lbp"] >> lbpval;  
		for( int i = 0; i < (int)lbpval.size(); i++ )  
			cout << " " << (int)lbpval[i];  
		cout << ")" << endl;  
	}  
	fs2.release();  

	//程序结束,输出一些帮助文字
	printf("\n文件读取完毕,请输入任意键结束程序~");
	getchar();

	return 0;  
}  

你可能感兴趣的:(机器学习,opencv,计算机视觉)