创客神器NanoPi
直播中

罗伟

8年用户 12经验值
擅长:嵌入式技术 处理器/DSP 控制/MCU
私信 关注
[经验]

【NanoPi NEO Plus2开发板试用体验】mjpg-streamer二次开发

本帖最后由 绿萝晨梦 于 2017-8-31 15:24 编辑

mjpg-streamer二次开发之网络摄像头的降分辨率

10.jpg
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,就可以和板子串口通信了。开机完成后,串口显示入下图所示:
1.png
输入sudo nmcli dev,可以看到板子的设备状态信息。
输入sudo nmcli r wifi on,打开wifi。
输入sudo nmcli dev wifi,显示wifi列表
2.png
输入sudo nmcli dev wifi connect “wifi名儿” password “wifi密码”,提示连接成功如下图所示。
3.png
以后重启就会自动连接上此wifi,重启后的效果如下图,可以看到出现了IP:192.168.137.210
4.png
有了IP地址就可以通过ssh连接板子敲终端了,ssh和串口能实现的功能一样,但是ssh的使用体验会比串口好很多,主要体现在排版的配色方面。高端大气的开机界面如下:
5.png
下面言归正传,如图所示是mjpg-streamer的源码目录。
mjpg-streamer文件夹下有两个十分重要的文件夹,一是plugins,包含了各类功能插件具体如何实现的代码,二是www,包含了前端网页的显示代码。
关于对整个源码的入门解读,请参考:http://blog.csdn.net/pengrui18/article/details/8146814
如果想将摄像头数据直接隔行隔列删除,直接到进入plugins下的input_uvc目录,执行vim output_uvc.c进行修改源码。
6.png
后面加了斜杠部分和两段斜杠中间的部分是添加的代码,摄像头原始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");
            continue;
        }
       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,帧率和图像质量可以看情况更改:
8.png
因为此处的分辨率改小了,但是实际上摄像头发出的数据还是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进行修改。可以观察到虽然画面变模糊了,但是流畅度有了大大提高。
9.png
最后,展示炫酷无敌的实物图,想不到nanopi简直是个无线图传利器呢。既然都知道图像数据保存的位置了,那么图像算法还会远吗?



更多回帖

×
20
完善资料,
赚取积分