Skip to content

Commit e481234

Browse files
authored
Add function for extracting single channel data from an RGB[A] image (#706)
Signed-off-by: Ian Chen <[email protected]>
1 parent bf7ae2d commit e481234

File tree

4 files changed

+128
-9
lines changed

4 files changed

+128
-9
lines changed

graphics/include/gz/common/Image.hh

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,19 @@ namespace gz
6363
/// \brief Encapsulates an image
6464
class GZ_COMMON_GRAPHICS_VISIBLE Image
6565
{
66+
/// \brief Image channel
67+
public: enum class Channel
68+
{
69+
/// \brief Red channel
70+
RED = 0,
71+
/// \brief Green channel
72+
GREEN = 1,
73+
/// \brief Blue channel
74+
BLUE = 2,
75+
/// \brief Alpha channel
76+
ALPHA = 3
77+
};
78+
6679
/// \brief Pixel formats enumeration
6780
public: enum PixelFormatType
6881
{
@@ -201,6 +214,12 @@ namespace gz
201214
/// \return true if image has a bitmap
202215
public: bool Valid() const;
203216

217+
/// \brief Extract a single channel (red, green, blue, or alpha) from
218+
/// an RGB[A] image and return a single channel 8 bit image data.
219+
/// \param[in] _channel Channel to extract
220+
/// \return 8 bit single channel image data
221+
public: std::vector<unsigned char> ChannelData(Channel _channel) const;
222+
204223
/// \brief Convert a single channel image data buffer into an RGB image.
205224
/// During the conversion, the input image data are normalized to 8 bit
206225
/// values i.e. [0, 255]. Optionally, specify min and max values to use

graphics/src/AssimpLoader.cc

Lines changed: 15 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ std::pair<std::string, ImagePtr> AssimpLoader::Implementation::LoadTexture(
572572

573573
std::pair<ImagePtr, ImagePtr>
574574
AssimpLoader::Implementation::SplitMetallicRoughnessMap(
575-
const Image& _img) const
575+
const Image &_img) const
576576
{
577577
std::pair<ImagePtr, ImagePtr> ret;
578578
// Metalness in B roughness in G
@@ -583,20 +583,26 @@ std::pair<ImagePtr, ImagePtr>
583583
std::vector<unsigned char> metalnessData(width * height * bytesPerPixel);
584584
std::vector<unsigned char> roughnessData(width * height * bytesPerPixel);
585585

586+
std::vector<unsigned char> metalnessData8bit =
587+
_img.ChannelData(Image::Channel::BLUE);
588+
std::vector<unsigned char> roughnessData8bit =
589+
_img.ChannelData(Image::Channel::GREEN);
586590
for (unsigned int y = 0; y < height; ++y)
587591
{
588592
for (unsigned int x = 0; x < width; ++x)
589593
{
590594
// RGBA so 4 bytes per pixel, alpha fully opaque
591-
auto baseIndex = bytesPerPixel * (y * width + x);
592-
auto color = _img.Pixel(x, (height - y - 1));
593-
metalnessData[baseIndex] = color.B() * 255.0;
594-
metalnessData[baseIndex + 1] = color.B() * 255.0;
595-
metalnessData[baseIndex + 2] = color.B() * 255.0;
595+
unsigned int idx = y * width + x;
596+
unsigned int colorB = metalnessData8bit[idx];
597+
unsigned int colorG = roughnessData8bit[idx];
598+
auto baseIndex = bytesPerPixel * idx;
599+
metalnessData[baseIndex] = colorB;
600+
metalnessData[baseIndex + 1] = colorB;
601+
metalnessData[baseIndex + 2] = colorB;
596602
metalnessData[baseIndex + 3] = 255;
597-
roughnessData[baseIndex] = color.G() * 255.0;
598-
roughnessData[baseIndex + 1] = color.G() * 255.0;
599-
roughnessData[baseIndex + 2] = color.G() * 255.0;
603+
roughnessData[baseIndex] = colorG;
604+
roughnessData[baseIndex + 1] = colorG;
605+
roughnessData[baseIndex + 2] = colorG;
600606
roughnessData[baseIndex + 3] = 255;
601607
}
602608
}

graphics/src/Image.cc

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <cstdint>
2323
#include <cstring>
2424
#include <string>
25+
#include <vector>
2526

2627
#include <gz/common/Console.hh>
2728
#include <gz/common/Util.hh>
@@ -702,3 +703,47 @@ FIBITMAP* Image::Implementation::SwapRedBlue(const unsigned int &_width,
702703

703704
return copy;
704705
}
706+
707+
//////////////////////////////////////////////////
708+
std::vector<unsigned char> Image::ChannelData(Channel _channel) const
709+
{
710+
if ((this->dataPtr->imageType != FIT_BITMAP) ||
711+
(this->dataPtr->colorType != FIC_RGB &&
712+
this->dataPtr->colorType != FIC_RGBALPHA))
713+
{
714+
gzerr << "Extraction of channel data is only support for RGB[A] images"
715+
<< std::endl;
716+
return {};
717+
}
718+
719+
FIBITMAP* channelData = nullptr;
720+
switch (_channel)
721+
{
722+
case Channel::RED:
723+
channelData = FreeImage_GetChannel(this->dataPtr->bitmap, FICC_RED);
724+
break;
725+
case Channel::GREEN:
726+
channelData = FreeImage_GetChannel(this->dataPtr->bitmap, FICC_GREEN);
727+
break;
728+
case Channel::BLUE:
729+
channelData = FreeImage_GetChannel(this->dataPtr->bitmap, FICC_BLUE);
730+
break;
731+
case Channel::ALPHA:
732+
channelData = FreeImage_GetChannel(this->dataPtr->bitmap, FICC_ALPHA);
733+
break;
734+
default:
735+
break;
736+
}
737+
738+
if (!channelData)
739+
{
740+
gzerr << "Invalid input channel: " << static_cast<int>(_channel)
741+
<< std::endl;
742+
return {};
743+
}
744+
745+
FIBITMAP *tmp = FreeImage_ConvertTo8Bits(channelData);
746+
std::vector<unsigned char> data = this->dataPtr->DataImpl(tmp);
747+
FreeImage_Unload(tmp);
748+
return data;
749+
}

graphics/src/Image_TEST.cc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -732,6 +732,55 @@ TEST_F(ImageTest, Color16bit)
732732
}
733733
}
734734

735+
/////////////////////////////////////////////////
736+
TEST_F(ImageTest, ChannelData)
737+
{
738+
// load image, extra data from each channel and verify values
739+
common::Image img;
740+
ASSERT_EQ(0, img.Load(kTestData));
741+
ASSERT_TRUE(img.Valid());
742+
743+
std::vector<unsigned char> red =
744+
img.ChannelData(common::Image::Channel::RED);
745+
std::vector<unsigned char> green =
746+
img.ChannelData(common::Image::Channel::GREEN);
747+
std::vector<unsigned char> blue =
748+
img.ChannelData(common::Image::Channel::BLUE);
749+
std::vector<unsigned char> alpha =
750+
img.ChannelData(common::Image::Channel::ALPHA);
751+
752+
EXPECT_FALSE(red.empty());
753+
ASSERT_EQ(img.Width() * img.Height(), red.size());
754+
ASSERT_EQ(red.size(), green.size());
755+
ASSERT_EQ(red.size(), blue.size());
756+
ASSERT_EQ(red.size(), alpha.size());
757+
758+
for (auto i = 0u; i < img.Height(); ++i)
759+
{
760+
for (auto j = 0u; j < img.Width(); ++j)
761+
{
762+
unsigned int idx = i * img.Width() + j;
763+
unsigned int r = red[idx];
764+
unsigned int g = green[idx];
765+
unsigned int b = blue[idx];
766+
unsigned int a = alpha[idx];
767+
768+
ASSERT_EQ(0u, g);
769+
ASSERT_EQ(255u, a);
770+
if (j < 80)
771+
{
772+
ASSERT_EQ(255u, r);
773+
ASSERT_EQ(0u, b);
774+
}
775+
else
776+
{
777+
ASSERT_EQ(0u, r);
778+
ASSERT_EQ(255u, b);
779+
}
780+
}
781+
}
782+
}
783+
735784
using string_int2 = std::tuple<const char *, unsigned int, unsigned int>;
736785

737786
class ImagePerformanceTest : public ImageTest,

0 commit comments

Comments
 (0)