好久没碰过OpenCV了,如今又要重新捡起来,重头来过,这次是根据一篇重庆大学博士学位论文《基于计算机视觉的运动目标跟踪算法研究》–尹宏鹏中的思路,打算一步步探索运动物体识别的过程。
这篇博客中记录的是我在完成图像输入–>边缘检测过程中碰到的Bug,编程环境是Visual Studio 2017,OpenCV 3.4.0
这次测试用的物体是彩球(绣球),视频输入为手机拍摄。
我们调用OpenCV中videocapture这个类型获取视频图像数据
VideoCapture capture("D:/Documents/Videos/ballTest.mp4");
括号中的路径为本人机器上的本地视频路径,接下来通过几个属性常量就可以获得和设置视频数据的信息:
capture.get(CV_CAP_PROP_FPS);
capture.get(CV_CAP_PROP_FRAME_COUNT)
capture.set(CV_CAP_PROP_POS_FRAMES, frameToStart)
这里frameToStart表示我们需要从视频的哪一帧开始播放并获取图像;第一句和第二句分别用于从VideoCpature对象中获取帧率和总帧数。
capture.read(frame);
read方法的参数为Mat类型用于存储读取到的图像;这里使用currentFrame来表示当前读取到的帧数,判断用户点击结束按钮时是否已经在视频播放完成后。最后调用release方法使用capture对象关闭视频文件。
这里提一下在视频播放过程中帧率的使用:
int delay = 1000 / rate;
其中rate表示帧率,delay表示相邻一帧图像播放时的间隔时间;我们将在等待用户输入按键的设置思路中使用到这个变量。
//当时间没结束前按键按下时,返回值为-1,否则返回按键
int c = waitKey(delay);
//按下ESC或者到达指定的结束帧后退出读取视频
if ((char)c == 27 || currentFrame > frameToStop)
{
stop = true;
}
图像读取到这里就介绍完了,一步步走的话不会出现大问题的。
边缘检测我选择了第一次接触到的prewitt算子,算子结构如下:
float prewitt_x[9] = {
-1,0,1,
-1,0,1,
-1,0,1
};
float prewitt_y[9] = {
1,1,1,
0,0,0,
-1,-1,-1
};
我设计了prewitt方法,参数src和dst作为Mat类型对象,使用卷积核的函数为
filter2D(src, dst_x, 8, px, cvPoint(-1, -1));
对于dst的每个坐标的点,其值为使用纵轴prewitt_y和使用横轴prewitt_x后得到的对应的坐标的值的欧几里得距离。
“the application wrote to memeory after end of heap buffer”表示写入越界操作。我的下意识有以下几点:
1.肯定问题出在main()函数调用子函数的完成后退栈的时候发生的问题
2.子函数中也许存在没有释放的内存空间导致写入的位置出错
在OpenCV的编程中,占空间最大的莫过于Mat类型的数据了,所以我毫不犹豫的调用了Mat类型对象的release()方法(虽然这个方法在析构函数中会自动调用),但是并没有什么作用。
一段段语句的调试最终来到了prewitt()方法的最后一步,我调用了minMaxLoc()方法,第一次调用这个方法用于获取图像中最大最小像素值,后来查看了网上有关这个函数的用法才知道了“阴谋”:
从手机拍摄的视频中读取到的图像默认为多通道的,所以如果直接作为参数输入,在调用minMaxLoc()函数时就会发生每个点的数据占用的空间远大于单通道数据,所以我索性就不要这个功能了。
从图二可以看到ptr以及指针,下意识的就想到了指针问题的报错,这里不妨将我的“原”代码贴上来作为前师之鉴吧。
在给输出图像dst赋值时,我使用的模板类型是int,而在Mat类型对象中每个点的数据用uchar类型表示,想必又是数据类型的问题,这里我改为uchar类型问题解决。
总结:C,C++这类编程语言使用指针用于内存空间的手动配置,在使编程代码具体准确的同时,也提出了对数据类型的用法准确的要求,这点很重要。