从 uint32 或 uchar 数组创建 ARGB QImage
Creating ARGB QImage from uint32 or uchar array
当我尝试使用 QImage 构造函数从 reinterpret_cast<uchar*>(quint32*)
创建 ARGB32 QImage 时,图像失去了它的颜色和 alpha 通道,并且生成的 QImage 是灰度的!
灰度图像按预期显示,如果我试图以灰度显示它。所以我知道 ushort 数据到 quint32 数组的缩放和索引进行得很顺利,但是出了什么问题?
A Qt forum post 建议按照我现在的方式来做(据我所知),但也许自 Qt 版本以来行为发生了变化? (我使用的是 Qt 5.9)
我知道文档说:
data must be 32-bit aligned, and each scanline of data in the image
must also be 32-bit aligned.
但我希望 quint32 即使在 reinterpret_cast<uchar*>()
之后也是 32 位对齐的?
现在详情:
我正在将计算结果(具有无符号短值的数组)转换为半透明的蓝色到绿色到红色图像,如下所示:
inline uchar val_to_blue(const double val) {
if (val > 0.5)
return 0;
else if (val < 0.25)
return 255;
else // x={.5,...,.25}:a=255/(.25-.5)=-4*255 & b=-255*0.5/(0.25-0.5)=4/2*255=2*255
return (uchar)(val * -4.0 * 255.0) + 2 * 255;
}
inline uchar val_to_green(const double val) {
if (val > 0.25 && val < 0.75)
return 255;
else if (val < 0.25)// x={0,...,.25}:a=255/(.25-0)=4*255 & b=-255*0/(0.25-0)=0
return (uchar)(val * 4.0 * 255.0);
else // if (val > .75) // x={.75,...,1}:a=255/(.75-.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * -4.0 * 255.0) - 2 * 255;
}
inline uchar val_to_red(const double val) {
if (val < 0.5)
return 0;
if (val > 0.75)
return 255;
else // x={0.5,...,0.75}:a=255/(0.75-0.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * 4.0 * 255.0) - 2 * 255;
}
inline QRgb val_to_rgba_scale(const double val) {
return qRgba( // ax+b={0,...,255} for x={i,...,j}, a=255/(j-i), b= -255i/(j-i)
val_to_blue(val),
val_to_green(val),
val_to_red(val),
(uchar)(val * 81)
);
}
其中 val
是从 ushort 数据缩放而来的介于 0 和 1 之间的双精度数。
每个 QRgb
值都存储在 quint32
数组的相应索引处,如下所示:
if (m_pData[i*m_iWidth + j] >= uppVal)
tmpData[tmpIdx] = 0x45ff0000;
else if (m_pData[i*m_iWidth + j] <= lowVal)
tmpData[tmpIdx] = 0x00000000;
else
tmpData[tmpIdx] = val_to_rgba_scale((m_pData[i*m_iWidth + j] - lowVal) / (double)winWidth);
其中(m_pData[i*m_iWidth + j] - lowVal) / (double)winWidth
是ushort-to-double缩放方法。
这是在 for 循环中完成的。
最后 我尝试构建图像:
QImage tmpQImage = QImage(reinterpret_cast<unsigned char*>(tmpData), m_iWidth, m_iHeight, QImage::Format_ARGB32);
但这并没有像我预期的那样工作,因为 tmpQImage.allGray()
returns 在之后立即调用时为真!
我做错了什么,我应该怎么做才能创建 ARGB 图像并同时保留颜色和 Alpha 通道?
我试图重现你的问题,但我不能。
要么 OP 的实际问题不是所提供代码的一部分,要么我在尝试从 OP 形成 MCVE 时不小心错过了一个细节。
但是,我想展示一下我得到的结果,因为这可能有助于修复 OP。
我的来源testQImageGrayToRGB.cc
:
#include <vector>
#include <QtWidgets>
typedef unsigned char uchar;
namespace AGA {
uchar val_to_blue(const double val) {
if (val > 0.5)
return 0;
else if (val < 0.25)
return 255;
else // x={.5,...,.25}:a=255/(.25-.5)=-4*255 & b=-255*0.5/(0.25-0.5)=4/2*255=2*255
return (uchar)(val * -4.0 * 255.0) + 2 * 255;
}
uchar val_to_green(const double val) {
if (val > 0.25 && val < 0.75)
return 255;
else if (val < 0.25)// x={0,...,.25}:a=255/(.25-0)=4*255 & b=-255*0/(0.25-0)=0
return (uchar)(val * 4.0 * 255.0);
else // if (val > .75) // x={.75,...,1}:a=255/(.75-.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * -4.0 * 255.0) - 2 * 255;
}
uchar val_to_red(const double val) {
if (val < 0.5)
return 0;
if (val > 0.75)
return 255;
else // x={0.5,...,0.75}:a=255/(0.75-0.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * 4.0 * 255.0) - 2 * 255;
}
} // namespace AGA
namespace DS {
uchar val_to_blue(const double val)
{
return val < 0.25 ? 255
: val < 0.5 ? (0.5 - val) * 4 * 255
: 0;
}
uchar val_to_green(const double val)
{
return val < 0.25 ? val * 4 * 255
: val < 0.75 ? 255
: (1.0 - val) * 4 * 255;
}
uchar val_to_red(const double val)
{
return val < 0.5 ? 0
: val < 0.75 ? (val - 0.5) * 4 * 255
: 255;
}
} // namespace DS
std::vector<quint32> buildImageData(
const int w, const int h,
uchar (*pFuncValToR)(double),
uchar (*pFuncValToG)(double),
uchar (*pFuncValToB)(double))
{
// make temp. buffer to build up raw image data
std::vector<quint32> data(w * h);
// fill raw image - make values 0 ... 1 in n steps
const int n = w - 1;
for (int x = 0; x < w; ++x) {
const double v = (double)x / n;
QRgb qRgb = qRgba(pFuncValToR(v), pFuncValToG(v), pFuncValToB(v), 255);
for (int y = 0; y < h; ++y) data[y * w + x] = qRgb;
}
// done
return data;
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
QApplication app(argc, argv);
// build contents
enum { w = 256, h = 32 };
std::vector<quint32> dataAGA = buildImageData(w, h,
&AGA::val_to_red, &AGA::val_to_green, &AGA::val_to_blue);
QImage qImgAGA((const uchar*)dataAGA.data(), w, h, QImage::Format_ARGB32);
std::vector<quint32> dataDS = buildImageData(w, h,
&DS::val_to_red, &DS::val_to_green, &DS::val_to_blue);
QImage qImgDS((const uchar*)dataDS.data(), w, h, QImage::Format_ARGB32);
// build some GUI
QWidget win;
QVBoxLayout qVBox;
QLabel qLblAGA(
QString::fromUtf8("QImage (Functions of Andreas Gravgaard Andersen):"));
qVBox.addWidget(&qLblAGA);
QLabel qLblImgAGA;
qLblImgAGA.setPixmap(QPixmap::fromImage(qImgAGA));
qVBox.addWidget(&qLblImgAGA);
QLabel qLblDS(
QString::fromUtf8("QImage (Functions of Scheff):"));
qVBox.addWidget(&qLblDS);
QLabel qLblImgDS;
qLblImgDS.setPixmap(QPixmap::fromImage(qImgDS));
qVBox.addWidget(&qLblImgDS);
win.setLayout(&qVBox);
win.show();
// exec. application
return app.exec();
}
我在 Windows 10(64 位)上用 VS2013、Qt5.6 编译和测试它:
备注:
val_to_
函数让我有点怀疑:一个表达式转换为 (uchar)
,然后添加一个常数项(这绝对不适合 (uchar)
, 结果返回为 uchar
...
嗯...
因此,我重新制作了它们——稍微清理了一下。
实际上,视觉比较显示差异几乎是看不见的(唯一的例外是黄色区域中的红线)。
我可以毫无问题地从原始 quint32
数组中创建 QImage
(包括 cast-to-uchar*
-hack)。
更新:
可能是,并不明显:示例代码经过精心设计,允许缓冲区数据(std::vector<quint32> dataAGA
和std::vector<quint32> dataDS
)的生命周期长于Qt的生命周期图片(QImage qImgAGA
和 QImage qImgDS
)。这是根据 Qt 文档完成的。 QImage::QImage()
:
The buffer must remain valid throughout the life of the QImage
and all copies that have not been modified or otherwise detached from the original buffer. The image does not delete the buffer at destruction. You can provide a function pointer cleanupFunction along with an extra pointer cleanupInfo that will be called when the last copy is destroyed.
图像数据可能会占用大量内存。因此,QImage
实现试图防止不必要的复制(安全内存 space 和时间)。相反,"user"(即应用程序开发人员)负责确保正确存储图像数据。
当我尝试使用 QImage 构造函数从 reinterpret_cast<uchar*>(quint32*)
创建 ARGB32 QImage 时,图像失去了它的颜色和 alpha 通道,并且生成的 QImage 是灰度的!
灰度图像按预期显示,如果我试图以灰度显示它。所以我知道 ushort 数据到 quint32 数组的缩放和索引进行得很顺利,但是出了什么问题?
A Qt forum post 建议按照我现在的方式来做(据我所知),但也许自 Qt 版本以来行为发生了变化? (我使用的是 Qt 5.9)
我知道文档说:
data must be 32-bit aligned, and each scanline of data in the image must also be 32-bit aligned.
但我希望 quint32 即使在 reinterpret_cast<uchar*>()
之后也是 32 位对齐的?
现在详情: 我正在将计算结果(具有无符号短值的数组)转换为半透明的蓝色到绿色到红色图像,如下所示:
inline uchar val_to_blue(const double val) {
if (val > 0.5)
return 0;
else if (val < 0.25)
return 255;
else // x={.5,...,.25}:a=255/(.25-.5)=-4*255 & b=-255*0.5/(0.25-0.5)=4/2*255=2*255
return (uchar)(val * -4.0 * 255.0) + 2 * 255;
}
inline uchar val_to_green(const double val) {
if (val > 0.25 && val < 0.75)
return 255;
else if (val < 0.25)// x={0,...,.25}:a=255/(.25-0)=4*255 & b=-255*0/(0.25-0)=0
return (uchar)(val * 4.0 * 255.0);
else // if (val > .75) // x={.75,...,1}:a=255/(.75-.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * -4.0 * 255.0) - 2 * 255;
}
inline uchar val_to_red(const double val) {
if (val < 0.5)
return 0;
if (val > 0.75)
return 255;
else // x={0.5,...,0.75}:a=255/(0.75-0.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * 4.0 * 255.0) - 2 * 255;
}
inline QRgb val_to_rgba_scale(const double val) {
return qRgba( // ax+b={0,...,255} for x={i,...,j}, a=255/(j-i), b= -255i/(j-i)
val_to_blue(val),
val_to_green(val),
val_to_red(val),
(uchar)(val * 81)
);
}
其中 val
是从 ushort 数据缩放而来的介于 0 和 1 之间的双精度数。
每个 QRgb
值都存储在 quint32
数组的相应索引处,如下所示:
if (m_pData[i*m_iWidth + j] >= uppVal)
tmpData[tmpIdx] = 0x45ff0000;
else if (m_pData[i*m_iWidth + j] <= lowVal)
tmpData[tmpIdx] = 0x00000000;
else
tmpData[tmpIdx] = val_to_rgba_scale((m_pData[i*m_iWidth + j] - lowVal) / (double)winWidth);
其中(m_pData[i*m_iWidth + j] - lowVal) / (double)winWidth
是ushort-to-double缩放方法。
这是在 for 循环中完成的。
最后 我尝试构建图像:
QImage tmpQImage = QImage(reinterpret_cast<unsigned char*>(tmpData), m_iWidth, m_iHeight, QImage::Format_ARGB32);
但这并没有像我预期的那样工作,因为 tmpQImage.allGray()
returns 在之后立即调用时为真!
我做错了什么,我应该怎么做才能创建 ARGB 图像并同时保留颜色和 Alpha 通道?
我试图重现你的问题,但我不能。
要么 OP 的实际问题不是所提供代码的一部分,要么我在尝试从 OP 形成 MCVE 时不小心错过了一个细节。
但是,我想展示一下我得到的结果,因为这可能有助于修复 OP。
我的来源testQImageGrayToRGB.cc
:
#include <vector>
#include <QtWidgets>
typedef unsigned char uchar;
namespace AGA {
uchar val_to_blue(const double val) {
if (val > 0.5)
return 0;
else if (val < 0.25)
return 255;
else // x={.5,...,.25}:a=255/(.25-.5)=-4*255 & b=-255*0.5/(0.25-0.5)=4/2*255=2*255
return (uchar)(val * -4.0 * 255.0) + 2 * 255;
}
uchar val_to_green(const double val) {
if (val > 0.25 && val < 0.75)
return 255;
else if (val < 0.25)// x={0,...,.25}:a=255/(.25-0)=4*255 & b=-255*0/(0.25-0)=0
return (uchar)(val * 4.0 * 255.0);
else // if (val > .75) // x={.75,...,1}:a=255/(.75-.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * -4.0 * 255.0) - 2 * 255;
}
uchar val_to_red(const double val) {
if (val < 0.5)
return 0;
if (val > 0.75)
return 255;
else // x={0.5,...,0.75}:a=255/(0.75-0.5)=4*255 & b=-255*0.5/(0.75-0.5)=-4/2*255=-2*255
return (uchar)(val * 4.0 * 255.0) - 2 * 255;
}
} // namespace AGA
namespace DS {
uchar val_to_blue(const double val)
{
return val < 0.25 ? 255
: val < 0.5 ? (0.5 - val) * 4 * 255
: 0;
}
uchar val_to_green(const double val)
{
return val < 0.25 ? val * 4 * 255
: val < 0.75 ? 255
: (1.0 - val) * 4 * 255;
}
uchar val_to_red(const double val)
{
return val < 0.5 ? 0
: val < 0.75 ? (val - 0.5) * 4 * 255
: 255;
}
} // namespace DS
std::vector<quint32> buildImageData(
const int w, const int h,
uchar (*pFuncValToR)(double),
uchar (*pFuncValToG)(double),
uchar (*pFuncValToB)(double))
{
// make temp. buffer to build up raw image data
std::vector<quint32> data(w * h);
// fill raw image - make values 0 ... 1 in n steps
const int n = w - 1;
for (int x = 0; x < w; ++x) {
const double v = (double)x / n;
QRgb qRgb = qRgba(pFuncValToR(v), pFuncValToG(v), pFuncValToB(v), 255);
for (int y = 0; y < h; ++y) data[y * w + x] = qRgb;
}
// done
return data;
}
int main(int argc, char **argv)
{
qDebug() << "Qt Version: " << QT_VERSION_STR;
QApplication app(argc, argv);
// build contents
enum { w = 256, h = 32 };
std::vector<quint32> dataAGA = buildImageData(w, h,
&AGA::val_to_red, &AGA::val_to_green, &AGA::val_to_blue);
QImage qImgAGA((const uchar*)dataAGA.data(), w, h, QImage::Format_ARGB32);
std::vector<quint32> dataDS = buildImageData(w, h,
&DS::val_to_red, &DS::val_to_green, &DS::val_to_blue);
QImage qImgDS((const uchar*)dataDS.data(), w, h, QImage::Format_ARGB32);
// build some GUI
QWidget win;
QVBoxLayout qVBox;
QLabel qLblAGA(
QString::fromUtf8("QImage (Functions of Andreas Gravgaard Andersen):"));
qVBox.addWidget(&qLblAGA);
QLabel qLblImgAGA;
qLblImgAGA.setPixmap(QPixmap::fromImage(qImgAGA));
qVBox.addWidget(&qLblImgAGA);
QLabel qLblDS(
QString::fromUtf8("QImage (Functions of Scheff):"));
qVBox.addWidget(&qLblDS);
QLabel qLblImgDS;
qLblImgDS.setPixmap(QPixmap::fromImage(qImgDS));
qVBox.addWidget(&qLblImgDS);
win.setLayout(&qVBox);
win.show();
// exec. application
return app.exec();
}
我在 Windows 10(64 位)上用 VS2013、Qt5.6 编译和测试它:
备注:
val_to_
函数让我有点怀疑:一个表达式转换为(uchar)
,然后添加一个常数项(这绝对不适合(uchar)
, 结果返回为uchar
...
嗯...
因此,我重新制作了它们——稍微清理了一下。
实际上,视觉比较显示差异几乎是看不见的(唯一的例外是黄色区域中的红线)。我可以毫无问题地从原始
quint32
数组中创建QImage
(包括 cast-to-uchar*
-hack)。
更新:
可能是,并不明显:示例代码经过精心设计,允许缓冲区数据(std::vector<quint32> dataAGA
和std::vector<quint32> dataDS
)的生命周期长于Qt的生命周期图片(QImage qImgAGA
和 QImage qImgDS
)。这是根据 Qt 文档完成的。 QImage::QImage()
:
The buffer must remain valid throughout the life of the
QImage
and all copies that have not been modified or otherwise detached from the original buffer. The image does not delete the buffer at destruction. You can provide a function pointer cleanupFunction along with an extra pointer cleanupInfo that will be called when the last copy is destroyed.
图像数据可能会占用大量内存。因此,QImage
实现试图防止不必要的复制(安全内存 space 和时间)。相反,"user"(即应用程序开发人员)负责确保正确存储图像数据。