Skip to content

Commit 3887950

Browse files
authored
Merge pull request #699 from gazebosim/iche033/rgb16
Support reading pixel values from color 16 bit images
2 parents ac476aa + 3276a52 commit 3887950

File tree

5 files changed

+145
-74
lines changed

5 files changed

+145
-74
lines changed

graphics/include/gz/common/Image.hh

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ namespace gz
4343
"RGBA_INT8",
4444
"BGRA_INT8",
4545
"RGB_INT16",
46+
"RGBA_INT16",
4647
"RGB_INT32",
4748
"BGR_INT8",
4849
"BGR_INT16",
@@ -72,6 +73,7 @@ namespace gz
7273
RGBA_INT8,
7374
BGRA_INT8,
7475
RGB_INT16,
76+
RGBA_INT16,
7577
RGB_INT32,
7678
BGR_INT8,
7779
BGR_INT16,

graphics/src/Image.cc

Lines changed: 84 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#endif
2020
#include <FreeImage.h>
2121

22+
#include <cstdint>
2223
#include <cstring>
2324
#include <string>
2425

@@ -37,11 +38,17 @@ namespace gz
3738
class Image::Implementation
3839
{
3940
/// \brief bitmap data
40-
public: FIBITMAP *bitmap;
41+
public: FIBITMAP *bitmap{nullptr};
4142

4243
/// \brief path name of the image file
4344
public: std::string fullName;
4445

46+
/// \brief Color type for this image
47+
public: FREE_IMAGE_COLOR_TYPE colorType{FIC_RGB};
48+
49+
/// \brief Image type, i.e. pixel format
50+
public: FREE_IMAGE_TYPE imageType{FIT_UNKNOWN};
51+
4552
/// \brief Implementation of Data, returns vector of bytes
4653
public: std::vector<unsigned char> DataImpl(FIBITMAP *_img) const;
4754

@@ -150,6 +157,9 @@ int Image::Load(const std::string &_filename)
150157
return -1;
151158
}
152159

160+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
161+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
162+
153163
return 0;
154164
}
155165

@@ -257,6 +267,8 @@ void Image::SetFromData(const unsigned char *_data,
257267
this->Height());
258268
FreeImage_Unload(toDelete);
259269
}
270+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
271+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
260272
}
261273

262274
//////////////////////////////////////////////////
@@ -285,6 +297,8 @@ void Image::SetFromCompressedData(unsigned char *_data,
285297
FIMEMORY *fiMem = FreeImage_OpenMemory(_data, _size);
286298
this->dataPtr->bitmap = FreeImage_LoadFromMemory(format, fiMem);
287299
FreeImage_CloseMemory(fiMem);
300+
this->dataPtr->colorType = FreeImage_GetColorType(this->dataPtr->bitmap);
301+
this->dataPtr->imageType = FreeImage_GetImageType(this->dataPtr->bitmap);
288302
}
289303
else
290304
{
@@ -415,16 +429,22 @@ math::Color Image::Pixel(unsigned int _x, unsigned int _y) const
415429
if (!this->Valid())
416430
return clr;
417431

418-
FREE_IMAGE_COLOR_TYPE type = FreeImage_GetColorType(this->dataPtr->bitmap);
432+
if (_x >= this->Width() || _y >= this->Height())
433+
{
434+
gzerr << "Image: Coordinates out of range["
435+
<< _x << ", " << _y << "] \n";
436+
return clr;
437+
}
419438

420-
if (type == FIC_RGB || type == FIC_RGBALPHA)
439+
if ((this->dataPtr->colorType == FIC_RGB ||
440+
this->dataPtr->colorType == FIC_RGBALPHA) &&
441+
(this->dataPtr->imageType == FIT_BITMAP))
421442
{
422443
RGBQUAD firgb;
423-
424444
if (FreeImage_GetPixelColor(this->dataPtr->bitmap, _x, _y, &firgb) == FALSE)
425445
{
426-
gzerr << "Image: Coordinates out of range["
427-
<< _x << " " << _y << "] \n";
446+
gzerr << "Failed to get pixel value at ["
447+
<< _x << ", " << _y << "] \n";
428448
return clr;
429449
}
430450
clr.Set(firgb.rgbRed / 255.0f, firgb.rgbGreen / 255.0f,
@@ -435,8 +455,8 @@ math::Color Image::Pixel(unsigned int _x, unsigned int _y) const
435455
if (this->dataPtr->PixelIndex(
436456
this->dataPtr->bitmap, _x, _y, clr) == FALSE)
437457
{
438-
gzerr << "Image: Coordinates out of range ["
439-
<< _x << " " << _y << "] \n";
458+
gzerr << "Failed to get pixel value at ["
459+
<< _x << ", " << _y << "] \n";
440460
return clr;
441461
}
442462
}
@@ -473,64 +493,20 @@ math::Color Image::AvgColor() const
473493
//////////////////////////////////////////////////
474494
math::Color Image::MaxColor() const
475495
{
476-
unsigned int x, y;
477-
math::Color clr;
478496
math::Color maxClr;
479497

480-
maxClr.Set(0, 0, 0, 0);
481-
482498
if (!this->Valid())
483-
return clr;
484-
485-
FREE_IMAGE_COLOR_TYPE type = FreeImage_GetColorType(this->dataPtr->bitmap);
486-
487-
if (type == FIC_RGB || type == FIC_RGBALPHA)
488-
{
489-
RGBQUAD firgb;
499+
return maxClr;
490500

491-
for (y = 0; y < this->Height(); y++)
492-
{
493-
for (x = 0; x < this->Width(); x++)
494-
{
495-
clr.Set(0, 0, 0, 0);
496-
497-
if (FALSE ==
498-
FreeImage_GetPixelColor(this->dataPtr->bitmap, x, y, &firgb))
499-
{
500-
gzerr << "Image: Coordinates out of range["
501-
<< x << " " << y << "] \n";
502-
continue;
503-
}
504-
clr.Set(firgb.rgbRed / 255.0f, firgb.rgbGreen / 255.0f,
505-
firgb.rgbBlue / 255.0f);
506-
507-
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
508-
{
509-
maxClr = clr;
510-
}
511-
}
512-
}
513-
}
514-
else
501+
maxClr.Set(0, 0, 0, 0);
502+
for (unsigned int y = 0; y < this->Height(); y++)
515503
{
516-
for (y = 0; y < this->Height(); y++)
504+
for (unsigned int x = 0; x < this->Width(); x++)
517505
{
518-
for (x = 0; x < this->Width(); x++)
506+
math::Color clr = this->Pixel(x, y);
507+
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
519508
{
520-
clr.Set(0, 0, 0, 0);
521-
522-
if (this->dataPtr->PixelIndex(
523-
this->dataPtr->bitmap, x, y, clr) == FALSE)
524-
{
525-
gzerr << "Image: Coordinates out of range ["
526-
<< x << " " << y << "] \n";
527-
continue;
528-
}
529-
530-
if (clr.R() + clr.G() + clr.B() > maxClr.R() + maxClr.G() + maxClr.B())
531-
{
532-
maxClr = clr;
533-
}
509+
maxClr = clr;
534510
}
535511
}
536512
}
@@ -545,9 +521,11 @@ BOOL Image::Implementation::PixelIndex(
545521
if (!_dib)
546522
return FALSE;
547523

548-
FREE_IMAGE_TYPE imageType = FreeImage_GetImageType(_dib);
524+
if (_x >= FreeImage_GetWidth(_dib) || _y >= FreeImage_GetHeight(_dib))
525+
return FALSE;
526+
549527
// 8 bit images
550-
if (imageType == FIT_BITMAP)
528+
if (this->imageType == FIT_BITMAP)
551529
{
552530
BYTE byteValue;
553531
// FreeImage_GetPixelIndex should also work with 1 and 4 bit images
@@ -563,21 +541,51 @@ BOOL Image::Implementation::PixelIndex(
563541
_color.Set(value, value, value);
564542
}
565543
// 16 bit images
566-
else if (imageType == FIT_UINT16)
544+
else if (this->imageType == FIT_UINT16)
567545
{
568-
if ((_x < FreeImage_GetWidth(_dib)) && (_y < FreeImage_GetHeight(_dib)))
569-
{
570-
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
571-
uint16_t word = static_cast<uint16_t>(bits[_x]);
572-
// convert to float value between 0-1
573-
float value = word / static_cast<float>(math::MAX_UI16);
574-
_color.Set(value, value, value);
575-
}
576-
else
577-
{
578-
return FALSE;
579-
}
546+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
547+
uint16_t word = static_cast<uint16_t>(bits[_x]);
548+
// convert to float value between 0-1
549+
float value = word / static_cast<float>(math::MAX_UI16);
550+
_color.Set(value, value, value);
580551
}
552+
else if (this->imageType == FIT_INT16)
553+
{
554+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
555+
int16_t word = static_cast<int16_t>(bits[_x]);
556+
// convert to float value between 0-1
557+
float value = word / static_cast<float>(math::MAX_I16);
558+
_color.Set(value, value, value);
559+
}
560+
else if (this->imageType == FIT_RGB16)
561+
{
562+
const unsigned int channels = 3u;
563+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
564+
uint16_t r = static_cast<uint16_t>(bits[_x * channels]);
565+
uint16_t g = static_cast<uint16_t>(bits[_x * channels + 1]);
566+
uint16_t b = static_cast<uint16_t>(bits[_x * channels + 2]);
567+
// convert to float value between 0-1
568+
float valueR = r / static_cast<float>(math::MAX_UI16);
569+
float valueG = g / static_cast<float>(math::MAX_UI16);
570+
float valueB = b / static_cast<float>(math::MAX_UI16);
571+
_color.Set(valueR, valueG, valueB);
572+
}
573+
else if (this->imageType == FIT_RGBA16)
574+
{
575+
const unsigned int channels = 4u;
576+
WORD *bits = reinterpret_cast<WORD *>(FreeImage_GetScanLine(_dib, _y));
577+
uint16_t r = static_cast<uint16_t>(bits[_x * channels]);
578+
uint16_t g = static_cast<uint16_t>(bits[_x * channels + 1]);
579+
uint16_t b = static_cast<uint16_t>(bits[_x * channels + 2]);
580+
uint16_t a = static_cast<uint16_t>(bits[_x * channels + 3]);
581+
// convert to float value between 0-1
582+
float valueR = r / static_cast<float>(math::MAX_UI16);
583+
float valueG = g / static_cast<float>(math::MAX_UI16);
584+
float valueB = b / static_cast<float>(math::MAX_UI16);
585+
float valueA = a / static_cast<float>(math::MAX_UI16);
586+
_color.Set(valueR, valueG, valueB, valueA);
587+
}
588+
581589
return TRUE;
582590
}
583591

@@ -634,6 +642,8 @@ Image::PixelFormatType Image::PixelFormat() const
634642
}
635643
else if (type == FIT_RGB16)
636644
fmt = RGB_INT16;
645+
else if (type == FIT_RGBA16)
646+
fmt = RGBA_INT16;
637647
else if (type == FIT_RGBF)
638648
fmt = RGB_FLOAT32;
639649
else if (type == FIT_UINT16 || type == FIT_INT16)

graphics/src/Image_TEST.cc

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
*
1616
*/
1717
#include <fstream>
18+
#include <string>
1819

1920
#include <gtest/gtest.h>
2021

@@ -413,6 +414,8 @@ TEST_F(ImageTest, ConvertPixelFormat)
413414
Image::ConvertPixelFormat("RGBA_INT8"));
414415
EXPECT_EQ(Image::PixelFormatType::RGB_INT16,
415416
Image::ConvertPixelFormat("RGB_INT16"));
417+
EXPECT_EQ(Image::PixelFormatType::RGBA_INT16,
418+
Image::ConvertPixelFormat("RGBA_INT16"));
416419
EXPECT_EQ(Image::PixelFormatType::RGB_INT32,
417420
Image::ConvertPixelFormat("RGB_INT32"));
418421
EXPECT_EQ(Image::PixelFormatType::BGR_INT8,
@@ -673,6 +676,62 @@ TEST_F(ImageTest, Grayscale)
673676
}
674677
}
675678

679+
680+
/////////////////////////////////////////////////
681+
TEST_F(ImageTest, Color16bit)
682+
{
683+
{
684+
common::Image img;
685+
std::string fileName = common::testing::TestFile("data",
686+
"rgb_16bit.png");
687+
EXPECT_EQ(0, img.Load(fileName));
688+
const unsigned int width = 4u;
689+
const unsigned int height = 4u;
690+
const unsigned int channels = 3u;
691+
const unsigned int bpp = channels * 16u;
692+
EXPECT_TRUE(img.Valid());
693+
EXPECT_EQ(width, img.Width());
694+
EXPECT_EQ(height, img.Height());
695+
EXPECT_EQ(bpp, img.BPP());
696+
EXPECT_EQ(width * bpp / 8u, img.Pitch());
697+
EXPECT_EQ(common::Image::PixelFormatType::RGB_INT16, img.PixelFormat());
698+
math::Color maxColor(0.0f, 0.0f, 0.847f);
699+
EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3);
700+
EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3);
701+
EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3);
702+
math::Color pixelWithMaxColor = img.Pixel(3, 1);
703+
EXPECT_NEAR(maxColor.R(), pixelWithMaxColor.R(), 1e-3);
704+
EXPECT_NEAR(maxColor.G(), pixelWithMaxColor.G(), 1e-3);
705+
EXPECT_NEAR(maxColor.B(), pixelWithMaxColor.B(), 1e-3);
706+
}
707+
{
708+
common::Image img;
709+
std::string fileName = common::testing::TestFile("data",
710+
"rgba_16bit.png");
711+
EXPECT_EQ(0, img.Load(fileName));
712+
const unsigned int width = 4u;
713+
const unsigned int height = 4u;
714+
const unsigned int channels = 4u;
715+
const unsigned int bpp = channels * 16u;
716+
EXPECT_TRUE(img.Valid());
717+
EXPECT_EQ(width, img.Width());
718+
EXPECT_EQ(height, img.Height());
719+
EXPECT_EQ(bpp, img.BPP());
720+
EXPECT_EQ(width * bpp / 8u, img.Pitch());
721+
EXPECT_EQ(common::Image::PixelFormatType::RGBA_INT16, img.PixelFormat());
722+
math::Color maxColor(0.0f, 0.0f, 0.847f, 0.5f);
723+
EXPECT_NEAR(maxColor.R(), img.MaxColor().R(), 1e-3);
724+
EXPECT_NEAR(maxColor.G(), img.MaxColor().G(), 1e-3);
725+
EXPECT_NEAR(maxColor.B(), img.MaxColor().B(), 1e-3);
726+
EXPECT_NEAR(maxColor.A(), img.MaxColor().A(), 1e-3);
727+
math::Color pixelWithMaxColor = img.Pixel(3, 1);
728+
EXPECT_NEAR(maxColor.R(), pixelWithMaxColor.R(), 1e-3);
729+
EXPECT_NEAR(maxColor.G(), pixelWithMaxColor.G(), 1e-3);
730+
EXPECT_NEAR(maxColor.B(), pixelWithMaxColor.B(), 1e-3);
731+
EXPECT_NEAR(maxColor.A(), pixelWithMaxColor.A(), 1e-3);
732+
}
733+
}
734+
676735
using string_int2 = std::tuple<const char *, unsigned int, unsigned int>;
677736

678737
class ImagePerformanceTest : public ImageTest,

test/data/rgb_16bit.png

250 Bytes
Loading

test/data/rgba_16bit.png

266 Bytes
Loading

0 commit comments

Comments
 (0)