本帖最后由 绿萝晨梦 于 2017-8-31 15:24 编辑
mjpg-streamer二次开发之网络摄像头的降分辨率
nanopi拿到手已经三个星期了,刚拿到手的时候根据网上的资料学了一些基本操作,对nanopi的功能和软硬件构成有了一个整体的认识。资料适合刚入手板子的同学看看,网址如下:http://wiki.friendlyarm.com/wiki/index.php/NanoPi_NEO_Plus2/zh
跟着网上的介绍,随便接了和USB摄像头,运行起mjpg-streamer,就成功在浏览器看到图像了。但是由于家里网速太差,所以无线实时图传有明显的卡顿,因此想先研究一下mjpg-streamer的源码,找到采集的图像数据,做隔行隔列去掉处理,就可以将图像的分辨率降低一半了,这样有助于提高图传帧率,也有助于一些复杂图像算法的实时运行。当然也可以直接修改jpeg的图像质量,也可以达到提高帧率的效果。
因为是为了实现无线图传,因此首先介绍一下nanopi的连接wifi过程。
将USB转串口小板连好,然后在linux终端输入sudo su,进入root模式,然后输入minicom,就可以和板子串口通信了。开机完成后,串口显示入下图所示:
输入sudo nmcli dev,可以看到板子的设备状态信息。
输入sudo nmcli r wifi on,打开wifi。
输入sudo nmcli dev wifi,显示wifi列表
输入sudo nmcli dev wifi connect “wifi名儿” password “wifi密码”,提示连接成功如下图所示。
以后重启就会自动连接上此wifi,重启后的效果如下图,可以看到出现了IP:192.168.137.210
有了IP地址就可以通过ssh连接板子敲终端了,ssh和串口能实现的功能一样,但是ssh的使用体验会比串口好很多,主要体现在排版的配色方面。高端大气的开机界面如下:
下面言归正传,如图所示是mjpg-streamer的源码目录。
mjpg-streamer文件夹下有两个十分重要的文件夹,一是plugins,包含了各类功能插件具体如何实现的代码,二是www,包含了前端网页的显示代码。
关于对整个源码的入门解读,请参考:http://blog.csdn.net/pengrui18/article/details/8146814
如果想将摄像头数据直接隔行隔列删除,直接到进入plugins下的input_uvc目录,执行vim output_uvc.c进行修改源码。
后面加了斜杠部分和两段斜杠中间的部分是添加的代码,摄像头原始yuv数据在pcontext->videoIn->framebuffer缓冲区内。一颗像素的yuv数据由四个字节组成,而不是两个字节,关于yuv和rgb的区别可以从百度学习。本代码将摄像头采集的1280*720图像转换为了320*180的图像,只需要每隔四列删掉三列,每隔四行删掉三行即可。然后通过compress_yuv_to_jpeg函数,将yuv图像转换为jpeg图像,通过以太网发送数据包进行显示。
#define WID 320///
#define HEI 180///
#define N1 4////
#define N2 16////
void*cam_thread(void *arg)
{
unsigned int i=0,j=0;///////////
unsigned char*yuv,*low_yuv,*my_buf;//////////////
context*pcontext = arg;
pglobal =pcontext->pglobal;
pthread_cleanup_push(cam_cleanup, pcontext);
while(!pglobal->stop) {
while(pcontext->videoIn->streamingState ==STREAMING_PAUSED) {
usleep(1);
}
if(uvcGrab(pcontext->videoIn) < 0) {
IPRINT("Error grabbing framesn");
exit(EXIT_FAILURE);
}
DBG("received frame of size: %d from plugin: %dn",pcontext->videoIn->buf.bytesused, pcontext->id);
if(pcontext->videoIn->buf.bytesused
DBG("dropping too small frame, assuming it a***rokenn");
}
pthread_mutex_lock(&pglobal->in[pcontext->id].db);
/////////////////////////////////////////////////////////////////////////////////////////////
yuv=pcontext->videoIn->framebuffer;
my_buf=calloc(WID*2*HEI, 1);
low_yuv=my_buf;
for(j=0;j<720;j++)
{
for(i=0;i<1280*2;i++)
{
if((i%N2==0 || i%N2==1 || i%N2==2 || i%N2==3)&& j%N1==0) {*low_yuv=*yuv;low_yuv++;}
*yuv=0;
yuv++;
}
}
low_yuv=my_buf;
yuv=pcontext->videoIn->framebuffer;
my_buf=calloc(WID*2*HEI, 1);
low_yuv=my_buf;
for(j=0;j<720;j++)
{
for(i=0;i<1280*2;i++)
{
if((i%N2==0 || i%N2==1 || i%N2==2 || i%N2==3)&& j%N1==0) {*low_yuv=*yuv;low_yuv++;}
*yuv=0;
yuv++;
}
}
low_yuv=my_buf;
yuv=pcontext->videoIn->framebuffer;
for(j=0;j
{
for(i=0;i
{
*yuv=*low_yuv;
yuv++;
low_yuv++;
}
}
pcontext->videoIn->width=WID;
pcontext->videoIn->height=HEI;
/////////////////////////////////////////////////////////////////////////////////////////////
if(pcontext->videoIn->formatIn !=V4L2_PIX_FMT_MJPEG) {
DBG("compressing frame from input: %dn", (int)pcontext->id);
pglobal->in[pcontext->id].size =compress_yuv_to_jpeg(pcontext->videoIn,pglobal->in[pcontext->id].buf,WID*2*HEI/*pcontext->videoIn->framesizeIn*/,gquality, pcontext->videoIn->formatIn);
} else {
DBG("copying frame from input: %dn", (int)pcontext->id);
pglobal->in[pcontext->id].size =memcpy_picture(pglobal->in[pcontext->id].buf,pcontext->videoIn->tmpbuffer, pcontext->videoIn->buf.bytesused);
}
free(my_buf);///////
...
}
最后在mjpg_streamer目录下,vim start.sh,将分辨率改小成320x180,帧率和图像质量可以看情况更改:
因为此处的分辨率改小了,但是实际上摄像头发出的数据还是1280*720的,因此在input_uvc.c的input_init函数中,将参数需要改为原始分辨率的大小,否则摄像头数据的采集可能会出错。
if(init_videoIn(cams[id].videoIn, dev,1280,720,/*width, height,*/ fps, format, 1, cams[id].pglobal, id) < 0) { IPRINT("init_VideoIn failedn"); closelog(); exit(EXIT_FAILURE);}
这是最终的实验效果,此界面的图像预览显示尺寸可以通过修改www目录下的stram_simple.html和style.css进行修改。可以观察到虽然画面变模糊了,但是流畅度有了大大提高。
最后,展示炫酷无敌的实物图,想不到nanopi简直是个无线图传利器呢。既然都知道图像数据保存的位置了,那么图像算法还会远吗?