最近由于项目的原因,有一台工控机在没有打永恒之蓝补丁之前,应用程序使用opencv 3.4.1打开摄像头后显示视频内容都可以正常显示,但当安装完windows 7 x86永恒之蓝补丁后(KB4012215,KB4012212),应用程序无法正常显示摄像头的视频,表现为:OPENCV打开摄像头成功,但获取摄像头视频帧得到的数据为空,使用AMCAP2.exe进行测试摄像头,使用MJPG格式可以正常浏览,但把视频设置为YUV的格式后也无法预览。
于是想到把OPENCV设置为MJPG方式打开摄像头进行处理,却同样无法预览,测试代码如下:
int main(int argc, const char** argv)
{
//if (!initLicense()) return -1;
string assetFolder = "";
string inputVideo = "";
if (argc >= 2)
{
assetFolder = argv[1];
}
if (argc >= 3)
{
inputVideo = argv[2];
}
VideoCapture cap;
cv::VideoWriter l_writer;
int camid=0;
if (!inputVideo.empty())
{
// open file
cap.open(inputVideo);
}
else
{
cout<<" please input camera index:";
cin>>camid;
//设置图像宽度
cap.set(CAP_PROP_FRAME_WIDTH,640);
//设置图像高度
cap.set(CAP_PROP_FRAME_HEIGHT,480);
//设置帧率
cap.set(CAP_PROP_FPS,30);
//获取MJPE编码 这个值是固定的1196444237
int fourcc= l_writer.fourcc('M','J','P','G');
//设置编码格式,这里只能写在分辨率和帧率后面,否则无效
cap.set(CAP_PROP_FOURCC,fourcc);
// open camera
cap.open(camid);
}
if (!cap.isOpened())
{
cerr << "can not open camera!" << endl;
return -1;
}
//打印摄像头参数
cout << "Cam :" << camid;
cout << "宽度(width) =" << cap.get(CV_CAP_PROP_FRAME_WIDTH)<<endl; //宽度
cout << "高度(height) =" << cap.get(CV_CAP_PROP_FRAME_HEIGHT)<<endl; //高度
cout << "帧数(fbs) =" << cap.get(CV_CAP_PROP_FPS)<<endl; //帧数
cout << "亮度(brightness) =" << cap.get(CV_CAP_PROP_BRIGHTNESS)<<endl; //亮度
cout << "对比度(contrast) =" << cap.get(CV_CAP_PROP_CONTRAST)<<endl; //对比度
cout << "饱和度(saturation) =" << cap.get(CV_CAP_PROP_SATURATION)<<endl; //饱和度
cout << "色调(hue) =" << cap.get(CV_CAP_PROP_HUE)<<endl; //色调
cout << "曝光(exposure) =" << cap.get(CV_CAP_PROP_EXPOSURE)<<endl; //曝光
int ex = static_cast<int>(cap.get(CV_CAP_PROP_FOURCC));//格式
// Transform from int to char via Bitwise operators
char EXT[] = { (char)(ex & 0XFF), (char)((ex & 0XFF00) >> 8), (char)((ex & 0XFF0000) >> 16), (char)((ex & 0XFF000000) >> 24), 0 };
cout << "格式(fourcc) =" << EXT<<endl;
string winName = "canvas";
namedWindow(winName);
moveWindow(winName, 10, 10);
Mat frame;
bool finish_get_faceimg = false;
//Timer tmr;
while (true)
{
//tmr.tic();
cap >> frame;
cap >> frame;
if (frame.empty())
{
cerr << "frame empty!" << endl;
break;
}
double fps = 0;//1000 / tmr.toc();
putText(frame, "fps:" + std::to_string((long double)fps), Point(10, 20), FONT_HERSHEY_SIMPLEX, 0.6, CV_RGB(255, 0, 0), 1);
imshow(winName, frame);
if (waitKey(1) == 27)
break;
}
cap.release();
return 0;
}
经过多个版本的尝试依然没有解决问题,最终想到要不进行调试一步步进行跟进,就使用opencv3.0.0的debug版本进行调试(为什么使用opencv3.0.0,原因是由于这个版本比较老,原想这个问题会不会是新版本的问题,旧版本会不会没有这个问题呢),在调试过程中,终端程序在打开摄像头时发现输出信息显示" trying format YUV2…"等,后面就没有见到有mjpg格式了,心想明明是设置了mjpg,为什么还是使用yuv方式打开,进行翻查opencv代码,最终找到opencv在打开摄像头时会进行每一种编码方式尝试打开,并且是按顺序进行,当某一种方式打开成功后就不会再尝试别的方式打开,opencv打开摄像头的方式:
//-------------------------cap_dshow.cpp : int videoInput::start(int deviceID, videoDevice *VD) getMediaSubtypeAsString(mediaSubtypes[i], guidStr);---------
char guidStr[8];
// try specified format and size
getMediaSubtypeAsString(VD->tryVideoType, guidStr);
DebugPrintOut("SETUP: trying specified format %s @ %ix%i\n", guidStr, VD->tryWidth, VD->tryHeight);
if( setSizeAndSubtype(VD, VD->tryWidth, VD->tryHeight, VD->tryVideoType) ){
VD->setSize(VD->tryWidth, VD->tryHeight);
VD->videoType = VD->tryVideoType;
foundSize = true;
} else {
// try specified size with all formats
for(int i = 0; i < VI_NUM_TYPES; i++){
getMediaSubtypeAsString(mediaSubtypes[i], guidStr);
DebugPrintOut("SETUP: trying format %s @ %ix%i\n", guidStr, VD->tryWidth, VD->tryHeight);
if( setSizeAndSubtype(VD, VD->tryWidth, VD->tryHeight, mediaSubtypes[i]) ){
VD->setSize(VD->tryWidth, VD->tryHeight);
VD->videoType = mediaSubtypes[i];
foundSize = true;
break;
}
}
}
//-------------------------cap_dshow.cpp : videoInput::videoInput(){---------
mediaSubtypes[0] = MEDIASUBTYPE_RGB24;
mediaSubtypes[1] = MEDIASUBTYPE_RGB32;
mediaSubtypes[2] = MEDIASUBTYPE_RGB555;
mediaSubtypes[3] = MEDIASUBTYPE_RGB565;
mediaSubtypes[4] = MEDIASUBTYPE_YUY2;
mediaSubtypes[5] = MEDIASUBTYPE_YVYU;
mediaSubtypes[6] = MEDIASUBTYPE_YUYV;
mediaSubtypes[7] = MEDIASUBTYPE_IYUV;
mediaSubtypes[8] = MEDIASUBTYPE_UYVY;
mediaSubtypes[9] = MEDIASUBTYPE_YV12;
mediaSubtypes[10] = MEDIASUBTYPE_YVU9;
mediaSubtypes[11] = MEDIASUBTYPE_Y411;
mediaSubtypes[12] = MEDIASUBTYPE_Y41P;
mediaSubtypes[13] = MEDIASUBTYPE_Y211;
mediaSubtypes[14] = MEDIASUBTYPE_AYUV;
mediaSubtypes[15] = MEDIASUBTYPE_MJPG; // MGB 原位置
当YUV打开成功后,使用的摄像头的数据格式就会停止,因此就会使用yuv进行打开,但此时系统无法使用YUV读取摄像头数据,因而导致无法显示视频。
为了解决此问题,无法从windows 7 32位系统分析解决,因此使用修改opencv打开视频格式的方式解决了此问题,把打开的视频数据格式修改为以下顺序:mediaSubtypes[15] = MEDIASUBTYPE_MJPG;----- >mediaSubtypes[4] = MEDIASUBTYPE_MJPG;:
mediaSubtypes[0] = MEDIASUBTYPE_RGB24;
mediaSubtypes[1] = MEDIASUBTYPE_RGB32;
mediaSubtypes[2] = MEDIASUBTYPE_RGB555;
mediaSubtypes[3] = MEDIASUBTYPE_RGB565;
mediaSubtypes[4] = MEDIASUBTYPE_MJPG; // MGB 调整后的位置
mediaSubtypes[5] = MEDIASUBTYPE_YUY2;
mediaSubtypes[6] = MEDIASUBTYPE_YVYU;
mediaSubtypes[7] = MEDIASUBTYPE_YUYV;
mediaSubtypes[8] = MEDIASUBTYPE_IYUV;
mediaSubtypes[9] = MEDIASUBTYPE_UYVY;
mediaSubtypes[10] = MEDIASUBTYPE_YV12;
mediaSubtypes[11] = MEDIASUBTYPE_YVU9;
mediaSubtypes[12] = MEDIASUBTYPE_Y411;
mediaSubtypes[13] = MEDIASUBTYPE_Y41P;
mediaSubtypes[14] = MEDIASUBTYPE_Y211;
mediaSubtypes[15] = MEDIASUBTYPE_AYUV;
总结:
1.opencv存在一定的问题,无法通过接口设置其打开视频的格式
2.windows 7 32位更新永恒之蓝补丁后导致无法预览摄像头数据(YUV格式),找了两天都没有找到原因,此问题在另一个系统的windows 7 32下又没有问题(补丁更新的数量不一样,除了永恒之蓝补丁外,还更新了其他补丁),给我的感觉是由于永恒之蓝补丁导致当前系统的directx无法兼容所致,由于时间比较紧急,打过电话给微软,微软却说不是他们系统的问题,应该找一下设备应用程序开发商分析问题,工控机厂商也没有办法一时解决此问题。