Skip to content

Commit 634ac12

Browse files
authored
Merge pull request #1301 from alicevision/dev/rawDisableFlip
Disable libraw flip
2 parents 07354f0 + 5fe182b commit 634ac12

File tree

2 files changed

+149
-23
lines changed

2 files changed

+149
-23
lines changed

src/aliceVision/image/io.cpp

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,14 @@ oiio::ParamValueList readImageMetadata(const std::string& path, int& width, int&
419419

420420
oiio::ImageSpec readImageSpec(const std::string& path)
421421
{
422-
std::unique_ptr<oiio::ImageInput> in(oiio::ImageInput::open(path));
422+
oiio::ImageSpec configSpec;
423+
#if OIIO_VERSION >= (10000 * 2 + 100 * 4 + 12) // OIIO_VERSION >= 2.4.12
424+
// To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669,
425+
// so we can disable the auto orientation and keep the metadata.
426+
configSpec.attribute("raw:user_flip", 0); // disable auto rotation of the image buffer but keep exif metadata orientation valid
427+
#endif
428+
429+
std::unique_ptr<oiio::ImageInput> in(oiio::ImageInput::open(path, &configSpec));
423430
oiio::ImageSpec spec = in->spec();
424431

425432
if(!in)
@@ -551,6 +558,12 @@ void readImage(const std::string& path,
551558
// libRAW configuration
552559
// See https://openimageio.readthedocs.io/en/master/builtinplugins.html#raw-digital-camera-files
553560

561+
#if OIIO_VERSION >= (10000 * 2 + 100 * 4 + 12) // OIIO_VERSION >= 2.4.12
562+
// To disable the application of the orientation, we need the PR https://github.com/OpenImageIO/oiio/pull/3669,
563+
// so we can disable the auto orientation and keep the metadata.
564+
configSpec.attribute("raw:user_flip", 0); // disable auto rotation of the image buffer but keep exif metadata orientation valid
565+
#endif
566+
554567
if (imageReadOptions.rawColorInterpretation == ERawColorInterpretation::None)
555568
{
556569
if (imageReadOptions.workingColorSpace != EImageColorSpace::NO_CONVERSION)
@@ -648,13 +661,30 @@ void readImage(const std::string& path,
648661
if (inBuf.spec().nchannels == 2)
649662
ALICEVISION_THROW_ERROR("Load of 2 channels is not supported. Image file: '" + path + "'.");
650663

664+
oiio::ParamValueList imgMetadata = readImageMetadata(path);
665+
666+
if (isRawImage)
667+
{
668+
// Check orientation metadata. If image is mirrored, mirror it back and update orientation metadata
669+
int orientation = imgMetadata.get_int("orientation", -1);
670+
671+
if (orientation == 2 || orientation == 4 || orientation == 5 || orientation == 7)
672+
{
673+
// horizontal mirroring
674+
oiio::ImageBuf inBufMirrored = oiio::ImageBufAlgo::flop(inBuf);
675+
inBuf = inBufMirrored;
676+
677+
orientation += (orientation == 2 || orientation == 4) ? -1 : 1;
678+
}
679+
}
680+
651681
// Apply DCP profile
652682
if (!imageReadOptions.colorProfileFileName.empty() &&
653683
imageReadOptions.rawColorInterpretation == ERawColorInterpretation::DcpLinearProcessing)
654684
{
655685
image::DCPProfile dcpProfile(imageReadOptions.colorProfileFileName);
656686

657-
oiio::ParamValueList imgMetadata = readImageMetadata(path);
687+
//oiio::ParamValueList imgMetadata = readImageMetadata(path);
658688
std::string cam_mul = "";
659689
if (!imgMetadata.getattribute("raw:cam_mul", cam_mul))
660690
{

src/software/pipeline/main_panoramaInit.cpp

Lines changed: 117 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -45,15 +45,6 @@ namespace po = boost::program_options;
4545
namespace fs = boost::filesystem;
4646
namespace pt = boost::property_tree;
4747

48-
struct Contact
49-
{
50-
int rank;
51-
std::string path;
52-
int width;
53-
int height;
54-
sfmData::EEXIFOrientation orientation;
55-
};
56-
5748
/**
5849
* A simple class for gaussian pyramid
5950
*/
@@ -665,16 +656,93 @@ class CircleDetector
665656
size_t _minimal_size;
666657
};
667658

668-
void resample(image::Image<image::RGBfColor>& output, const image::Image<image::RGBfColor>& input)
659+
/**
660+
* @brief Utility function for resizing an image.
661+
*/
662+
void resample(image::Image<image::RGBfColor>& output,
663+
const image::Image<image::RGBfColor>& input)
669664
{
670665
const oiio::ImageBuf inBuf(oiio::ImageSpec(input.Width(), input.Height(), 3, oiio::TypeDesc::FLOAT),
671666
const_cast<image::RGBfColor*>(input.data()));
667+
672668
oiio::ImageBuf outBuf(oiio::ImageSpec(output.Width(), output.Height(), 3, oiio::TypeDesc::FLOAT),
673669
(image::RGBfColor*)output.data());
674670

675671
oiio::ImageBufAlgo::resample(outBuf, inBuf, false);
676672
}
677673

674+
/**
675+
* @brief Utility function for rotating an image given its orientation metadata.
676+
*/
677+
void applyOrientation(image::Image<image::RGBfColor>& output,
678+
const image::Image<image::RGBfColor>& input,
679+
sfmData::EEXIFOrientation orientation)
680+
{
681+
const oiio::ImageBuf inBuf(oiio::ImageSpec(input.Width(), input.Height(), 3, oiio::TypeDesc::FLOAT),
682+
const_cast<image::RGBfColor*>(input.data()));
683+
684+
oiio::ImageBuf outBuf(oiio::ImageSpec(output.Width(), output.Height(), 3, oiio::TypeDesc::FLOAT),
685+
(image::RGBfColor*)output.data());
686+
687+
switch (orientation)
688+
{
689+
case sfmData::EEXIFOrientation::UPSIDEDOWN:
690+
oiio::ImageBufAlgo::rotate180(outBuf, inBuf);
691+
break;
692+
case sfmData::EEXIFOrientation::LEFT:
693+
oiio::ImageBufAlgo::rotate90(outBuf, inBuf);
694+
break;
695+
case sfmData::EEXIFOrientation::RIGHT:
696+
oiio::ImageBufAlgo::rotate270(outBuf, inBuf);
697+
break;
698+
default:
699+
outBuf.copy(inBuf);
700+
break;
701+
}
702+
}
703+
704+
/**
705+
* @brief Utility struct for contact sheet elements.
706+
*/
707+
struct Contact
708+
{
709+
int rank;
710+
std::string path;
711+
int width;
712+
int height;
713+
sfmData::EEXIFOrientation orientation;
714+
};
715+
716+
/**
717+
* @brief Width of contact sheet element, taking into account orientation metadata.
718+
*/
719+
int orientedWidth(const Contact& contact)
720+
{
721+
switch (contact.orientation)
722+
{
723+
case sfmData::EEXIFOrientation::LEFT:
724+
case sfmData::EEXIFOrientation::RIGHT:
725+
return contact.height;
726+
default:
727+
return contact.width;
728+
}
729+
}
730+
731+
/**
732+
* @brief Height of contact sheet element, taking into account orientation metadata.
733+
*/
734+
int orientedHeight(const Contact& contact)
735+
{
736+
switch (contact.orientation)
737+
{
738+
case sfmData::EEXIFOrientation::LEFT:
739+
case sfmData::EEXIFOrientation::RIGHT:
740+
return contact.width;
741+
default:
742+
return contact.height;
743+
}
744+
}
745+
678746
bool buildContactSheetImage(image::Image<image::RGBfColor>& output,
679747
const std::map<int, std::map<int, Contact>>& contactSheetInfo, int contactSheetItemMaxSize)
680748
{
@@ -686,8 +754,8 @@ bool buildContactSheetImage(image::Image<image::RGBfColor>& output,
686754
{
687755
for(const auto& item : rowpair.second)
688756
{
689-
maxdim = std::max(maxdim, item.second.width);
690-
maxdim = std::max(maxdim, item.second.height);
757+
maxdim = std::max(maxdim, orientedWidth(item.second));
758+
maxdim = std::max(maxdim, orientedHeight(item.second));
691759
}
692760
}
693761
double ratioResize = double(contactSheetItemMaxSize) / double(maxdim);
@@ -702,8 +770,8 @@ bool buildContactSheetImage(image::Image<image::RGBfColor>& output,
702770

703771
for(const auto& item : rowpair.second)
704772
{
705-
int resizedHeight = int(ratioResize * double(item.second.height));
706-
int resizedWidth = int(ratioResize * double(item.second.width));
773+
int resizedHeight = int(ratioResize * double(orientedHeight(item.second)));
774+
int resizedWidth = int(ratioResize * double(orientedWidth(item.second)));
707775

708776
rowHeight = std::max(rowHeight, resizedHeight);
709777
rowWidth += resizedWidth + space;
@@ -729,8 +797,8 @@ bool buildContactSheetImage(image::Image<image::RGBfColor>& output,
729797

730798
for(const auto& item : rowpair.second)
731799
{
732-
int resizedHeight = int(ratioResize * double(item.second.height));
733-
int resizedWidth = int(ratioResize * double(item.second.width));
800+
int resizedHeight = int(ratioResize * double(orientedHeight(item.second)));
801+
int resizedWidth = int(ratioResize * double(orientedWidth(item.second)));
734802

735803
rowHeight = std::max(rowHeight, resizedHeight);
736804
rowWidth += resizedWidth + space;
@@ -742,15 +810,20 @@ bool buildContactSheetImage(image::Image<image::RGBfColor>& output,
742810
int posX = space;
743811
for(const auto& item : rowpair.second)
744812
{
745-
int resizedHeight = int(ratioResize * double(item.second.height));
746-
int resizedWidth = int(ratioResize * double(item.second.width));
813+
int rawResizedHeight = int(ratioResize * double(item.second.height));
814+
int rawResizedWidth = int(ratioResize * double(item.second.width));
815+
816+
int resizedHeight = int(ratioResize * double(orientedHeight(item.second)));
817+
int resizedWidth = int(ratioResize * double(orientedWidth(item.second)));
747818

748819
image::Image<image::RGBfColor> input;
820+
image::Image<image::RGBfColor> rawThumbnail(rawResizedWidth, rawResizedHeight);
749821
image::Image<image::RGBfColor> thumbnail(resizedWidth, resizedHeight);
750822

751823
image::readImage(item.second.path, input, image::EImageColorSpace::SRGB);
752824

753-
resample(thumbnail, input);
825+
resample(rawThumbnail, input);
826+
applyOrientation(thumbnail, rawThumbnail, item.second.orientation);
754827

755828
rowOutput.block(0, posX, resizedHeight, resizedWidth) = thumbnail;
756829
posX += resizedWidth + space;
@@ -1154,9 +1227,32 @@ int main(int argc, char* argv[])
11541227
for(const auto& item_rotation : rotations)
11551228
{
11561229
IndexT viewIdx = namesWithRank[index].second;
1157-
if(item_rotation.second.trace() != 0)
1230+
const sfmData::View& v = sfmData.getView(viewIdx);
1231+
1232+
sfmData::EEXIFOrientation orientation = v.getMetadataOrientation();
1233+
double orientationAngle = 0.;
1234+
switch (orientation)
1235+
{
1236+
case sfmData::EEXIFOrientation::UPSIDEDOWN:
1237+
orientationAngle = boost::math::constants::pi<double>();
1238+
break;
1239+
case sfmData::EEXIFOrientation::LEFT:
1240+
orientationAngle = boost::math::constants::pi<double>() * .5;
1241+
break;
1242+
case sfmData::EEXIFOrientation::RIGHT:
1243+
orientationAngle = boost::math::constants::pi<double>() * -.5;
1244+
break;
1245+
default:
1246+
break;
1247+
}
1248+
1249+
const Eigen::AngleAxis<double> Morientation(orientationAngle, Eigen::Vector3d::UnitZ());
1250+
1251+
const Eigen::Matrix3d viewRotation = Morientation.toRotationMatrix().transpose() * item_rotation.second;
1252+
1253+
if(viewRotation.trace() != 0)
11581254
{
1159-
sfmData::CameraPose pose(geometry::Pose3(item_rotation.second, Eigen::Vector3d::Zero()));
1255+
sfmData::CameraPose pose(geometry::Pose3(viewRotation, Eigen::Vector3d::Zero()));
11601256
sfmData.setAbsolutePose(viewIdx, pose);
11611257
}
11621258
++index;

0 commit comments

Comments
 (0)