From fbbaef214a47ad97a35f8af4616ab60038578fa0 Mon Sep 17 00:00:00 2001 From: Ian Chen Date: Tue, 20 Sep 2022 13:45:45 -0700 Subject: [PATCH 1/4] fix reading value from single channel 16 bit image Signed-off-by: Ian Chen --- geospatial/src/ImageHeightmap.cc | 1 + graphics/src/Image.cc | 65 ++++++++++++++++++++++++++----- graphics/src/Image_TEST.cc | 44 +++++++++++++++++++++ test/data/grayscale_16bit.png | Bin 0 -> 7781 bytes test/data/grayscale_8bit.png | Bin 0 -> 7765 bytes 5 files changed, 100 insertions(+), 10 deletions(-) create mode 100644 test/data/grayscale_16bit.png create mode 100644 test/data/grayscale_8bit.png diff --git a/geospatial/src/ImageHeightmap.cc b/geospatial/src/ImageHeightmap.cc index 301d549ec..c64a4dbd4 100644 --- a/geospatial/src/ImageHeightmap.cc +++ b/geospatial/src/ImageHeightmap.cc @@ -113,5 +113,6 @@ unsigned int ImageHeightmap::Width() const ////////////////////////////////////////////////// float ImageHeightmap::MaxElevation() const { + std::cerr << "max elevation " << std::endl; return this->img.MaxColor().R(); } diff --git a/graphics/src/Image.cc b/graphics/src/Image.cc index 720f9df57..d4b0a2ad4 100644 --- a/graphics/src/Image.cc +++ b/graphics/src/Image.cc @@ -67,6 +67,14 @@ namespace gz /// \return bitmap data with red and blue pixels swapped public: FIBITMAP* SwapRedBlue(const unsigned int &_width, const unsigned int &_height) const; + + /// \brief Get pixel value at specified index. + /// \param[in] _dib Pointer to Freeimage bitmap + /// \param[in] _x Pixel index in horizontal direction + /// \param[in] _y Pixel index in vertical direction + /// \param[out] _color Pixel value at specified index + public: BOOL PixelIndex(FIBITMAP *_dib, unsigned _x, unsigned _y, + math::Color &_color) const; }; } } @@ -507,16 +515,13 @@ math::Color Image::Pixel(unsigned int _x, unsigned int _y) const } else { - BYTE byteValue; - if (FreeImage_GetPixelIndex( - this->dataPtr->bitmap, _x, _y, &byteValue) == FALSE) + if (this->dataPtr->PixelIndex( + this->dataPtr->bitmap, _x, _y, clr) == FALSE) { gzerr << "Image: Coordinates out of range [" << _x << " " << _y << "] \n"; return clr; } - - clr.Set(byteValue, byteValue, byteValue); } return clr; @@ -590,23 +595,21 @@ math::Color Image::MaxColor() const } else { - BYTE byteValue; for (y = 0; y < this->Height(); y++) { for (x = 0; x < this->Width(); x++) { clr.Set(0, 0, 0, 0); - if (FreeImage_GetPixelIndex( - this->dataPtr->bitmap, x, y, &byteValue) == FALSE) + if (this->dataPtr->PixelIndex( + this->dataPtr->bitmap, x, y, clr) == FALSE) + { gzerr << "Image: Coordinates out of range [" << x << " " << y << "] \n"; continue; } - clr.Set(byteValue, byteValue, byteValue); - if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B()) { maxClr = clr; @@ -618,6 +621,48 @@ math::Color Image::MaxColor() const return maxClr; } +////////////////////////////////////////////////// +BOOL Image::Implementation::PixelIndex( + FIBITMAP *_dib, unsigned _x, unsigned _y, math::Color &_color) const +{ + if (!_dib) + return FALSE; + + FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(_dib); + // 8 bit images + if (imageType == FIT_BITMAP) + { + BYTE byteValue; + // FreeImage_GetPixelIndex should also work with 1 and 4 bit images + if (FreeImage_GetPixelIndex( + _dib, _x, _y, &byteValue) == FALSE) + return FALSE; + + unsigned int bpp = FreeImage_GetBPP(_dib); + // convert to float value between 0-1 + float value = byteValue / + static_cast(((1 << (bpp)) - 1)); + _color.Set(value, value, value); + } + // 16 bit images + else if (imageType == FIT_UINT16) + { + if ((_x < FreeImage_GetWidth(_dib)) && (_y < FreeImage_GetHeight(_dib))) + { + WORD *bits = reinterpret_cast(FreeImage_GetScanLine(_dib, _y)); + uint16_t word = static_cast(bits[_x]); + // convert to float value between 0-1 + float value = word / static_cast(math::MAX_UI16); + _color.Set(value, value, value); + } + else + { + return FALSE; + } + } + return TRUE; +} + ////////////////////////////////////////////////// void Image::Rescale(int _width, int _height) { diff --git a/graphics/src/Image_TEST.cc b/graphics/src/Image_TEST.cc index a28bd49e8..d1bd29a2f 100644 --- a/graphics/src/Image_TEST.cc +++ b/graphics/src/Image_TEST.cc @@ -594,6 +594,50 @@ TEST_F(ImageTest, ConvertToRGBImage) } } +///////////////////////////////////////////////// +TEST_F(ImageTest, Grayscale) +{ + { + common::Image img; + std::string fileName = common::testing::TestFile("data", + "grayscale_8bit.png"); + EXPECT_EQ(0, img.Load(fileName)); + unsigned int width = 4u; + unsigned int height = 4u; + unsigned int bits = 8u; + EXPECT_TRUE(img.Valid()); + EXPECT_EQ(width, img.Width()); + EXPECT_EQ(height, img.Height()); + EXPECT_EQ(bits, img.BPP()); + EXPECT_EQ(width * bits / 8u, img.Pitch()); + EXPECT_EQ(common::Image::PixelFormatType::L_INT8, img.PixelFormat()); + math::Color maxColor(0.847, 0.847, 0.847); + EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3); + EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3); + EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3); + } + + { + common::Image img; + std::string fileName = common::testing::TestFile("data", + "grayscale_16bit.png"); + EXPECT_EQ(0, img.Load(fileName)); + unsigned int width = 4u; + unsigned int height = 4u; + unsigned int bits = 16u; + EXPECT_TRUE(img.Valid()); + EXPECT_EQ(width, img.Width()); + EXPECT_EQ(height, img.Height()); + EXPECT_EQ(bits, img.BPP()); + EXPECT_EQ(width * bits / 8u, img.Pitch()); + EXPECT_EQ(common::Image::PixelFormatType::L_INT16, img.PixelFormat()); + math::Color maxColor(0.847, 0.847, 0.847); + EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3); + EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3); + EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3); + } +} + using string_int2 = std::tuple; class ImagePerformanceTest : public ImageTest, diff --git a/test/data/grayscale_16bit.png b/test/data/grayscale_16bit.png new file mode 100644 index 0000000000000000000000000000000000000000..96d3b2b0fd7a28aa1133447f41c22632fed3889d GIT binary patch literal 7781 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 Qfu$TpL`?XW(2uA57tXk~3IG5A literal 0 HcmV?d00001 diff --git a/test/data/grayscale_8bit.png b/test/data/grayscale_8bit.png new file mode 100644 index 0000000000000000000000000000000000000000..66d505b1ac094cd1fce4421b09787b91e35836ed GIT binary patch literal 7765 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 Date: Tue, 20 Sep 2022 15:37:30 -0700 Subject: [PATCH 2/4] remove println Signed-off-by: Ian Chen --- geospatial/src/ImageHeightmap.cc | 1 - 1 file changed, 1 deletion(-) diff --git a/geospatial/src/ImageHeightmap.cc b/geospatial/src/ImageHeightmap.cc index c64a4dbd4..301d549ec 100644 --- a/geospatial/src/ImageHeightmap.cc +++ b/geospatial/src/ImageHeightmap.cc @@ -113,6 +113,5 @@ unsigned int ImageHeightmap::Width() const ////////////////////////////////////////////////// float ImageHeightmap::MaxElevation() const { - std::cerr << "max elevation " << std::endl; return this->img.MaxColor().R(); } From d1d05950ca2b3a5c37448c11d0d994f264b2f2c1 Mon Sep 17 00:00:00 2001 From: Nate Koenig Date: Wed, 21 Sep 2022 14:41:50 -0700 Subject: [PATCH 3/4] documentation and style Signed-off-by: Nate Koenig --- graphics/src/Image.cc | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/graphics/src/Image.cc b/graphics/src/Image.cc index d4b0a2ad4..ad65dc757 100644 --- a/graphics/src/Image.cc +++ b/graphics/src/Image.cc @@ -73,6 +73,8 @@ namespace gz /// \param[in] _x Pixel index in horizontal direction /// \param[in] _y Pixel index in vertical direction /// \param[out] _color Pixel value at specified index + /// \return TRUE value if the pixel index was found and the color + /// value set, FALSE otherwise. public: BOOL PixelIndex(FIBITMAP *_dib, unsigned _x, unsigned _y, math::Color &_color) const; }; @@ -636,12 +638,13 @@ BOOL Image::Implementation::PixelIndex( // FreeImage_GetPixelIndex should also work with 1 and 4 bit images if (FreeImage_GetPixelIndex( _dib, _x, _y, &byteValue) == FALSE) + { return FALSE; + } unsigned int bpp = FreeImage_GetBPP(_dib); // convert to float value between 0-1 - float value = byteValue / - static_cast(((1 << (bpp)) - 1)); + float value = byteValue / static_cast(((1 << (bpp)) - 1)); _color.Set(value, value, value); } // 16 bit images From ea4289c47fa8cf519199951efbb6199eedd113d7 Mon Sep 17 00:00:00 2001 From: ahcorde Date: Thu, 22 Sep 2022 08:34:16 +0200 Subject: [PATCH 4/4] Removed windows warning Signed-off-by: ahcorde --- graphics/src/Image_TEST.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/graphics/src/Image_TEST.cc b/graphics/src/Image_TEST.cc index d1bd29a2f..c8b0ac6e7 100644 --- a/graphics/src/Image_TEST.cc +++ b/graphics/src/Image_TEST.cc @@ -611,7 +611,7 @@ TEST_F(ImageTest, Grayscale) EXPECT_EQ(bits, img.BPP()); EXPECT_EQ(width * bits / 8u, img.Pitch()); EXPECT_EQ(common::Image::PixelFormatType::L_INT8, img.PixelFormat()); - math::Color maxColor(0.847, 0.847, 0.847); + math::Color maxColor(0.847f, 0.847f, 0.847f); EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3); EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3); EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3); @@ -631,7 +631,7 @@ TEST_F(ImageTest, Grayscale) EXPECT_EQ(bits, img.BPP()); EXPECT_EQ(width * bits / 8u, img.Pitch()); EXPECT_EQ(common::Image::PixelFormatType::L_INT16, img.PixelFormat()); - math::Color maxColor(0.847, 0.847, 0.847); + math::Color maxColor(0.847f, 0.847f, 0.847f); EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3); EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3); EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3);