2012-04-15 5 views
9

Ich arbeite an einer interaktiven Anwendung, die mehrere sehr große Bilder auf einmal lesen und bearbeiten muss (25 Bilder gleichzeitig, ungefähr 350 MB Gesamtgröße). OpenCV ist ziemlich schnell und behandelt die Algorithmen relativ einfach. Aber sie mit Qt zu zeichnen, erweist sich als ein Problem. Hier sind zwei weniger als ideale Lösungen, die ich ausprobiert habe.Effiziente Integration von Qt und OpenCV

Lösung 1 (zu langsam)

Jedes Mal, wenn Sie brauchen eine andere OpenCV Bild zu zeichnen, wandelt es in ein QImage und zeichnen. Die Konvertierung dauert leider eine Weile und wir können nicht mit interaktiven Geschwindigkeiten zwischen Bildern wechseln.

Lösung 2 (zu speicherintensiven)

Pflegen zwei Stapel von Bildern, eines für OpenCV und eine für Qt. Verwenden Sie zum geeigneten Zeitpunkt das passende .

Ich habe direkten Zugriff auf die OpenCV-Pixeldaten. Ich kenne die Breite und Höhe des Bildes, und ich weiß, dass Pixel 3-Byte-RGB-Werte sind. Es scheint, dass es möglich sein sollte, das OpenCV-Bild schnell zu zeichnen, ohne es in einen QImage Container zu kopieren, der (soweit ich das beurteilen kann) nur ein Duplikat der Daten enthält.

Wo muss ich suchen, um diese Art von Fähigkeiten aus Qt zu bekommen?

Antwort

5

Sie können die Daten zwischen QImage und openCV teilen - beide haben einen ctor, der vorhandene Daten verwendet -, die von einem Zeiger geliefert werden.

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)

Es könnte ein Problem mit der Polsterung, wenn die Zeilen am Ende nicht ein Vielfaches von 4 Bytes sein, aber ich würde erwarten, dass die Polsterung auf beiden Typen mit der gleichen Pixelgröße auszurichten - bei mindestens auf der gleichen Hardware

Ein Problem ist, dass OpenCV standardmäßig BGR verwendet, die für QImage (oder eine andere Anzeige) nicht optimal ist. Obwohl ich nicht sicher bin, dass QImage :: Format_ARGB32_Premultiplied auf Qt, das beschleunigtes OpenGL zum Rendern von QImage verwendet, unbedingt schneller ist.

Eine Alternative besteht darin, opencv zu verwenden, dann die resultierenden Daten direkt in eine OpenGL-Textur zu kopieren und dann QGlWidget zu verwenden, um das Bild ohne eine andere Kopie anzuzeigen.

+0

Die BGR-Sache ist sicher nervig; Wir umgehen es jetzt, indem wir OpenCV bitten, Bilder im BGR-Format zu speichern (was Qt's RGB entspricht; leider hat Qt kein BGR-Format). Das bedeutet, dass wir mit einigen Routinen von OpenCV vorsichtig sein müssen, aber das sollte kein Problem sein. Ich werde auch in die OpenGL-Textur-Option schauen. Vielen Dank! – Calvin

+0

FWIW, da ich nicht weiß, ob es effizient ist: Methode QImage :: rgbSwapped konvertiert RGB in BGR, siehe http://qt-project.org/doc/qt-5.0/qtgui/qimage.html#rgbSwapped. Ich habe das aus dem Quellcode dieses Projekts gelernt: http://code.google.com/p/qt-opencv-multithreaded/ –

+0

@JongBorLeem Es ist nicht das RGB/BGR-Problem, dass die Anzeige-APIs dazu neigen, BGRA zu verwenden (einschließlich QImage), während Bildverarbeitungs-APIs BGR verwenden - also müssen Sie das gesamte Bild durchgehen und es kopieren das zusätzliche 'A' Byte –

8

Ich weiß nicht, ob dies jetzt nach 3 Monaten für Sie nützlich sein könnte. Aber ich habe die gleiche Art von Anwendung, wo ich einen Strom von Bildern mit OpenCV bearbeiten und auf einer QT-Oberfläche anzeigen muss. Nachdem ich ziemlich viel gegoogelt hatte, stieß ich auf eine sehr glatte Lösung. Verwenden Sie opengls glDrawPixels, um Rohbilddaten direkt auf der Qt-Oberfläche zu zeichnen. Der beste Teil, Sie müssen keinen zusätzlichen Conversion-Code schreiben. Nur der grundlegende Code für opengl zum Einrichten eines Ansichtsfensters und einer Koordinate. Sehen Sie sich den Code mit einer Funktion an, die einen IplImage * -Zeiger verwendet und diese Daten zum Zeichnen des Bildes verwendet. Möglicherweise müssen Sie die Parameter (insbesondere die WIDTH- und HEIGHT-Variablen) etwas anpassen, um ein Bild mit einer bestimmten Größe anzuzeigen. Und ja, ich weiß nicht, welches Build-System Sie verwenden. Ich benutzte cmake und musste Abhängigkeiten für opengl einrichten, obwohl ich OpenGL-Bibliotheken von Qt verwende.

Ich habe eine Klasse QIplImage implementiert, die von QGLWidget abgeleitet ist und ihre paintGL-Methode überschreibt, um die Pixeldaten auf den Frame zu zeichnen.

//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(); 
} 

Hoffe, dass hilft.

+1

Wir haben ein paar Overlays, die oben auf den Bildern (nicht auf den Bildern selbst, aber über ihnen auf dem Bildschirm) in der Anwendung gezeichnet werden. OpenGL-Ansichten neigen dazu, etwas in dieser Art von etwas seltsam zu sein; Ich mag diesen Ansatz, aber (ich denke) es bedeutet, dass wir einen Teil unseres Codes von Qt-Zeichnungsroutinen in OpenGL-Zeichnungsroutinen portieren müssen. +1 für das großartige Codebeispiel. – Calvin

+0

Nur ein Update zu diesem, glDrawPixels war langsam und fehlerhaft. Ich habe stattdessen texturierte Quads verwendet. Leistung ist großartig ohne irgendwelche Speicherlecks oder andere Fehler. –

+0

@RichardMacwan Können Sie anstelle dieser 'glDrawPixels' Version Ihre 'textured quad' Version teilen? – klefevre