I want to use DIPlip library for my project in Qt framework to show the result of DIPlib Image I need to convert dip::Image
class to Qt framework QImage
class, in format QImage::Format_RGB888
.
How can I convert DIPlib dip::Image
to QImage
for grey Image in C++ language?
Qt's QImage::Format_RGB888
is storage-compatible with DIPlib, as far as I can tell from its documentation. The main difference is that it expects each scanline (row) to be 32-bit aligned. So, depending on the width of the image, there might be between 1 and 3 bytes of padding at the end of each line. DIPlib does not do this by default, and it is not trivial to get it to do so in all cases (it is easy for the gray-scale case, but not for the RGB case).
So the best approach is to have Qt allocate the image data, encapsulate that in a dip::Image
object, and write to that.
You need to make sure that you keep the Qt object alive while you use the DIPlib one. The Qt object owns the data.
In most cases, Qt image data has some padding at the end of each line, so we need to explicitly tell DIPlib about the strides (size of a step to get to the next pixel along each dimension). We use this dip::Image
constructor:
int width = 800;
int height = 600;
QImage qt_image(width, height, QImage::Format::Format_RGB888);
dip::Image dip_image(
dip::NonOwnedRefToDataSegment(qt_image.constBits()),
const_cast<uchar*>(qt_image.constBits()),
dip::DT_UINT8,
{qt_image.width(), qt_image.height()},
{3, qt_image.bytesPerLine()},
dip::Tensor{3},
1
);
dip_image.Protect(); // prevents the image from being reallocated
You can now write into dip_image
with any DIPlib function (as long as they accept 3-channel output), and qt_image
will be updated as well.
You can do the same thing with some of the other formats (RGBA64, RGB32, RGBX32FPx4, etc.), but the formats where multiple sample values are packed into fewer bytes (such as RGB16 or RGB666) need to be converted to a different format first (and consequently DIPlib cannot write into those).
Formats where each pixel takes up 32 bits or a multiple (RGBA64, RGB32, RGBX32FPx4) will not need any padding. In this case you can use the simplified dip::Image
constructor (documentation):
dip::Image dip_image(const_cast<uchar*>(qt_image.constBits()), {qt_image.width(), qt_image.height()}, 3);
dip_image.Protect(); // prevents the image from being reallocated
For this case, where there is no padding, we can also create a DIPlib image object and encapsulate that in a Qt object. QImage
has a constructor (documentation) that takes a pointer to the pixel data and the sizes and format as input:
dip::Image dip_image({padded, height}, 4, dip::DT_UINT8);
QImage::QImage qt_image(static_cast<uchar*>(dip_image.Origin()), dip_image.Size(0), dip_image.Size(1), QImage::Format::Format_RGB32);
You can now display qt_image
, and the user will see the image in dip_image
. Modifying either will modify the other too, as they're sharing data.
If dip_image
is modified with methods such as Mirror()
or Rotation90()
, then its layout no longer matches what Qt expects. Use dip_image.ForceNormalStrides()
to rewrite the data in the standard format (note that there will be no padding after this, so that doesn't work for RGB888).
DIPlib also supports automatically allocating a Qt image whenever a DIPlib image is reforged. This is done through dip::ExternalInterface
. There are some examples available if you want to go this route. It's not trivial to write an external interface for Qt, but once you've written it, it will be really easy to use.
For your specific use case:
dip::String fname = "2.jpg";
auto info = dip::ImageReadJPEGInfo(fname);
QImage qt_image(info.sizes[0], info.sizes[1], QImage::Format::Format_RGB888);
dip::Image dip_image(
dip::NonOwnedRefToDataSegment(qt_image.constBits()),
const_cast<uchar*>(qt_image.constBits()),
dip::DT_UINT8,
info.sizes,
{3, qt_image.bytesPerLine()},
dip::Tensor{3},
1
);
dip_image.Protect();
if (info.tensorElements == 1) {
dip::Image channel = dip_image[0];
dip::ImageReadJPEG(channel, fname);
dip_image[1] = channel;
dip_image[2] = channel;
} else {
dip::ImageReadJPEG(dip_image, fname);
}