Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3534588
  • 博文数量: 864
  • 博客积分: 14125
  • 博客等级: 上将
  • 技术积分: 10634
  • 用 户 组: 普通用户
  • 注册时间: 2007-07-27 16:53
个人简介

https://github.com/zytc2009/BigTeam_learning

文章分类

全部博文(864)

文章存档

2023年(1)

2021年(1)

2019年(3)

2018年(1)

2017年(10)

2015年(3)

2014年(8)

2013年(3)

2012年(69)

2011年(103)

2010年(357)

2009年(283)

2008年(22)

分类: C/C++

2010-03-16 10:32:36

发表于: 2007-02-08 17:09    发表主题: 请教:QT图片循环显示,闪烁如何优化?谢谢 

--------------------------------------------------------------------------------

我刚刚接触QT,本应该从头慢慢学起的,但目前有一个问题急于解决,还望各位高手不吝赐教一下。

为了使几副图片循环显示,在paintEvent里加了几副图片的显示调用painter.drawPixmap();并在主窗口里加了一个Timer,timeout触发repaint();

虽然可以实现,但效果不理想,刷新时,感觉图片会有闪烁。

查了资料
可以利用double buffer缓存方法解决,但我却看不明白,能否指导一下,谢谢。

double buffer是不是指开一个新的空间,提前将所要显示的内容存储进去,然后再进行显示?因为我所要显示的是透明背景的图片,是否需要将背景和图片先进行叠加,然后再进行显示?如果需要叠加,那应该调用什么函数?
盼复,谢谢。


大致的代码,可以缩减为如下:

#include "t1111dialog.h"
#include
#include

t1111Dialog::t1111Dialog( QWidget* parent, const char* name, bool modal, WFlags f )
: t1111DialogBase( parent, name, modal, f )
{
num = 0;
// Add your code
QTimer *movieTimer = new QTimer( this ); // create internal timer
connect( movieTimer, SIGNAL(timeout()), SLOT(timeout()) );
movieTimer->start( 500 ); // emit signal every 5 seconds
}


void t1111Dialog::timeout()
{
repaint();
}


void t1111Dialog::paintEvent( QPaintEvent *evt )
{
QPainter painter;
QPixmap image1("PICTURE-01.png");
QPixmap image2("PICTURE-02.png");
QPixmap image3("PICTURE-03.png");
QPixmap image4("PICTURE-04.png");

painter.begin(this);


switch(num){
case 0:
painter.drawPixmap(0,0,image1);
break;
case 1:
painter.drawPixmap(0,0,image2);
break;
case 2:
painter.drawPixmap(0,0,image3);
break;
case 3:
painter.drawPixmap(0,0,image4);
break;
default:
break;
}
painter.end();
num++;
if( num >= 4 )
num = 0;


}

double buffer建议你看看C++ Programming with Qt 3/4
里面都有专门的小节讲这个

你是想做动画?
想不闪烁建议用update
不要用repaint
这部分看看文档吧


谢谢站长的回复.

我去找下C++ Programming with Qt 3/4看一下.

但另外的你所指的文档,是指?QT 类的说明吗?

或者是否可以提供个网址给我,谢谢了.

我是想做动画,显示的图片是透明背景的,因为刚接触QT,所以调用的函数也显得很局限.

我原先一开始就是用update(),后来改用repaint()感觉没太大区别,呵呵.

另外,我用的是QDialog,这个跟用QWidget在调用double buffer处理闪烁问题应该没什么区别吧?

话罗嗦了点,烦请指教.

好好看看QWidget中有关paint(), update(), repaint()的介绍吧
Double Buffering 


Flicker闪烁 can be completely avoided if there is no delay延迟 between painting the background and painting the content. This can be achieved完成 by rendering 着色 the background into a pixmap 象素映射, then painting the content onto that pixmap, and finally painting the pixmap onto the widget. This technique is known as double buffering.

The quick and easy way to do this is to create a pixmap that's the same size as the rectangle that needs to be repainted. Then paint everything into the pixmap and finally paint the pixmap onto the widget. While doing this is completely flicker-free, it means paying a price in memory consumption and processing. For example, you would need to allocate and deallocate a pixmap of width × height × color-depth bits in every paint event, and this is a lot of memory if the widget gets maximized on a 32-bpp (bits per pixel) 1280 × 1024 screen. It is possible to reuse the same pixmap to save reallocations, but it would need to be the maximum possible size of the widget, so it will still consume a big chunk大块 of memory.

A neat灵巧的 solution is to combine double buffering with stripes条纹. Instead of storing the whole widget's rectangle in a pixmap, we can store one line instead. We then paint the background and content of a single line at a time and copy the pixmap back to the widget for each line that's painted. This requires a pixmap that's only line-width × line-height × color-depth bits in size; a lot less than the whole widget. We can also reuse the same pixmap, resizing it to be larger if necessary.

Here's another version of the paintEvent() we wrote earlier, this time using double buffering, reusing the pixmap, and painting in stripes.

    void MyWidget::paintEvent(QPaintEvent *evt)
    {
        // any initialization
        QPainter painter(this);
        QRegion unpainted(evt->clipRegion());
        static QPixmap *doubleBuffer = 0;
        if (!doubleBuffer)
            doubleBuffer = new QPixmap;
        QPainter dbPainter(doubleBuffer);

    for (int i = 0; i < lines.count(); i++) {
            Line *line = lines;
            doubleBuffer->resize(QMAX(doubleBuffer->width(), line->boundingRect().width()),
                 QMAX(doubleBuffer->height(), line->boundingRect().height()));
            doubleBuffer->fill(backgroundColor);
            line->draw(&dbPainter);
            painter.drawPixmap(0, line->boundingRect().y(), *doubleBuffer, 0, 0,
                              line->boundingRect().width(), line->boundingRect().height());
            unpainted -= line->boundingRect();
        }
        painter.setClipRegion(unpainted);
        painter.fillRect(unpainted.boundingRect(), backgroundColor);
    }
    We reuse the pixmap by declaring it as a static pointer and allocating it if it is 0 (i.e. the first time we paint). Instead of painting directly on the widget, we paint on the pixmap. We must ensure that the pixmap is large enough each time we use it, resizing it if necessary. The painting code is almost the same as before because Qt provides a device-independent painting API. We just have to copy the pixmap to the widget after painting, using drawPixmap(). (We could have used bitBlt(), but drawPixmap() is faster if the painter is already open.)

This technique completely eliminates flicker, without consuming too much memory. Qt uses this approach in many standard widgets, including QTextEdit, QListView and QMenuBar. In fact, Qt applies a further memory optimization. Instead of allocating a double buffer pixmap for every single widget, Qt allocates a global pixmap for every type of widget, no matter how many instances of that widget are used.
 
Video4Linux 编程获取数据。
  现有的video4linux有两个版本,v4l和v4l2。本文主要是关于v4l的编程。
  利用v4l API获取视频图像一般有以下几步:
    a>  打开设备
    b>  设置设备的属性,比如图像的亮度,对比度等等
    c>  设定传输格式和传输方式
    d>  开始传输数据,一般是一个循环,用以连续的传输数据
    e>  关闭设备
    下面具体介绍v4l编程的过程。首先指出,
    在video4linux编程时要包含头文件,
    其中包含了video4linux的数据结构和函数定义。
  1)v4l的数据结构
    在video4linux API中定义了如下数据结构,详细的数据结构定义可以参考v4l API的文档,
    这里就编程中经常使用的数据结构作出说明。
    首先我们定义一个描述设备的数据结构,它包含了v4l中定义的所有数据结构:
      typedef struct _v4ldevice
      {
int fd;    //设备号
struct video_capability capability;
struct video_channel channel[10];
struct video_picture picture;
struct video_clip clip;
struct video_window window;
struct video_capture capture;
struct video_buffer buffer;
struct video_mmap mmap;
struct video_mbuf mbuf;
struct video_unit unit;
unsigned char *map;  //mmap方式获取数据时,数据的首地址
pthread_mutex_t mutex;
int frame;
int framestat[2];
int overlay;
      }v4ldevice;
      下面解释上面这个数据结构中包含的数据结构,这些结构的定义都在中。
      * struct video_capability
        name[32]  Canonical name for this interface
        type  Type of interface
        channels  Number of radio/tv channels if appropriate
        audios  Number of audio devices if appropriate
        maxwidth  Maximum capture width in pixels
        maxheight  Maximum capture height in pixels
        minwidth  Minimum capture width in pixels
        minheight  Minimum capture height in pixels
      这一个数据结构是包含了摄像头的属性,name是摄像头的名字,
      maxwidth maxheight是摄像头所能获取的最大图像大小,用橡素作单位。
      在程序中,通过ioctl函数的VIDIOCGCAP控制命令读写设备通道已获取这个结构,
      有关ioctl的使用,比较复杂,这里就不说了。下面列出获取这一数据结构的代码:
int v4lgetcapability(v4ldevice *vd)
{
  if(ioctl(vd->fd, VIDIOCGCAP, &(vd->capability)) < 0)
  {
      v4lperror("v4lopen:VIDIOCGCAP");
      return -1;
  }
  return 0;
}
      * struct video_picture
        brightness    Picture brightness
        hue            Picture hue (colour only)
        colour        Picture colour (colour only)
        contrast      Picture contrast
        whiteness    The whiteness (greyscale only)
        depth        The capture depth (may need to match the frame buffer depth)
        palette      Reports the palette that should be used for this image
      这个数据结构主要定义了图像的属性,诸如亮度,对比度,等等。
      这一结构的获取通过ioctl发出VIDIOCGPICT控制命令获取。
      * struct    video_mbuf
        size      The number of bytes to map
        frames    The number of frames
        offsets  The offset of each frame
      这个数据结构在用mmap方式获取数据时很重要:
      size表示图像的大小,如果是640*480的彩色图像,size=640*480*3
      frames表示帧数
      offsets表示每一帧在内存中的偏移地址,通过这个值可以得到数据在图像中的地址。
      得到这个结构的数据可以用ioctl的VIDIOCGMBUF命令。源码如下:
int v4lgetmbuf(v4ldevice *vd)
{
  if(ioctl(vd->fd, VIDIOCGMBUF, &(vd->mbuf))<0)
  {
    v4lperror("v4lgetmbuf:VIDIOCGMBUF");
    return -1;
  }
  return 0;
}
  而数据的地址可以有以下方式计算:
unsigned char *v4lgetaddress(v4ldevice *vd)
{
    return (vd->map + vd->mbuf.offsets[vd->frame]);
}
2)获取影像mmap方式。
      在video4linux下获取影像有两种方式:overlay和mmap。由于我的摄像头不支持overlay方式,所以这里只谈mmap方式。
      mmap方式是通过内存映射的方式获取数据,系统调用ioctl的VIDIOCMCAPTURE后,将图像映射到内存中,
      然后可以通过前面的v4lgetmbuf(vd)函数和v4lgetaddress(vd)函数获得数据的首地址,
      这是李可以选择是将它显示出来还是放到别的什么地方。
      下面给出获取连续影像的最简单的方法(为了简化,将一些可去掉的属性操作都去掉了):
  char* devicename="/dev/video0";
  char* buffer;
  v4ldevice device;
  int width = 640;
  int height = 480;
  int frame = 0;
  v4lopen("/dev/video0",&device);    //打开设备
  v4lgrabinit(&device,width,height); //初始化设备,定义获取的影像的大小
  v4lmmap(&device);                  //内存映射
  v4lgrabstart(&device,frame);      //开始获取影像
  while(1)
  {
    v4lsync(&device,frame);          //等待传完一帧
    frame = (frame+1)%2;              //下一帧的frame
    v4lcapture(&device,frame);        //获取下一帧
    buffer = (char*)v4lgetaddress(&device);//得到这一帧的地址
    //buffer给出了图像的首地址,你可以选择将图像显示或保存......
    //图像的大小为 width*height*3
  ..........................
  }
为了更好的理解源码,这里给出里面的函数的实现,这里为了简化,将所有的出错处理都去掉了。
int v4lopen(char *name, v4ldevice *vd)
{
  int i;
  if((vd->fd = open(name,O_RDWR)) < 0)
  {
      return -1;
  }
  if(v4lgetcapability(vd))
      return -1;
}
int v4lgrabinit(v4ldevice *vd, int width, int height)
{
  vd->mmap.width = width;
  vd->mmap.height = height;
  vd->mmap.format = vd->picture.palette;
  vd->frame = 0;
  vd->framestat[0] = 0;
  vd->framestat[1] = 0;
  return 0;
}
int v4lmmap(v4ldevice *vd)
{
  if(v4lgetmbuf(vd)<0)
      return -1;
  if((vd->map = mmap(0, vd->mbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, vd->fd, 0)) < 0)
  {
      return -1;
  }
  return 0;
}
int v4lgrabstart(v4ldevice *vd, int frame)
{
  vd->mmap.frame = frame;
  if(ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0)
  {
      return -1;
  }
  vd->framestat[frame] = 1;
  return 0;
}
int v4lsync(v4ldevice *vd, int frame)
{
  if(ioctl(vd->fd, VIDIOCSYNC, &frame) < 0)
  {
      return -1;
  }
  vd->framestat[frame] = 0;
  return 0;
}
int c4lcapture(v4ldevice *vd, int frame)
{
  vd->mmap.frame = frame;
  if(ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0)
  {
      return -1;
  }
  vd->framestat[frame] = 1;
  return 0;
}
Video4Linux 编程获取数据。
  现有的video4linux有两个版本,v4l和v4l2。本文主要是关于v4l的编程。
  利用v4l API获取视频图像一般有以下几步:
    a>  打开设备
    b>  设置设备的属性,比如图像的亮度,对比度等等
    c>  设定传输格式和传输方式
    d>  开始传输数据,一般是一个循环,用以连续的传输数据
    e>  关闭设备
    下面具体介绍v4l编程的过程。首先指出,
    在video4linux编程时要包含头文件,
    其中包含了video4linux的数据结构和函数定义。
  1)v4l的数据结构
    在video4linux API中定义了如下数据结构,详细的数据结构定义可以参考v4l API的文档,
    这里就编程中经常使用的数据结构作出说明。
    首先我们定义一个描述设备的数据结构,它包含了v4l中定义的所有数据结构:
      typedef struct _v4ldevice
      {
int fd;    //设备号
struct video_capability capability;
struct video_channel channel[10];
struct video_picture picture;
struct video_clip clip;
struct video_window window;
struct video_capture capture;
struct video_buffer buffer;
struct video_mmap mmap;
struct video_mbuf mbuf;
struct video_unit unit;
unsigned char *map;  //mmap方式获取数据时,数据的首地址
pthread_mutex_t mutex;
int frame;
int framestat[2];
int overlay;
      }v4ldevice;
      下面解释上面这个数据结构中包含的数据结构,这些结构的定义都在中。
      * struct video_capability
        name[32]  Canonical name for this interface
        type  Type of interface
        channels  Number of radio/tv channels if appropriate
        audios  Number of audio devices if appropriate
        maxwidth  Maximum capture width in pixels
        maxheight  Maximum capture height in pixels
        minwidth  Minimum capture width in pixels
        minheight  Minimum capture height in pixels
      这一个数据结构是包含了摄像头的属性,name是摄像头的名字,
      maxwidth maxheight是摄像头所能获取的最大图像大小,用橡素作单位。
      在程序中,通过ioctl函数的VIDIOCGCAP控制命令读写设备通道已获取这个结构,
      有关ioctl的使用,比较复杂,这里就不说了。下面列出获取这一数据结构的代码:
int v4lgetcapability(v4ldevice *vd)
{
  if(ioctl(vd->fd, VIDIOCGCAP, &(vd->capability)) < 0)
  {
      v4lperror("v4lopen:VIDIOCGCAP");
      return -1;
  }
  return 0;
}
      * struct video_picture
        brightness    Picture brightness
        hue            Picture hue (colour only)
        colour        Picture colour (colour only)
        contrast      Picture contrast
        whiteness    The whiteness (greyscale only)
        depth        The capture depth (may need to match the frame buffer depth)
        palette      Reports the palette that should be used for this image
      这个数据结构主要定义了图像的属性,诸如亮度,对比度,等等。
      这一结构的获取通过ioctl发出VIDIOCGPICT控制命令获取。
      * struct    video_mbuf
        size      The number of bytes to map
        frames    The number of frames
        offsets  The offset of each frame
      这个数据结构在用mmap方式获取数据时很重要:
      size表示图像的大小,如果是640*480的彩色图像,size=640*480*3
      frames表示帧数
      offsets表示每一帧在内存中的偏移地址,通过这个值可以得到数据在图像中的地址。
      得到这个结构的数据可以用ioctl的VIDIOCGMBUF命令。源码如下:
int v4lgetmbuf(v4ldevice *vd)
{
  if(ioctl(vd->fd, VIDIOCGMBUF, &(vd->mbuf))<0)
  {
    v4lperror("v4lgetmbuf:VIDIOCGMBUF");
    return -1;
  }
  return 0;
}
  而数据的地址可以有以下方式计算:
unsigned char *v4lgetaddress(v4ldevice *vd)
{
    return (vd->map + vd->mbuf.offsets[vd->frame]);
}
2)获取影像mmap方式。
      在video4linux下获取影像有两种方式:overlay和mmap。由于我的摄像头不支持overlay方式,所以这里只谈mmap方式。
      mmap方式是通过内存映射的方式获取数据,系统调用ioctl的VIDIOCMCAPTURE后,将图像映射到内存中,
      然后可以通过前面的v4lgetmbuf(vd)函数和v4lgetaddress(vd)函数获得数据的首地址,
      这是李可以选择是将它显示出来还是放到别的什么地方。
      下面给出获取连续影像的最简单的方法(为了简化,将一些可去掉的属性操作都去掉了):
  char* devicename="/dev/video0";
  char* buffer;
  v4ldevice device;
  int width = 640;
  int height = 480;
  int frame = 0;
  v4lopen("/dev/video0",&device);    //打开设备
  v4lgrabinit(&device,width,height); //初始化设备,定义获取的影像的大小
  v4lmmap(&device);                  //内存映射
  v4lgrabstart(&device,frame);      //开始获取影像
  while(1)
  {
    v4lsync(&device,frame);          //等待传完一帧
    frame = (frame+1)%2;              //下一帧的frame
    v4lcapture(&device,frame);        //获取下一帧
    buffer = (char*)v4lgetaddress(&device);//得到这一帧的地址
    //buffer给出了图像的首地址,你可以选择将图像显示或保存......
    //图像的大小为 width*height*3
  ..........................
  }
为了更好的理解源码,这里给出里面的函数的实现,这里为了简化,将所有的出错处理都去掉了。
int v4lopen(char *name, v4ldevice *vd)
{
  int i;
  if((vd->fd = open(name,O_RDWR)) < 0)
  {
      return -1;
  }
  if(v4lgetcapability(vd))
      return -1;
}
int v4lgrabinit(v4ldevice *vd, int width, int height)
{
  vd->mmap.width = width;
  vd->mmap.height = height;
  vd->mmap.format = vd->picture.palette;
  vd->frame = 0;
  vd->framestat[0] = 0;
  vd->framestat[1] = 0;
  return 0;
}
int v4lmmap(v4ldevice *vd)
{
  if(v4lgetmbuf(vd)<0)
      return -1;
  if((vd->map = mmap(0, vd->mbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, vd->fd, 0)) < 0)
  {
      return -1;
  }
  return 0;
}
int v4lgrabstart(v4ldevice *vd, int frame)
{
  vd->mmap.frame = frame;
  if(ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0)
  {
      return -1;
  }
  vd->framestat[frame] = 1;
  return 0;
}
int v4lsync(v4ldevice *vd, int frame)
{
  if(ioctl(vd->fd, VIDIOCSYNC, &frame) < 0)
  {
      return -1;
  }
  vd->framestat[frame] = 0;
  return 0;
}
int c4lcapture(v4ldevice *vd, int frame)
{
  vd->mmap.frame = frame;
  if(ioctl(vd->fd, VIDIOCMCAPTURE, &(vd->mmap)) < 0)
  {
      return -1;
  }
  vd->framestat[frame] = 1;
  return 0;
}

USB摄像头我现在的项目设计需要从USB摄像头读图片到开发板。
USB控制器为cy7c67300,操作系统为嵌入式LINUX,
现在需要让USB控制器支持等时传输模式,不知道该如何修改驱动。
/////////////////////////////////////////////////////
moonse给个联系方式吧,我们好一起交流
我的邮箱:
qq:3833905
USB摄像头的问题在于:
一、现在提供的代码中有部分操作是多余的,因为只用到一个PORT,
    不需要对所有的PORT操作;
二、底层硬件处理完TD_List后,上层HCD对TD数据的处理不正确,
    主要是无法得到正确的读取的数据的字节数(在ISOC模式下)
三、设备驱动程序方面,对ov511的支持不够好,
    原因在于2.4.18中提供的ov511驱动程序并不完善,需要修改。
    其中一个重要的结构是URB数据包的大小.
修改完这些以后就可以很顺利地使用ov511驱动采集视频了。
//////////////////////////////////////////////////
图像采集的实现
    图像采集程序的编写基于linux内核中提供的Video4Linux 接口。
Video4Linux是2.2.0版本之后linux内核提供给网络摄像头、视频采集卡、
电视卡等设备软件开发的接口标准。这个标准为内核、驱动、应用程序提供一个API进行交流。
目前的最新Video4Linux版本为V4L2。
    使用双URB轮流通信对于对时间敏感而对数据的正确性要求不高的图像采集应用,
USB总线定义了ISOC传输模式,USB摄像头应当使用这种传输方式。
为了尽可能快地得到图像数据,应当在URB中指定USB_ISO_ASAP标志。
urb->transfer_flags=USB_ISO_ASAP;//尽可能快地发出本URB
    Linux系统中任何USB传输都通过URB实现。为提高速度,可以考虑扩大URB的缓冲,
也可以建立两个URB,在等待一个URB被回收时,也就是图像正在被传感器采集时,
处理、初始化另一个URB,并在回收后立刻将其发出。两个URB交替使用,
大大减少了额外时间。使用内存映射并用双帧缓冲提高效率
    Linux系统通过read,write等来实现对硬件的操作,它们通过copy_to_user()、
copy_from_user()等函数在内核和用户内存空间中互相拷贝。
但是对于视频采集这类需要大量高速传输数据的应用来说,
这种方法耗费的硬件资源过大,通过内存映射的方法可以使这一问题得到有效解决。
    首先使用vmalloc()申请足够大的核态内存,将其作为图像数据缓冲空间,
两个URB带回的图像数据在这里暂存;然后使用remap_page_range()函数将其逐页映射到用户空间中。
户态的图像采集处理程序使用mmap()函数,直接读写内核图像缓冲内存,大大减少额外开销。
另外,为了进一步提高帧速率,本文采用双帧缓冲方式进行图像采集。
/////////////////////////////////////////////////////
XSBase中如何使用USB摄像头各位好!
我想在XSBase上使用USB摄像头,采集图像,摄像头芯片是ov511,内核中已经包含该驱动.
插上摄像头后,摄像头的灯也亮了,但在采集图像时,却看不到,console下出现下面的信息:
qu_queue_active_urb: add urb to iso_list, start_frame = 0x2c6, # Pkts = 0xa, err Cnt = 0x0
当cat /dev/video 时,也出现上面的信息.
请问如何使用ov511 USB摄像头?
这是因为usb host的驱动程序中不支持IOSC传输模式,
你需要增加IOSC的传输模式才可以支持usb camera
/////////////////////////////////////////////////////
Linux内核编译另外:
还想问一下斑竹,在编译内核时,为了支持usb存储,需不需要
UHCI OHCI。。。之类??
我在原有基础上只添加了:
1 SCSI support
  SCSI disk support
2 DOs FAT fs
  MSDOS fs
  VfAf fs
3.USB mess support
  preliminary usb device
不知道是否少选了某些模块???
Preliminary USB DEVICE Filesystem
USB Controllers-> CY7C67300,你的可能不一样
USB Mass Storage support
阅读(4551) | 评论(0) | 转发(1) |
0

上一篇: QT显示图片

下一篇:QT显示图片(3)

给主人留下些什么吧!~~