初探core组件:opencv - core组件进阶

news2024/12/22 23:35:02

core组件进阶

    • 1.访问图像中的像素
      • 1.1 图像在内存之中的存储方式
      • 1.2 颜色空间缩减
      • 1.3 LUT函数:Look up table
      • 1.4 计时函数
    • 2. ROI区域图像叠加&图像混合
      • 2.1 感兴趣区域:ROI
      • 2.2 ROI案例
      • 2.2 线性混合操作
      • 2.3 计算数组加权和:addWeighted()函数
    • 3 分离颜色通道、多通道图像混合
      • 3.1 通道分离:split()函数
      • 3.2 通道合并:merge()函数
      • 3.3 案例:多通道图像混合
    • 4.图像对比度、亮度值调整
      • 4.1 亮度和对比度综合案例
    • 5.离散傅里叶变换
      • 5.1 离散傅里叶变换的原理
      • 5.2 dft()函数详解
      • 5.3 返回DFT最优尺寸大小:getOptimalDFTSize()函数
      • 5.4 扩充图像边界:copyMakeBorder()函数
      • 5.5 计算二维矢量的幅值:magnitude()函数
      • 5.6 计算自然对数:log()函数
      • 5.7 矩阵归一化:normalize()函数
    • 6.输入输出XML和YAML文件
      • 6.1 XML和YAML文件简介
      • 6.2 FileStorage类操作文件的使用引导
      • 6.3 示例程序:XML和YAML文件的写入

1.访问图像中的像素

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

矩阵的大小取决于使用的颜色系统。更准确地说,它取决于使用的通道数量。如果是灰度图像,矩阵如图:
在这里插入图片描述
对于多通道图像,列中包含与通道数相同数量的子列。例如,在BGR色彩系统的情况下:
在这里插入图片描述

1.2 颜色空间缩减

若矩阵元素存储的是单通道像素,那么像素可有256个不同的值。但若是三通道,这种存储格式的颜色就有1600多万种。用如此之多的颜色来进行处理,可能对我们的算法性能造成严重的影响。
颜色空间缩减(color space reduciton)便可以派上用场了,它在很多应用中可以大大降低运算复杂度。

颜色空间缩减的做法是:
将现有的颜色空间值除以某个输入值,以获得较少的颜色数。也就是“做减法”,比如颜色值从0到9可取为新值0,10到19可取为10。
这样操作将颜色取值降低为262626种情况。这个操作可以用一个简单的公式来实现。因为C++中int类型除法操作会自动截余。
例如:Iold=14
Inew=(Iold/10)*10=(14/10)*10=1*10=10

1.3 LUT函数:Look up table

在这里插入图片描述

1.4 计时函数

可以利用两个简单的计时函数——getTickCount()和getTickFrequency()。

  • getTickCount()函数返回CPU自某个时间(如启动电脑)以来走过的时钟周期数。
  • getTickFrequency()函数返回CPU一秒钟所走的时钟周期数。这样我们就可以轻松地以秒为单位进行计时。
	double time0=static_cast<double>(getTickCount());//记录起始时间
	time0=((double)getTickCount()-time0)/getTickFrequency();
	cout<<"运行时间为:"<<time0<<"秒"<<endl;

2. ROI区域图像叠加&图像混合

2.1 感兴趣区域:ROI

在图像处理领域,我们常常需要设置感兴趣区域(ROI,region of interest)来专注或者简化工作过程。也就是从图像中选择的一个图像区域,这个区域是图像分析所关键的重点。我们圈定这个区域,以便进行进一步处理。而且,使用ROI指定想读入的目标,可以减少处理时间,增加精度,给图像处理带来不小的便利。

定义ROI区域有两种方法:

  • 第一种是使用表示矩形区域的Rect。它指定矩形的左上角坐标和矩形的长宽,以定义一个矩形区域。
	Mat imageROI;
	imageROI = image(Rect(500,250,logo.cols,logo.rows));
  • 第二种是指定感兴趣的行或列的范围(Range)。Range是指从起始索引到终止索引(不包括终止索引)的一段连续序列。cRange可以用来定义Range。如果使用Range来定义ROI,那么前例中定义ROI的代码可以重写为
	imageROI = image(Range(250,250+logo.rows),Range(500,500+logo.cols));

2.2 ROI案例

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

//利用ROI将一个图像加到另一个图像指定位置

int main() {
	Mat srcImage1 = imread("dota_pa.jpg");
	Mat srcImage2 = imread("dota_logo.jpg");
	if (!srcImage1.data) {
		cout << "srcImage1读取错误" << endl;
		return -1;
	}
	if (!srcImage2.data) {
		cout << "srcImage2读取错误" << endl;
		return -1;
	}

	//定义一个Mat类型并给其设定ROI区域
	Mat imageROI = srcImage1(Rect(200, 250, srcImage2.cols, srcImage2.rows));
	
	//加载掩码
	Mat mask = imread("dota_logo.jpg", 0);
	srcImage2.copyTo(imageROI, mask);

	//显示结果
	namedWindow("利用ROI实现图像叠加窗口");
	imshow("利用ROI实现图像叠加窗口", srcImage1);

	waitKey(0);
	
	return 0;

}

在这里插入图片描述

2.2 线性混合操作

线性混合操作是一种典型的二元(两个输入)的像素操作,它的理论公式:
g ( x ) = ( 1 − a ) ∗ f 1 ( x ) + a ∗ f 2 ( x ) g(x)=(1-a)*f1(x)+a*f2(x) g(x)=(1a)f1(x)+af2(x)

我们通过在范围0到1之间改变alpha的值,来对两幅图像或者两个视频产生时间上的画面重叠。就像幻灯片放映和电影制作中的那样,也就是在幻灯片翻页时设置过度叠加效果。

实现方面主要运用了OpenCV中addWeighted函数

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

void addWeighted(InputArray src1, double alpha, InputArray src2, double deta, double gamma, outputArray dst, int dtype=-1)

  • 第一个参数:表示需要加权的第一个数组
  • 第二个参数:表示第一个数组的权重
  • 第三个参数:表示需要加权的第二个数组
  • 第四个参数:表示第二个数组的权重
  • 第五个参数:gamma,一个加到权重总和上的标量值。
  • 第六个参数:输出的数组,他和输入的两个数组拥有相同 的尺寸和通道数。
  • 第七个参数:int类型的dtype,输出阵列的可选深度,有默认值-1。当两个输入数组具有相同的深度是,这个参数设置为-1。

下面属数学公式表示:用addWeighted()计算一下两个数组的加权和,得到结果输出给第四个参数,也就是adWeighted函数的作用的矩阵表达式。
d s t = s r c 1 [ I ] ∗ a l p h a + s r c 2 [ I ] ∗ b e t a + g a m m a dst=src1[I]*alpha+src2[I]*beta+gamma dst=src1[I]alpha+src2[I]beta+gamma

addWeighted()案例

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

//利用addWeighted()实现图像线性混合
int main() {
	
	double alphaValue = 0.5;
	double betaValue;
	Mat srcImage1, srcImage2, dstImage;

	srcImage1 = imread("mogu.jpg");
	srcImage2 = imread("rain.jpg");
	
	if (!srcImage1.data) {
		cout << "srcImage1读取错误" << endl;
		return -1;
	}
	if (!srcImage2.data) {
		cout << "srcImage2读取失败" << endl;
		return -1;
	}

	//图像混合加权操作
	betaValue = (1.0 - alphaValue);
	addWeighted(srcImage1, alphaValue, srcImage2, betaValue, 0.0, dstImage);


	namedWindow("原图", 0);
	imshow("原图", srcImage1);
	
	namedWindow("效果图", 0);
	imshow("效果图", dstImage);

	waitKey();
	return 0;
}

*在这里插入图片描述*

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

3.1 通道分离:split()函数

split函数用于将一个多通道数组分离成几个单通道数组。
void split(InputArray m, OutputArrayOfArrays mv)

  • 第一个参数:填我们需要进行分离的多通道数组
  • 第二个参数:填函数的输出数组或者输出的vector容器

split函数分割多通道数组转换成独立的单通道数组,公式如下:
m v [ c ] ( I ) = s r c ( I ) mv[c](I)=src(I) mv[c](I)=src(I)

3.2 通道合并:merge()函数

merge()函数是split()函数的逆向操作——将多个数组合并成一个多通道的数组。
void merge(InputArrayOfArrays mv,OutputArray dst)

  • 第一个参数:需要被合并的输入矩阵或vector容器的阵列,这个mv参数中所有的矩阵必须有着一样的尺寸和深度。
  • 第二个参数:输出矩阵,和mv[0]有一样的尺寸和深度,并且通道数的是矩阵阵列中的通道数总和。

一般用at()函数对某个通道进行存取:channels.at(0)

3.3 案例:多通道图像混合

#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
bool  MultiChannelBlending();


int main()
{
	system("color 9F");


	if (MultiChannelBlending())
	{
		cout << endl << "\n运行成功,得出了需要的图像~! ";
	}

	waitKey(0);
	return 0;
}




//-----------------------------【MultiChannelBlending( )函数】--------------------------------
//	描述:多通道混合的实现函数
bool  MultiChannelBlending()
{
	//【0】定义相关变量
	Mat srcImage;
	Mat logoImage;
	vector<Mat> channels;
	Mat  imageBlueChannel;

	//=================【蓝色通道部分】=================
	//	描述:多通道混合-蓝色分量部分
	//============================================

	// 【1】读入图片
	logoImage = imread("dota_logo.jpg", 0);
	srcImage = imread("dota.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(0);
	//【4】将原图的蓝色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageBlueChannel中
	addWeighted(imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0,
		logoImage, 0.5, 0, imageBlueChannel(Rect(500, 250, logoImage.cols, logoImage.rows)));

	//【5】将三个单通道重新合并成一个三通道
	merge(channels, srcImage);

	//【6】显示效果图	
	namedWindow(" <1>游戏原画+logo蓝色通道",0);
	imshow(" <1>游戏原画+logo蓝色通道", srcImage);


	//=================【绿色通道部分】=================
	//	描述:多通道混合-绿色分量部分
	//============================================

	//【0】定义相关变量
	Mat  imageGreenChannel;

	//【1】重新读入图片
	logoImage = imread("dota_logo.jpg", 0);
	srcImage = imread("dota.jpg");

	if (!logoImage.data) { printf("读取logoImage错误~! \n"); return false; }
	if (!srcImage.data) { printf("读取srcImage错误~! \n"); return false; }

	//【2】将一个三通道图像转换成三个单通道图像
	split(srcImage, channels);//分离色彩通道

	//【3】将原图的绿色通道的引用返回给imageGreenChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变
	imageGreenChannel = channels.at(1);

	//【4】将原图的绿色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageGreenChannel中
	addWeighted(imageGreenChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0,
		logoImage, 0.5, 0., imageGreenChannel(Rect(500, 250, logoImage.cols, logoImage.rows)));

	//【5】将三个独立的单通道重新合并成一个三通道
	merge(channels, srcImage);

	//【6】显示效果图
	namedWindow("<2>游戏原画+logo绿色通道",0);
	imshow("<2>游戏原画+logo绿色通道", srcImage);

	//=================【红色通道部分】=================
	//	描述:多通道混合-红色分量部分
	//============================================

	//【0】定义相关变量
	Mat  imageRedChannel;

	//【1】重新读入图片
	logoImage = imread("dota_logo.jpg", 0);
	srcImage = imread("dota.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】将原图的红色通道引用返回给imageRedChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变
	imageRedChannel = channels.at(2);
	//【4】将原图的红色通道的(500,250)坐标处右下方的一块区域和logo图进行加权操作,将得到的混合结果存到imageRedChannel中
	addWeighted(imageRedChannel(Rect(500, 250, logoImage.cols, logoImage.rows)), 1.0,
		logoImage, 0.5, 0., imageRedChannel(Rect(500, 250, logoImage.cols, logoImage.rows)));
	//【5】将三个独立的单通道重新合并成一个三通道
	merge(channels, srcImage);
	//【6】显示效果图
	namedWindow("<3>游戏原画+logo红色通道 ",0);
	imshow("<3>游戏原画+logo红色通道 ", srcImage);
	return true;
}

在这里插入图片描述

4.图像对比度、亮度值调整

图像亮度和对比度的调整操作,其实属于图像处理中比较简单的一种——点操作。仅仅根据输入像素值来计算输出像素值。这类算子包括亮度(brightness)和对比度(contrast)调整、颜色校正(colorcorrection)和变换(transformations)。

两种最常用的点操作是乘上一个常数以及加上一个常数:
g ( x ) = a ∗ f ( x ) + b g(x)=a*f(x)+b g(x)=af(x)+b

  • f(x)表示源图像像素
  • g(x)表示输出图像像素
  • 参数a(a>0)被称为增益,常常被用来控制图像的对比度
  • 参数b通常被称为偏置,常常被用来控制图像的亮度

更进一步,可以这样改写这个式子:其中i和j表示该像素位于第i行和第j列。
g ( i , j ) = α ⋅ f ( i , j ) + β g(i,j)=α⋅f(i,j)+β g(i,j)=αf(i,j)+β

4.1 亮度和对比度综合案例

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

//全局变量声明
int g_nContrastValue; //对比度值
int g_nBrightValue;//亮度值
Mat g_srcImage, g_dstImage;


//on_ContrastAndBright():改变图像对比度和亮度值的回调函数
static void on_ContrastAndBright(int, void*) {
	//创建窗口
	namedWindow("【原始图窗口】", 1);

	//三个for循环,执行g_dstImage(i,j)=a*g_srcImage(i,j)+b
	for (int x = 0; x < g_srcImage.rows; x++) {
		for (int y = 0; y < g_srcImage.cols; y++) {
			for (int c = 0; c < 3; c++) {
				g_dstImage.at<Vec3b>(x, y)[c] =
					saturate_cast<uchar>((g_nContrastValue * 0.01) * (g_srcImage.at < Vec3b>(x, y)[c]) + g_nBrightValue);
			}
		}
	}
	imshow("【原始图窗口】", g_srcImage);
	imshow("【效果图窗口】", g_dstImage);
}

int main() {
	g_srcImage = imread("../../image/1.tif");
	if (!g_srcImage.data) {
		cout << "读取图片错误" << endl;
		return false;
	}
	g_dstImage = Mat::zeros(g_srcImage.size(), g_srcImage.type());

	//设置对比度和亮度
	g_nContrastValue = 80;
	g_nBrightValue = 80;

	//创建效果图窗口
	namedWindow("【效果图窗口】", 1);

	//创建轨迹条
	createTrackbar("对比度:", "【效果图窗口】", &g_nContrastValue, 300, on_ContrastAndBright);
	createTrackbar("亮  度:", "【效果图窗口】", &g_nBrightValue, 200, on_ContrastAndBright)	;

	//回调函数初始化
	on_ContrastAndBright(g_nContrastValue, 0);
	on_ContrastAndBright(g_nBrightValue, 0);

	//按下q键退出
	while(char(waitKey(1))!='q'){}
	return 0;
}

saturate_cast模板函数,其用于颜色溢出保护

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

at函数
单通道图像:image.at<uchar>(i,j)就表示在第i行第j列的像素值。
对于多通道图像如RGB:image.at<Vec3b>(i,j)[c]来表示某个通道中(i,j)位置的像素值。

  • uchar、Vec3b表示元素的类型:Vec3b =vector<uchar,3> 即一个uchar类型、长度为3的vector向量
    简单的来说Vec3b就是一个uchar类型的数组,长度为3。

在这里插入图片描述

5.离散傅里叶变换

5.1 离散傅里叶变换的原理

傅里叶变换(Discrete Fourier Transform, 缩写为DFT)将把一个图像分解成正弦和余弦成分。换句话说,它将把图像从其空间域转化为其频率域。其原理是,任何函数都可以用无限的正弦和余弦函数的总和来精确近似。傅里叶变换是一种如何做到这一点的方法。在数学上,一个二维图像的傅里叶变换是:
在这里插入图片描述
式中 f f f是空间域值, F F F是频域值。转换后的频域值是负数。因此,显示傅里叶变换之后的结果需要使用实数图像(real image)加虚数图像(complex image),或者幅度图像(magitude image)加相位图像(phase image)的形式。在实际的图像处理过程中,仅仅使用了幅度图像,因为幅度图像包含了原图像的几乎所有我们需要的几何信息。然而,如果想通过修改幅度图像或者相位图像的方法来间接修改原空间图像,需要使用逆傅里叶变换得到修改后的空间图像,这样就必须同时保留幅度图像和相位图像了。

在此示例中,我们将展示如何计算以及显示傅里叶变换后的幅度图像。由于数字图像的离散性,像素值的取值范围也是有限的。比如在一张灰度图像中,像素值灰度值一般在0-255之间。

在频域里面,对于一幅图像,高频部分代表了图像的细节、纹理信息;低频部分代表了图像的轮廓信息。如果对一幅精细的图像使用低通滤波器 那么滤波后的结果就只剩下轮廓了。这与信号处理的基本思想是相通的。如果图像受到的噪声恰好位于某个特定的"频率"范围内,则可以通过滤波器来恢复原来的图像。傅里叶变换在图像处理中可以做到图像增强与图像去噪、图像分割之边缘检测、图像特征提取、图像压缩等。

5.2 dft()函数详解

dft函数的作用是对一维或二维浮点数数组进行正向或反向离散傅里叶变换。
void dft(InputArray src, outputArray dst, int flags=0,int nonzeroRows=0)

  • 第一个参数:输入矩阵,可以为实数或者虚数
  • 第二个参数:函数调用后的运算结果存在这里,其尺寸和类型取决于标识符,也就是第三个参数flags。
  • 第三个参数:转换的标识符,默认值为0,取值可参考下表
    在这里插入图片描述
  • 第四个参数:nonezeroRows,有默认值为0。当此参数设为非零时(最好是取值为想要处理的那一行的值,比如C.rows),函数会假设只有输入矩阵的第一个非零行元素(没有设置DFT_INVERSE标识符),或只有输出矩阵的第一个非零行包含非零元素(设置了DFT_INVERSE标识符)。这样的话函数就可对其他行进行更高效的处理,以节省时间开销。这项技术尤其是在采用DFT计算矩阵卷积时非常有效。

5.3 返回DFT最优尺寸大小:getOptimalDFTSize()函数

getOptimalDFTSize()函数返回给定向量尺寸的傅里叶最优尺寸大小。为了提高离散傅里叶变换的运行速度,需要扩充图像,而具体扩充多少,就由这个函数来计算得到。
int getOptimalDFTSize(int vecsize)
此函数的为一个参数为int类型的vecsize,向量尺寸,也就是图片的rows和cols。

5.4 扩充图像边界:copyMakeBorder()函数

copyMakeBorder()函数就是扩充图像边界
void copyMakeBorder(InputArray src, OutputArray dst,int top,int bottom, int left, int right, int borderType,const Scalar & value=Scalar())

  • 第一个参数:源图像,填Mat类的对象即可。
  • 第二个参数:这个参数用于存放函数调用后的输出结果,需和源图片有一 样的尺寸和类型,且 size应该为Size (src.cols+left+right, src.rows+top+bottom)
  • 接下来 参数分别为 int 类型的 top、bottom、left、right。分别表示在源图像的四个向上扩充多少像素 ,例如 top=2、bottom=2 、left=2、right=2 就意味着在源图像的上下左右各扩充两个像素宽度的边。
  • 第七个参数:边界类型。常见的取值类型为BORDER_CONSTANT,可参考borderInterpolate()。
  • 第八个参数:const Scalar&类型的 value ,有默认值Scalar()可以理解为默认值为0。当 bordeType 值为 BORDER_CONSTANT 时,这个参数表示
    边界值。

5.5 计算二维矢量的幅值:magnitude()函数

void magnitude(InputArray x,InputArray y, Output Array magnitude)

  • 第一个参数:表示矢量的浮点型X坐标值,也就是实部。
  • 第二个参数:标识适量的浮点型Y坐标值,也就是虚部。
  • 第三个参数:输出的幅值,他和第一个参数x有着同样的尺寸和类型。
    在这里插入图片描述

5.6 计算自然对数:log()函数

log()函数的功能是计算每个数组元素绝对值的自然对数。
void log(InputArray src, OutputArray dst)
在这里插入图片描述

5.7 矩阵归一化:normalize()函数

normalize(InputArray src, OutputArray dst, double alpha=1, double beta=0, int norm_type=NORM_L2, int dtype=-1, InputArray mask=noArray())

  • 第一个参数:输入图像,即源图像,填 Mat类的对象即可。
  • 第二个参数:函数调用后得到运算结果存这里,和原图片有一样的尺寸和类型。
  • 第三个参数:alpha:归一化的规范值,或者在范围归一化的情况下,归一化的下限范围边界。
  • 第四个参数:beta:在范围归一化的情况下,是范围的上限边界;它不用于规范归一化。
  • 第五个参数:norm_type

在这里插入图片描述

  • 第六个参数:当为负数时,输出数组的类型与src相同;否则,它的通道数与src相同,深度=CV_MAT_DEPTH(type)。
    -第七个参数:可选的操作掩码。

6.输入输出XML和YAML文件

6.1 XML和YAML文件简介

所谓XML,即eXtensible Markup Language,翻译成中文为“可扩展标识语言”。首先,XML是一种元标记语言。所谓“元标记”,就是开发者可以根据自身需要定义自己的标记,比如可以定义标记、。任何满足XML命名规则的名称都可以标记,这就向不同的应用程序打开了的大门。此外,XML是一种语义/结构化语言,它描述了文档的结构和语义。
YAML是“YAML Ain’t a Markup Language”(译为“YAML不是一种置标语言”)的递回缩写。在开发的这种语言时,YAML的原意是:“Yet Another MarkupLanguage”(仍是一种置标语言),但为了强调这种语言以数据为中心,而不是以置标语言为重点,而用返璞词进行重新命名。YAML是一个可读性高,用来表达资料序列的格式。它参考了其他多种语言,包括:XML、C语言、Python、Perl,以及电子邮件格式RFC2822。

6.2 FileStorage类操作文件的使用引导

我们一般使用如下过程来写入或者读取数据到XML或YAML文件中。

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

第一步:XML、YAML文件的打开
(1)准备文件写操作
FileStorage是Opencv中XML和YAML文件的存储类,封装了所有相关的信息。它是OpenCV从文件中读取数据或向文件中写数据时必须要使用的一个类。
FileStorage::FileSotrage()
FileStorage::FileStorage(const string& source, int flags, const string& encoding=string())

1)对于第二种带参数的构造函数,进行写操作返利如下
FileStorage fs("abs.xml",FileStorage::WRITE);
2)对于第一种不带参数的构造函数,可以使用其他成员函数FileStorage::open进行数据的写操作

FileStorage fs;
fs.open("abc.xml",FileStorage::WRITE);

(2)准备文件读操作
1)第一种方式
FileStorage fs("abc.xml",FileStorage::READ);
2)第二种方式

	FileStorage fs;
	fs.open("abc.xml",FileStorage::READ);

第二步:进行文件读写操作
(1)文本和数字的输入和输出
写入文件可以使用"<<"运算符。

	fs<<"iteartionNr"<<100;

而读文件,使用">>"运算符

	int itNr;
	fs["iterationNr"]>>itNr;
	tNr=(int) fs["iterationNr"];

(2)OpenCV数据结构的输入和输出

	Mat R=Mat_<uchar>::eye(3,3),
	Mat T=Mat_<double>::zeros(3,1);
	//写数据
	fs<<"R"<<R;
	fs<<"T"<<T;
	//读数据
	fs["R"]>>R;
	fs["T"]<<T;

第三步:vector(arrays)和maps的输入和输出
对于vector结构的输入和输出,要注意在第一个元素前加上"[“,在最后一个元素前加上”]"。例如:

	fs<<"strings"<<"[";//开始读入stirng文本序列
	fs<<"image1.jpg"<<"Awesomeness"<<"baboon.jpg";
	fs<<"]";//关闭序列

而对于map结构的操作,使用的符号是“{}”

	fs<<"Mapping";
	fs<<"{"<<"One"<<1;
	fs<<"Two"<<2<<"}";

读取这些结构的时候,会用到FileNode和FileNodeIterator数据结构。对FileStorage类的"[]"操作符会返回FileNode数据类型;对一连串的node,可以使用FileNodeIterator结构。

	FileNode n=fs["strings"];//读取字符串序列以得到节点
	if(n.type()!=FileNode::SEQ{
		cerr<<"发生错误!字符串不是一个序列"<<endl;
		return 1;
	}
FileNodeIteraotr it=n.begin(),it_end=n.end();//遍历节点
for(;it!=it_end;it++)
	cout<<(string)*it<<endl;

第四步:文件关闭
文件关闭操作会在FileStorage类销毁时自动进行,但我们也可现实的调用其析构函数FileStorage::release()实现。FileStorage::release()函数会析构掉FileStorage类对象,同时关闭文件。
fs.release();

6.3 示例程序:XML和YAML文件的写入

#include<opencv2/opencv.hpp>
#include<time.h>
using namespace std;
using namespace cv;

int main() {
	//初始化
	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 bp = rand() % 256;
		fs << "{:" << "x" << x << "y" << y << "bp" << "[:";
		for (int j = 0; j < 8; j++)
			fs << ((bp >> j) & 1);
		fs << "]" << "}";
	}
	fs << "]";
	fs.release();
	cout << "文件读写完毕" << endl;
	getchar();
	return 0;
}

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/653182.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

python-segno:二维码制作

目录 二维码版本 微二维码、数据掩码、数据流、模式 微二维码 数据掩码 数据流 二维码模式 二维码背景 二维码参数 helpers方法 其他库制作及二维码读取&#xff1a;python生成和读取二维码_觅远的博客-CSDN博客 安装&#xff1a;pip install segno import segnoqr …

Qt+QtWebApp开发笔记(六):http服务器html实现静态相对路径调用第三方js文件

若该文为原创文章&#xff0c;转载请注明原文出处 本文章博客地址&#xff1a;https://hpzwl.blog.csdn.net/article/details/131244269 红胖子网络科技博文大全&#xff1a;开发技术集合&#xff08;包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬…

经典30个嵌入式面试问题

经典30个嵌入式面试问题 嵌入式系统的面试经典问题有很多&#xff0c;以下是其中的30个常见问题&#xff1a; 1. 什么是嵌入式系统&#xff1f; 2. 嵌入式系统和普通计算机系统有什么区别&#xff1f; 3. 嵌入式系统的主要应用领域有哪些&#xff1f; 4. 嵌入式系统的设计…

接口测试工具之postman

概念 接口测试是什么&#xff1f; 百度百科给出的解释是&#xff1a; 接口测试是测试系统组件间接口的一种测试。接口测试主要用于检测外部系统与系统之间以及内部各个子系统之间的交互点。测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间…

构造函数初始化列表的问题

构造函数初始化列表的问题 无法按照表达式中的算符来修改值原因基本原则由此引申的问题使用初始化列表对类成员初始化在构造函数中赋值对类成员初始化 针对构造函数传参,使用引用的情况使用初始化列表对类成员初始化在构造函数中赋值对类成员初始化 将属性也使用引用总结 无法按…

STM32开发——看门狗

目录 1.独立看门狗 1.1需求 1.2CubeMX设置 1.3函数代码 2.窗口看门狗 2.1需求 ​2.2WWDG配置&#xff1a; 2.3函数代码 3.独立看门狗和窗口看门狗的异同点 1.独立看门狗 监测单片机程序运行状态的模块或者芯片&#xff0c;俗称“看门狗”(watchdog) 。 独立看门狗本质 本…

6、DuiLib控件消息响应处理

文章目录 1、DuiLib控件消息响应处理2、基本的消息响应处理 Notify3、仿 MFC 形式消息响应 DUI_DECLARE_MESSAGE_MAP4、事件委托 MakeDelegate5、消息捕获&#xff08;拦截&#xff09;原生消息 HandleMessage 1、DuiLib控件消息响应处理 <?xml version"1.0" en…

软件测试基础教程学习3

文章目录 软件质量与测试3.1 软件质量问题的原因3.2 对软件质量特性的理解3.3 基于软件质量特性的测试3.4 软件能力成熟度模型&#xff08;CMM&#xff09; 软件质量与测试 3.1 软件质量问题的原因 软件质量问题的原因有以下几种&#xff1a; 软件本身的特点和目前普遍采用的…

AWTK实现汽车仪表Cluster/DashBoard嵌入式GUI开发(五):芯片型号

前言 随着汽车四化的推进,可以说汽车仪表也变成越来越智能化的一个ECU部件了。市面上的车型很多,油车发展了很多年,都有仪表,电车也发展起来了,车型也非常丰富,也都有仪表。仪表现在是作为多屏中一个屏存在的,现在车上最多的就是屏了,最大的要算中控屏了,有的还有空调…

【基础】MQTT -- MQTT 协议详解

【基础】MQTT -- MQTT协议详解 与 Broker 建立连接CONNECT 数据包CONNACK 数据包 断开连接DISCONNECT 数据包 订阅与发布PUBLISH 数据包SUBSCRIBE 数据包SUBACK 数据包UNSUBSCRIBE 数据包UNSUBACK 数据包 本文内容针对 MQTT 3.1.1 版本&#xff0c;从连接、发布与订阅等方面对协…

OpenCV做个熊猫表情

有的时候很想把一些有意思的图中的人脸做成熊猫表情&#xff0c;但是由于不太会ps&#xff0c;只能无奈放弃&#xff0c;so sad... 正好最近想了解下opencv的使用&#xff0c;那就先试试做个简单的熊猫表情生成器把~~ 思路就是&#xff0c;工具给两个参数&#xff0c;一个是人…

最小系统板STM32F103C8T6烧录程序指南

STM32F103C8T6烧录程序 【购买链接】&#xff1a;STM32F103C8T6最小系统板 方法一&#xff1a;使用SWD模式烧录 此时BOOT0 0&#xff0c;BOOT1 X&#xff08;任意&#xff09;&#xff0c;跳线帽接法如下图所示 接好后&#xff0c;若手边有STLINK的话&#xff0c;可以使用…

DAY25:二叉树(十五)修剪二叉搜索树+有序数组转换为二叉搜索树+二叉搜索树转化为累加树

文章目录 669.修剪二叉搜索树思路错误代码示例最开始的写法debug测试逻辑错误&#xff1a;需要两次递归的原因内存操作报错&#xff1a;操作了已经被删除的内存的指针&#xff08;力扣平台delete操作的问题&#xff0c;放IDE里就好了&#xff09;打日志debug示例 力扣平台delet…

高并发之限流-RateLimiter

背景 限流是保护高并发系统的三把利器之一&#xff0c;另外两个是缓存和降级。限流在很多场景中用来限制并发和请求量&#xff0c;比如说秒杀抢购&#xff0c;保护自身系统和下游系统不被巨型流量冲垮等。 限流的目的是通过对并发访问/请求进行限速或者一个时间窗口内的的请求进…

华为OD机试之 ABR 车路协同场景(Java源码)

ABR 车路协同场景 题目描述 数轴有两个点的序列 A{A1&#xff0c; A2, …, Am}和 B{B1, B2, ..., Bn}&#xff0c; Ai 和 Bj 均为正整数&#xff0c; A、 B 已经从小到大排好序&#xff0c; A、 B 均肯定不为空&#xff0c; 给定一个距离 R&#xff08;正整数&#xff09;&a…

一文详解!Selenium浏览器自动化测试框架

目录 前言&#xff1a; selenium简介 介绍 功能 优势 基本使用 获取单节点 获取多节点 节点交互 动作链 执行JavaScript代码 获取节点信息 切换frame 延时等待 前进和后退 cookies 选项卡管理 异常处理 选项卡切换 无头浏览器 前言&#xff1a; Selenium是…

双功能螯合剂:NOTA-C6-amine,NOTA-C6-氨基,含有大环配体NOTA和氨基

文章关键词&#xff1a;双功能螯合剂&#xff0c;大环化合物 【产品描述】 西安凯新生物科技有限公司供应的​NOTA-C6-amine中含有大环配体NOTA和氨基&#xff0c;其中氨基与羧酸、活化的 NHS 酯、羰基&#xff08;酮、醛&#xff09;等反应。NOTA及其衍生物是新型双功能整合剂…

【Android】移动端设备介绍(工业手持机)

系列文章 【Android】移动端设备介绍&#xff08;工业手持机&#xff09; 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/130604517 【Android】开发”打地鼠“小游戏 本文链接&#xff1a;https://blog.csdn.net/youcheng_ge/article/details/129…

华为OD机试之检查是否存在满足条件的数字组合(Java源码)

检查是否存在满足条件的数字组合 题目描述 给定一个正整数数组&#xff0c;检查数组中是否存在满足规则的数字组合 规则&#xff1a;A B 2C 输入描述 第一行输出数组的元素个数。 接下来一行输出所有数组元素&#xff0c;用空格隔开。 输出描述 如果存在满足要求的数…

C++入门前必看,超详细

目录 前言 一.C的关键字 二.命名空间 2.1命名空间定义 2.2命名空间的使用 三.C的输入及输出 四.缺省参数 4.1概念 4.2缺省参数分类 4.3缺省参数的注意点 五.引用 5.1 概念 5.2引用的特性 六.内联函数 6.1概念 6.2内联函数的特性 七.auto 7.1auto概念 7.2auto的…