将 QImage 转换为灰度
Convert a QImage to grayscale
我有一个 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 转换为灰度的最佳方式是什么?
与其使用 slow functions 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)
专业文件:
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。我期待更好的性能改进。
我将 post @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 class 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;
}
从 Qt 5.5 开始,您可以调用 QImage::convertToFormat() 将 QImage 转换为灰度,如下所示:
QImage image = ...;
image.convertToFormat(QImage::Format_Grayscale8);
我有一个 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 转换为灰度的最佳方式是什么?
与其使用 slow functions 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)
专业文件:
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。我期待更好的性能改进。
我将 post @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 class 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;
}
从 Qt 5.5 开始,您可以调用 QImage::convertToFormat() 将 QImage 转换为灰度,如下所示:
QImage image = ...;
image.convertToFormat(QImage::Format_Grayscale8);