从 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 编译和测试它:

备注:

  1. val_to_ 函数让我有点怀疑:一个表达式转换为 (uchar),然后添加一个常数项(这绝对不适合 (uchar), 结果返回为 uchar...
    嗯...
    因此,我重新制作了它们——稍微清理了一下。
    实际上,视觉比较显示差异几乎是看不见的(唯一的例外是黄色区域中的红线)。

  2. 我可以毫无问题地从原始 quint32 数组中创建 QImage(包括 cast-to-uchar*-hack)。


更新:

可能是,并不明显:示例代码经过精心设计,允许缓冲区数据(std::vector<quint32> dataAGAstd::vector<quint32> dataDS)的生命周期长于Qt的生命周期图片(QImage qImgAGAQImage 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"(即应用程序开发人员)负责确保正确存储图像数据。