Skip to content

Commit 2e575b8

Browse files
committed
Add function for extracting single channel data from an RGB[A] image (#706)
Signed-off-by: Ian Chen <[email protected]> (cherry picked from commit e481234)
1 parent 60de1e7 commit 2e575b8

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
@@ -62,6 +62,19 @@ namespace gz
6262
/// \brief Encapsulates an image
6363
class GZ_COMMON_GRAPHICS_VISIBLE Image
6464
{
65+
/// \brief Image channel
66+
public: enum class Channel
67+
{
68+
/// \brief Red channel
69+
RED = 0,
70+
/// \brief Green channel
71+
GREEN = 1,
72+
/// \brief Blue channel
73+
BLUE = 2,
74+
/// \brief Alpha channel
75+
ALPHA = 3
76+
};
77+
6578
/// \brief Pixel formats enumeration
6679
public: enum PixelFormatType
6780
{
@@ -199,6 +212,12 @@ namespace gz
199212
/// \return true if image has a bitmap
200213
public: bool Valid() const;
201214

215+
/// \brief Extract a single channel (red, green, blue, or alpha) from
216+
/// an RGB[A] image and return a single channel 8 bit image data.
217+
/// \param[in] _channel Channel to extract
218+
/// \return 8 bit single channel image data
219+
public: std::vector<unsigned char> ChannelData(Channel _channel) const;
220+
202221
/// \brief Convert a single channel image data buffer into an RGB image.
203222
/// During the conversion, the input image data are normalized to 8 bit
204223
/// 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>
@@ -700,3 +701,47 @@ FIBITMAP* Image::Implementation::SwapRedBlue(const unsigned int &_width,
700701

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

graphics/src/Image_TEST.cc

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -729,6 +729,55 @@ TEST_F(ImageTest, Color16bit)
729729
}
730730
}
731731

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

734783
class ImagePerformanceTest : public ImageTest,

0 commit comments

Comments
 (0)