From 83d0411921624480d604ba261bcec90fe30e3fcd Mon Sep 17 00:00:00 2001 From: fabien servant Date: Mon, 14 Nov 2022 13:02:28 +0100 Subject: [PATCH 1/8] [io] disable raw flip --- src/aliceVision/image/io.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/aliceVision/image/io.cpp b/src/aliceVision/image/io.cpp index 6418dace00..fdb4ac76f9 100644 --- a/src/aliceVision/image/io.cpp +++ b/src/aliceVision/image/io.cpp @@ -419,7 +419,10 @@ oiio::ParamValueList readImageMetadata(const std::string& path, int& width, int& oiio::ImageSpec readImageSpec(const std::string& path) { - std::unique_ptr in(oiio::ImageInput::open(path)); + oiio::ImageSpec configSpec; + configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of buffer + + std::unique_ptr in(oiio::ImageInput::open(path, &configSpec)); oiio::ImageSpec spec = in->spec(); if(!in) @@ -516,6 +519,7 @@ void readImage(const std::string& path, const bool isRawImage = isRawFormat(path); image::DCPProfile::Triple neutral = {1.0,1.0,1.0}; + configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of buffer if (isRawImage) { From a45d15db065e80f499c0b1d7c04dbfdcb2b33452 Mon Sep 17 00:00:00 2001 From: Fabien Castan Date: Tue, 3 Jan 2023 08:35:11 +0100 Subject: [PATCH 2/8] [image] only use user_flip if the version of oiio is higher than 2.4.5 --- src/aliceVision/image/io.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/aliceVision/image/io.cpp b/src/aliceVision/image/io.cpp index fdb4ac76f9..fb55f66d34 100644 --- a/src/aliceVision/image/io.cpp +++ b/src/aliceVision/image/io.cpp @@ -420,7 +420,11 @@ oiio::ParamValueList readImageMetadata(const std::string& path, int& width, int& oiio::ImageSpec readImageSpec(const std::string& path) { oiio::ImageSpec configSpec; - configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of buffer +#if OIIO_VERSION > (10000 * 2 + 100 * 4 + 5) // OIIO_VERSION > 2.4.5 + // To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669, + // so we can disable the auto orientation and keep the metadata. + configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of the image buffer +#endif std::unique_ptr in(oiio::ImageInput::open(path, &configSpec)); oiio::ImageSpec spec = in->spec(); @@ -555,6 +559,12 @@ void readImage(const std::string& path, // libRAW configuration // See https://openimageio.readthedocs.io/en/master/builtinplugins.html#raw-digital-camera-files +#if OIIO_VERSION > (10000 * 2 + 100 * 4 + 5) // OIIO_VERSION > 2.4.5 + // To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669, + // so we can disable the auto orientation and keep the metadata. + configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of the image buffer +#endif + if (imageReadOptions.rawColorInterpretation == ERawColorInterpretation::None) { if (imageReadOptions.workingColorSpace != EImageColorSpace::NO_CONVERSION) From c97b059b17f651849d6c406f0f009d1eb73bde74 Mon Sep 17 00:00:00 2001 From: demoulinv Date: Thu, 25 May 2023 09:18:06 +0200 Subject: [PATCH 3/8] Code cleaning --- src/aliceVision/image/io.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/aliceVision/image/io.cpp b/src/aliceVision/image/io.cpp index fb55f66d34..18b05ecfc0 100644 --- a/src/aliceVision/image/io.cpp +++ b/src/aliceVision/image/io.cpp @@ -523,7 +523,6 @@ void readImage(const std::string& path, const bool isRawImage = isRawFormat(path); image::DCPProfile::Triple neutral = {1.0,1.0,1.0}; - configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of buffer if (isRawImage) { @@ -560,8 +559,8 @@ void readImage(const std::string& path, // See https://openimageio.readthedocs.io/en/master/builtinplugins.html#raw-digital-camera-files #if OIIO_VERSION > (10000 * 2 + 100 * 4 + 5) // OIIO_VERSION > 2.4.5 - // To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669, - // so we can disable the auto orientation and keep the metadata. + // To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669, + // so we can disable the auto orientation and keep the metadata. configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of the image buffer #endif From a40fdfa604bf1e4635fc0ee8383de9ba4ba9d8c1 Mon Sep 17 00:00:00 2001 From: demoulinv Date: Tue, 30 May 2023 14:58:12 +0200 Subject: [PATCH 4/8] [IO] Managed mirrored orientations. --- src/aliceVision/image/io.cpp | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/src/aliceVision/image/io.cpp b/src/aliceVision/image/io.cpp index 18b05ecfc0..046f699b6c 100644 --- a/src/aliceVision/image/io.cpp +++ b/src/aliceVision/image/io.cpp @@ -661,13 +661,33 @@ void readImage(const std::string& path, if (inBuf.spec().nchannels == 2) ALICEVISION_THROW_ERROR("Load of 2 channels is not supported. Image file: '" + path + "'."); + oiio::ParamValueList imgMetadata = readImageMetadata(path); + + if (isRawImage) + { + // Check orientation metadata. If image is mirrored, mirror it back and update orientation metadata + int orientation = imgMetadata.get_int("orientation", -1); + + float red[] = {1, 0, 0, 1}; + oiio::ImageBufAlgo::render_text(inBuf, 1000, 1000, std::to_string(orientation), 200, "Arial", red); + + if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 7) + { + // horizontal mirroring + oiio::ImageBuf inBufMirrored = oiio::ImageBufAlgo::flop(inBuf); + inBuf = inBufMirrored; + + orientation += (orientation == 2 || orientation == 4) ? -1 : 1; + } + } + // Apply DCP profile if (!imageReadOptions.colorProfileFileName.empty() && imageReadOptions.rawColorInterpretation == ERawColorInterpretation::DcpLinearProcessing) { image::DCPProfile dcpProfile(imageReadOptions.colorProfileFileName); - oiio::ParamValueList imgMetadata = readImageMetadata(path); + //oiio::ParamValueList imgMetadata = readImageMetadata(path); std::string cam_mul = ""; if (!imgMetadata.getattribute("raw:cam_mul", cam_mul)) { From c4d0ed874362766e8a5f2d9430369ddc99b5f0c8 Mon Sep 17 00:00:00 2001 From: demoulinv Date: Fri, 2 Jun 2023 14:51:29 +0200 Subject: [PATCH 5/8] update raw:user_flip value from 1 to 0 --- src/aliceVision/image/io.cpp | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/src/aliceVision/image/io.cpp b/src/aliceVision/image/io.cpp index 046f699b6c..17819aceaf 100644 --- a/src/aliceVision/image/io.cpp +++ b/src/aliceVision/image/io.cpp @@ -423,7 +423,7 @@ oiio::ImageSpec readImageSpec(const std::string& path) #if OIIO_VERSION > (10000 * 2 + 100 * 4 + 5) // OIIO_VERSION > 2.4.5 // To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669, // so we can disable the auto orientation and keep the metadata. - configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of the image buffer + configSpec.attribute("raw:user_flip", 0); // disable auto rotation of the image buffer but keep exif metadata orientation valid #endif std::unique_ptr in(oiio::ImageInput::open(path, &configSpec)); @@ -561,7 +561,7 @@ void readImage(const std::string& path, #if OIIO_VERSION > (10000 * 2 + 100 * 4 + 5) // OIIO_VERSION > 2.4.5 // To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669, // so we can disable the auto orientation and keep the metadata. - configSpec.attribute("raw:user_flip", 1); // set flip to 1 to disable auto rotation of the image buffer + configSpec.attribute("raw:user_flip", 0); // disable auto rotation of the image buffer but keep exif metadata orientation valid #endif if (imageReadOptions.rawColorInterpretation == ERawColorInterpretation::None) @@ -668,9 +668,6 @@ void readImage(const std::string& path, // Check orientation metadata. If image is mirrored, mirror it back and update orientation metadata int orientation = imgMetadata.get_int("orientation", -1); - float red[] = {1, 0, 0, 1}; - oiio::ImageBufAlgo::render_text(inBuf, 1000, 1000, std::to_string(orientation), 200, "Arial", red); - if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 7) { // horizontal mirroring From 06a792c3b81e583bb7b5c9cc1186298a7e899f64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Vital?= Date: Mon, 5 Jun 2023 17:41:25 +0200 Subject: [PATCH 6/8] [io] update minimal oiio version for user_flip usage --- src/aliceVision/image/io.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/aliceVision/image/io.cpp b/src/aliceVision/image/io.cpp index 17819aceaf..2c1e3dae80 100644 --- a/src/aliceVision/image/io.cpp +++ b/src/aliceVision/image/io.cpp @@ -420,7 +420,7 @@ oiio::ParamValueList readImageMetadata(const std::string& path, int& width, int& oiio::ImageSpec readImageSpec(const std::string& path) { oiio::ImageSpec configSpec; -#if OIIO_VERSION > (10000 * 2 + 100 * 4 + 5) // OIIO_VERSION > 2.4.5 +#if OIIO_VERSION >= (10000 * 2 + 100 * 4 + 12) // OIIO_VERSION >= 2.4.12 // To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669, // so we can disable the auto orientation and keep the metadata. configSpec.attribute("raw:user_flip", 0); // disable auto rotation of the image buffer but keep exif metadata orientation valid @@ -558,7 +558,7 @@ void readImage(const std::string& path, // libRAW configuration // See https://openimageio.readthedocs.io/en/master/builtinplugins.html#raw-digital-camera-files -#if OIIO_VERSION > (10000 * 2 + 100 * 4 + 5) // OIIO_VERSION > 2.4.5 +#if OIIO_VERSION >= (10000 * 2 + 100 * 4 + 12) // OIIO_VERSION >= 2.4.12 // To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669, // so we can disable the auto orientation and keep the metadata. configSpec.attribute("raw:user_flip", 0); // disable auto rotation of the image buffer but keep exif metadata orientation valid From 6611c4af258221288eb2e841417c06018bc17d77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Vital?= Date: Thu, 8 Jun 2023 12:37:28 +0200 Subject: [PATCH 7/8] [software] panoramaInit: apply orientation rotation to poses --- src/software/pipeline/main_panoramaInit.cpp | 27 +++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/src/software/pipeline/main_panoramaInit.cpp b/src/software/pipeline/main_panoramaInit.cpp index f2be8e5d62..7d921da111 100644 --- a/src/software/pipeline/main_panoramaInit.cpp +++ b/src/software/pipeline/main_panoramaInit.cpp @@ -1154,9 +1154,32 @@ int main(int argc, char* argv[]) for(const auto& item_rotation : rotations) { IndexT viewIdx = namesWithRank[index].second; - if(item_rotation.second.trace() != 0) + const sfmData::View& v = sfmData.getView(viewIdx); + + sfmData::EEXIFOrientation orientation = v.getMetadataOrientation(); + double orientationAngle = 0.; + switch (orientation) + { + case sfmData::EEXIFOrientation::UPSIDEDOWN: + orientationAngle = boost::math::constants::pi(); + break; + case sfmData::EEXIFOrientation::LEFT: + orientationAngle = boost::math::constants::pi() * .5; + break; + case sfmData::EEXIFOrientation::RIGHT: + orientationAngle = boost::math::constants::pi() * -.5; + break; + default: + break; + } + + const Eigen::AngleAxis Morientation(orientationAngle, Eigen::Vector3d::UnitZ()); + + const Eigen::Matrix3d viewRotation = Morientation.toRotationMatrix().transpose() * item_rotation.second; + + if(viewRotation.trace() != 0) { - sfmData::CameraPose pose(geometry::Pose3(item_rotation.second, Eigen::Vector3d::Zero())); + sfmData::CameraPose pose(geometry::Pose3(viewRotation, Eigen::Vector3d::Zero())); sfmData.setAbsolutePose(viewIdx, pose); } ++index; From 5fe182bfff395a89181ac61bd81c46283b9f466f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lo=C3=AFc=20Vital?= Date: Thu, 8 Jun 2023 15:49:30 +0200 Subject: [PATCH 8/8] [software] panoramaInit: apply orientation metadata to contact sheet elements --- src/software/pipeline/main_panoramaInit.cpp | 111 ++++++++++++++++---- 1 file changed, 92 insertions(+), 19 deletions(-) diff --git a/src/software/pipeline/main_panoramaInit.cpp b/src/software/pipeline/main_panoramaInit.cpp index 7d921da111..3576cb39a8 100644 --- a/src/software/pipeline/main_panoramaInit.cpp +++ b/src/software/pipeline/main_panoramaInit.cpp @@ -45,15 +45,6 @@ namespace po = boost::program_options; namespace fs = boost::filesystem; namespace pt = boost::property_tree; -struct Contact -{ - int rank; - std::string path; - int width; - int height; - sfmData::EEXIFOrientation orientation; -}; - /** * A simple class for gaussian pyramid */ @@ -665,16 +656,93 @@ class CircleDetector size_t _minimal_size; }; -void resample(image::Image& output, const image::Image& input) +/** + * @brief Utility function for resizing an image. + */ +void resample(image::Image& output, + const image::Image& input) { const oiio::ImageBuf inBuf(oiio::ImageSpec(input.Width(), input.Height(), 3, oiio::TypeDesc::FLOAT), const_cast(input.data())); + oiio::ImageBuf outBuf(oiio::ImageSpec(output.Width(), output.Height(), 3, oiio::TypeDesc::FLOAT), (image::RGBfColor*)output.data()); oiio::ImageBufAlgo::resample(outBuf, inBuf, false); } +/** + * @brief Utility function for rotating an image given its orientation metadata. + */ +void applyOrientation(image::Image& output, + const image::Image& input, + sfmData::EEXIFOrientation orientation) +{ + const oiio::ImageBuf inBuf(oiio::ImageSpec(input.Width(), input.Height(), 3, oiio::TypeDesc::FLOAT), + const_cast(input.data())); + + oiio::ImageBuf outBuf(oiio::ImageSpec(output.Width(), output.Height(), 3, oiio::TypeDesc::FLOAT), + (image::RGBfColor*)output.data()); + + switch (orientation) + { + case sfmData::EEXIFOrientation::UPSIDEDOWN: + oiio::ImageBufAlgo::rotate180(outBuf, inBuf); + break; + case sfmData::EEXIFOrientation::LEFT: + oiio::ImageBufAlgo::rotate90(outBuf, inBuf); + break; + case sfmData::EEXIFOrientation::RIGHT: + oiio::ImageBufAlgo::rotate270(outBuf, inBuf); + break; + default: + outBuf.copy(inBuf); + break; + } +} + +/** + * @brief Utility struct for contact sheet elements. + */ +struct Contact +{ + int rank; + std::string path; + int width; + int height; + sfmData::EEXIFOrientation orientation; +}; + +/** + * @brief Width of contact sheet element, taking into account orientation metadata. + */ +int orientedWidth(const Contact& contact) +{ + switch (contact.orientation) + { + case sfmData::EEXIFOrientation::LEFT: + case sfmData::EEXIFOrientation::RIGHT: + return contact.height; + default: + return contact.width; + } +} + +/** + * @brief Height of contact sheet element, taking into account orientation metadata. + */ +int orientedHeight(const Contact& contact) +{ + switch (contact.orientation) + { + case sfmData::EEXIFOrientation::LEFT: + case sfmData::EEXIFOrientation::RIGHT: + return contact.width; + default: + return contact.height; + } +} + bool buildContactSheetImage(image::Image& output, const std::map>& contactSheetInfo, int contactSheetItemMaxSize) { @@ -686,8 +754,8 @@ bool buildContactSheetImage(image::Image& output, { for(const auto& item : rowpair.second) { - maxdim = std::max(maxdim, item.second.width); - maxdim = std::max(maxdim, item.second.height); + maxdim = std::max(maxdim, orientedWidth(item.second)); + maxdim = std::max(maxdim, orientedHeight(item.second)); } } double ratioResize = double(contactSheetItemMaxSize) / double(maxdim); @@ -702,8 +770,8 @@ bool buildContactSheetImage(image::Image& output, for(const auto& item : rowpair.second) { - int resizedHeight = int(ratioResize * double(item.second.height)); - int resizedWidth = int(ratioResize * double(item.second.width)); + int resizedHeight = int(ratioResize * double(orientedHeight(item.second))); + int resizedWidth = int(ratioResize * double(orientedWidth(item.second))); rowHeight = std::max(rowHeight, resizedHeight); rowWidth += resizedWidth + space; @@ -729,8 +797,8 @@ bool buildContactSheetImage(image::Image& output, for(const auto& item : rowpair.second) { - int resizedHeight = int(ratioResize * double(item.second.height)); - int resizedWidth = int(ratioResize * double(item.second.width)); + int resizedHeight = int(ratioResize * double(orientedHeight(item.second))); + int resizedWidth = int(ratioResize * double(orientedWidth(item.second))); rowHeight = std::max(rowHeight, resizedHeight); rowWidth += resizedWidth + space; @@ -742,15 +810,20 @@ bool buildContactSheetImage(image::Image& output, int posX = space; for(const auto& item : rowpair.second) { - int resizedHeight = int(ratioResize * double(item.second.height)); - int resizedWidth = int(ratioResize * double(item.second.width)); + int rawResizedHeight = int(ratioResize * double(item.second.height)); + int rawResizedWidth = int(ratioResize * double(item.second.width)); + + int resizedHeight = int(ratioResize * double(orientedHeight(item.second))); + int resizedWidth = int(ratioResize * double(orientedWidth(item.second))); image::Image input; + image::Image rawThumbnail(rawResizedWidth, rawResizedHeight); image::Image thumbnail(resizedWidth, resizedHeight); image::readImage(item.second.path, input, image::EImageColorSpace::SRGB); - resample(thumbnail, input); + resample(rawThumbnail, input); + applyOrientation(thumbnail, rawThumbnail, item.second.orientation); rowOutput.block(0, posX, resizedHeight, resizedWidth) = thumbnail; posX += resizedWidth + space;