【问题标题】:Creating ARGB QImage from uint32 or uchar array从 uint32 或 uchar 数组创建 ARGB QImage
【发布时间】:2017-08-05 17:22:06
【问题描述】:

当我尝试使用 QImage 构造函数从 reinterpret_cast<uchar*>(quint32*) 创建 ARGB32 QImage 时,Image 会丢失其颜色和 alpha 通道,并且生成的 QImage 是灰度的!

如果我尝试以灰度显示,灰度图像会按预期显示。所以我知道 quint32 数组的 ushort 数据的缩放和索引很顺利,但是出了什么问题?

A Qt forum post 建议按照我的方式进行操作(据我所见),但自该版本的 Qt 以来行为可能发生了变化? (我使用的是 Qt 5.9)

我意识到文档说:

数据必须是 32 位对齐的,并且图像中数据的每个扫描线 也必须是 32 位对齐的。

但我希望 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() 在之后立即调用时返回 true!

我做错了什么,我应该怎么做才能创建 ARGB 图像并同时保留颜色和 Alpha 通道?

【问题讨论】:

  • 疑难解答建议:尝试用二维for循环和QImage的“putpixel”方法逐像素生成图像,看看是否正确。如果是正确的,那么将这个正确图像中的原始数据与灰度图像中的数据进行比较。如果不对,那么问题一定出在原始的原始图像数据中……

标签: c++ qt qimage argb


【解决方案1】:

我试图重现您的问题,但我不能。

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 数组(包括 cast-to-uchar*-hack)中创建一个 QImage


更新:

可能是,这并不明显:示例代码经过精心设计,以确保缓冲区数据的生命周期(std::vector&lt;quint32&gt; dataAGAstd::vector&lt;quint32&gt; dataDS)比 Qt 图像的生命周期长(QImage qImgAGAQImage qImgDS)。这是根据 Qt 文档完成的。对于QImage::QImage()

缓冲区必须在QImage 的整个生命周期内以及所有未修改或以其他方式与原始缓冲区分离的副本保持有效。图像在销毁时不会删除缓冲区。您可以提供一个函数指针 cleanupFunction 以及一个额外的指针 cleanupInfo,该指针将在最后一个副本被销毁时调用。

图像数据可能会消耗大量内存。因此,QImage 实现试图防止不必要的复制(以安全的内存空间和时间)。相反,“用户”(即应用程序开发人员)负责确保正确存储图像数据。

【讨论】:

  • 使用std::vector&lt;quint32&gt; 代替tmpData 而不是quint32* 成功了!谢谢你,也对我的颜色转换进行了更正(我需要一段时间才能找到那条红线的来源!)
  • @AndreasGravgaardAndersen 您是否知道“缓冲区必须在 QImage 的整个生命周期以及所有未修改或以其他方式与原始缓冲区分离的副本保持有效。”? (引自文档:QImage::QImage()
  • 我知道,但我可能不完全理解这意味着什么。我认为当 .allGray() 在构造后立即返回 true 时,(在进行复制和/或修改之前)缓冲区在那时仍然有效吗?至少在调试模式下(VS2015)。或者我必须从字面上理解 “整个 QImage 的生命周期” - 由于编译时的一些副作用?
  • @AndreasGravgaardAndersen 恕我直言,您必须“从字面上”理解。我对此的回答添加了更新。关于您提到的.allGray() 效果:根据文档。我猜,allGray() 检查图像数据(索引图像的颜色表或像素数据本身)。明确暗示该方法对于非颜色表图像很慢,这让我想到了这一点。错误的结果可能是由未定义的行为引起的,即如果访问了不再为 Qt 图像保留的图像数据(并且可能在不同的使用中同时使用)。但这只是一个疯狂的猜测......
猜你喜欢
  • 2021-05-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-16
  • 2011-03-23
  • 1970-01-01
  • 2014-10-14
相关资源
最近更新 更多