11

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

解决方案1(太慢)

每次您需要绘制不同的 OpenCV 图像时,将其转换为 a QImage并进行绘制。不幸的是,转换需要一段时间,我们无法以交互速度在图像之间切换。

解决方案 2(太占用内存)

维护两组图像,一组用于 OpenCV,一组用于 Qt。在适当的时候使用适当的一种。

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

我需要在哪里寻找从 Qt 中获得这种功能的方法?

4

2 回答 2

10

我不知道这在 3 个月后现在对你是否有用。但是我有相同类型的应用程序,我必须使用 OpenCV 操作图像流并将其显示在 QT 界面上。在谷歌搜索了很多之后,我遇到了一个非常巧妙的解决方案。使用opengl的glDrawPixels直接在Qt界面上绘制原始图像数据。最好的部分,你不必编写任何额外的转换代码。只是用于设置视口和坐标的 opengl 的基本代码。查看代码,该代码具有一个函数,该函数采用 IplImage* 指针并使用该数据绘制图像。您可能需要稍微调整参数(尤其是 WIDTH 和 HEIGHT 变量)以显示具有特定尺寸的图像。是的,我不知道您使用的是什么构建系统。

我已经实现了一个派生自 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();
 }

希望有帮助。

于 2012-07-20T12:37:59.627 回答
7

您可以在 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 显示图像而无需另一个副本。

于 2012-04-15T04:02:56.607 回答