利用valgrind定位opencv的cvCloneImage()内存泄漏问题

/*************************************************
抓图片
*************************************************/
IplImage * JustCapture()
{
	char imgnamesaved[100];
	int send_len;
	char buffer[256];

	IplImage *pFrame = NULL,*img=NULL;//声明IplImage指针  
  	CvCapture *pCapture = NULL;
	/*CvCapture 是一个结构体,用来保存图像捕获所需要的信息。opencv提供两种方式从外部捕获图像:

	一种是从摄像头获取,一种是通过解码视频得到图像。
        两种方式都必须从第一帧开始一帧一帧的按顺序获取,因此每获取一帧后都要保存相应的状态和参数。
        比如从视频文件中获取,需要保存视频文件的文件名,相应的解码器类型,下一次如果要

        获取将需要解码哪一帧等。 这些信息都保存在CvCapture结构中,每获取一帧后,这些信息

        都将被更新,获取下一帧需要将新信息传给获取的api接口*/

	getSystemTime(imgtime);//获取系统时间,当作图片的名称
	sprintf(imgname,"%s.jpg",imgtime);
	sprintf(imgnamesaved,"/home/fyp/sda3/GodKnows/fromPan/capimg/%s.jpg",imgname);//图片存储位置
 	bzero(buffer, BUFFER_SIZE); 
	sprintf(buffer,"%s",imgname);

 	//pCapture = cvCaptureFromFile(gRtspAdd);
	pCapture = cvCaptureFromAVI("20180607.mp4");// 函数cvCaptureFromAVI从摄像头或者文件中抓取一帧,然后解压并返回这一帧。    
	numFrames = (int)cvGetCaptureProperty(pCapture, CV_CAP_PROP_FRAME_COUNT);//设置视频获取属性,numFrames时视频文件的总帧数。 
	cvSetCaptureProperty(pCapture,CV_CAP_PROP_POS_FRAMES,time_frame);//设置pCapture的开始帧,第二个参数是单位为帧数的位置 从位置time_frame=0开始播放视频 
  	while(!pCapture)
	{
  		printf("Can not get the video stream from the camera wait 100 sec!\r\n");	
		sleep(100);//将程序挂起100s	
            pCapture = cvCaptureFromAVI("20180607.mp4");
if(!pCapture)//如果依然无法获取到视频流,继续执行循环 continue;elsebreak; } if (img==NULL) {pFrame = cvQueryFrame(pCapture);/*cvQueryFrame(pCapture)表示从摄像头或者文件中抓取并返回一帧。输入一个CvCapture 类型的指针,该函数主要功能是将视频文件的下一帧加载到内存。与cvLoadImage的不同之处是,该函数不重新分配内存空间。*///cvReleaseImage(&img); //img=cvCloneImage(pFrame);//用这一句产生内存泄漏问题,换成下面的两句img = cvCreateImage(CvSize(cvGetSize(pFrame)),IPL_DEPTH_8U, 3);cvCopy(pFrame,img,NULL);cvSaveImage(imgnamesaved,img,NULL);//保存图片sleep(sleep_first);        printf("get picturename %s,sleep %d, then continue \r\n",imgname,sleep_first);send_len= send(sock_idtcp,buffer,sizeof(buffer),MSG_NOSIGNAL); //用send不成功来判断掉线,然后关闭socket,重新创建socket来连接。while(send_len< 0){perror("Send filename failed,wait 100 sec\n");close(sock_idtcp);InitTcpSocket();send_len= send(sock_idtcp,buffer,sizeof(buffer), MSG_NOSIGNAL);if (send_len< 0){sleep(30);continue;}else printf("Sending PictureName Successfully\n\n");}bzero(buffer, BUFFER_SIZE);//少写了这一句,程序运行时出错报错:*** stack smashing detected ***: ./DetectWaterDepth terminated 已放弃 (核心已转储) } cvReleaseCapture(&pCapture); //cvReleaseImage(&pFrame); return img; }

关于网上说的cvCloneImage函数:用cvCopy函数代替,我就一直纠结于把这两个换来换去,结果发现内存泄漏的真正原因不在这里。

抓图片的函数返回值是一个IplImage指针,关于函数返回值是指针的问题,转自https://blog.csdn.net/rentingting870927/article/details/5994173

先看一个例子,初学者经常碰到的问题

char *GetString(void)  
{  
 char p[] = "hello world";  
 return p; //编译器一般将提出警告信息  
}  
void main(void)  
{  
 char *str = NULL;  
 str = GetString(); //str 的内容是垃圾,得不到想要的内容  
 count<< str <<end;  
}  

函数Getstring中定义的变量p属于local,当函数结束时自动消失,所以在返回时,根本就得不到p所指的内容。解决办法有以上常见的几种

(1)可以使用全局数组,使用全局变量时,在程序结束时才释放。

(2)在函数Getstring()中使用new在堆上动态分配内存来建立数组。C语言中可以使用malloc()函数,不过不要忘记,使用完成后要进行内存释放,不然会造成内存泄漏。分别使用delete,free释放,使用delete时,会调用类的析构函数,而free则不会。

char *GetString()  
  {  
  char *p;  
  p = (char *)malloc(100);  
  return p;  
  }  
      
  void main()  
  {  
  char *str=NULL;  
      
  str=GetString();  
  strcpy(str,"Hello");  
  printf("%s", str);  
  free(str); //free memroy  
 }  xxxx

(3)用static声明一个指针可以,但也不太好,因为如果你多次调用这个函数返回多个指针,但这几个指针实际上指向同一块地址,改变任何一个的内容将改变所有指针的内容,这样也不是很多情况所需要的。

char* GetString(void)  
  {  
          static char p[]="hello world";  
          return p; //p为静态创建,程序退出时才释放  
  }   

(4)用String类型,用string   实现,是值拷贝!不存在释放内存会影响拷贝的问题。

string GetString(void)  
  {  
            char p[] = "hello world";  
            return p;  
  }  
  void Test4(void)  
  {  
            string str;  
            str = GetString();  
            cout<< str.c_str() << endl;  
  }   
5)使用字符串常量,因为字符串常量存储再静态存储区域,所以一直都存在,p是临时变量,但过程结束并不 会释放这个字符串常量.而p[]就不一样了,它是一个数组,数组里面存放了字符串,这个字符串没有放在字符 串常量存储再静态存储区域,p是临时变量,跳出函数之后一般保留一步就释放了,数组的空间回收了,字符串 没有了
const char *GetString(void)  
  {  
            const char *p = "hello world";  
            return p;  
  }  
  char *GetString(void)  
  {  
            char *p = "hello world";  
            return p;  
  }  
 void GetString(char* p)  
  {  
            strcpy(p, "hello world");  
  }  
  void Test4(void)  
  {  
            char str[100];  
            GetString(str);  
            cout<< str << endl;  
  }  

  1、在栈上建立局部变量。注意,在栈上时!栈用于函数是为了返回时找得到调用点(在调用时压入栈的)

,那么,返回时要POP才能得到。函数体中建立的任何东西都消失了(返回值除外),你返回的指针指向的内
容现在不知被用作什么用途了,如果你还要修改的话,那么后果不能确定。   
  2、在堆中分配。返回时不会摧毁,因为堆是全局存在的。但函数的调用者要记得delete回来的指针。

cvCloneImage()每次使用时编译器会分配新的内存空间,不会覆盖以前的内容,所以如果在循环中使用内存会迅速减小,每次用完都需要用cvRelease来释放。

   解决方法是使用cvCopy函数代替。
   cvCopy(pSrcImg,pImg,NULL);   //代替 pImg = cvCloneImage(pSrcImg);
   pImg初始化时必须分配空间,否则上述函数不能执行。
   pImg = cvCreateImage(cvSize(IMGWIDHT,IMGHEIGHT),IPL_DEPTH_8U, 3);

while (1)
	{
	IplImage *imgDic = NULL; 
		imgDic = JustCapture();//调用justcapture函数

		time_frame=time_frame+numFrames/20;
            	if(time_frame >= numFrames)
			break;
		Mat imgDicMat(imgDic);
cvReleaseImage(&imgDic);//最后把这个释放掉就不会有内存泄漏问题。


猜你喜欢

转载自blog.csdn.net/weixin_39371711/article/details/80640233