Instead of using the slow functions QImage::pixel and QImage::setPixel use QImage::scanline to access the data. The pixels on the scan (horizontal line) are sequential. Assuming you have a 32-bit image, you can use QRgb to repeat the scan. Finally, always put the x coordinate in the inner loop. What gives:
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(); } }
A quick test with an image of 3585 x 2386 gave
********* 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 *********
Source code: File 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 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 file:
QT += core gui greaterThan(QT_MAJOR_VERSION, 4): QT += widgets TARGET = QImageTest TEMPLATE = app CONFIG += qtestlib SOURCES += testimage.cpp HEADERS += testimage.h
Important Note:
- You already get an important performance boost only by inverting loops. In this case, it was
~90ms . - You can use other libraries like opencv to convert grayscale and then build Qimage from the opencv buffer. I expect even better performance improvements.