Преобразовать в оттенки серого QImage
у меня есть QImage, и мне нужно преобразовать его в оттенки серого, а затем закрасить это цветами. Я нашел allGray() и isGrayScale() функция для проверки, если изображение уже в оттенках серого, но нет toGrayScale() или аналогично названная функция.
прямо сейчас я использую этот код, но это не очень хорошие показатели:
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());
    }
}
каков был бы лучший способ, с точки зрения производительности, преобразовать QImage в оттенки серого?
4 ответов
вместо медленная функций QImage::pixel и QImage::setPixel используйте 
QImage::scanline доступ к данным. Пиксели на сканировании (горизонтальная линия ) являются последовательными. Предполагая, что у вас есть изображение 32 bpp, вы можете использовать QRgb для итерации по сканированию. Наконец, всегда помещайте координату x во внутренний цикл. Что дает :
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();
    }
}
быстрый тест с изображением 3585 x 2386 дал
********* 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 *********
источник код: 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
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 файл:
QT       += core gui
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = QImageTest
TEMPLATE = app
CONFIG  += qtestlib
SOURCES += testimage.cpp
HEADERS += testimage.h
важное замечание:
- вы уже получаете важное повышение производительности, просто перевернув петли. В этом тестовом случае это было 
~90ms. - вы можете использовать другие библиотеки, такие как opencv для преобразования оттенков серого, а затем построить Qimage из буфера opencv. Я ожидаю еще лучшее улучшение производительности.
 
я опубликую слегка измененную версию кода @UmNyobe. Я просто увеличиваю указатель для линий сканирования вместо вычисления каждого пикселя с помощью индекса.
// 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();
    }
}
начиная с Qt 5.5, вы можете вызвать QImage::convertToFormat() чтобы преобразовать QImage в оттенки серого следующим образом:
QImage image = ...;
image.convertToFormat(QImage::Format_Grayscale8);
внутренний класс qt QPixmapColorizeFilter использование функции grayscale это решает аналогичную тему.
я получил от него следующую функцию, которая должна решить проблему.
важной частью является преобразование изображения в 32-разрядный формат, поэтому вы можете рассматривать каждый пиксель как 32-разрядное значение, и вам не нужно беспокоиться о выравнивании битов.
вы также можете использовать bits функция непосредственно и итерации по всем пикселям вместо итерации по строкам и столбцам. С этим трюком ты избегайте умножения, выполняемого в