TQ2440 基于V4L2编程框架的 LCD实时显示(下)
绪上一篇:
本次实验为基于V4L2框架,使用USB摄像头进行图像采集,用4.3寸LCD屏进行实时显示。由于,知识缺乏未能完整的进行下去,失败在图像实时显示上面,因为要放一段时间,所以在此做次记录,希望能帮助到一些小伙伴,站在前人的路上把该问题解决掉。
一、情况说明
1、使用TQ2440开发板、4.3寸TFT LCD屏,分辨率为480*272 16bpp
2、普通USB摄像头,采集的图像格式为YUV422,分辨率为320*240
二、实验进展
1、USB摄像头可以进行图片采集,但在lcd显示时不能全屏显示,只显示lcd屏上面一半,且出现三个窗口暂时不知怎么解决
2、摄像头采集的图片为YUV422格式要在LCD屏显示需要转成RGB565格式,这个已经完成
3、由于二十多块买的摄像头其分辨率320*240,需要将其放大到480*272,这个代码已经完成
三、所遇问题
现在不知如何解决图像正确显示问题。。。。
/*************************************************以上是上一次遇到的并停下的问题********************************************************************************/
最新情况:
又经过了一个多星期,应该说放了一个多星期,就在前天突然在韦东山老师的第三个项目中找到的可以相关代码,然后参考其实现代码,我在原来的基础上改了图片显示的方式。
之前由于摄像头采集到的图像像素为320*240,而lcd显示屏像素为480*272,因此采集到的图像像素和显示屏像素是不相等的。当时,我的想法是直接在lcd上从fb0的映射地址起始处写入数据,然后就出现了之前出现的三个一个屏三个窗口的结果。无奈之下,我就想就参考下韦东山老师项目中的居中显示吧,没想到一试竟然马上成功了。如下图所示。
然后仔细看代码,以及修改了一些代码进行测试,终于找出了以前困扰我许久的显示问题:为什么会出现三个或多个窗口?原来我之前是吧一张像素为320*240的图像,将其数据从其buf[0]->buf[Max_size]一股脑的直接写入fb映射的显存中去,而没有分行分行的去显示所采集图像的内容,这就导致原本下一行的像素数据直接填充到了这一行,这样自然会出现这些错位,多窗口的现象。
以前的代码:
memcpy(fb_addr,buf[index].start,buf[index].length) //这是错误的,将导致一开始的多窗口效果
修改之后:
for (i = 0; i <camdev->cHeight; i++) //camdev->cHeight为采集到的图像高度
{
//这里每次只拷贝一行的像素数据
memcpy(pucDst, pucSrc, camdev->cLineBytes);
pucSrc += camdev->cLineBytes;//到下一行取数据
pucDst += myfb->iLineBytes;//到下一行显示
}
修改代码后便可正常显示。下面这张截图是320*240像素放大到480*272像素的图像显示效果,图像放大,采用了最近邻插值法,这是最简单,但效果最不好的一种方法,所以图片会有点模糊,但已经可以完成由摄像头采集图像信息,并在lcd实时显示了。
以下是部分核心代码:
/* *函数功能:图像处理并显示 */ void display_pic(Cam_dev *camdev,Fb_dev *myfb) { struct v4l2_buffer buf; unsigned char* ptmp; unsigned long i,j; CLEAR (buf); buf.memory = V4L2_MEMORY_MMAP; buf.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; /*取出一缓冲帧*/ if (-1 == ioctl(camdev->cfd, VIDIOC_DQBUF, &buf)) { printf(" VIDIOC_DQBUF error!\n"); exit(-1); } /*检查缓冲帧是否有效*/ assert(buf.index < 4); /*图片格式转换:YUV422->RGB565*/ ptmp = (unsigned char*)buffers[buf.index].start; Pyuv422torgb565(ptmp,camdev->displaybuf,camdev->cHeight,camdev->cWidth); /*图像放大后LCD显示*/ PicZoomF(camdev,myfb); /*小图像居中显示*/ //PicMerge(camdev,myfb); if (-1 == ioctl(camdev->cfd, VIDIOC_QBUF, &buf)) { printf("VIDIOC_QBUF error!\n"); exit(-1); } } /* *函数功能:居中显示小的图像 */ int PicMerge(Cam_dev *camdev,Fb_dev *myfb) { int i; int iX,iY; unsigned char *pucSrc; unsigned char *pucDst; iX = (myfb->iWidth -camdev->cWidth )/2; iY = (myfb->iHeight -camdev->cHeight )/2; //指向yuv422->RGB565转换后的图像起始位置 pucSrc = camdev->displaybuf; //计算空出来的起始的空格 pucDst = myfb->fb_addr + iY * myfb->iLineBytes + iX *myfb->iBpp/8; for (i = 0; i <camdev->cHeight; i++) { //这里每次只拷贝一行的像素数据 memcpy(pucDst, pucSrc, camdev->cLineBytes); pucSrc += camdev->cLineBytes;//到下一行取数据 pucDst += myfb->iLineBytes;//到下一行显示 } return 0; } /* *函数功能:进行图像缩放并显示 */ int PicZoomF(Cam_dev *camdev,Fb_dev *myfb) { unsigned long dwDstWidth = myfb->iWidth; //LCD一行像素宽度 unsigned long* pdwSrcXTable; unsigned long x; unsigned long y; unsigned long dwSrcY; unsigned char *pucDest; unsigned char *pucSrc; unsigned long dwPixelBytes =myfb->iBpp/2; //每个像素字节数 //创建了放一行像素的空间 pdwSrcXTable = malloc(sizeof(unsigned long) * dwDstWidth); if (NULL == pdwSrcXTable) { printf("malloc error!\n"); return -1; } //生成表 pdwSrcXTable for (x = 0; x < dwDstWidth; x++) { pdwSrcXTable[x]=(x*camdev->cWidth/myfb->iWidth); } //进行列的缩放 for (y = 0; y < myfb->iHeight; y++) { dwSrcY = (y * camdev->cHeight / myfb->iHeight); //目标图像的第x行起始地址 pucDest = myfb->fb_addr + y*myfb->iLineBytes; //原始图像的第x行起始地址 pucSrc = camdev->displaybuf + dwSrcY*camdev->cLineBytes; for (x = 0; x <dwDstWidth; x++) { /* 原图座标: pdwSrcXTable[x],srcy * 缩放座标: x, y */ memcpy(pucDest+x*dwPixelBytes, pucSrc+pdwSrcXTable[x]*dwPixelBytes, dwPixelBytes); } } free(pdwSrcXTable); return 0; } |