2012-04-15 13 views
9

Pracuję na interaktywnej aplikacji, która potrzebuje do odczytu i manipulować kilka bardzo dużych obrazów na raz (25 zdjęć na raz, około 350 MB całkowitej wielkości). OpenCV jest dość szybki i obsługuje algorytmy z względną łatwością. Ale rysowanie ich za pomocą Qt okazuje się być problemem. Oto dwa mniej niż idealne rozwiązania, które próbowałem.Skuteczna integracja z Qt i OpenCV

Rozwiązanie 1 (zbyt powolny)

Za każdym razem trzeba wyciągnąć inny OpenCV obraz, przekształcić go w QImage i wyciągnąć to. Konwersja niestety zajmuje trochę czasu i nie można przełączać się między obrazami z prędkością interaktywną.

Roztwór 2 (zbyt dużej ilości pamięci)

utrzymania dwóch stosów obrazów, jeden dla OpenCV i jeden Qt. Użyj odpowiedniego urządzenia w odpowiednim czasie.

Mam bezpośredni dostęp do danych pikseli OpenCV. Znam szerokość i wysokość obrazu i wiem, że piksele to 3-bajtowe wartości RGB. Wygląda na to, że powinno być możliwe szybkie narysowanie obrazu OpenCV bez kopiowania go do kontenera QImage, który (o ile wiem) zawiera tylko duplikat danych.

Gdzie muszę patrzeć, aby uzyskać tego rodzaju zdolności z Qt?

Odpowiedz

5

Można udostępniać dane między QImage i OpenCV - obaj mają konstruktor, który wykorzystuje istniejące dane - dostarczony przez wskaźnik.

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)

Może być problem z wyściółki jeśli wiersze nie skończyć się wielokrotności 4bytes ale spodziewałbym wyściółka wyrównać na obu typach o tej samej wielkości piksela - na najmniej na tym samym sprzęcie

jeden problem jest to, że używa OpenCV BGR domyślnie, który nie jest bardzo optymalne dla QImage (lub jakikolwiek inny wyświetlacz). Chociaż nie jestem pewien, że QImage :: Format_ARGB32_Premultiplied jest koniecznie o wiele szybszy na Qt, który używa przyspieszonego OpenGL do renderowania QImage.

Alternatywą jest użycie OpenCV następnie skopiować dane wynikowe bezpośrednie do tekstur OpenGL i następnie użyć QGlWidget do wyświetlania obrazu bez innej kopii.

+0

Rzecz BGR jest irytująca; teraz obejdziemy go, prosząc OpenCV o przechowywanie obrazów w formacie BGR (co odpowiada Qt's RGB, niestety Qt nie ma formatu BGR). Oznacza to, że musimy uważać na niektóre z procedur OpenCV, ale nie powinno to stanowić problemu. Zajrzę też do opcji tekstury OpenGL. Dzięki! – Calvin

+0

FWIW, ponieważ nie wiem, czy jest wydajny: metoda QImage :: rgbSwapped przekształca RGB na BGR, zobacz http://qt-project.org/doc/qt-5.0/qtgui/qimage.html#rgbSwapped. Nauczyłem się tego z kodu źródłowego tego projektu: http://code.google.com/p/qt-opencv-multithreaded/ –

+0

@JongBorLeem to nie RGB/BGR, ale problem polega na tym, że interfejsy API używają wyświetlacza BGRA (w tym QImage), podczas gdy API przetwarzania obrazu używa BGR - musisz przejść cały obraz i skopiować go, uzupełniając dodatkowy bajt "A" –

8

ja nie wiem, czy to może być przydatna teraz po 3 miesiącach. Ale mam ten sam rodzaj aplikacji, w której muszę manipulować strumieniem obrazów za pomocą OpenCV i wyświetlać go w interfejsie QT. Po dość szybkim przeszukiwaniu, natknąłem się na bardzo pomysłowe rozwiązanie. Użyj glDrawPixels OpenGL, aby narysować nieprzetworzone dane obrazu bezpośrednio w interfejsie Qt. Najlepsza część, nie musisz pisać żadnego dodatkowego kodu konwersji. Tylko podstawowy kod dla opengl do ustawienia rzutni i współrzędnych. Sprawdź kod, który ma funkcję, która przyjmuje wskaźnik IplImage * i używa tych danych do narysowania obrazu. Konieczne może być dostosowanie parametrów (szczególnie zmiennych WIDTH i HEIGHT) do wyświetlenia obrazu o określonym rozmiarze. I tak, nie wiem, jakiego systemu budowy używasz. Użyłem cmake i musiałem ustawić zależności dla OpenGL, chociaż używam biblioteki OpenGL Qt.

Zaimplementowałem klasowy QIplImage, który pochodzi z QGLWidget i zastępuje jego metodę paintGL, aby narysować dane pikseli w ramce.

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

Nadzieję, że pomaga.

+1

Mamy kilka nakładek, które są rysowane na wierzchu obrazów (nie na samych obrazach, ale nad nimi na ekranie) w aplikacji. Widoki OpenGL wydają się trochę dziwne z tego typu rzeczy; Podoba mi się to podejście, ale (jak sądzę) oznacza to, że musimy przenieść część naszego kodu z procedur rysunkowych Qt na procedury rysowania OpenGL. +1 za świetną próbkę kodu. – Calvin

+0

Właśnie aktualizacja tego, glDrawPixels była powolna i błędna. Zamiast tego użyłem teksturowanych quadów. Wydajność jest świetna bez żadnych wycieków pamięci ani innych błędów. –

+0

@RichardMacwan Czy możesz udostępnić swoją wersję "teksturowanego quada" zamiast tej wersji "glDrawPixels"? – klefevre