Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions src/aliceVision/image/io.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -419,7 +419,14 @@ oiio::ParamValueList readImageMetadata(const std::string& path, int& width, int&

oiio::ImageSpec readImageSpec(const std::string& path)
{
std::unique_ptr<oiio::ImageInput> in(oiio::ImageInput::open(path));
oiio::ImageSpec configSpec;
#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
#endif

std::unique_ptr<oiio::ImageInput> in(oiio::ImageInput::open(path, &configSpec));
oiio::ImageSpec spec = in->spec();

if(!in)
Expand Down Expand Up @@ -551,6 +558,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 + 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
#endif

if (imageReadOptions.rawColorInterpretation == ERawColorInterpretation::None)
{
if (imageReadOptions.workingColorSpace != EImageColorSpace::NO_CONVERSION)
Expand Down Expand Up @@ -648,13 +661,30 @@ 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);

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))
{
Expand Down
138 changes: 117 additions & 21 deletions src/software/pipeline/main_panoramaInit.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
*/
Expand Down Expand Up @@ -665,16 +656,93 @@ class CircleDetector
size_t _minimal_size;
};

void resample(image::Image<image::RGBfColor>& output, const image::Image<image::RGBfColor>& input)
/**
* @brief Utility function for resizing an image.
*/
void resample(image::Image<image::RGBfColor>& output,
const image::Image<image::RGBfColor>& input)
{
const oiio::ImageBuf inBuf(oiio::ImageSpec(input.Width(), input.Height(), 3, oiio::TypeDesc::FLOAT),
const_cast<image::RGBfColor*>(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<image::RGBfColor>& output,
const image::Image<image::RGBfColor>& input,
sfmData::EEXIFOrientation orientation)
{
const oiio::ImageBuf inBuf(oiio::ImageSpec(input.Width(), input.Height(), 3, oiio::TypeDesc::FLOAT),
const_cast<image::RGBfColor*>(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<image::RGBfColor>& output,
const std::map<int, std::map<int, Contact>>& contactSheetInfo, int contactSheetItemMaxSize)
{
Expand All @@ -686,8 +754,8 @@ bool buildContactSheetImage(image::Image<image::RGBfColor>& 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);
Expand All @@ -702,8 +770,8 @@ bool buildContactSheetImage(image::Image<image::RGBfColor>& 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;
Expand All @@ -729,8 +797,8 @@ bool buildContactSheetImage(image::Image<image::RGBfColor>& 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;
Expand All @@ -742,15 +810,20 @@ bool buildContactSheetImage(image::Image<image::RGBfColor>& 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<image::RGBfColor> input;
image::Image<image::RGBfColor> rawThumbnail(rawResizedWidth, rawResizedHeight);
image::Image<image::RGBfColor> 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;
Expand Down Expand Up @@ -1154,9 +1227,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<double>();
break;
case sfmData::EEXIFOrientation::LEFT:
orientationAngle = boost::math::constants::pi<double>() * .5;
break;
case sfmData::EEXIFOrientation::RIGHT:
orientationAngle = boost::math::constants::pi<double>() * -.5;
break;
default:
break;
}

const Eigen::AngleAxis<double> 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;
Expand Down