OpenCV常见的优化方法和技巧总结

【尊重原创,转载请注明出处】http://blog.csdn.net/guyuealian/article/details/78540206


目录

OpenCV常见的优化方法和技巧总结

一、OpenCV常见的优化方法总结

1.1 cv::imread()设置reduce模式:

1.2 查表法:LUT,

1.3 像素遍历

1.4 openCL加速:使用UMat结构代替Mat

1.5 release内存释放

1.6 多线程加速处理OpenMP

1.7 使用积分图:integral

二、OpenCV常用技巧总结

释放Mat图像内存空间:

Mat多通道合并和分割

释放图像通道分割的图像空间

计算运行时间

关于vector内存释放的问题:

OpenCV LUT:

opencv多通道的使用

opencv中Mat与数组之间值传递的快速方法

图像的遍历的快速方法

图像遍历优化和加速的常用方法:

防止图像Rect区域越界的好方法

获取OpenCV版本

读写XML或者yml文件数据的

Mat矩阵的运算,易错的问题

Mat和IplImage相互转换

Mat::data指针讲解

OpenCV Mat数据类型及位数总结

Mat矩阵插入新的矩阵

颜色空间缩减的方法

YUV444,YUV422,YUV420解释


一、OpenCV常见的优化方法总结

1.1 cv::imread()设置reduce模式:

  cv::imread()设置reduce模式, 读取缩放的低分辨率小图,或者直接读取灰度图,可以做到自适应,先用EXIF信息读取图像的分辨率,当分辨率大于一定阈值,则设置读取模式为:IMREAD_REDUCED_COLOR_2或者IMREAD_REDUCED_COLOR_4 ,避免内存过大以及编程resieze耗时

1.2 查表法:LUT,

使用lut的方法法,远快于每个像素都计算的方法

1.3 像素遍历

openCV像素遍历常用的是三种方法:ptr指针,迭代器(iterator)以及动态地址at

实现方式: https://blog.csdn.net/keith_bb/article/details/53071133

使用Mat的ptr指针进行图像遍历更加高效,

特别的:一般图像行与行之间往往存储是不连续的,但是有些图像可以是连续的,Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行进行处理。

动态地址at不适合用于像素遍历,速度太慢了,比较适合随机访问的方式

1.4 openCL加速:使用UMat结构代替Mat

    高性能:OpenCL的相关用法:UMat

在OpenCV3中,OCL module已经被舍弃。而是使用更易上手的Transparent API来替代 OCL module。因此只需要使用 UMat来替换Mat,而其余的代码保持不变,即可实现加速.

Mat转换成UMat可以使用Mat::copyTo(OutputArray dst),也可以使用Mat::getUMat(int access_flags)

#include <chrono>
#include <opencv2/opencv.hpp>
#define  millisecond 1000000
#define DEBUG_PRINT(...)  printf( __VA_ARGS__); printf("\n")
#define DEBUG_TIME(time_) auto time_ =std::chrono::high_resolution_clock::now()
#define RUN_TIME(time_)  (double)(time_).count()/millisecond
using namespace std;void main() {string image_path = "1.jpg";cv::Mat image1 = cv::imread(image_path);cv::Mat dest1;DEBUG_PRINT("image size:[%d,%d]", image1.cols, image1.rows);//Mat convert to UMat//cv::UMat image2= image1.getUMat(cv::ACCESS_FAST);//ACCESS_READ, ACCESS_WRITE, ACCESS_RW和ACCESS_FASTcv::UMat image2;image1.copyTo(image2);cv::UMat dest2;DEBUG_TIME(T0);cv::blur(image1, dest1, cv::Size(15, 15));DEBUG_TIME(T1);cv::blur(image2, dest2, cv::Size(15, 15));DEBUG_TIME(T2);//UMat convert to Matcv::Mat dest3;dest2.copyTo(dest3);DEBUG_PRINT("CPU:%3.3fms", RUN_TIME(T1 - T0));DEBUG_PRINT("GPU:%3.3fms", RUN_TIME(T2 - T1));
}

image size:[2000,3008]
CPU:18.039ms
GPU:9.623ms

说明:图像越大,计算越复杂时,使用OpenCL加速的效果更明显,如果使用分辨率小图,其GPU的计算速度未必比CPU的快!!

参考资料: https://blog.csdn.net/amusi1994/article/details/79529870

《OpenCV3.x中UMat对象介绍与使用》https://blog.csdn.net/jia20003/article/details/69802932

1.5 release内存释放

1.6 多线程加速处理OpenMP

举个例子:并行化for语句

  #pragma omp parallel for [clause[clause…]]for(index = first; qualification; index_expr){…}

第一句中[]的部分是可选的,由自己的程序并行特点而定。大家先不要把精力放到这里面。后面的文章中会继续讲解的。

编写规则
    1、index的值必须是整数,一个简单的for形式:for(int i = start; i < end; i++){…} 。

2、start和end可以是任意的数值表达式,但是它在并行化的执行过程中值不能改变,也就是说在for并行化执行之前,编译器必须事先知道你的程序执行多少次,因为编译器要把这些计算分配到不同的线程中执行。

3、循环语句只能是单入口但出口的。这里只要你避免使用跳转语句就行了。具体说就是不能使用goto、break、return。但是可以使用continue,因为它并不会减少循环次数。另外exit语句也是可以用的,因为它的能力太大,他一来,程序就结束了。

openMP参考资料:

https://www.cnblogs.com/ospider/p/5265975.html 

《openMP编程探索1——编程基础》:https://blog.csdn.net/bendanban/article/details/6302857 

《openMP编程探索2——循环并行化》:https://blog.csdn.net/bendanban/article/details/6303100

1.7 使用积分图:integral

积分图定义为:积分图中坐P(x,y)的值为其左上角的所有像素之和

如上图所示,为了求该矩形区域的灰度之和。我们可以用以下公式表示: 

需要注意的是,这里的总和并不包含点A,B,C的像素值。如上图所示的小像素点,A,B,C所在的像素点不在矩形区域以内。

另外,有的时候不一定只是像素灰度值和,只要符合积分图这一种计算思想的,均可以用积分图来简化计算。 
如果是求矩形区域的像素点平方和,也可以用

二、OpenCV常用技巧总结

释放Mat图像内存空间:

 Mat image  = imread("D:\\OpencvTest\\1.jpg");image.release();

Mat多通道合并和分割


#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;/*将多个mat合并为多通道mat
*/
cv::Mat mergeMultiChannels(cv::Mat A,cv::Mat B) {cv::Mat AB;vector<cv::Mat> ABchannels;ABchannels.push_back(A);ABchannels.push_back(B);cv::merge(ABchannels, AB);return AB;
}/*将6通道的mat分割成2个三通道的mat
*/
void splitMultiChannels(cv::Mat mat,cv::Mat &A,cv::Mat &B) {vector<cv::Mat> channels;cv::split(mat, channels);//分割image1的通vector<cv::Mat> Avec, Bvec;Avec.push_back(channels[0]);Avec.push_back(channels[1]);Avec.push_back(channels[2]);Bvec.push_back(channels[3]);Bvec.push_back(channels[4]);Bvec.push_back(channels[5]);cv::merge(Avec, A);cv::merge(Bvec, B);
}
void main(){int width = 100;int height = 100;//cv::Mat AB = cv::Mat::zeros(cv::Size(width,height),CV_32FC(6));cv::Mat A1 = cv::Mat::zeros(cv::Size(width, height), CV_32FC3) + cv::Scalar(0.1, 0.2, 0.3);cv::Mat B1 = cv::Mat::zeros(cv::Size(width, height), CV_32FC3) + cv::Scalar(0.4, 0.5, 0.6);cv::Mat AB=mergeMultiChannels(A1, B1);//合并为6通道cv::Mat A2,B2;splitMultiChannels(AB, A2, B2);//分割为三通道return;
}

释放图像通道分割的图像空间

 std::vector<cv::Mat> layers;split(image, layers);// free memoryfor (auto ii = 0; ii < layers.size(); ii++)layers[ii].release();layers.clear();

计算运行时间

using namespace cv;//设置宏定义
#define TB__(A) int64 A; A = cv::getTickCount()
#define TE__(A) cout << #A << " : " << 1.E3 * double(cv::getTickCount() - A)/double(cv::getTickFrequency()) << "ms" << endl// 使用方法:
TB__(cpu_cvt);
#pragma omp parallel for num_threads(4)for (int k = 0; k < REPEATES; k++)cv::cvtColor(cpu_src, cpu_dst, CV_BGR2Lab);
TE__(cpu_cvt);
#include <chrono>//run times test...
#ifdef  __DEBUG__TIME__ON
#define LOG_TIME  LOGE
#define RUN_TIME(time_)  (double)(time_).count()/millisecond//设置计算运行时间的宏定义
#define DEBUG_TIME(time_) auto time_ =std::chrono::high_resolution_clock::now()
#define DEBUG_TIME_PRINT(time_) printf("run time: %s=%3.3f ms\n", #time_,(double)(time_).count()/millisecond)
#else
#define DEBUG_TIME(time_)
#endif

关于vector内存释放的问题:

由于vector的内存占用空间只增不减,比如你首先分配了10,000个字节,然后erase掉后面9,999个,留下一个有效元素,但是内存占用仍为10,000个。所有内存空间是在vector析构时候才能被系统回收。empty()用来检测容器是否为空的,clear()可以清空所有元素。但是即使clear(),vector所占用的内存空间依然如故,无法保证内存的回收。

如果需要空间动态缩小,可以考虑使用deque。如果是vector类型,可以考虑用swap()来帮助你释放内存。具体方法如下:
标准模板:

//放在头文件
template < class T >
void ClearVector(vector< T >& vt)
{vector<T> vtTemp;vtTemp.swap(vt);//或者直接:vector<T>().swap(vt);
};

swap()是交换函数,使vector离开其自身的作用域,从而强制释放vector所占的内存空间,总而言之,释放vector内存最简单的方法是vector<Point>().swap(pointVec)。当时如果pointVec是一个类的成员,不能把vector<Point>().swap(pointVec)写进类的析构函数中,否则会导致double free or corruption (fasttop)的错误,原因可能是重复释放内存。(前面的pointVec.swap(vector<Point> ())用G++编译没有通过)
     如果vector中存放的是指针,那么当vector销毁时,这些指针指向的对象不会被销毁,那么内存就不会被释放。如下面这种情况,vector中的元素时由new操作动态申请出来的对象指针:

#include <vector>
using namespace std;
vector<void *> v;

每次new之后调用v.push_back()该指针,在程序退出或者根据需要,用以下代码进行内存的释放:

for (vector<void *>::iterator it = v.begin(); it != v.end(); it ++) if (NULL != *it) {delete *it; *it = NULL;}
v.clear();

OpenCV LUT:

多用LUT可以大大降低处理时间:比如下面是实现图像Gamma矫正的函数,其中使用了LUT,这比使用for循环遍历每个像素值,快很多,特别是图片很大的时候:

 void gammaCorrection(cv::Mat& dst, float fGamma){cv::Mat lut(1, 256, CV_8U);uchar *p = lut.data;for (int i = 0; i < 256; i++){p[i] = cv::saturate_cast<uchar>(pow((float)(i / 255.0), fGamma) * 255.0f);}cv::LUT(dst, lut, dst);}

opencv多通道的使用

 typedef cv::Vec<float, 8> Vec8f;E2 = cv::Mat::zeros(srcImage.rows, srcImage.cols, CV_32FC(8));//背景 E(0)值//Mat bigMat = cv::Mat::zeros(cv::Size(256, 256), CV_8UC(8));//CV_32FC3std::cout << "channel = " << E2.channels() << " \n ";for (int i = 0; i < E2.rows; i++){for (int j = 0; j < E2.cols; j++){for (int c = 0; c < E2.channels(); c++){std::cout << "channel = " << c << "  ";std::cout << E2.at<Vec8f>(i, j)[c] << " ;i = " << i << " j= " << j << " c= " << c << "\n";}}}

opencv中Mat与数组之间值传递的快速方法

利用Mat来存储数据,避免使用数组等操作

 cv::Mat mean = (cv::Mat_<float>(2, 1) << 0.4404, 0.3111);cout << "mean=" << mean << endl;float a=mean.at<float>(0, 0);float b = mean.at<float>(0, 0);

将数组内容传递给Mat,示例代码:

unsigned char cbuf[height][width];
cv::Mat img(height, width, CV_8UC1, (unsigned char*)cbuf);

将Mat中的内容传递给数组,如果Mat中的数据是连续的,那么对于传递到一维vector我们可以这样:

std::vector<uchar> array(mat.rows*mat.cols);
if (mat.isContinuous())array = mat.data;

同样的,传递到一维数组我们可以这样

unsigned char *array=new unsigned char[mat.rows*mat.cols];
if (mat.isContinuous())array = mat.data;

对于二维vector的传值,我们可以这样处理

uchar **array = new uchar*[mat.rows];
for (int i=0; i<mat.rows; ++i)array[i] = new uchar[mat.cols];for (int i=0; i<mat.rows; ++i)array[i] = mat.ptr<uchar>(i);

图像的遍历的快速方法

OpenCV图像遍历最高效的方法是指针遍历方法。因为图像在OpenCV里的存储机制问题,行与行之间可能有空白单元(一般是补够4的倍数或8的倍数,有些地方也称作“位对齐”,目前我用到的FreeImage和c#中的bitmap中的存储机制也是这样的)。这些空白单元对图像来说是没有意思的,只是为了在某些架构上能够更有效率,比如intel MMX可以更有效的处理那种个数是4或8倍数的行。Mat提供了一个检测图像是否连续的函数isContinuous()。当图像连通时,我们就可以把图像完全展开,看成是一行。因此最高效的遍历方法如下:

void imageCopy(const Mat& image,Mat& outImage)
{     int nr=image.rows;      int nc=image.cols;      outImage.create(image.size(),image.type());      if(image.isContinuous()&&outImage.isContinuous())  {  nr=1;  nc=nc*image.rows*image.channels();  }   for(int i=0;i<nr;i++)  {       const uchar* inData=image.ptr<uchar>(i);         uchar* outData=outImage.ptr<uchar>(i);        for(int j=0;j<nc;j++)  {  *outData++=*inData++;  }  }
}  

     PS:一般经过裁剪的Mat图像,都不再连续了,如cv::Mat crop_img = src(rect);crop_img 是不连续的Mat图像,如果想转为连续的,最简单的方法,就是将不连续的crop_img 重新clone()一份给新的Mat就是连续的了。关于Mat连续存储的问题,可见:http://blog.csdn.net/guyuealian/article/details/78614662


图像遍历优化和加速的常用方法:

1、加减法比乘除法快,因此应避免使用乘除法

2、不能避免乘除法时,考虑使用“移位运算”

3、像素查表法LUT,远快于每个像素都计算的方法

4、使用常量,会比使用变量快:如:

//使用变量相乘
int a=100;
int b=200;
int c=a*b;//使用常量相乘
int c=100*200;

PS:特别是在for循环中,能用常量表示的,就不要用变量表示,所以“多使用宏定义define,准没错

实例代码:第四种的fast_ergodic4遍历方法是最快的

#include <opencv2/opencv.hpp>
#include <iostream>using namespace std;
#define chans 3cv::Mat fast_ergodic4(cv::Mat image) {if (!image.isContinuous()){return image;}int rows = image.rows;int cols = image.cols;/*使用常量会比变量快,因此当图像通道确定时,请将chans改为数字常量,如1,3,4等*///int chans = image.channels();cv::Mat outImage = cv::Mat::zeros(image.size(), image.type());cols = cols*rows*chans;uchar* inData = image.data;uchar* outData = outImage.data;for (int j = 0; j < cols; j += chans){outData[j] = 10 + inData[j];//BoutData[j + 1] = 10 + inData[j + 1];//GoutData[j + 2] = 10 + inData[j + 2];//R//outData[j + 3] = 10 + inData[j + 3];//A}return outImage;
}cv::Mat fast_ergodic3(cv::Mat image) {int rows = image.rows;int cols = image.cols;/*使用常量会比变量快,因此当图像通道确定时,请将chans改为数字常量,如1,3,4等*///int chans = image.channels();cv::Mat outImage = cv::Mat::zeros(image.size(), image.type());//outImage.create(image.size(), image.type());if (image.isContinuous()){cols = cols*rows;rows = 1;}//else//{//   image = image.clone();//   rows = 1;//    cols = cols*image.rows*chans;//}cols *= chans;for (int i = 0; i < rows; i++){uchar* inData = image.ptr<uchar>(i);uchar* outData = outImage.ptr<uchar>(i);for (int j = 0; j < cols; j += chans){outData[j] = 10 + inData[j];//BoutData[j + 1] = 10 + inData[j + 1];//GoutData[j + 2] = 10 + inData[j + 2];//R//outData[j + 3] = 10 + inData[j + 3];//A}}return outImage;
}/*避免乘法*/
cv::Mat fast_ergodic2(cv::Mat image) {int rows = image.rows;int cols = image.cols;/*使用常量会比变量快,因此当图像通道确定时,请将chans改为数字常量,如1,3,4等*///int chans = image.channels();cv::Mat outImage = cv::Mat::zeros(image.size(), image.type());cols *= chans;for (int i = 0; i < rows; i++){uchar* inData = image.ptr<uchar>(i);uchar* outData = outImage.ptr<uchar>(i);for (int j = 0; j < cols; j += chans){outData[j] = 10 + inData[j];//BoutData[j + 1] = 10 + inData[j + 1];//GoutData[j + 2] = 10 + inData[j + 2];//R//outData[j + 3] = 10 + inData[j + 3];//A}}return outImage;
}/*一般的指针遍历方法*/
cv::Mat fast_ergodic1(cv::Mat image) {int rows = image.rows;int cols = image.cols;/*使用常量会比变量快,因此当图像通道确定时,请将chans改为数字常量,如1,3,4等*///const int chans = image.channels();cv::Mat outImage = cv::Mat::zeros(image.size(), image.type());for (int i = 0; i < rows; i++){uchar* inData = image.ptr<uchar>(i);uchar* outData = outImage.ptr<uchar>(i);for (int j = 0; j < cols; j++){outData[j * chans] = 10 + inData[j * chans];//BoutData[j * chans + 1] = 10 + inData[j * chans + 1];//GoutData[j * chans + 2] = 10 + inData[j * chans + 2];//R//outData[j * chans + 3] = 10 + inData[j * chans + 3];//A}}return outImage;
}int main() {string path = "D:\\imageEnhance\\images\\1.jpg";cv::Mat src = cv::imread(path);printf("image size =  w=%d, h=%d\n", src.cols, src.rows);cv::Mat image1 = src.clone();cv::Mat image2 = src.clone();cv::Mat image3 = src.clone();cv::Mat image4 = src.clone();double T0 = static_cast<double>(cv::getTickCount());cv::Mat outImage1 = fast_ergodic1(image1);double T1 = static_cast<double>(cv::getTickCount());cv::Mat outImage2 = fast_ergodic2(image2);double T2 = static_cast<double>(cv::getTickCount());cv::Mat outImage3 = fast_ergodic3(image3);double T3 = static_cast<double>(cv::getTickCount());cv::Mat outImage4 = fast_ergodic4(image4);double T4= static_cast<double>(cv::getTickCount());printf("fast_ergodic1=%3.3fms\n", (T1 - T0) * 1000 / cv::getTickFrequency());printf("fast_ergodic2=%3.3fms\n", (T2 - T1) * 1000 / cv::getTickFrequency());printf("fast_ergodic3=%3.3fms\n", (T3 - T2) * 1000 / cv::getTickFrequency());printf("fast_ergodic4=%3.3fms\n", (T4 - T3) * 1000 / cv::getTickFrequency());//cv::imshow("src", src); cv::waitKey(30);//cv::imshow("outImage1", outImage1);//cv::imshow("outImage2", outImage2);//cv::imshow("outImage3", outImage3);//cv::imshow("outImage4", outImage4);cv::waitKey(0);
}

防止图像Rect区域越界的好方法

OpenCV的cv::Rect提供了很多实用的方法,可参考:http://blog.csdn.net/da_yuan8421/article/details/60959419:

在对图像进行处理时,经常需要截取图像中的某一区域进行处理,如果截取的区域越界时,就容易导致图像崩溃。

//求两个矩形的交集和并集
rect = rect1 & rect2;
rect = rect1 | rect2;
//对矩形进行对比,返回布尔变量
rect1 == rect2;
rect1 != rect2;

利用两个Rect的交集,我们可以很轻松的避免图像裁剪区域越界的情况,如下:

Rect rect;
rect.x = -10;
rect.y = -10;
rect.height = 100000;
rect.width = 20000;
rect &= Rect(0, 0, src.cols, src.rows);//求交集
cv::Mat crop_img = src(rect);

上例子,原图src的大小=200*200,需要裁剪为rect=[-10,-10,10000,20000],为了避免裁剪Rect越界,需要特殊的保护,最简单的方法就是,加入这句话:rect &= Rect(0, 0, src.cols, src.rows),这个交集的Rect肯定是不会越界。


获取OpenCV版本

#define CV_VERSION_ID CVAUX_STR(CV_MAJOR_VERSION) CVAUX_STR(CV_MINOR_VERSION) CVAUX_STR(CV_SUBMINOR_VERSION)//若你OpenCV的版本是3.2.0,那麽输出为:
cout << CV_VERSION_ID << endl;//320
cout << CVAUX_STR(CV_MAJOR_VERSION) << endl;//3
cout << CVAUX_STR(CV_MINOR_VERSION) << endl;//2
cout << CVAUX_STR(CV_SUBMINOR_VERSION) << endl;//0

读写XML或者yml文件数据的

read.xml文本内容:

<?xml version="1.0"?>
<opencv_storage>
<TrainingData type_id="opencv-matrix"><rows>10</rows><cols>8</cols><dt>f</dt><data>10. 10. 10. 10. 10. 10. 10. 10.11. 11. 11. 11. 11. 11. 11. 11.12. 12. 12. 12. 12. 12. 12. 12.13. 13. 13. 13. 13. 13. 13. 13.14. 14. 14. 14. 14. 14. 14. 14.15. 15. 15. 15. 15. 15. 15. 15.16. 16. 16. 16. 16. 16. 16. 16.17. 17. 17. 17. 17. 17. 17. 17.18. 18. 18. 18. 18. 18. 18. 18.19. 19. 19. 19. 19. 19. 19. 19.
</data></TrainingData>
<classes type_id="opencv-matrix"><rows>10</rows><cols>1</cols><dt>f</dt><data>0. 1. 2. 3. 4. 5. 6. 7. 8. 9. </data></classes>
</opencv_storage>

OpenCV读写方法:

//读xml_test.xml文本的数据  FileStorage fs_read;Mat TrainningData;Mat Classes;string readPath = "D:\\SmartAlbum\\image1\\read.xml";bool bR = fs_read.open(readPath, FileStorage::READ);if (bR){fs_read["TrainingData"] >> TrainningData;fs_read["classes"] >> Classes;cout << TrainningData << endl;cout << Classes << endl;}fs_read.release();//将数据写到xml_write.xml文本中 (若不存在会自动创建一个空的xml文件)string writePath = "D:\\SmartAlbum\\image1\\write.xml";cv::FileStorage fs_write;bool bW=fs_write.open(writePath, FileStorage::WRITE);if (bW){fs_write << "TrainingData" << TrainningData;fs_write << "classes" << Classes;}fs_write.release();

保存Vector数据的方法

#include <vector>
#include<algorithm>
#include <iostream>
#include <string>
#include "opencv2/opencv.hpp"using namespace std;
using namespace cv;template<typename _Tp>
void saveVector(FileStorage &fs, vector<_Tp> v,string nodeName) {fs << nodeName << "[";                          // 开始时,先输入"["for (size_t i = 0; i < v.size(); i++){fs << v.at(i);}fs << "]";
}template<typename _Tp>
bool readVector(FileStorage &fs, vector<_Tp> &v, string nodeName) {FileNode n = fs[nodeName];if (n.type() != FileNode::SEQ){cout << "err" << endl;   return false;}FileNodeIterator it = n.begin(), it_end = n.end();for (; it != it_end; ++it) {v.push_back((_Tp)*it);}return true;
}int main()
{string savePath = "D:\\SmartAlbum\\image1\\data.xml";vector<string> imageName;imageName.push_back("image1.jpg");imageName.push_back("image2.jpg");imageName.push_back("image3.jpg");vector<int> level;level.push_back(1);level.push_back(2);level.push_back(3);FileStorage fw;string nodeName1 = "imageName";string nodeName2 = "level";//将数据写到xml_write.xml文本中 (若已存在该文件,则会清空当前文件内容再写入)  if (fw.open(savePath, FileStorage::WRITE)) {saveVector<string>(fw, imageName, nodeName1);saveVector<int>(fw, level, nodeName2);}fw.release();//读取文件内容vector<string> imageName2;vector<int> level2;string readPath = savePath;FileStorage fr;if (fr.open(readPath, FileStorage::READ)) {readVector<string>(fr, imageName2, nodeName1);readVector<int>(fr, level2, nodeName2);}fr.release();system("pause");return 0;
}

若不未知结点名称,可以直接遍历文件的结点,访问元素,如:

cv::FileStorage pfs(fileToRead, cv::FileStorage::READ);
cv::FileNode fn = pfs.root();
for (cv::FileNodeIterator fit = fn.begin(); fit != fn.end(); ++fit)
{cv::FileNode item = *fit;std::string somekey = item.name();//可以获得node的名称std::cout << somekey << std::endl;
}

Mat矩阵的运算,易错的问题

注意Mat矩阵可以进行加减乘除的基本运算,但一个int型的常数和一个Scalar类型的常数进行运算是有区别的,以“+”为例子(也可以用cv::add()代替)

 cv::Mat test = cv::Mat::zeros(cv::Size(100,100), CV_8UC3);cv::Mat test1 = test + 128;//仅第1通道被赋值为128cv::Mat test2 = test + cv::Scalar(128, 128, 128);//三个通道都被赋值为128

Mat和IplImage相互转换

Mat 是OpenCV和C++的接口矩阵类,ImlImage是OpenCV和C语言的接口的结构体,但是C++程序有时候时候还是要用到ImlImage,例如在MFC中的Picture Control显示图片。Mat和IplImage相互转换方法:

//IplImage—>Mat
//EXAMPLE:
//浅拷贝:
IplImage* pBinary=cvLoadImage("c://temp.jpg",0);
Mat Img;
Img=cvarrToMat(pBinary);
//深拷贝只需要再在Mat里创建一个新的Mat对象,然后进行数据的复制,再用上述的函数进行数据头的复制(浅拷贝):
IplImage* pBinary=cvLoadImage("c://temp.jpg", 0);
Mat ImgTemp;
Img=cvarrToMat(pBinary);
Mat Img = ImgTemp.clone();  //Mat—>IplImage
//EXAMPLE:
//浅拷贝:
Mat Img=imread("1.jpg");
IplImage* pBinary = &IplImage(Img);
//深拷贝只要再加一次复制数据:
IplImage *input = cvCloneImage(pBinary);  

https://blog.csdn.net/lijiayu2015/article/details/52438160


Mat::data指针讲解

http://lib.csdn.net/article/opencv/24030


OpenCV Mat数据类型及位数总结

char ->CV_8SC
unsigned char,uchar ->CV_8UC
unsigned short int,ushort->CV_16UC
short int->CV_16SC
int   ->CV_32SC
float ->CV_32FC
double->CV_64FC

float:  4字节,6-7位有效数字 -3.4E-38 到 3.4E38    
double: 8字节,15~16位有效数字 -1.7E-308 到 1.7E308

在OpenCV里面,许多数据结构为了达到內存使用的最优化,通常都会用它最小上限的空间来分配变量,有的数据结构也会因为图像文件格式的关系而给予适当的变量,因此需要知道它们声明的空间大小来配置适当的变量。一 般标准的图片,为RGB格式它们的大小为8bits格式,范围为0~255,对一个int空间的类型来说实在是太小,整整浪费了24bits的空间,假设有个640*480的BMP文件空间存储內存,那整整浪费了640*480*3*(32-8)bits的內存空间,总共浪费了2.6MB!,也就是那 2.6MB内什么东西都没存储,如果今天以8bits的格式来存储则只使用到0.6MB的內存而已(640*480*3*(8)+54 bits),因此,对于文件格式的对应是一件很重要的事。
    在这边除了要考虑bits的空间大小外,还要考虑使用类型的正负号的问题,一般的图像文件是不存在负号的,如果今天即使选则正确的空间大小,可是出现的结果却是负的,那就功亏一篑了。这里除了Float及double类型,char,int,short int都是用二的补数表示法,它们不具正负号bit,而Float,double则是用IEEE 754,在第32bit,64bit上有一个正负号bit.

cvCreateImage()及cvCreateMat()对应

1.Unsigned 8bits(一般的图像文件格式使用的大小)
IplImage数据结构参数:IPL_DEPTH_8U
CvMat数据结构参数:CV_8UC1,CV_8UC2,CV_8UC3,CV_8UC4

变量类型 空间大小 范围 其他
uchar 8bits 0~255 (OpenCV缺省变量,同等unsigned char)
unsigned char 8bits 0~255  

2.Signed 8bits
IplImage数据结构参数:IPL_DEPTH_8S
CvMat数据结构参数:CV_8SC1,CV_8SC2,CV_8SC3,CV_8SC4

变量类型 空间大小 范围 其他
char 8bits -128~127  

3.Unsigned 16bits
IplImage数据结构参数:IPL_DEPTH_16U
CvMat数据结构参数:CV_16UC1,CV_16UC2,CV_16UC3,CV_16UC4

变量类型 空间大小 范围 其他
ushort 16bits 0~65535 (OpenCV缺省变量,同等unsigned short int)
unsigned short int 16bits 0~65535 (unsigned short)

4.Signed 16bits
IplImage数据结构参数:IPL_DEPTH_16S
CvMat数据结构参数:CV_16SC1,CV_16SC2,CV_16SC3,CV_16SC4

变量类型 空间大小 范围 其他
short int 16bits -32768~32767 (short)

5.Signed 32bits
IplImage数据结构参数:IPL_DEPTH_32S
CvMat数据结构参数:CV_32SC1,CV_32SC2,CV_32SC3,CV_32SC4

变量类型 空间大小 范围 其他
int 32bits -2147483648~2147483647 (long)

6.Float 32bits

IplImage数据结构参数:IPL_DEPTH_32F
CvMat数据结构参数:CV_32FC1,CV_32FC2,CV_32FC3,CV_32FC4

变量类型 空间大小 范围 其他
float 32bits 1.18*10-38~3.40*1038  

7.Double 64bits

CvMat数据结构参数:CV_64FC1,CV_64FC2,CV_64FC3,CV_64FC4

变量类型 空间大小 范围 其他
double 64bits 2.23*10-308~1.79*10308  

8.Unsigned 1bit

IplImage数据结构参数:IPL_DEPTH_1U

变量类型 空间大小 范围 其他
bool 1bit 0~1  

其他变量对应

1.Signed 64bits

int64

long long

2.Unsigned 64 bits

uint64

unsigned long long


Mat矩阵插入新的矩阵

enum insertType
{Top = 0,Bottom = 1,Left = 2,Right = 3
};/* * cv::Mat src:原始数据矩阵* cv::Mat inMat:被插入的数据矩阵* insertType fea_type:插入类型:Top = 0    //在src矩阵顶部插入矩阵inMatBottom = 1 //在src矩阵低部插入矩阵inMatLeft = 2   //在src矩阵左部插入矩阵inMatRight = 3  //在src矩阵右部插入矩阵inMat*/
cv::Mat insertMat(cv::Mat src, cv::Mat inMat,insertType type) {cv::Mat dest= src.clone();int inNum = 0;if (type== Top){inNum = inMat.rows;cv::copyMakeBorder(dest, dest, inNum, 0, 0, 0, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0, 0));cv::Rect r(0, 0, dest.cols, inNum);inMat.copyTo(dest(r));}else if(type == Bottom){inNum = inMat.rows;cv::copyMakeBorder(dest, dest, 0, inNum,0, 0, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0, 0));cv::Rect r(0, dest.rows - inNum, dest.cols, inNum);inMat.copyTo(dest(r));}else if (type == Left){inNum = inMat.cols;cv::copyMakeBorder(dest, dest, 0, 0, inNum, 0, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0, 0));cv::Rect r(0, 0, inNum, dest.rows);inMat.copyTo(dest(r));}else if (type == Right){inNum = inMat.cols;cv::copyMakeBorder(dest, dest, 0, 0, 0, inNum, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0, 0));cv::Rect r(dest.cols- inNum, 0, inNum, dest.rows);inMat.copyTo(dest(r));}return dest;
}

颜色空间缩减的方法

cv::Mat CreatTable(int level) {Mat lookUpTable(1, 256, CV_8UC1);uchar *p = lookUpTable.data;int div = 256 / level;for (size_t i = 0; i < 256; i++){p[i] = (i / div)*div;}return lookUpTable;
}
static cv::Mat  lookUpTable = CreatTable(64);void main() {string p1= "D:\\SmartAlbum\\image1\\B\\B15.jpg";cv::Mat image1 = cv::imread(p1);cv::Mat dest;cv::LUT(image1, lookUpTable2, dest);cv::waitKey(0);
}

YUV444,YUV422,YUV420解释

https://blog.csdn.net/mandagod/article/details/78605586

通常我们用RGB表示一种彩色。计算机系统里的LCD显示的数据就是RGB来表示每个像素的颜色。
而在我们生活里,有黑白电视机与彩色电视机两种,拍摄节目源时不可以用两种不同的摄像机来存放两种图像数据。
所以为了兼容两种电视机,专家就引入YUV格式代替RGB,其中Y表示亮度, U和V表示色差。 黑白电视机只用Y信号, 而彩色电视机可由YUV转换成RGB再显示颜色。

通常我们所用的YUV格式是 ITU-R 的标准 , 也叫YCbCr。YUV是由RGB格式的数据转换得来。

Y     Y = 0.299 x R + 0.587 x G + 0.114 x B + 0
U    Cb = -0.169 x R - 0.331 x G + 0.499 x B + 128
V    Cr = 0.499 x R - 0.418 x G - 0.0813 x B + 128 Y    Y = 0.299 x R + 0.587 x G + 0.114 x B + 0
U    Cb = -0.169 x R - 0.331 x G + 0.499 x B + 128
V    Cr = 0.499 x R - 0.418 x G - 0.0813 x B + 128

YUV4:4:4 
     其实就是YUV的数据各占用8位, 每个像素都由YUV组成

  1. 同一行的相邻4个像素数据: Y0U0V0 Y1U1V1 Y2U2V2 Y3U3V3

  2. 存储时: Y0 U0 V0 Y1 U1 V1 Y2 U2 V2 Y3 U3 V3 //即每个像素YUV的数据都会存放起来

  3. 为什么叫4:4:4 , 意思就是4个像素里的数据有4个Y, 4个U, 4个V

YUV4:2:2
     其实绝大部分相邻的两个像素,数据差异应不大。所以为了节点空间便于存储,丢失每个像素的部分数据。
     专家研究表明我们人对亮度比较敏感,而对色彩不怎么敏感。所以每个像素的亮度Y数据是绝对不动的,而色差数据可以进行丢弃。

  1. 同一行的相邻4个像素数据: Y0U0V0 Y1U1V1 Y2U2V2 Y3U3V3

  2. 存储时: Y0 U0 Y1 V1 Y2 U2 Y3 V3 // 每两个相邻的像素, 一个丢弃V数据,一个丢弃U数据

  3. 为什么叫4:2:2, 意思就是相邻的4个像素里有4个Y, 2个U, 2个V。 按上面存储的顺序也叫YUYV.

  4. 但还原成RGB数据必须需要YUV, 像第一个像素只有Y0U0是没法还原的,这时只能用下一像素的V1数据。

  5. 还原时的YUV: [Y0U0V1] [Y1U0V1] [Y2U2V3] [Y3U2V3] //这样还原理论上会对图像的质量有影响的,但我们看不出来的.

YUV4:2:0
     专家们进一步研究表示,每一行的相邻两个像素与下一行同位置的两个像素数据差异不大,可以进一步的丢数据。

  1. 如两行的像素数据:

  2. Y00U00V00 Y01U01V01 Y02U02V02 Y03U03V03 ....

  3. Y88U88V88 Y89U89V89 Y90U90V90 Y91U91V91 ....

  4. 存储时: Y00U00 Y01 Y02U02 Y03 //每个像素的Y数据保留, 两个像素数据只保留一个U数据。这一行不保留V数据(YUV: 420)

  5. Y88V88 Y89 Y90V90 Y91 // .... 两个像素数据只保留一个V数据, 这行不保留U数据(YUV: 402)

  6. 还原时只能相同位置的上下两行4个像素结合还原:

  7. Y00U00V88 Y01U00V88 Y02U02V90 Y03U02V90

  8. Y88U00V88 Y89U00V88 Y90U02V90 Y91U02V90

yuv数据还分成打包的,平面的。
     打包的意思是: yuv数据是顺序存放Y,接着U,再接着V数据存放。
     平面的意思是: yuv数据是分成三个地方存放, 一个地方只存Y数据, 一个只存U数据, 一个只存V数据

OpenCV常见的优化方法和技巧总结相关推荐

  1. 什么是长尾关键词?长尾关键词优化方法和技巧

    做网站SEO优化想必你也已经发现,一个网站的主要流量80%是来自长尾关键词.长尾关键词最常见的是分布在网站内容页,其次是栏目title.标签页.专题页.那么什么是长尾关键词呢?其实长尾关键词就是词比较 ...

  2. OpenCV程序效率优化方法1

    OpenCV程序效率优化方法 使用指针方法遍历像素点 OpenCV中图像的存储对象为Mat类,该类提供了多种方式访问像素的的值.一般来说分为以at方法类与ptr指针的方式访问,相较之下使用指针ptr的 ...

  3. CPU CACHE优化 性能优化方法和技巧

    转载来源:性能优化方法和技巧 系列目录 性能优化方法和技巧 性能优化的方法和技巧:概述 性能优化的方法和技巧:代码 性能优化的方法和技巧:工具 这是一个可以用一本书来讲的话题,用一系列博客来讲,可能会 ...

  4. 深度学习常见的优化方法(Optimizer)总结:Adam,SGD,Momentum,AdaGard等

    机器学习的常见优化方法在最近的学习中经常遇到,但是还是不够精通.将自己的学习记录下来,以备不时之需 https://www.cnblogs.com/GeekDanny/p/9655597.html

  5. 网站手机端seo优化方法和技巧

    作为一个寻求名声的网站所有者,从你选择创建一个网站的那一刻起,你就知道SEO(搜索引擎优化)是一个高度优先事项,这就是为什么现在是调整的好时机.SEO是一个不断发展的领域,百度对网站排名的方式有很多修 ...

  6. 机器学习优化方法总结比较(SGD,Adagrad,Adadelta,Adam,Adamax,Nadam)

    SGD: 此处的SGD指mini-batch gradient descent,关于batch gradient descent, stochastic gradient descent, 以及 mi ...

  7. spark 应用程序性能优化:12 个优化方法

    1. 优化? Why? How? When? What? "spark 应用程序也需要优化?",很多人可能会有这个疑问,"不是已经有代码生成器,执行优化器,pipelin ...

  8. 【TensorFlow】优化方法optimizer总结(SGD,Adagrad,Adadelta,Adam,Adamax,Nadam)解析(十三)

    版权声明:本文为博主原创文章,未经博主允许不得转载. https://blog.csdn.net/brucewong0516/article/details/78838124 本文仅对一些常见的优化方 ...

  9. 04 | 函数与优化方法:模型的自我学习(上)

    目录 一.损失函数.代价函数和目标函数 损失函数与代价函数 目标函数 常用损失函数 1.0-1 损失函数 2.平方损失函数 3.均方误差损失函数 4.交叉熵损失函数 5.Softmax 损失函数 二. ...

最新文章

  1. [ASP.NET4之旅]Circular file references are not allowed
  2. Crystal Reports中的字段
  3. python代码需要背吗-20个Python代码段,你需要立刻学会,好用到哭!
  4. GdiPlus[28]: IGPPen: 建立复合画笔
  5. Python中小括号( )、中括号[ ]和大括号{}分别代表什么?
  6. ogre plugin for 3dmax 最新进度和功能说明
  7. 从阿里中台战略看企业IT架构转型之道(上)
  8. 【计算机视觉】基于OpenCV的人脸识别
  9. linux修改密码最短生存时间,Linux基础命令---修改用户密码
  10. mysql执行效率低_如何查询mysql中执行效率低的sql语句
  11. 章节三、2-方法_演示实例
  12. 【转】Prewitt 算子
  13. ESPNet: Efficient Spatial Pyramid of Dilated Convolutions for Semantic Segmentation(自动驾驶领域轻量级模型)
  14. pbrt gamma
  15. tomcat8下载安装教程
  16. 【铨顺宏项目推荐】RFID无线射频识别技术的设计思路
  17. 3.21 Refused Bequest 被拒绝的馈赠
  18. 安卓应用市场互相抓取app包
  19. 陈莉君教授: 回望踏入Linux内核之旅(上篇)
  20. 搞事课堂 C++格式化硬盘

热门文章

  1. 【差分+前缀和】BZOJ1637: [Usaco2007 Mar]Balanced Lineup
  2. “流式”前端构建工具——gulp.js 简介
  3. htmlcss笔记--标签默认值样式重置css reset(2)
  4. Axure函数与变量
  5. 王家林 云计算分布式大数据Hadoop实战高手之路第七讲Hadoop图文训练课程:通过HDFS的心跳来测试replication具体的工作机制和流程...
  6. LiteDB源码解析系列(3)索引原理详解
  7. Scratch-介绍“克隆”
  8. 如何在Linux(CentOS, Debian, Fedora和Ubuntu)上安装MyCLI
  9. C#:导入Excel通用类(CSV格式)
  10. [POJ2155] Matrix(二维线段树,树套树)