2015-01-14 13 views
6

Ho una QImage e ho bisogno di convertirla in scala di grigi, quindi dipingere su quella con i colori. Ho trovato una funzione allGray() e isGrayScale() per verificare se un'immagine è già in scala di grigi, ma nessuna funzione toGrayScale() o con nome simile.Converti una QImage in scala di grigi

In questo momento sto usando questo codice, ma è non avere una prestazione molto buona:

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

Quale sarebbe il modo migliore, performance, per convertire un QImage in scala di grigi?

+0

Anche se questo non sarà ancora il modo migliore, provare a passare il vostro per i loop (in modo da eseguire iterazioni ii prima, jj secondo). A seconda del layout della memoria, ciò potrebbe migliorare la coerenza della cache e rendere il codice più veloce. – Daerst

+0

@Daerst Sì, buon suggerimento, ma inutile ottimizzare una soluzione se trovo comunque una soluzione migliore. Se non esiste altra soluzione, allora forse. – sashoalm

risposta

8

Invece di utilizzare il slow functionsQImage::pixel e QImage::setPixel, utilizzare QImage::scanline per accedere ai dati. I pixel su una scansione (linea orizzontale) sono consecutivi. Supponendo che tu abbia un'immagine a 32 bpp, puoi usare QRgb per iterare sulla scansione. Infine metti sempre la coordinata x nel loop interno. Il che dà:

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

Un test rapido con un'immagine 3585 x 2386 diede

********* 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 ********* 

Codice sorgente: file di testimage.h:

#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 

file di testimage.cpp:

#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 lima:

QT  += core gui 

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets 

TARGET = QImageTest 
TEMPLATE = app 

CONFIG += qtestlib 

SOURCES += testimage.cpp 

HEADERS += testimage.h 

Nota importante:

  • È già ottenere un importante incremento delle prestazioni semplicemente invertendo i cicli. In questo caso di test era ~90ms.
  • È possibile utilizzare altre librerie come opencv per effettuare la conversione in scala di grigi e quindi creare Qimage da un buffer opencv. Mi aspetto un miglioramento delle prestazioni ancora migliore.
4

Inserirò una versione leggermente modificata del codice di @ UmNyobe. Semplicemente incremento un puntatore per le linee di scansione invece di calcolare ciascun pixel tramite un indice.

// 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

interno classe qt QPixmapColorizeFilter utilizza la funzione grayscale che risolve soggetti simili.

Ho derivato la seguente funzione, che dovrebbe risolvere il problema.

Parte importante è la conversione dell'immagine in formato a 32 bit, quindi è possibile considerare ciascun pixel come valore a 32 bit e non è necessario preoccuparsi dell'allineamento dei bit.

È anche possibile utilizzare la funzione bits direttamente e iterare su tutti i pixel invece di scorrere su linee e colonne.Con questo trucco si evita la moltiplicazione eseguita nella funzione scanLine.

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

bit funziona quando è garantito avere pixel consecutivi tra due righe. Ho usato scanline per essere più prudente. Ad esempio un'immagine creata con 'QImage :: QImage (dati uchar *, larghezza int, altezza int, int byPerLine, formato formato)' può avere 'bytesPerLine> width * sizeofpixel()'. – UmNyobe

1

Dal Qt 5.5, è possibile chiamare QImage::convertToFormat() per convertire un QImage in scala di grigio come segue:

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