可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
I have a QImage and I need to convert it to grayscale, then later paint over that with colors. I found an allGray()
and isGrayScale()
function to check if an image is already grayscale, but no toGrayScale()
or similarly-named function.
Right now I'm using this code, but it's does not have a very good performance:
for (int ii = 0; ii
What would be the best way, performance-wise, to convert a QImage to grayscale?
回答1:
Rather than using the slow functions QImage::pixel
and QImage::setPixel
, use QImage::scanline
to access the data. Pixels on a scan (horizontal line ) are consecutive. Assuming you have a 32 bpp image, you can use QRgb to iterate over the scan. Finally always put the x coordinate in the inner loop. Which gives :
for (int ii = 0; ii (scan + jj*depth); int gray = qGray(*rgbpixel); *rgbpixel = QColor(gray, gray, gray).rgba(); } }
A quick test with an 3585 x 2386 image 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: testimage.h file:
#ifndef TESTIMAGE_H #define TESTIMAGE_H #include #include #include 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 file:
#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 (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 just by inverting the loops. In this test case it was
~90ms
. - You may use other libraries like opencv to make the grayscale conversion and then build the Qimage from an opencv buffer. I expect an even better performance improvement.
回答2:
I'll post a slightly modified version of @UmNyobe's code. I just increment a pointer for the scan lines instead of calculating each pixel via an index.
// We assume the format to be RGB32!!! Q_ASSERT(image.format() == QImage::Format_RGB32); for (int ii = 0; ii (image.scanLine(ii)); QRgb *end = pixel + image.width(); for (; pixel != end; pixel++) { int gray = qGray(*pixel); *pixel = QColor(gray, gray, gray).rgb(); } }
回答3:
Since Qt 5.5, you can call QImage::convertToFormat() to convert a QImage to grayscale as follows:
QImage image = ...; image.convertToFormat(QImage::Format_Grayscale8);
回答4:
Internal qt class QPixmapColorizeFilter
uses function grayscale
that solves similar topic.
I derived following function from it, that should solve the problem.
Important part is converting image to 32-bit format, so you can consider each pixel as 32-bit value and you do not need to concern about bit alignment.
You can also use bits
function directly and iterate over all pixels instead of iterating over lines and columns. With this trick you avoid multiplication performed in scanLine
function.
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