【问题标题】:Convert a QImage to grayscale将 QImage 转换为灰度
【发布时间】:2017-11-23 21:28:47
【问题描述】:

我有一个 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 转换为灰度的最佳方法是什么?

【问题讨论】:

  • 虽然这仍然不是最好的方法,但请尝试切换您的 for 循环(因此您首先迭代 ii,其次是 jj)。根据内存布局,这可能会导致更好的缓存一致性并使代码更快。
  • @Daerst 是的,很好的建议,但如果我找到更好的解决方案,优化解决方法毫无意义。如果不存在其他解决方案,那么可能。

标签: c++ qt grayscale qimage


【解决方案1】:

从 Qt 5.5 开始,您可以调用 QImage::convertToFormat() 将 QImage 转换为灰度,如下所示:

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

【讨论】:

  • 这应该是公认的答案。为我工作-谢谢:)
  • 这不处理透明度。
【解决方案2】:

不要使用slow functionsQImage::pixelQImage::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)

专业文件:

QT       += core gui

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

TARGET = QImageTest
TEMPLATE = app

CONFIG  += qtestlib

SOURCES += testimage.cpp

HEADERS += testimage.h

重要提示:

  • 您已经通过反转循环获得了重要的性能提升。在这个测试用例中是~90ms
  • 您可以使用其他库(如 opencv)进行灰度转换,然后从 opencv 缓冲区构建 Qimage。我期待更好的性能改进。

【讨论】:

    【解决方案3】:

    我将发布 @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();
        }
    }
    

    【讨论】:

      【解决方案4】:

      内部 qt 类 QPixmapColorizeFilter 使用函数 grayscale 解决类似的主题。

      我从中派生了以下功能,应该可以解决问题。

      重要的部分是将图像转换为 32 位格式,因此您可以将每个像素视为 32 位值,而无需担心位对齐。

      您也可以直接使用bits 函数并遍历所有像素,而不是遍历行和列。通过这个技巧,您可以避免在 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;
        }
      

      【讨论】:

      • bits 在保证两行之间有连续像素时有效。我使用扫描线更保守。例如,使用QImage::QImage(uchar * data, int width, int height, int bytesPerLine, Format format) 创建的图像可能具有bytesPerLine &gt; width*sizeofpixel()
      猜你喜欢
      • 2013-03-18
      • 1970-01-01
      • 1970-01-01
      • 2020-04-29
      • 2020-04-01
      • 1970-01-01
      • 2018-08-27
      • 2010-10-15
      • 1970-01-01
      相关资源
      最近更新 更多