2012-04-15 85 views
9

我正在开发一个交互式应用程序,它需要一次读取和处理几张非常大的图像(每次25张图像,总大小约350 Mb)。 OpenCV非常快速,相对容易处理算法。但用Qt绘制它们被证明是一个问题。这是我尝试过的两种不太理想的解决方案。Qt和OpenCV的高效集成

解决方案1(太慢)

每当你需要绘制不同的OpenCV的图像时,将其转换为 QImage和绘制。不幸的是,转换需要一段时间,我们无法以交互式速度在图像之间切换。

溶液2(太内存密集型)

保持图像的两个堆叠,一个用于OpenCV的,一个用于Qt的。在适当的时候使用合适的 。

我可以直接访问OpenCV像素数据。我知道图像的宽度和高度,我知道像素是3字节的RGB值。似乎应该可以快速绘制OpenCV映像,而不需要将其复制到只能包含数据副本的QImage容器(尽我所知)。

我需要从Qt中获得这种能力吗?

回答

5

您可以在QImage和openCV之间共享数据 - 它们都有一个使用现有数据的ctor - 由指针提供。

cv::Mat(int _rows, int _cols, int _type, void* _data, size_t _step=AUTO_STEP)
QImage (uchar * data, int width, int height, int bytesPerLine, Format format)

如果行最终不会成为4字节的整数倍,但我希望填充到这两种类型具有相同的像素尺寸对准有可能是与填充的问题 - 在至少在同一个硬件上

一个问题是,openCV默认使用BGR,这对于QImage(或任何其他显示器)来说并不是非常理想的。尽管我不确定QImage :: Format_ARGB32_Premultiplied在使用加速的OpenGL渲染QImage的Qt上是否会更快。

另一种方法是使用opencv,然后将结果数据直接复制到openGL纹理,然后使用QGlWidget在没有其他副本的情况下显示图像。

+0

BGR的东西肯定是讨厌的;我们现在要求OpenCV以BGR格式存储图像(这相当于Qt的RGB;不幸的是Qt没有BGR格式)。这意味着我们需要小心一些OpenCV的例程,但不应该是一个问题。我也将看看OpenGL纹理选项。谢谢! – Calvin 2012-04-18 22:33:00

+0

FWIW,因为我不知道它是否有效:方法QImage :: rgbSwapped将RGB转换为BGR,请参阅http://qt-project.org/doc/qt-5.0/qtgui/qimage.html#rgbSwapped。我从这个项目的源代码中了解到了这一点:http://code.google.com/p/qt-opencv-multithreaded/ – 2013-06-20 16:51:03

+0

@JongBorLeem它不是RGB/BGR,这是显示API倾向于使用BGRA的问题(包括QImage),而图像处理API使用BGR - 所以你需要浏览整个图像,并复制它填充额外的'A'字节 – 2013-06-20 17:06:42

8

我不知道3个月后这对您是否有用。但是我有相同的应用程序,我必须使用OpenCV处理图像流并将其显示在QT界面上。经过相当多的搜索后,我遇到了一个非常漂亮的解决方案。使用opengl的glDrawPixels直接在Qt界面上绘制原始图像数据。最好的部分,你不必写任何额外的转换代码。只是opengl建立视口和坐标的基本代码。查看具有带IplImage *指针的函数的代码,并使用该数据绘制图像。您可能需要稍微调整参数(尤其是WIDTH和HEIGHT变量)以显示特定大小的图像。 是的,我不知道你正在使用什么构建系统。我使用cmake,并且必须为opengl设置依赖关系,尽管我使用的是Qt的opengl库。

我已经实现了一个从QGLWidget派生的类QIplImage,并重写了它的paintGL方法来将像素数据绘制到框架上。

//File qiplimage.h 
class QIplImage : public QGLWidget 
{ 
    Q_OBJECT 

public: 
    QIplImage(QWidget *parent = 0,char *name=0); 
    ~QIplImage(); 
    void paintGL(); 
    void initializeGL(); 
    void resizeGL(int,int); 
    bool drawing; 

public slots: 
    void setImage(IplImage); 

private: 
    Ui::QIplImage ui; 
    IplImage* original; 
    GLenum format; 
    GLuint texture; 
    QColor bgColor; 
    char* name; 
    bool hidden; 
    int startX,startY,endX,endY; 
    QList<QPointF*> slopes; 
    QWidget* parent; 
    int mouseX,mouseY; 

}; 
//End of file qiplimage.h 

//file qiplimage.cpp 
#include "qiplimage.h" 
#include <Globals.h> 

QIplImage::QIplImage(QWidget *parent) : 
    QGLWidget(parent) 
{ 

} 
QIplImage::QIplImage(QWidget *parent,char* name): QGLWidget(parent) 
{ 
    ui.setupUi(this); 
    //This is required if you need to transmit IplImage over 
    // signals and slots.(That's what I am doing in my application 
    qRegisterMetaType<IplImage>("IplImage"); 
    resize(384,288); 
    this->name=name; 
    this->parent=parent; 
    hidden=false; 
    bgColor= QColor::fromRgb(0xe0,0xdf,0xe0); 

    original=cvCreateImage(cvSize(this->width(),this->height()),IPL_DEPTH_8U,3); 
    cvZero(original); 
    switch(original->nChannels) { 
     case 1: 
      format = GL_LUMINANCE; 
      break; 
     case 2: 
      format = GL_LUMINANCE_ALPHA; 
      break; 
     case 3: 
      format = GL_BGR; 
      break; 
     default: 
      return; 
} 
    drawing=false; 
    setMouseTracking(true); 
    mouseX=0;mouseY=0; 
    initializeGL(); 

} 
void QIplImage::initializeGL() 
{ 
    qglClearColor(bgColor); 
    //glClearColor(0.5f, 0.5f, 0.5f, 1.0f);    
    glDisable(GL_DEPTH_TEST); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
     glOrtho(0,this->width(),this->height(),0.0f,0.0f,1.0f); 
    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 

    glEnable(GL_TEXTURE_2D); 
    glGenTextures(3,&texture); 
    glBindTexture(GL_TEXTURE_2D,texture); 
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); 
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); 
    glBindTexture(GL_TEXTURE_2D,texture);    glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,this->width(),this->height(),0,GL_BGR,GL_UNSIGNED_BYTE,NULL); 
    glDisable(GL_TEXTURE_2D); 


} 
void QIplImage::setImage(IplImage image){ 
original=&image; 
//cvShowImage(name,original); 

updateGL(); 
} 

void QIplImage::paintGL(){ 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
glDisable(GL_DEPTH_TEST); 
if(!hidden){ 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
      glOrtho(0.0f,this->width(),this->height(),0.0f,0.0f,1.0f); 
    glMatrixMode(GL_MODELVIEW); 
    glLoadIdentity(); 
    glEnable(GL_TEXTURE_2D); 
      glBindTexture(GL_TEXTURE_2D,texture); 
      glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,original->width,original->height,0,GL_BGR_EXT,GL_UNSIGNED_BYTE,original->imageData); 
    glBegin(GL_QUADS); 
      glTexCoord2i(0,1); glVertex2i(0,this->height()); 
    glTexCoord2i(0,0); glVertex2i(0,0); 
      glTexCoord2i(1,0); glVertex2i(this->width(),0); 
      glTexCoord2i(1,1); glVertex2i(this->width(),this->height()); 
    glEnd(); 
    glFlush(); 
    } 

} 


void QIplImage::resizeGL(int width,int height){ 

    glViewport(0,0,this->width(),this->height()); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity();  
    glOrtho(0.0f,this->width(),this->height(),0.0f,0.0f,1.0f); 
    glMatrixMode(GL_MODELVIEW);   
    glLoadIdentity(); 
} 

希望有所帮助。

+1

我们在应用程序中绘制了一些覆盖图(不是图像本身,而是覆盖它们顶部)。对于这类事情,OpenGL的观点往往有点不可思议;我喜欢这种方法,但是(我认为)这意味着我们需要将我们的一些代码从Qt绘图例程移植到OpenGL绘图例程中。尽管如此,这个伟大的代码示例+1。 – Calvin 2012-07-21 01:01:26

+0

只是对此进行更新,glDrawPixels是缓慢和越野车。我使用纹理四边形而不是..性能是伟大的没有任何内存泄漏或其他错误。 – 2012-10-18 10:02:57

+0

@RichardMacwan你可以分享你的'纹理四'版本,而不是'glDrawPixels'版本? – klefevre 2012-12-14 19:20:06