您的位置 首页 新闻

Opencv之Core组件

了解过之前老版本OpenCV的童鞋们都应该清楚,对于OpenCV1.0时代的基于C语言接口而建的图像存储格式IplImage*,如果在退出前忘记release掉的话,就会照成内存泄…

了解过之前老版本OpenCV的童鞋们都应该清楚,对于OpenCV1.0时代的基于C语言接口而建的图像存储格式IplImage*,如果在退出前忘记release掉的话,就会照成内存泄露.自从OpenCV踏入2.0时代,用Mat类数据结构作为主打之后,OpenCV变得越发像需要很少编程涵养的Matlab那样,上手超级快。甚至有些函数名称都和matlab一样,比如大家所熟知的imread,imwrite,imshow等函数。

mat类型

cv::Mat类是用于保存图像以及其他矩阵数据的数据结构。默认情况下,其尺寸为0,我们也可以指定初始尺寸,比如,比如定义一个Mat类对象,就要写cv::Mat pic(320,640,cv::Scalar(100));

Mat有多种创建方法,此处省略。

其他常用数据结构

二维点:Point2f

三维点: Point3f

点的表示:Point

颜色的表示:Scalar类

尺寸的表示:Size类

矩形的表示:Rect类

颜色空间转换:cvtColor

基本图形的绘制

画椭圆

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

画圆

void DrawFilledCircle( Mat img, Point center ) { int thickness = -1; int lineType = 8; circle( img, center, WINDOW_WIDTH/32, Scalar( 0, 0, 255 ), thickness, lineType ); }

画多边形

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

画线

void DrawLine( Mat img, Point start, Point end ) { int thickness = 2; int lineType = 8; line( img, start, end, Scalar( 0, 0, 0 ), thickness, lineType ); }

用指针访问像素

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(i); //获取第i行的首地址 for(int j = 0;j < colNumber;j++) //列循环 { // ---------【开始处理每个像素】------------- data[j] = data[j]/div*div + div/2; // ----------【处理结束】--------------------- } //行处理结束 } } 用迭代器访问像素 void colorReduce(Mat& inputImage, Mat& outputImage, int div) { //参数准备 outputImage = inputImage.clone(); //拷贝实参到临时变量 //获取迭代器 Mat_::iterator it = outputImage.begin(); //初始位置的迭代器 Mat_::iterator itend = outputImage.end(); //终止位置的迭代器 //存取彩色图像像素 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(i,j)[0] = outputImage.at(i,j)[0]/div*div + div/2; //蓝色通道 outputImage.at(i,j)[1] = outputImage.at(i,j)[1]/div*div + div/2; //绿色通道 outputImage.at(i,j)[2] = outputImage.at(i,j)[2]/div*div + div/2; //红是通道 // ————————-【处理结束】—————————- } // 行处理结束 } }

注意:opencv中的彩色图像不是按rgb顺序排列的,而是bgr。

ROI与图像混合

定义ROI感兴趣区域有两种方法:

//方法一 imageROI=image(Rect(500,250,logo.cols,logo.rows)); //方法二 imageROI=srcImage3(Range(250,250+logoImage.rows),Range(200,200+logoImage.cols));

线性混合

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

我们通过在范围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错误!

“); return false; } if( !srcImage3.data ) { printf(“读取srcImage3错误!

“); return false; } // 【2】进行图像混合加权操作 betaValue = ( 1.0 – alphaValue ); addWeighted( srcImage2, alphaValue, srcImage3, betaValue, 0.0, dstImage); // 【3】显示原图窗口 imshow( “<2>线性混合示例窗口【原图】”, srcImage2 ); imshow( “<3>线性混合示例窗口【效果图】”, dstImage ); return true; }

分离颜色通道,多通道颜色混合

通道分离split函数

//【2】将一个三通道图像转换成三个单通道图像 split(srcImage,channels);//分离色彩通道 //【3】将原图的红色通道引用返回给imageBlueChannel,注意是引用,相当于两者等价,修改其中一个另一个跟着变 imageRedChannel= channels.at(2);

通道合并,merge函数

merge是split的逆向操作,是把多个通道合并

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

图像对比度、亮度值调整

static void ContrastAndBright(int, void *) { // 创建窗口 namedWindow(“【原始图窗口】”, 1); // 三个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(y,x)[c] = saturate_cast( (g_nContrastValue*0.01)*( g_srcImage.at(y,x)[c] ) + g_nBrightValue ); } } } // 显示图像 imshow(“【原始图窗口】”, g_srcImage); imshow(“【效果图窗口】”, g_dstImage); }

离散傅立叶变换

dft函数用于对一维或二维浮点数组进行正向或反向傅立叶变换。

getOptimalDFTSize返回dft最优尺寸

copyMakeBorder扩充图片边界

magnitude计算二维矢量的幅值

normalize矩阵归一化

主要步骤如下

【1】以灰度模式读取原始图像并显示

2】将输入图像延扩到最佳的尺寸,边界用0补充

3】为傅立叶变换的结果(实部和虚部)分配存储空间

4】进行就地离散傅里叶变换

【5】将复数转换为幅值,即=> log(1 + sqrt(Re(DFT(I))^2 + Im(DFT(I))^2))

6】进行对数尺度(logarithmic scale)缩放

【7】剪切和重分布幅度图象限

【8】归一化,用0到1之间的浮点值将矩阵变换为可视的图像格式

【9】显示效果图

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

“); 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_(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; }

傅立叶变换结果

输入输出xml与yaml文件

一般步骤如下:

1.实例化FileStorage类对象,完成初始化。

2.使用流操作符《写入,》读取。

3.析构FileStorage对象,同时关闭文件。

XML和YAML文件的写入

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_(3,3) << 1000, 0, 320, 0, 1000, 240, 0, 0, 1); Mat distCoeffs = (Mat_(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(" 文件读写完毕,请在工程目录下查看生成的文件~"); getchar(); return 0; } XML和YAML文件的读取

本文来自网络,不代表Xnewv立场,转载请注明出处:https://xnewv.com/2061.html

为您推荐

联系我们

联系我们

18873343099

在线咨询: QQ交谈

邮箱: [email protected]

关注微信
微信扫一扫关注我们

微信扫一扫关注我们

关注微博
返回顶部