因为在录制并生成avi的时候,程序突然崩溃导致,虽然写入的图片信息,但是avi最后没有帧数的信息。
导致了win播放器可以正常的播放,但是通过opencv没办法正确读取出avi的帧数。
问题1:
//获取avi文件基本信息
int nFps = videoCap.get(CV_CAP_PROP_FPS); //正常
int nFrameCount = videoCap.get(CV_CAP_PROP_FRAME_COUNT); //不正常:0
int n = videoCap.get(cv::CAP_PROP_FRAME_COUNT);//不正常:0
百度发现确实存在这种无法正常读取帧数的情况。
【cvgetCaptureProperty始终为cv_cap_prop_frame_count返回0!】
int nFrameCount = videoCap.get(CV_CAP_PROP_FRAME_COUNT);
int n = videoCap.get(cv::CAP_PROP_FRAME_COUNT);
if (nFrameCount <= 0)
{
ifstream videoFile(m_sAviPath, ios::in | ios::binary);
// Checking the availablity of the file
if (!videoFile) {
std::cout << "Couldn't open the input file " << m_sAviPath << std::endl;
}
else
{
char tempSize[4] = {1};
videoFile.seekg(0x30, ios::beg);
videoFile.read(tempSize, 4);
int frames = (unsigned char)tempSize[0] + 0x100 * (unsigned char)tempSize[1] + 0x10000 * (unsigned char)tempSize[2] + 0x1000000 * (unsigned char)tempSize[3];
//失败了,因为二进制文件中的0x30之后的4个字节都是0x00。
videoFile.close();
nFrameCount = frames;
}
}
所以,想到avi一帧一帧的读取也能正常,所以想通过不断读取到cv::mat中的方法,当读取到的mat是空的时候,就表示结束了。这时候就能统计出帧数。
#include <QTime>
bool CAviTool::getAviFrameCount(std::string sAviPath, uint64_t& nFrameCount)
{
//打开avi文件
cv::VideoCapture videoCap(sAviPath);
if (!videoCap.isOpened()) //检查是否打开
{
return false;
}
uint64_t nIndex = 0;//0; //23652 是会让cv 读取错误的。但是为什么呢?我只是读取有没渲染。
QTime time;
time.start();
while (true)
{
videoCap.set(cv::CAP_PROP_POS_FRAMES, nIndex );
cv::Mat mat;
//[!] 获取单帧的方式
//videoCap >> mat;
videoCap.read(mat);
if (mat.empty())
{
std::cout << "mat emtpy" << std::endl;
break;
}
std::cout << "index = " << nIndex <<" use time = (ms) " << time.elapsed() << std::endl;
time.restart();
nIndex++;
}
nFrameCount = nIndex;
return true;
}
最后一帧之后,会报警告信息:
但是这还有一个问题,就是while循环中,读取每帧的数据差不多1ms,所以当有2万帧的时候,就会出现等待20m的情况。这个太久了。
同时,不知道是有问题的avi导致的还是正常avi也有这个问题:
videoCap.set(cv::CAP_PROP_POS_FRAMES, nIndex ); nIndex 从20k开始,依旧会再读取20k后才出现cv::mat为空的情况。似乎不受nIndex 具体在影响,这样导致了 视频跳转的功能无法实现的情况了。【未找到原因和解决方法】
而这个之所以依旧可以正常获取mat的原因猜测可能是因为:videoCap >> mat; 或
videoCap.read(mat); 每次执行后都会跳转到下一帧的位置,从而实现循环读取。
## 问题2: 如何把获取到的帧数写入到avi使得可以正常使用?
基于上面的 0x30 + 4字节的思路,想着把帧数写入到对应字节中(高位在后)。但是写入后,发现依旧没有什么改变。 和之前的没什么区别(二进制内容是改变了了,但是通过cv::** get(CV_CAP_PROP_FRAME_COUNT) 的方式还是无法正常帧数。
## 问题3: 其实是问题1中后面发现的videoCap.set(cv::CAP_PROP_POS_FRAMES, nIndex ); nIndex 是什么值都可以的问题。 这就导致我就算知道了avi的总帧数也没什么用了。
这个有问题的avi导致了跳转到指定帧的功能是无效的,即无法实现跳转功能。【未找到原因和解决方法】
## 问题4: 通过avi文件大小,摄像头的像素640*360*3 (单个图片大小)来估计帧数。结果失败了,结果是avi明显较小,猜测其实在把cv::mat 写入到avi的过程中就有进行某些压缩的处理。所以这个方法完全不可行。
videoFile.seekg(0, ios::end);
uint64_t nSize = videoFile.tellg();
int nFrame = nSize / (640 * 360);
## 其他信息
注意: cv::mat 只要 release 后 就是mat.empty() 的了。
【OpenCV VideoCapture 设置和获取摄像头参数】: 但是其中没有办法写入总帧数。