2015-01-14 17 views
6

Ich habe ein QImage und ich muss es in Graustufen konvertieren, dann später mit Farben übermalen. Ich habe eine allGray() und isGrayScale() Funktion gefunden, um zu überprüfen, ob ein Bild bereits Graustufen ist, aber keine toGrayScale() oder ähnlich benannte Funktion.Konvertiere ein QImage in Graustufen

Im Moment verwende ich diesen Code, aber es wird nicht über eine sehr gute Leistung:

for (int ii = 0; ii < image.width(); ii++) { 
    for (int jj = 0; jj < image.height(); jj++) { 
     int gray = qGray(image.pixel(ii, jj)); 
     image.setPixel(ii, jj, QColor(gray, gray, gray).rgb()); 
    } 
} 

Was wäre der beste Weg, Performance-weise, sei ein QImage in Graustufen zu konvertieren?

+0

Während dies noch nicht der beste Weg sein, versuchen Sie, für Schleifen Schalten (so Sie ii iterieren zuerst, jj Sekunde). Abhängig vom Speicherlayout könnte dies zu einer besseren Cache-Kohärenz führen und den Code schneller machen. – Daerst

+0

@Daerst Ja, guter Vorschlag, aber keine Optimierung eines Workaround, wenn ich sowieso eine bessere Lösung finde. Wenn keine andere Lösung existiert, dann vielleicht. – sashoalm

Antwort

8

Anstatt mit dem slow functionsQImage::pixel und QImage::setPixel verwenden QImage::scanline auf die Daten zugreifen. Pixel auf einem Scan (horizontale Linie) sind aufeinanderfolgend. Angenommen, Sie haben ein 32 bpp-Bild, können Sie QRgb verwenden, um über den Scan zu iterieren. Schließlich lege immer die x-Koordinate in die innere Schleife. Welche gibt:

for (int ii = 0; ii < image.height(); ii++) { 
    uchar* scan = image.scanLine(ii); 
    int depth =4; 
    for (int jj = 0; jj < image.width(); jj++) { 

     QRgb* rgbpixel = reinterpret_cast<QRgb*>(scan + jj*depth); 
     int gray = qGray(*rgbpixel); 
     *rgbpixel = QColor(gray, gray, gray).rgba(); 
    } 
} 

Ein schneller Test mit einem Bild 3585 x 2386 gab

********* Start testing of TestImage ********* 
Config: Using QTest library 4.7.4, Qt 4.7.4 
PASS : TestImage::initTestCase() 

RESULT : TestImage::grayscaleOp(): 
    390 msecs per iteration (total: 390, iterations: 1) 
PASS : TestImage::grayscaleOp() 

RESULT : TestImage::grayscaleFast(): 
    125 msecs per iteration (total: 125, iterations: 1) 
PASS : TestImage::grayscaleFast() 

PASS : TestImage::cleanupTestCase() 
Totals: 4 passed, 0 failed, 0 skipped 
********* Finished testing of TestImage ********* 

Quellcode: testimage.h Datei:

#ifndef TESTIMAGE_H 
#define TESTIMAGE_H 

#include <QtTest/QtTest> 

#include <QObject> 
#include <QImage> 

class TestImage : public QObject 
{ 
    Q_OBJECT 
public: 
    explicit TestImage(QObject *parent = 0); 

signals: 

private slots: 
    void grayscaleOp(); 

    void grayscaleFast(); 

private: 
    QImage imgop; 
    QImage imgfast; 
}; 

#endif // TESTIMAGE_H 

testimage.cpp Datei:

#include "testimage.h" 

TestImage::TestImage(QObject *parent) 
    : QObject(parent) 
    , imgop("path_to_test_image.png") 
    , imgfast("path_to_test_image.png") 
{ 
} 


void TestImage::grayscaleOp() 
{ 
    QBENCHMARK 
    { 
     QImage& image = imgop; 

     for (int ii = 0; ii < image.width(); ii++) { 
      for (int jj = 0; jj < image.height(); jj++) { 
       int gray = qGray(image.pixel(ii, jj)); 
       image.setPixel(ii, jj, QColor(gray, gray, gray).rgb()); 
      } 
     } 
    } 
} 

void TestImage::grayscaleFast() 
{ 

    QBENCHMARK { 

    QImage& image = imgfast; 


    for (int ii = 0; ii < image.height(); ii++) { 
     uchar* scan = image.scanLine(ii); 
     int depth =4; 
     for (int jj = 0; jj < image.width(); jj++) { 

      QRgb* rgbpixel = reinterpret_cast<QRgb*>(scan + jj*depth); 
      int gray = qGray(*rgbpixel); 
      *rgbpixel = QColor(gray, gray, gray).rgba(); 
     } 
    } 

    } 
} 

QTEST_MAIN(TestImage) 

pro Datei:

QT  += core gui 

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 

TARGET = QImageTest 
TEMPLATE = app 

CONFIG += qtestlib 

SOURCES += testimage.cpp 

HEADERS += testimage.h 

Wichtiger Hinweis:

  • Sie bereits eine wichtige Leistungssteigerung nur erhalten, indem die Schlaufen zu invertieren. In diesem Testfall war es ~90ms.
  • Sie können andere Bibliotheken wie opencv verwenden, um die Graustufen-Konvertierung durchzuführen und dann Qimage aus einem opencv-Puffer zu erstellen. Ich erwarte eine noch bessere Leistungsverbesserung.
4

Ich poste eine leicht modifizierte Version von @ UmNyobe's Code. Ich inkrementiere nur einen Zeiger für die Scanzeilen, anstatt jedes Pixel über einen Index zu berechnen.

// We assume the format to be RGB32!!! 
Q_ASSERT(image.format() == QImage::Format_RGB32); 
for (int ii = 0; ii < image.height(); ii++) { 
    QRgb *pixel = reinterpret_cast<QRgb*>(image.scanLine(ii)); 
    QRgb *end = pixel + image.width(); 
    for (; pixel != end; pixel++) { 
     int gray = qGray(*pixel); 
     *pixel = QColor(gray, gray, gray).rgb(); 
    } 
} 
0

Interne qt Klasse QPixmapColorizeFilter verwendet Funktion grayscale, die ähnliche Thema löst.

Ich abgeleitet folgende Funktion daraus, das sollte das Problem lösen.

Wichtiger Bestandteil ist die Konvertierung des Bilds in das 32-Bit-Format. Sie können also jedes Pixel als 32-Bit-Wert betrachten und müssen sich nicht um die Bitausrichtung kümmern.

Sie können auch die Funktion bits direkt verwenden und über alle Pixel iterieren, anstatt über Zeilen und Spalten zu iterieren.Mit diesem Trick vermeiden Sie Multiplikationen, die in der Funktion scanLine ausgeführt werden.

QImage convertToGrayScale(const QImage &srcImage) { 
    // Convert to 32bit pixel format 
    QImage dstImage = srcImage.convertToFormat(srcImage.hasAlphaChannel() ? 
       QImage::Format_ARGB32 : QImage::Format_RGB32); 

    unsigned int *data = (unsigned int*)dstImage.bits(); 
    int pixelCount = dstImage.width() * dstImage.height(); 

    // Convert each pixel to grayscale 
    for(int i = 0; i < pixelCount; ++i) { 
     int val = qGray(*data); 
     *data = qRgba(val, val, val, qAlpha(*data)); 
     ++data; 
    } 

    return dstImage; 
    } 
+0

Bits funktionieren, wenn Sie garantiert aufeinanderfolgende Pixel zwischen zwei Zeilen haben. Ich benutzte Scanline, um konservativer zu sein. Zum Beispiel kann ein Bild, das mit 'QImage :: QImage (uchar * data, int width, int height, int bytesPerLine, Format format) erstellt wurde,' bytesPerLine> width * sizeofpixel() 'haben. – UmNyobe

1

Da Qt 5.5, Sie QImage::convertToFormat() nennen kann eine QImage umwandeln in Graustufen wie folgt:

QImage image = ...; 
image.convertToFormat(QImage::Format_Grayscale8);