Matthias Pospiech
Matthias Pospiech

Reputation: 3488

How to convert Ximea xiAPI camera data into QImage?

I have data from a camera in mono 8bit. This is converted into an int vector using

    std::vector<int> grayVector(size);
    // convert / copy pointer data into vector: 8 bit
    if (static_cast<XI_IMG_FORMAT>(format) == XI_MONO8)
    {
        quint8* imageIterator = reinterpret_cast<quint8*> (pMemVoid);
        for (size_t count = 0; count < size; ++count)
        {
            grayVector[count] = static_cast<int>(*imageIterator);
            imageIterator++;
        }
    }

Next, I need to convert this into a QImage. If I set the image format to QImage::Format_Mono the app crashes. With QImage::Format_RGB16 I get strippes, and with QImage::Format_RGB32 everything is black.

I would like to know how to do this the best, efficient and correct way?

    // convert gray values into QImage data
    QImage image = QImage(static_cast<int>(sizeX), static_cat<int>(sizeY), QImage::Format_RGB16);
    for ( int y = 0; y < sizeY; ++y )
    {
        int yoffset = sizeY*y;
        QRgb *line = reinterpret_cast<QRgb *>(image.scanLine(y)) ;
        for ( int x = 0; x < sizeX  ; ++x )
        {
            int pos = x + yoffset;
            int color = grayVector[static_cast<size_t>(pos)];
            *line++ = qRgb(color, color, color);
        }
    }

Upvotes: 1

Views: 339

Answers (1)

The conversion to int is unnecessary and you do it in a very inefficient way; all you need is to use the QImage::Format_Grayscale8 available since Qt 5.5 (mid-2015).

Anyway, what you really want is a way to go from XI_IMG to QImage. The default BP_UNSAFE buffering policy should be adequate - the QImage will do a format conversion, so taking the data from XiApi's internal buffer is OK. Thus the following - all of the conversions are implemented in Qt and are quite efficient - much better than most any naive code.

I didn't check whether some Xi formats may need a BGR swap. If so, then the swap can be set to true in the format selection code and the rest will happen automatically.

See also: xiAPI manual.

static QVector<QRgb> grayScaleColorTable() {
  static QVector<QRgb> table;
  if (table.isEmpty()) {
    table.resize(256);
    auto *data = table.data();
    for (int i = 0; i < table.size(); ++i)
      data[i] = qRgb(i, i, i);
  }
  return table;
}

constexpr QImage::Format grayScaleFormat() {

  return (QT_VERSION >= QT_VERSION_CHECK(5,5,0))
         ? QImage::Format_Grayscale8
         : QImage::Format_Indexed8;
}

QImage convertToImage(const XI_IMG *src, QImage::Format f) {
  Q_ASSERT(src->fmt == XI_MONO16);
  Q_ASSERT((src->padding_x % 2) == 0);
  if (src->fmt != XI_MONO16) return {};
  const quint16 *s = static_cast<const quint16*>(src->bp);
  const int s_pad = src->padding_x/2;
  if (f == QImage::Format_BGR30 ||
      f == QImage::Format_A2BGR30_Premultiplied ||
      f == QImage::Format_RGB30 ||
      f == QImage::Format_A2RGB30_Premultiplied)
  {
    QImage ret{src->width, src->height, f};
    Q_ASSERT((ret->bytesPerLine() % 4) == 0);
    const int d_pad = ret->bytesPerLine()/4 - ret->width();
    quint32 *d = (quint32*)ret.bits();
    if (s_pad == d_pad) {
      const int N = (src->width + s_pad) * src->height - s_pad;
      for (int i = 0; i < N; ++i) {
        quint32 const v = (*s++) >> (16-10);
        *d++ = 0xC0000000 | v << 20 | v << 10 | v;
      }          
    } else {
      for (int j = 0; j < src->height; ++j) {
        for (int i = 0; i < src->width; ++i) {
          quint32 const v = (*s++) >> (16-10);
          *d++ = 0xC0000000u | v << 20 | v << 10 | v;
        }
        s += s_pad;
        d += d_pad;
      }
    }
    return ret;
  }
  QImage ret{src->width, src->height, grayScaleFormat()};
  const int d_pad = ret->bytesPerLine() - ret->width();
  auto *d = ret.bits();
  if (s_pad == d_pad) {
    const int N = (src->width + s_pad) * src->height - s_pad;
    for (int i = 0; i < N; ++i) {
      *d++ = (*s++) >> 8;
  } else {
    for (int j = 0; j < src->height; ++j) {
      for (int i = 0; i < src->width; ++i) 
        *d++ = (*s++) >> 8;
      s += s_pad;
      d += d_pad;
    }
  }
  return ret;
}

QImage fromXiImg(const XI_IMG *src, QImage::Format dstFormat = QImage::Format_ARGB32Premultiplied) {
  Q_ASSERT(src->width > 0 && src->height > 0 && src->padding_x >= 0 && src->bp_size > 0);
  Q_ASSERT(dstFormat != QImage::Format_Invalid);
  bool swap = false;
  int srcPixelBytes = 0;
  bool externalConvert = false;
  QImage::Format srcFormat = QImage::Format_Invalid;
  switch (src->fmt) {
  case XI_MONO8:
    srcPixelBytes = 1;
    srcFormat = grayScaleFormat();
    break;
  case XI_MONO16:
    srcPixelBytes = 2;
    externalConvert = true;
    break;
  case XI_RGB24:
    srcPixelBytes = 3;
    srcFormat = QImage::Format_RGB888;
    break;
  case XI_RGB32:
    srcPixelBytes = 4;
    srcFormat = QImage::Format_RGB32;
    break;
  };
  if (srcFormat == QImage::Format_Invalid && !externalConvert) {
    qWarning("Unhandled XI_IMG image format");
    return {};
  }
  Q_ASSERT(srcPixelBytes > 0 && srcPixelBytes <= 4);
  int bytesPerLine = src->width * srcPixelBytes + src->padding_x;
  if ((bytesPerLine * src->height - src->padding_x) > src->bp_size) {
    qWarning("Inconsistent XI_IMG data");
    return {};
  }
  QImage ret;
  if (!externalConvert)
    ret = QImage{static_cast<const uchar*>(src->bp), src->width, src->height,
                 bytesPerLine, srcFormat};
  else
    ret = convertToImage(src, dstFormat);
  if (ret.format() == QImage::Format_Indexed8)
    ret.setColorTable(grayScaleColorTable());
  if (ret.format() != dstFormat)
    ret = std::move(ret).convertToFormat(dstFormat);
  if (swap)
    ret = std::move(ret).rgbSwapped();
  if (!ret.isDetached()) // ensure that we don't share XI_IMG's data buffer
    ret.detach();
  return ret;
}

Upvotes: 0

Related Questions