今天开始学习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;