高效集成Qt和OpenCV

11

我正在开发一个交互式应用程序,需要同时读取和处理几张非常大的图像(每次25张,大约总大小为350MB)。OpenCV速度很快,可以相对轻松地处理算法。但是使用Qt绘制它们却成了一个问题。以下是我尝试过的两个不太理想的解决方案。

解决方案1(速度太慢)

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

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

维护两个图像栈,一个用于OpenCV,另一个用于Qt。在适当的时间使用相应的图像栈。

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

我需要查看哪些内容,才能从Qt中获得这种功能?

2个回答

10

我不知道这个回答是否对你有用,因为距离提问已经过去了三个月。但是我也遇到了类似的应用场景,需要使用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 绘图例程。非常感谢提供的优秀代码示例。 - Calvin
@RichardMacwan,我正在尝试您的方法,但是当我尝试读取imagedata时,我遇到了读取访问冲突。您可以在此处阅读我的代码:https://stackoverflow.com/questions/45013214/qt-signal-slot-cvmat-unable-to-read-memory-access-violation。当您发出信号时,是否有任何方式可以共享您的类? - Pete
@Pete 我在聊天记录中看到你已经解决了问题。很抱歉我不能早些回复你。希望现在一切都正常了。 - Richard Macwan
@RichardMacwan 谢谢你回复,但我已经解决了我的问题。你的帖子对我达成目标非常有帮助。向你致敬,伙计。 - Pete
@Pete 很高兴能帮到你!祝你的项目好运! - Richard Macwan
显示剩余3条评论

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 在Qt上是否仍然比较快,因为Qt使用加速的OpenGL来渲染QImage。
另一种选择是使用opencv,然后将生成的数据直接复制到OpenGL纹理中,然后使用QGlWidget显示图像而无需再次复制。

BGR 这个东西确实很麻烦;我们现在正在绕过它,通过要求 OpenCV 以 BGR 格式存储图像(这相当于 Qt 的 RGB;不幸的是,Qt 没有 BGR 格式)。这意味着我们需要小心处理一些 OpenCV 的例程,但不应该有问题。我也会研究一下 OpenGL 纹理选项。谢谢! - Calvin
值得一提的是,由于我不知道它是否高效,方法QImage :: rgbSwapped将RGB转换为BGR,请参见http://qt-project.org/doc/qt-5.0/qtgui/qimage.html#rgbSwapped。我是从此项目的源代码中学到了这一点:http://code.google.com/p/qt-opencv-multithreaded/。 - Jong Bor Lee
@JongBorLeem 问题不在于RGB / BGR,而是显示API倾向于使用BGRA(包括QImage),而图像处理API使用BGR-因此您需要浏览整个图像并复制填充额外的“ A”字节。 - Martin Beckett
@MartinBeckett 我有一个疑问:数据指针指向某个特定格式的单一数据源。由于OpenCV和QImage需要两种不同的格式,即使您共享数据,是否仍需要进行转换?例如:假设原始数据以BGR格式存在。这对于OpenCV来说很好,但是如果您不将其转换为RGB,则QImage将无法正确显示,是吗? - mehfoos yacoob
如果我没记错,Qt的RGB888格式实际上是BGR顺序。 - Martin Beckett
@MartinBeckett:你确定吗?http://www.qtcentre.org/threads/33720-Understanding-RGB888 另外,我尝试在qt上显示BGR像素格式的图像。直到我使用cvtColor将其转换为RGB之后才能正确显示。 - mehfoos yacoob

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接