opencv 常见的优化问题和技巧

【1】释放Mat图像内存空间:

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

【2】释放图像通道分割的图像空间

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

【3】关于vector内存释放的问题:

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

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

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

vector<Point>().swap(pointVec); //或者pointVec.swap(vector<Point> ())

标准模板:

template < class T >
void ClearVector( vector< T >& vt )
{
vector< T > vtTemp;
veTemp.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();

【4】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;
       同样的,传递到一维数组我们可以这样
  1. 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);

【5】图像的遍历

      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

 
  

【6】防止图像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肯定是不会越界。

【7】获取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

【8】读写XML或者yml文件数据的
   read.xml文本内容:

  1. <?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;
}
【9】Mat矩阵的运算,易错的问题
       注意Mat矩阵可以进行加减乘除的基本运算,但一个int型的常数和一个Scalar类型的常数进行运算是有区别的,以“+”为例子(也可以用cv::add()代替)
cv::Mat test = cv::Mat::zeros(cv::Size(100,100), CV_8UC3);
cv::Mat test1 = test + 128;//仅第1通道被赋值为128
cv::Mat test2 = test + cv::Scalar(128, 128, 128);//三个通道都被赋值为128
【10】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
【11】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


【】颜色空间缩减的方法
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);
}



转载于:http://blog.csdn.net/guyuealian/article/details/78540206



猜你喜欢

转载自blog.csdn.net/qq_42189368/article/details/80855743