今天开始学习VideoNet(一个很出名的在codeproject上的印度人写的关于点对点的视频聊天的程序,可以在codeproject上下载到源码,而且在vckbase上有中文的翻译)中H.263的编解码器部分,网上搜罗了几个例子,但是都是采用的VFW,有的能编译通过,但是运行时我的免驱USB摄像头根本打不开,没有图像,而且都是基于VideoNet那个程序的,所以干脆自己看看结合着opencv来弄算了(我在VC2005下使用opencv2.2前两天还能打开USB camera,今天不行了~~哎!)。
在codeproject上的文章中以及源码的头文件中有关于如何使用编码器,解码器的步骤,不多说。需要注意的是编码时使用了一个回调函数,不要忘记。
开始直接弄视频来测试,结果总是出问题,出现错位,开始以为是RGB与YUV转换的问题,于是加上转换部分的测试发现正常(但是发现了源码中的一个问题,见后面的附:),那可能是编解码器部分问题,找出decoder文件夹下的作者的一个测试解码的例子main来看了看,在程序中加了测试,发现也正常,就是出现上下翻转(这个是opencv与BMP文件的坐标系定义不一样的问题)。后来以为是动态图像问题,测试静态图像没问题了,那就只有是自己动态图像部分程序写错了,再检查。。。。。!!!!原来从视频中读取的图像大小忘了转换,不是标准的CIF或者QCIF格式,导致出现错位,而测试静态图像时进行了大小转换所以没出问题~~~~~~
修改问题,在测试。。。。。OK!恩~~~编码果然比解码花时间啊!!!!CPU使用率对比明显。
下面是测试的代码:使用VS2005+opencv2.2+VideoNet中的encoder+decoder---------程序有点乱,呵呵
#include <core.hpp> #include <highgui.hpp> #include <imgproc.hpp> #include <iostream> #include "encoder/libr263.h" #include "decoder/Tmndec.h" using namespace std; using namespace cv; #pragma comment(lib, "opencv_core220d.lib") #pragma comment(lib, "opencv_highgui220d.lib") #pragma comment(lib, "opencv_imgproc220d.lib") #define WIDTH_QCIF 176 #define HEIGHT_QCIF 144 #define WIDTH_CIF 352 #define HEIGHT_CIF 288 #ifdef WRITE #undef WRITE #endif #ifdef TESTAVI #undef TESTAVI #endif #ifdef H263 #undef H263 #endif #ifdef CIF #undef CIF #endif //定义H263时---使用类似decoder文件夹下main程序的例子测试解码器--succeed!--不过图像是反的 //不定义H263时,使用下面的测试自己的AVI或者静态图像的编解码 //#define H263 //定义TESTAVI---测试动态AVI的编码与#define WRITE配合使用-定义WRITE时写入文件, //--------------不定义WRITE时从文件中读出并播放 //不定义TESTAVI---测试静态图像的RGB与YUV420的转换 #define TESTAVI #define WRITE //定义了CIF则使用CIF大小,否则使用QCIF大小 #define CIF #ifdef CIF #define WIDTH WIDTH_CIF #define HEIGHT HEIGHT_CIF #define PARAM_FORM CPARAM_CIF #else #define WIDTH WIDTH_QCIF #define HEIGHT HEIGHT_QCIF #define PARAM_FORM CPARAM_QCIF #endif //这些值设的比较大,可能有浪费,但是如果小了就会出问题! int count=0; unsigned char cdata[20000]; int cbuffer_size=20000; unsigned char rgbdata[800000]; int buffersize=800000; //编码回调 void OwnWriteFunction(int byte) { if(::count<cbuffer_size) { cdata[::count]=(unsigned char)byte; ::count++; } } int main(int argc, char **argv) { //var Mat rgb; Mat temp[3]; VideoCapture capture; CParam EncodeParam; FILE *file; unsigned char *readdata = NULL; unsigned int yuv[WIDTH*HEIGHT*3/2]; Bits bits; //初始化 InitLookupTable(); EncodeParam.format = PARAM_FORM; InitH263Encoder(&EncodeParam); WriteByteFunction = OwnWriteFunction; InitH263Decoder(); ////////////////////////////////////////////// #ifdef H263 FILE *fin, *fout; uchar data[5000]; fin = fopen("h263","rb"); fout = fopen("my.263","wb"); int h263size = 2130; //----decoder中给出的h263文件的大小---根据自己的可以设置 int n = fread(data, sizeof(char), h263size, fin); if(n<h263size) { cout<<"read total size less than 2130, is: "<<n<<endl; goto over; } int ret=DecompressFrame(data, h263size, rgbdata, 800000); if(!ret) { cout<<"decompose failed...."<<endl; goto over; } { Mat h263(HEIGHT_QCIF, WIDTH_QCIF, CV_8UC3, rgbdata); imwrite("h263.bmp", h263); Mat tt = imread("d:\\picture\\123.bmp"); resize(tt, temp[0], cv::Size(WIDTH_QCIF, HEIGHT_QCIF)); ConvertRGB2YUV(WIDTH_QCIF, HEIGHT_QCIF, temp[0].data, yuv); ::count = 0; EncodeParam.format = CPARAM_QCIF; EncodeParam.inter = CPARAM_INTRA; EncodeParam.Q_intra = 8; EncodeParam.data = yuv; CompressFrame(&EncodeParam, &bits); cout<<"the size is: "<<(::count)<<endl; fwrite(cdata, ::count, 1, fout); } fclose(fin); fclose(fout); #else ////////////////////////////////////////////// #ifdef WRITE file = fopen("video.263", "wb"); #else file = fopen("video.263", "rb"); #endif capture.open("d:\\video\\petsc1.avi"); if (!capture.isOpened()) { cout<<"open camera failed"<<endl; return -1; } temp[1] = imread("d:\\picture\\123.bmp"); if(temp[1].empty()) { cout<<"read img failed!"<<endl; goto over; } namedWindow("show", CV_WINDOW_AUTOSIZE); namedWindow("decom", CV_WINDOW_AUTOSIZE); while( waitKey(30) != 27 ) { capture>>rgb; if (rgb.empty()) { cout<<"grab no frames, exit"<<endl; break; } #ifdef TESTAVI resize(rgb, temp[0], cv::Size(WIDTH, HEIGHT)); #else resize(temp[1], temp[0], cv::Size(WIDTH, HEIGHT)); #endif imshow("show", temp[0]); #ifdef TESTAVI //----write or read avi frames #ifdef WRITE //---write compressed frames to file ConvertRGB2YUV(WIDTH, HEIGHT, temp[0].data, yuv); ::count = 0; EncodeParam.format = PARAM_FORM; EncodeParam.inter = CPARAM_INTRA; EncodeParam.Q_intra = 8; EncodeParam.data = yuv; CompressFrame(&EncodeParam, &bits); cout<<"count num: "<<::count<<endl; size_t n = fwrite(&(::count), 4, 1, file); if(n==0) { cout<<"count write 0 byte, failed"<<endl; goto over; } n = fwrite(cdata, ::count, 1, file); if(n==0) { cout<<"data write 0 byte, failed"<<endl; goto over; } #else //read stored data and decompress then show int bytecount = 0; size_t n = fread(&bytecount, 4, 1, file); if(n==0) { cout<<"count read 0 byte, failed"<<endl; goto over; } cout<<"read byte size: "<<bytecount<<endl; readdata = new unsigned char[bytecount+1]; n = fread(readdata, bytecount, 1, file); if(n==0) { cout<<"data read 0 byte, failed"<<endl; goto over; } DecompressFrame(readdata, bytecount, rgbdata, buffersize); Mat decodeimg(HEIGHT, WIDTH, CV_8UC3, rgbdata); imshow("decom", decodeimg); delete []readdata; readdata = NULL; #endif //---for #ifdef WRITE end #else // not define TESTAVI------for the test of static convert functions ConvertRGB2YUV(WIDTH, HEIGHT, temp[0].data, yuv); readdata = new unsigned char[WIDTH*HEIGHT*3/2]; memset(readdata, 0, WIDTH*HEIGHT*3/2); for(int i=0; i<WIDTH*HEIGHT*3/2; ++i) readdata[i] = (unsigned char)yuv[i]; uchar *y = readdata; uchar *u = y+WIDTH*HEIGHT; uchar *v = u+WIDTH*HEIGHT/4; ConvertYUV2RGB(y, u, v, rgbdata, WIDTH, HEIGHT); Mat decodeimg(HEIGHT, WIDTH, CV_8UC3, rgbdata); imshow("decom", decodeimg); delete [] readdata; readdata = NULL; #endif //----for #ifdef TESTAVI end } fclose(file); cvDestroyAllWindows(); #endif //--for #ifdef H263 end over: cout<<"exit......"<<endl; //release resource ExitH263Decoder(); ExitH263Encoder(&EncodeParam); system("pause"); return 0; }
附:在VideoNet的源码convert.cpp中,ConvertRGB2YUV函数,开始时动态new的是数组:
uu=new unsigned int[w*h]; vv=new unsigned int[w*h];
但是最后:
delete uu; delete vv;
却没有使用使用正确的释放方法,虽然貌似也没多大问题,但是总归是不对的。改为如下就OK了:
delete [] uu; delete [] vv;