diff --git a/include/geode/io/image/internal/tiff_input.hpp b/include/geode/io/image/internal/tiff_input.hpp new file mode 100644 index 00000000..9ef97d54 --- /dev/null +++ b/include/geode/io/image/internal/tiff_input.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#pragma once + +#include + +namespace geode +{ + FORWARD_DECLARATION_DIMENSION_CLASS( RasterImage ); + ALIAS_2D( RasterImage ); +} // namespace geode + +namespace geode +{ + namespace internal + { + class TIFFInput final : public RasterImageInput< 2 > + { + public: + explicit TIFFInput( std::string_view filename ) + : RasterImageInput< 2 >( filename ) + { + } + + static std::vector< std::string > extensions() + { + static const std::vector< std::string > extensions{ "tiff", + "tif" }; + return extensions; + } + + RasterImage2D read() final; + }; + } // namespace internal +} // namespace geode \ No newline at end of file diff --git a/src/geode/io/image/CMakeLists.txt b/src/geode/io/image/CMakeLists.txt index deedc005..a8d1fe13 100644 --- a/src/geode/io/image/CMakeLists.txt +++ b/src/geode/io/image/CMakeLists.txt @@ -27,6 +27,7 @@ add_geode_library( "jpg_input.cpp" "png_input.cpp" "raster_image_input.cpp" + "tiff_input.cpp" "vti_raster_image_output.cpp" PUBLIC_HEADERS "common.hpp" @@ -39,6 +40,7 @@ add_geode_library( "internal/jpg_input.hpp" "internal/png_input.hpp" "internal/raster_image_input.hpp" + "internal/tiff_input.hpp" PUBLIC_DEPENDENCIES OpenGeode::image PRIVATE_DEPENDENCIES diff --git a/src/geode/io/image/common.cpp b/src/geode/io/image/common.cpp index 6bd39809..25219b05 100644 --- a/src/geode/io/image/common.cpp +++ b/src/geode/io/image/common.cpp @@ -32,6 +32,7 @@ #include #include #include +#include namespace { @@ -48,6 +49,12 @@ namespace geode::RasterImageInputFactory2D::register_creator< geode::internal::BMPInput >( geode::internal::BMPInput::extension().data() ); + + for( const auto& tif_ext : geode::internal::TIFFInput::extensions() ) + { + geode::RasterImageInputFactory2D::register_creator< + geode::internal::TIFFInput >( tif_ext ); + } } void register_raster_output() diff --git a/src/geode/io/image/raster_image_input.cpp b/src/geode/io/image/raster_image_input.cpp index 1c9a40c3..629da9f0 100644 --- a/src/geode/io/image/raster_image_input.cpp +++ b/src/geode/io/image/raster_image_input.cpp @@ -49,6 +49,32 @@ namespace "[ImageInputImpl] Failed to read color component" ); return values; } + std::array< geode::index_t, 3 > get_rgb_indices( + GDALDatasetUniquePtr& gdal_data ) + { + std::array< geode::index_t, 3 > rgb_indices{ 0, 0, 0 }; + const auto nb_color_components = + static_cast< geode::index_t >( gdal_data->GetRasterCount() ); + for( const auto id : geode::Range( nb_color_components ) ) + { + const auto band_id = id + 1; + GDALColorInterp colorInterp = + gdal_data->GetRasterBand( band_id )->GetColorInterpretation(); + if( colorInterp == GCI_RedBand ) + { + rgb_indices[0] = band_id; + } + else if( colorInterp == GCI_GreenBand ) + { + rgb_indices[1] = band_id; + } + else if( colorInterp == GCI_BlueBand ) + { + rgb_indices[2] = band_id; + } + } + return rgb_indices; + } template < typename SpecificRange > geode::RasterImage2D read_file( std::string_view filename ) @@ -64,6 +90,7 @@ namespace static_cast< geode::index_t >( height ) } }; const auto nb_color_components = static_cast< geode::index_t >( gdal_data->GetRasterCount() ); + std::cout << " nb color = " << nb_color_components << std::endl; if( nb_color_components <= 2 ) { const auto grey_scale = @@ -83,9 +110,13 @@ namespace } else if( nb_color_components <= 4 ) { - const auto red = read_color_component( raster, gdal_data, 1 ); - const auto green = read_color_component( raster, gdal_data, 2 ); - const auto blue = read_color_component( raster, gdal_data, 3 ); + const auto rgb_indices = get_rgb_indices( gdal_data ); + const auto red = + read_color_component( raster, gdal_data, rgb_indices[0] ); + const auto green = + read_color_component( raster, gdal_data, rgb_indices[1] ); + const auto blue = + read_color_component( raster, gdal_data, rgb_indices[2] ); geode::index_t cell{ 0 }; for( const auto image_j : SpecificRange{ raster.nb_cells_in_direction( 1 ) } ) diff --git a/src/geode/io/image/tiff_input.cpp b/src/geode/io/image/tiff_input.cpp new file mode 100644 index 00000000..c6c7ec8a --- /dev/null +++ b/src/geode/io/image/tiff_input.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2019 - 2025 Geode-solutions + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + * + */ + +#include + +#include + +#include + +namespace geode +{ + namespace internal + { + RasterImage2D TIFFInput::read() + { + ImageInputImpl reader{ filename() }; + return reader.read_reversed_y_axis_file(); + } + } // namespace internal +} // namespace geode diff --git a/tests/data/cea.og_img2d b/tests/data/cea.og_img2d new file mode 100644 index 00000000..2f116645 Binary files /dev/null and b/tests/data/cea.og_img2d differ diff --git a/tests/data/cea.tiff b/tests/data/cea.tiff new file mode 100644 index 00000000..77501e31 Binary files /dev/null and b/tests/data/cea.tiff differ diff --git a/tests/image/test-raster-image.cpp b/tests/image/test-raster-image.cpp index 718d9c85..1232d4cd 100644 --- a/tests/image/test-raster-image.cpp +++ b/tests/image/test-raster-image.cpp @@ -106,6 +106,25 @@ void test_png_input() geode::save_raster_image( raster, "test_grid_output_from_png.vti" ); } +void test_tiff_input() +{ + auto raster = geode::load_raster_image< 2 >( + absl::StrCat( geode::DATA_PATH, "cea.tiff" ) ); + auto ref_raster = geode::load_raster_image< 2 >( + absl::StrCat( geode::DATA_PATH, "cea.og_img2d" ) ); + OPENGEODE_EXCEPTION( raster.nb_cells() == ref_raster.nb_cells(), + "[TEST] Wrong number of cells." ); + for( const auto cell_id : geode::Range{ ref_raster.nb_cells() } ) + { + OPENGEODE_EXCEPTION( + raster.color( cell_id ) == ref_raster.color( cell_id ), + "[TEST] Wrong color value for pixel ", cell_id, + " on image loaded from reference pixel." ); + } + + geode::save_raster_image( raster, "cea.vti" ); +} + int main() { try @@ -116,7 +135,7 @@ int main() test_jpg_from_gimp_input(); test_jpg_from_paraview_input(); test_png_input(); - + test_tiff_input(); geode::Logger::info( "TEST SUCCESS" ); return 0; }