Convert a QImage to grayscale

前端 未结 4 1134
攒了一身酷
攒了一身酷 2020-12-06 06:19

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 c

相关标签:
4条回答
  • 2020-12-06 06:36

    Since Qt 5.5, you can call QImage::convertToFormat() to convert a QImage to grayscale as follows:

    QImage image = ...;
    image.convertToFormat(QImage::Format_Grayscale8);
    
    0 讨论(0)
  • 2020-12-06 06:43

    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 < pixelCount; ++i) {
            int val = qGray(*data);
            *data = qRgba(val, val, val, qAlpha(*data));
            ++data;
         }
    
         return dstImage;
      }
    
    0 讨论(0)
  • 2020-12-06 06:48

    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 < 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 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 <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 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 < 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 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.
    0 讨论(0)
  • 2020-12-06 06:49

    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.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 讨论(0)
提交回复
热议问题