Added Image constructor from PixelBuffer and QImage

This commit is contained in:
Matthias Koefferlein 2023-07-29 10:34:59 +02:00
parent abf2970438
commit aa4eeebfbb
4 changed files with 242 additions and 119 deletions

View File

@ -30,6 +30,10 @@
#include "dbTilingProcessor.h"
#include "layLayoutViewBase.h"
#if defined(HAVE_QT)
# include <QImage>
#endif
namespace gsi
{
@ -437,27 +441,22 @@ static ImageRef *new_image ()
return new ImageRef ();
}
static ImageRef *new_image_f (const std::string &filename)
{
return new ImageRef (img::Object (filename, db::DCplxTrans ()));
}
static ImageRef *new_image_ft (const std::string &filename, const db::DCplxTrans &trans)
{
return new ImageRef (img::Object (filename, trans));
}
/*
static ImageRef *new_image_whc (size_t w, size_t h, bool color)
static ImageRef *new_image_pbt (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans)
{
return new ImageRef (img::Object (w, h, db::DCplxTrans (), color));
return new ImageRef (img::Object (pixel_buffer, trans));
}
static ImageRef *new_image_whtc (size_t w, size_t h, const db::DCplxTrans &trans, bool color)
#if HAVE_QT
static ImageRef *new_image_qit (const QImage &image, const db::DCplxTrans &trans)
{
return new ImageRef (img::Object (w, h, trans, color));
return new ImageRef (img::Object (image, trans));
}
*/
#endif
static ImageRef *new_image_whd (size_t w, size_t h, const std::vector<double> &data)
{
@ -576,17 +575,8 @@ gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
"This will create an empty image without data and no particular pixel width or related.\n"
"Use the \\read_file or \\set_data methods to set image properties and pixel values.\n"
) +
gsi::constructor ("new", &gsi::new_image_f, gsi::arg ("filename"),
"@brief Constructor from a image file \n"
"\n"
"This constructor creates an image object from a file (which can have any format supported by Qt) and \n"
"a unit transformation. The image will originally be put to position 0,0 (lower left corner) and each pixel\n"
"will have a size of 1 (micron). \n"
"\n"
"@param filename The path to the image file to load.\n"
) +
gsi::constructor ("new", &gsi::new_image_ft, gsi::arg ("filename"), gsi::arg ("trans"),
"@brief Constructor from a image file \n"
gsi::constructor ("new", &gsi::new_image_ft, gsi::arg ("filename"), gsi::arg ("trans", db::DCplxTrans (), "unity"),
"@brief Constructor from a image file\n"
"\n"
"This constructor creates an image object from a file (which can have any format supported by Qt) and \n"
"a transformation. The image will originally be put to position 0,0 (lower left corner) and each pixel\n"
@ -595,6 +585,31 @@ gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
"@param filename The path to the image file to load.\n"
"@param trans The transformation to apply to the image when displaying it.\n"
) +
gsi::constructor ("new", &gsi::new_image_pbt, gsi::arg ("pixels"), gsi::arg ("trans", db::DCplxTrans (), "unity"),
"@brief Constructor from a image pixel buffer\n"
"\n"
"This constructor creates an image object from a pixel buffer object. This object holds RGB or mono image data similar to "
"QImage, except it is available also when Qt is not available (e.g. inside the Python module).\n"
"\n"
"The image will originally be put to position 0,0 (lower left corner) and each pixel\n"
"will have a size of 1. The transformation describes how to transform this image into micron space.\n"
"\n"
"@param filename The path to the image file to load.\n"
"@param trans The transformation to apply to the image when displaying it.\n"
) +
#if defined(HAVE_QT)
gsi::constructor ("new", &gsi::new_image_qit, gsi::arg ("image"), gsi::arg ("trans", db::DCplxTrans (), "unity"),
"@brief Constructor from a image pixel buffer\n"
"\n"
"This constructor creates an image object from a pixel QImage object and uses RGB or mono image data to generate the image.\n"
"\n"
"The image will originally be put to position 0,0 (lower left corner) and each pixel\n"
"will have a size of 1. The transformation describes how to transform this image into micron space.\n"
"\n"
"@param filename The path to the image file to load.\n"
"@param trans The transformation to apply to the image when displaying it.\n"
) +
#endif
gsi::constructor ("new", &gsi::new_image_whd, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("data"),
"@brief Constructor for a monochrome image with the given pixel values\n"
"\n"
@ -624,39 +639,6 @@ gsi::Class<ImageRef> decl_Image (decl_BasicImage, "lay", "Image",
"@param trans The transformation from pixel space to micron space\n"
"@param d The data (see method description)\n"
) +
/* HINT: these declarations cannot be used currently since any array is case to the boolean color parameter
gsi::constructor ("new", &img::new_image_whc, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("color"),
"@brief Constructor for monochrome or color images with zero pixel values\n"
"\n"
"This constructor creates an image object from a data set describing one monochrome channel\n"
"or three color channels.\n"
"Each channel consists of an array of x*y values where the first \"x\" values describe the first (lowest!) row\n"
"and so on. Note, that the rows are oriented in the mathematical sense (first one is the lowest) contrary to \n"
"the common convention for image data.\n"
"The data fields can be accessed with the \"data\", \"set_data\", \"pixel\" or \"set_pixel\" methods.\n"
"Initially the pixel width and height will be 1 micron and the data range will be 0 to 1.0 (black to white level). \n"
"\n"
"@param w The width of the image\n"
"@param h The height of the image\n"
"@param color True to create a color image.\n"
) +
gsi::constructor ("new", &img::new_image_whtc, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("trans"), gsi::arg ("color"),
"@brief Constructor for monochrome or color images with zero pixel values\n"
"\n"
"This constructor creates an image object from a data set describing one monochrome channel\n"
"or three color channels.\n"
"Each channel consists of an array of x*y values where the first \"x\" values describe the first (lowest!) row\n"
"and so on. Note, that the rows are oriented in the mathematical sense (first one is the lowest) contrary to \n"
"the common convention for image data.\n"
"The data fields can be accessed with the \"data\", \"set_data\", \"pixel\" or \"set_pixel\" methods.\n"
"Initially the pixel width and height will be 1 micron and the data range will be 0 to 1.0 (black to white level). \n"
"\n"
"@param w The width of the image\n"
"@param h The height of the image\n"
"@param trans The transformation to apply to the image when displaying it\n"
"@param color True to create a color image.\n"
) +
*/
gsi::constructor ("new", &gsi::new_image_whrgb, gsi::arg ("w"), gsi::arg ("h"), gsi::arg ("red"), gsi::arg ("green"), gsi::arg ("blue"),
"@brief Constructor for a color image with the given pixel values\n"
"\n"

View File

@ -851,6 +851,30 @@ Object::Object (const std::string &filename, const db::DCplxTrans &trans)
m_updates_enabled = true;
}
Object::Object (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans)
: m_filename ("<object>"), m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
m_updates_enabled = false;
mp_pixel_data = 0;
mp_data = 0;
create_from_pixel_buffer (pixel_buffer);
m_updates_enabled = true;
}
#if defined(HAVE_QT)
Object::Object (const QImage &qimage, const db::DCplxTrans &trans)
: m_filename ("<object>"), m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
m_updates_enabled = false;
mp_pixel_data = 0;
mp_data = 0;
create_from_qimage (qimage);
m_updates_enabled = true;
}
#endif
Object::Object (size_t w, size_t h, const db::Matrix3d &trans, bool color, bool byte_data)
: m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
@ -934,6 +958,31 @@ Object::Object (const std::string &filename, const db::Matrix3d &trans)
m_updates_enabled = true;
}
Object::Object (const tl::PixelBuffer &pixel_buffer, const db::Matrix3d &trans)
: m_filename ("<object>"), m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
m_updates_enabled = false;
mp_pixel_data = 0;
mp_data = 0;
create_from_pixel_buffer (pixel_buffer);
read_file ();
m_updates_enabled = true;
}
#if defined(HAVE_QT)
Object::Object (const QImage &qimage, const db::Matrix3d &trans)
: m_filename ("<object>"), m_trans (trans), m_id (make_id ()), m_min_value (0.0), m_max_value (1.0), m_min_value_set (false), m_max_value_set (false), m_visible (true), m_z_position (0)
{
m_updates_enabled = false;
mp_pixel_data = 0;
mp_data = 0;
create_from_qimage (qimage);
m_updates_enabled = true;
}
#endif
Object::Object (const img::Object &d)
{
m_updates_enabled = false;
@ -1566,65 +1615,7 @@ Object::read_file ()
#if defined(HAVE_QT)
QImage qimage (tl::to_qstring (m_filename));
if (! qimage.isNull ()) {
if (! m_min_value_set) {
m_min_value = 0.0;
}
if (! m_max_value_set) {
m_max_value = 255.0;
}
m_min_value_set = true;
m_max_value_set = true;
size_t w = qimage.width (), h = qimage.height ();
mp_data = new DataHeader (w, h, ! qimage.isGrayscale (), true);
mp_data->add_ref ();
size_t i = 0;
if (is_color ()) {
unsigned char *red = mp_data->byte_data (0);
unsigned char *green = mp_data->byte_data (1);
unsigned char *blue = mp_data->byte_data (2);
unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0;
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1)));
red[i] = qRed (rgb);
green[i] = qGreen (rgb);
blue[i] = qBlue (rgb);
if (msk) {
msk[i] = qAlpha (rgb) > 128;
}
++i;
}
}
} else {
unsigned char *d = mp_data->byte_data ();
unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0;
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1)));
*d++ = qGreen (rgb);
if (msk) {
msk[i] = qAlpha (rgb) > 128;
}
}
}
}
}
create_from_qimage (qimage);
#elif defined(HAVE_PNG)
@ -1635,6 +1626,82 @@ Object::read_file ()
img = tl::PixelBuffer::read_png (stream);
}
create_from_pixel_buffer (img);
#else
throw tl::Exception ("No PNG support compiled in - cannot load PNG files");
#endif
}
#if defined(HAVE_QT)
void
Object::create_from_qimage (const QImage &qimage)
{
if (qimage.isNull ()) {
return;
}
if (! m_min_value_set) {
m_min_value = 0.0;
}
if (! m_max_value_set) {
m_max_value = 255.0;
}
m_min_value_set = true;
m_max_value_set = true;
size_t w = qimage.width (), h = qimage.height ();
mp_data = new DataHeader (w, h, ! qimage.isGrayscale (), true);
mp_data->add_ref ();
size_t i = 0;
if (is_color ()) {
unsigned char *red = mp_data->byte_data (0);
unsigned char *green = mp_data->byte_data (1);
unsigned char *blue = mp_data->byte_data (2);
unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0;
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1)));
red[i] = qRed (rgb);
green[i] = qGreen (rgb);
blue[i] = qBlue (rgb);
if (msk) {
msk[i] = qAlpha (rgb) > 128;
}
++i;
}
}
} else {
unsigned char *d = mp_data->byte_data ();
unsigned char *msk = qimage.hasAlphaChannel () ? mp_data->set_mask () : 0;
for (size_t y = 0; y < h; ++y) {
for (size_t x = 0; x < w; ++x) {
QRgb rgb = qimage.pixel (QPoint (int (x), int (h - y - 1)));
*d++ = qGreen (rgb);
if (msk) {
msk[i] = qAlpha (rgb) > 128;
}
}
}
}
}
#endif
void
Object::create_from_pixel_buffer (const tl::PixelBuffer &img)
{
bool is_color = false;
for (unsigned int i = 0; i < img.height () && ! is_color; ++i) {
const tl::color_t *d = img.scan_line (i);
@ -1700,10 +1767,6 @@ Object::read_file ()
}
}
#else
throw tl::Exception ("No PNG support compiled in - cannot load PNG files");
#endif
}
void

View File

@ -21,7 +21,6 @@
*/
#ifndef HDR_imgObject
#define HDR_imgObject
@ -34,10 +33,15 @@
#include "dbPolygon.h"
#include "tlDataMapping.h"
#include "tlColor.h"
#include "tlPixelBuffer.h"
#include <string>
#include <vector>
#if defined(HAVE_QT)
class QImage;
#endif
namespace img {
class DataHeader;
@ -293,6 +297,26 @@ public:
*/
Object (const std::string &filename, const db::DCplxTrans &trans);
/**
* @brief Constructor from a PixelBuffer object
*
* This constructor creates an image object from a PixelBuffer object.
* The image will originally be put to position 0, 0 (lower left corner) and each pixel
* will have a size of 1. The transformation describes how to transform this image into micron space.
*/
Object (const tl::PixelBuffer &pixel_buffer, const db::DCplxTrans &trans);
#if defined(HAVE_QT)
/**
* @brief Constructor from a QImage object
*
* This constructor creates an image object from a QImage object.
* The image will originally be put to position 0, 0 (lower left corner) and each pixel
* will have a size of 1. The transformation describes how to transform this image into micron space.
*/
Object (const QImage &image, const db::DCplxTrans &trans);
#endif
/**
* @brief Constructor for monochrome or color images with zero pixel values
*
@ -418,6 +442,26 @@ public:
*/
Object (const std::string &filename, const db::Matrix3d &trans);
/**
* @brief Constructor from a PixelBuffer object
*
* This constructor creates an image object from a PixelBuffer object.
* The image will originally be put to position 0, 0 (lower left corner) and each pixel
* will have a size of 1. The transformation describes how to transform this image into micron space.
*/
Object (const tl::PixelBuffer &pixel_buffer, const db::Matrix3d &trans);
#if defined(HAVE_QT)
/**
* @brief Constructor from a QImage object
*
* This constructor creates an image object from a QImage object.
* The image will originally be put to position 0, 0 (lower left corner) and each pixel
* will have a size of 1. The transformation describes how to transform this image into micron space.
*/
Object (const QImage &image, const db::Matrix3d &trans);
#endif
/**
* @brief Copy constructor
*/
@ -984,6 +1028,8 @@ private:
void validate_pixel_data () const;
void allocate (bool color);
void read_file ();
void create_from_qimage (const QImage &qimage);
void create_from_pixel_buffer (const tl::PixelBuffer &img);
};
}

View File

@ -415,6 +415,38 @@ class IMG_TestClass < TestBase
end
# Construction from PixelBuffer
def test_6
fn = ENV["TESTSRC"] + "/testdata/img/gs.png"
pb = RBA::PixelBuffer::read_png(fn)
image = RBA::Image.new(pb)
assert_equal(image.trans.to_s, "r0 *1 -513.5,-349")
assert_equal(image.width, 1027)
assert_equal(image.height, 698)
end
# Construction from QImage
def test_7
if RBA.constants.find { |x| x == :QImage }
fn = ENV["TESTSRC"] + "/testdata/img/gs.png"
qimage = RBA::QImage::new(fn)
image = RBA::Image.new(qimage)
assert_equal(image.trans.to_s, "r0 *1 -513.5,-349")
assert_equal(image.width, 1027)
assert_equal(image.height, 698)
end
end
end
load("test_epilogue.rb")