-
Notifications
You must be signed in to change notification settings - Fork 42
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support 16 bit heightmaps #266
Support 16 bit heightmaps #266
Conversation
Tagging @iche033 for advices. |
Codecov Report
@@ Coverage Diff @@
## ign-common4 #266 +/- ##
===============================================
- Coverage 77.11% 77.01% -0.10%
===============================================
Files 75 76 +1
Lines 10696 10725 +29
===============================================
+ Hits 8248 8260 +12
- Misses 2448 2465 +17
Continue to review full report at Codecov.
|
Upon further investigation with more than 8 bits (apart from the fixes in this PR) the problem is stemming from inside maxColor function in Image.cc FreeImage_GetColorType |
Another update: Freeimage seems to have no way of reading 16-bit int values from 16-bit images. From the documentation it seems there is Here is the definition of RGBQUAD:
I can't seem to find a way of using freeimage to access 16-bit greyscale values, we can of course utilize another library for that though. Tagging @iche033, @chapulina and @nkoenig for any insights you might have. |
Upon more playing the following bit seems to be able to read the values in 16 bit integers, it's raw access but still doable:
|
This looks like the correct approach based on the documentation. I don't think there is any way around the raw access with the way the API is set up. |
I found the bigger bottleneck is implementing |
I just tried your changes as they are right now and I think it's working with just another minor change. In the code below, it's indexing into I made the following quick hack for testing and that was sufficient to load a 16bit grayscale png heightmap for me: diff --git a/graphics/src/ImageHeightmap.cc b/graphics/src/ImageHeightmap.cc
index fec86b5..9410355 100644
--- a/graphics/src/ImageHeightmap.cc
+++ b/graphics/src/ImageHeightmap.cc
@@ -100,6 +100,8 @@ void ImageHeightmap::FillHeightMap(int _subSampling,
return;
}
+ uint16_t *dataShort = reinterpret_cast<uint16_t *>(data);
+
// Iterate over all the vertices
for (unsigned int y = 0; y < _vertSize; ++y)
{
@@ -121,15 +123,19 @@ void ImageHeightmap::FillHeightMap(int _subSampling,
double dx = xf - x1;
double px1 = static_cast<int>(
- data[y1 * pitch + x1 * bpp]) / maxPixelValue;
+ // dataShort[y1 * pitch + x1 * bpp]) / maxPixelValue;
+ dataShort[y1 * pitch/bpp + x1 ]) / maxPixelValue;
double px2 = static_cast<int>(
- data[y1 * pitch + x2 * bpp]) / maxPixelValue;
+ // dataShort[y1 * pitch + x2 * bpp]) / maxPixelValue;
+ dataShort[y1 * pitch/bpp + x2]) / maxPixelValue;
float h1 = (px1 - ((px1 - px2) * dx));
double px3 = static_cast<int>(
- data[y2 * pitch + x1 * bpp]) / maxPixelValue;
+ // dataShort[y2 * pitch + x1 * bpp]) / maxPixelValue;
+ dataShort[y2 * pitch/bpp + x1]) / maxPixelValue;
double px4 = static_cast<int>(
- data[y2 * pitch + x2 * bpp]) / maxPixelValue;
+ // dataShort[y2 * pitch + x2 * bpp]) / maxPixelValue;
+ dataShort[y2 * pitch/bpp + x2]) / maxPixelValue;
float h2 = (px3 - ((px3 - px4) * dx));
float h = (h1 - ((h1 - h2) * dy)) * _scale.Z(); |
@iche033 thanks for having a look. That didn't work for me though, may you be using cache in this test? (I always make sure I remove the cache with: |
oh I see why we are getting different results. I'm testing with I do get the same error as you when testing with ogre 1.x. There could be a problem somewhere else when launching with ogre 1.x.
My understanding is that the raw data we get back from freeimage (done in |
Sorry for the delays, it worked with ign-rendering6 and I just pushed a commit with that version. Though common-4 is both used by ign-rendering5 (in Edifice) and ign-rendering6 (in Fortress). Would it be a problem to support just one of them? |
af865fe
to
fd9b018
Compare
4c073d8
to
3d92443
Compare
@iche033 I think it should be all good now with a nicer implementation, and ready to merge. For some reason the heightmap test was failing with 1 pixel off, I am not sure why it does, but might not be a major issue (last commit should fix that). Appreciate if you can drop a last review. |
I'll let @iche033 take a closer look but it works well for me! |
nice, thanks for cleaning up the code and making a generic function.
The quick workaround I did just made it work for single channel 16 bit grayscale image. Looks like the test was using 3 channel RGB image so that caused it to fail. I've made some changes in the patch below to make it work for both grayscale and rgb images. I also made some other ignition coding / naming style changes:
patchdiff --git a/graphics/include/ignition/common/ImageHeightmap.hh b/graphics/include/ignition/common/ImageHeightmap.hh
index 4c48bee..44f3bd0 100644
--- a/graphics/include/ignition/common/ImageHeightmap.hh
+++ b/graphics/include/ignition/common/ImageHeightmap.hh
@@ -65,15 +65,32 @@ namespace ignition
private: ignition::common::Image img;
/// \brief Get Heightmap heights given the image
+ /// \param[in] _data Image data
+ /// \param[in] _pitch Size of a row of image pixels in bytes
+ /// \param[in] _subSampling Subsampling factor
+ /// \param[in] _vertSize Number of points per row.
+ /// \param[in] _size Real dimmensions of the terrain.
+ /// \param[in] _scale Vector3 used to scale the height.
+ /// \param[in] _flipY If true, it inverts the order in which the vector
+ /// is filled.
+ /// \param[out] _heights Vector containing the terrain heights.
private: template <typename T>
- void getHeights(T data, const double& maxPixelValue,
- const int& imgHeight, const int& imgWidth,
- const unsigned int& pitch, const unsigned int& bpp,
- const int& _subSampling, unsigned int& _vertSize,
+ void FillHeights(T *_data, int _imgHeight, int _imgWidth,
+ unsigned int _pitch, int _subSampling, unsigned int _vertSize,
const ignition::math::Vector3d &_size,
const ignition::math::Vector3d &_scale,
- const bool& _flipY, std::vector<float> &_heights)
+ bool _flipY, std::vector<float> &_heights)
{
+ // bytes per pixel
+ unsigned int bpp = _pitch / _imgWidth;
+ // number of channels in a pixel
+ unsigned int channels = bpp / sizeof(T);
+ // number of pixels in a row of image
+ unsigned int pitchInPixels = _pitch / bpp;
+
+ double maxPixelValue =
+ static_cast<double>(std::numeric_limits<T>::max());
+
// Iterate over all the vertices
for (unsigned int y = 0; y < _vertSize; ++y)
{
@@ -81,8 +98,8 @@ namespace ignition
double yf = y / static_cast<double>(_subSampling);
int y1 = static_cast<int>(std::floor(yf));
int y2 = static_cast<int>(std::ceil(yf));
- if (y2 >= imgHeight)
- y2 = imgHeight-1;
+ if (y2 >= _imgHeight)
+ y2 = _imgHeight-1;
double dy = yf - y1;
for (unsigned int x = 0; x < _vertSize; ++x)
@@ -90,22 +107,21 @@ namespace ignition
double xf = x / static_cast<double>(_subSampling);
int x1 = static_cast<int>(std::floor(xf));
int x2 = static_cast<int>(std::ceil(xf));
- if (x2 >= imgWidth)
- x2 = imgWidth-1;
+ if (x2 >= _imgWidth)
+ x2 = _imgWidth-1;
double dx = xf - x1;
double px1 = static_cast<int>(
- data[y1 * pitch / bpp + x1]) / maxPixelValue;
+ _data[(y1 * pitchInPixels + x1) * channels]) / maxPixelValue;
double px2 = static_cast<int>(
- data[y1 * pitch / bpp + x2]) / maxPixelValue;
+ _data[(y1 * pitchInPixels + x2) * channels]) / maxPixelValue;
float h1 = (px1 - ((px1 - px2) * dx));
double px3 = static_cast<int>(
- data[y2 * pitch / bpp + x1]) / maxPixelValue;
+ _data[(y2 * pitchInPixels + x1) * channels]) / maxPixelValue;
double px4 = static_cast<int>(
- data[y2 * pitch / bpp + x2]) / maxPixelValue;
+ _data[(y2 * pitchInPixels + x2) * channels]) / maxPixelValue;
float h2 = (px3 - ((px3 - px4) * dx));
-
float h = (h1 - ((h1 - h2) * dy)) * _scale.Z();
// invert pixel definition so 1=ground, 0=full height,
@@ -121,7 +137,6 @@ namespace ignition
_heights[(_vertSize - y - 1) * _vertSize + x] = h;
}
}
- delete [] data;
}
};
}
diff --git a/graphics/src/ImageHeightmap.cc b/graphics/src/ImageHeightmap.cc
index 2c7543f..6c86308 100644
--- a/graphics/src/ImageHeightmap.cc
+++ b/graphics/src/ImageHeightmap.cc
@@ -54,9 +54,6 @@ void ImageHeightmap::FillHeightMap(int _subSampling,
// Bytes per row
unsigned int pitch = this->img.Pitch();
- // Bytes per pixel
- unsigned int bpp = pitch / imgWidth;
-
// Get the image format so we can arrange our heightmap
// Currently supported: 8-bit and 16-bit.
auto imgFormat = this->img.PixelFormat();
@@ -65,7 +62,7 @@ void ImageHeightmap::FillHeightMap(int _subSampling,
unsigned int count;
this->img.Data(&data, count);
- if(imgFormat == ignition::common::Image::PixelFormatType::L_INT8 ||
+ if (imgFormat == ignition::common::Image::PixelFormatType::L_INT8 ||
imgFormat == ignition::common::Image::PixelFormatType::RGB_INT8 ||
imgFormat == ignition::common::Image::PixelFormatType::RGBA_INT8 ||
imgFormat == ignition::common::Image::PixelFormatType::BAYER_BGGR8 ||
@@ -76,26 +73,26 @@ void ImageHeightmap::FillHeightMap(int _subSampling,
imgFormat == ignition::common::Image::PixelFormatType::BGR_INT8 ||
imgFormat == ignition::common::Image::PixelFormatType::BGRA_INT8)
{
- getHeights(data, 255.0, imgHeight, imgWidth, pitch, bpp,
- _subSampling, _vertSize, _size, _scale, _flipY, _heights);
+ this->FillHeights<unsigned char>(data, imgHeight, imgWidth, pitch,
+ _subSampling, _vertSize, _size, _scale, _flipY, _heights);
}
- else if(imgFormat == ignition::common::Image::PixelFormatType::BGR_INT16 ||
+ else if (imgFormat == ignition::common::Image::PixelFormatType::BGR_INT16 ||
imgFormat == ignition::common::Image::PixelFormatType::L_INT16 ||
imgFormat == ignition::common::Image::PixelFormatType::RGB_FLOAT16 ||
imgFormat == ignition::common::Image::PixelFormatType::RGB_INT16 ||
imgFormat == ignition::common::Image::PixelFormatType::R_FLOAT16)
{
uint16_t *dataShort = reinterpret_cast<uint16_t *>(data);
- getHeights(dataShort, 65535.0, imgHeight, imgWidth, pitch, bpp,
- _subSampling, _vertSize, _size, _scale, _flipY, _heights);
+ this->FillHeights<uint16_t>(dataShort, imgHeight, imgWidth, pitch,
+ _subSampling, _vertSize, _size, _scale, _flipY, _heights);
}
else
{
ignerr << "Unsupported image format, "
- "heightmap will not be loaded" << std::endl;
+ "heightmap will not be loaded" << std::endl;
return;
}
-
+ delete [] data;
}
//////////////////////////////////////////////////
diff --git a/graphics/src/ImageHeightmap_TEST.cc b/graphics/src/ImageHeightmap_TEST.cc
index c15f06c..568b324 100644
--- a/graphics/src/ImageHeightmap_TEST.cc
+++ b/graphics/src/ImageHeightmap_TEST.cc
@@ -94,7 +94,7 @@ TEST_F(ImageHeightmapTest, FillHeightmap)
// Check the elevation of some control points
EXPECT_NEAR(0.0, elevations.at(0), ELEVATION_TOL);
EXPECT_NEAR(10.0, elevations.at(elevations.size() - 1), ELEVATION_TOL);
- EXPECT_NEAR(5.0, elevations.at((elevations.size() / 2) - 2), ELEVATION_TOL);
+ EXPECT_NEAR(5.0, elevations.at((elevations.size() / 2)), ELEVATION_TOL);
}
///////////////////////////////////////////////// Please take a look. If the changes look good to you. Can you apply to your branch? thanks. |
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
df16848
to
92acedc
Compare
@iche033 thanks a ton for the patch. I applied it, and worked nicely for me. I think this should be good for a last review & merge now. |
Signed-off-by: Vatan Aksoy Tezer <[email protected]>
@vatanaksoytezer Looks good to me, I have a suggestion to add |
Signed-off-by: Michael Carroll <[email protected]>
Sure! |
This pull request has been mentioned on Gazebo Community. There might be relevant details there: https://community.gazebosim.org/t/new-ignition-releases-2022-01-24-citadel-edifice-fortress/1241/1 |
🦟 Bug fix
Fixes #265
Summary
Assuming PixelFormat function returns a correct value this should allow supporting 8, 16 and 32 bit heightmaps. Having said that I tried to test this with a hand constructed 16bit version of city_terrain.jpg (just multiplied each value by 256 didn't want to spend so much time on that),
the PixelFormat function is still returningThat was my bad that I left the extension of 16 bit image as .jpg (should have been png), it's now throwing a very suppressive error messages starting withL_INT8
(which is the same as the 8 bit version) causing the spikes. This needs to be resolved as well. I'll try to have a deeper look on why we aren't getting correct return from this function.[Err] [Image.cc:478] Image: Coordinates out of range [0 0]
and going through all the pixels.Checklist
codecheck
passed (See contributing)Note to maintainers: Remember to use Squash-Merge
🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸🔸
Note to maintainers: Remember to use Squash-Merge