Skip to content
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

Color Space management #1170

Merged
merged 15 commits into from
Oct 19, 2022
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
6 changes: 5 additions & 1 deletion .github/workflows/continuous-integration.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ jobs:
DEPS_INSTALL_DIR: /opt/AliceVision_install
BUILD_TYPE: Release
CTEST_OUTPUT_ON_FAILURE: 1
ALICEVISION_ROOT: ${{ github.workspace }}/../AV_install
steps:
- uses: actions/checkout@v1

Expand Down Expand Up @@ -106,6 +107,7 @@ jobs:
# tripletPath: '${{ github.workspace }}\..\vcpkg\triplets\community\x64-windows-release.cmake'
BUILD_TYPE: Release
CTEST_OUTPUT_ON_FAILURE: 1
ALICEVISION_ROOT: '${{ github.workspace }}/install'
COMMIT_ID: 8e8a3d7c1a6f7f587b486663b1e814911e6f2342
steps:
- name: Checkout
Expand Down Expand Up @@ -195,10 +197,11 @@ jobs:
cmakeListsOrSettingsJson: CMakeListsTxtAdvanced
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: ${{ env.buildDir }}
buildWithCMakeArgs: '--config Release'
buildWithCMakeArgs: '--config Release --target install'
cmakeAppendedArgs: -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }}
-DBUILD_SHARED_LIBS:BOOL=ON
-DTARGET_ARCHITECTURE=core
-DCMAKE_INSTALL_PREFIX:PATH=${{ env.ALICEVISION_ROOT }}
-DALICEVISION_BUILD_TESTS:BOOL=ON
-DALICEVISION_BUILD_EXAMPLES:BOOL=ON
-DALICEVISION_USE_OPENCV:BOOL=ON
Expand All @@ -219,6 +222,7 @@ jobs:
cmakeListsTxtPath: '${{ github.workspace }}/CMakeLists.txt'
buildDirectory: ${{ env.buildDir }}
buildWithCMakeArgs: '--config Release --target RUN_TESTS'
cmakeAppendedArgs: -DCMAKE_INSTALL_PREFIX:PATH=${{ env.ALICEVISION_ROOT }}
# This input tells run-cmake to consume the vcpkg.cmake toolchain file set by run-vcpkg.
cmakeBuildType: Release
useVcpkgToolchainFile: true
Expand Down
1 change: 1 addition & 0 deletions ci/env.sh
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export ALICEVISION_BUILD="${TRAVIS_BUILD_DIR}/build"
export ALICEVISION_INSTALL="${TRAVIS_BUILD_DIR}/install"
export ALICEVISION_BUILD_AS3RDPARTY="${TRAVIS_BUILD_DIR}/buildAs3rdparty"
export ALICEVISION_SAMPLE_AS3RDPARTY="${ALICEVISION_SOURCE}/src/samples/aliceVisionAs3rdParty/"
export ALICEVISION_ROOT="${ALICEVISION_INSTALL}"
# GT datasets for tests
export GT_TEST_ROOT="${TRAVIS_BUILD_DIR}/gt_test"
export GT_TEST_SOURCE="${GT_TEST_ROOT}/gt_source"
Expand Down
8 changes: 3 additions & 5 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -330,10 +330,6 @@ endif()
find_package(OpenImageIO 2.0.9 REQUIRED)
if(OPENIMAGEIO_FOUND OR OpenImageIO_FOUND)
message(STATUS "OpenImageIO found.")
if(UNIX)
# Add DL dependency on linux
set(OPENIMAGEIO_LIBRARIES "${OPENIMAGEIO_LIBRARIES};dl")
endif()
else()
message(SEND_ERROR "Failed to find OpenImageIO.")
endif()
Expand Down Expand Up @@ -882,7 +878,9 @@ else()
PROPERTY FOLDER AliceVision
)

install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc DESTINATION ${CMAKE_INSTALL_DOCDIR})
if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/doc)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/doc DESTINATION ${CMAKE_INSTALL_DOCDIR})
endif()

endif()
endif()
Expand Down
2 changes: 1 addition & 1 deletion src/aliceVision/hdr/sampling.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,7 @@ bool Sampling::extractSamplesFromImages(std::vector<ImageSample>& out_samples, c
const double exposure = times[idBracket];

image::ImageReadOptions options;
options.outputColorSpace = colorspace;
options.workingColorSpace = colorspace;
options.applyWhiteBalance = applyWhiteBalance;

// Load image
Expand Down
4 changes: 3 additions & 1 deletion src/aliceVision/image/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ set(image_files_headers
all.hpp
Image.hpp
imageAlgo.hpp
colorspace.hpp
concat.hpp
convertion.hpp
convolutionBase.hpp
Expand All @@ -23,6 +24,7 @@ set(image_files_headers

# Sources
set(image_files_sources
colorspace.cpp
convolution.cpp
filtering.cpp
io.cpp
Expand All @@ -45,7 +47,7 @@ alicevision_add_library(aliceVision_image
)

# Install config.ocio
install(FILES config.ocio DESTINATION ${CMAKE_INSTALL_DATADIR}/aliceVision)
install(FILES ./share/aliceVision/config.ocio DESTINATION ${CMAKE_INSTALL_DATADIR}/aliceVision)

# Unit tests
alicevision_add_test(image_test.cpp NAME "image" LINKS aliceVision_image)
Expand Down
266 changes: 266 additions & 0 deletions src/aliceVision/image/colorspace.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,266 @@
// This file is part of the AliceVision project.
// Copyright (c) 2022 AliceVision contributors.
// This Source Code Form is subject to the terms of the Mozilla Public License,
// v. 2.0. If a copy of the MPL was not distributed with this file,
// You can obtain one at https://mozilla.org/MPL/2.0/.

#include "colorspace.hpp"

#include <aliceVision/system/Logger.hpp>

#include <OpenImageIO/color.h>

#include <boost/algorithm/string.hpp>
#include <boost/filesystem.hpp>


namespace fs = boost::filesystem;

namespace aliceVision {
namespace image {

namespace
{
oiio::ColorConfig colorConfigOCIO(getDefaultColorConfigFilePath());
}

oiio::ColorConfig& getGlobalColorConfigOCIO()
{
return colorConfigOCIO;
}

std::string getColorConfigFilePathFromSourceCode()
{
const fs::path moduleFolder = fs::path(__FILE__).parent_path();
return (moduleFolder / "share/aliceVision/config.ocio").string();
}

std::string getDefaultColorConfigFilePath()
{
std::string configOCIOFilePath = "";

char const* ALICEVISION_OCIO = std::getenv("ALICEVISION_OCIO");
if (ALICEVISION_OCIO != NULL)
{
configOCIOFilePath = std::string(ALICEVISION_OCIO);
if (fs::exists(configOCIOFilePath))
{
// Check if a sRGB linear color space named "scene-linear Rec.709-sRGB" is present and set as scene_linear role
oiio::ColorConfig colorConfig(configOCIOFilePath);
const std::string linearColorSpace = colorConfig.getColorSpaceNameByRole("scene_linear");
if (linearColorSpace == "scene-linear Rec.709-sRGB")
{
ALICEVISION_LOG_TRACE("ALICEVISION_OCIO configuration file: '" << configOCIOFilePath << "' found.");
return configOCIOFilePath;
}
else
{
ALICEVISION_LOG_WARNING("ALICEVISION_OCIO configuration file is not valid: '" << configOCIOFilePath << "'.\n"
"The scene_linear role named \"scene-linear Rec.709-sRGB\" is required.\n"
"Skip this OCIO configuration file and use the embedded one.");
}
}
else if (configOCIOFilePath.empty())
{
ALICEVISION_LOG_TRACE("ALICEVISION_OCIO is empty.");
}
else
{
ALICEVISION_LOG_WARNING("ALICEVISION_OCIO is defined but does not point to an existing file: '" << configOCIOFilePath << "'");
}
}

// To be enabled if we decide to take OCIO env var in consideration before using the enbedded config file
if(false)
{
char const* OCIO = std::getenv("OCIO");
if(OCIO != NULL)
{
configOCIOFilePath = std::string(OCIO);
if(fs::exists(configOCIOFilePath))
{
ALICEVISION_LOG_TRACE("OCIO configuration file: '" << configOCIOFilePath << "' found.");
return configOCIOFilePath;
}
else if(configOCIOFilePath == "")
{
ALICEVISION_LOG_TRACE("OCIO is empty. Use embedded config...");
}
else
{
ALICEVISION_LOG_TRACE("OCIO does not point to an existing file. Use embedded config...");
}
}
}

char const* ALICEVISION_ROOT = std::getenv("ALICEVISION_ROOT");
if (ALICEVISION_ROOT == NULL)
{
const std::string configFromSource = getColorConfigFilePathFromSourceCode();
if(fs::exists(configFromSource))
{
ALICEVISION_LOG_DEBUG("ALICEVISION_ROOT is not defined, use embedded OCIO config file from source code: " << configFromSource);
return configFromSource;
}
// Output message with logging before throw as this function could be called before main.
ALICEVISION_LOG_ERROR("ALICEVISION_ROOT is not defined, embedded OCIO config file cannot be accessed.");
ALICEVISION_THROW_ERROR("ALICEVISION_ROOT is not defined, embedded OCIO config file cannot be accessed.");
}
configOCIOFilePath = std::string(ALICEVISION_ROOT);
configOCIOFilePath.append("/share/aliceVision/config.ocio");

if (!fs::exists(configOCIOFilePath))
{
const std::string configFromSource = getColorConfigFilePathFromSourceCode();
if(fs::exists(configFromSource))
{
ALICEVISION_LOG_DEBUG("Embedded OCIO config file in ALICEVISION_ROOT does not exist, use config from source code: " << configFromSource);
return configFromSource;
}
ALICEVISION_LOG_ERROR("Embedded OCIO configuration file: '" << configOCIOFilePath << "' cannot be accessed.");
ALICEVISION_THROW_ERROR("Embedded OCIO configuration file: '" << configOCIOFilePath << "' cannot be accessed.");
}
ALICEVISION_LOG_TRACE("Embedded OCIO configuration file: '" << configOCIOFilePath << "' found.");

return configOCIOFilePath;
}

void initColorConfigOCIO(const std::string& colorConfigFilePath)
{
colorConfigOCIO.reset(colorConfigFilePath);
if (!colorConfigOCIO.supportsOpenColorIO())
{
ALICEVISION_THROW_ERROR("OpenImageIO has not been compiled with OCIO.");
}
const std::string error = colorConfigOCIO.geterror();
if (!error.empty())
{
ALICEVISION_THROW_ERROR("Erroneous OCIO config file " << colorConfigFilePath << ":" << std::endl << error);
}
int ocioVersion = colorConfigOCIO.OpenColorIO_version_hex();
int ocioMajor = (ocioVersion & 0xFF000000) >> 24;
int ocioMinor = (ocioVersion & 0x00FF0000) >> 16;
int ocioPatch = (ocioVersion & 0x0000FF00) >> 8;
ALICEVISION_LOG_INFO("OCIO color config initialized with OCIO version: " << ocioMajor << "." << ocioMinor << "." << ocioPatch);
}

std::string EImageColorSpace_informations()
{
return EImageColorSpace_enumToString(EImageColorSpace::AUTO) + ", " +
EImageColorSpace_enumToString(EImageColorSpace::LINEAR) + ", " +
EImageColorSpace_enumToString(EImageColorSpace::SRGB) + ", " +
EImageColorSpace_enumToString(EImageColorSpace::ACES2065_1) + ", " +
EImageColorSpace_enumToString(EImageColorSpace::ACEScg) + ", " +
EImageColorSpace_enumToString(EImageColorSpace::LAB) + ", " +
EImageColorSpace_enumToString(EImageColorSpace::XYZ) + ", " +
EImageColorSpace_enumToString(EImageColorSpace::NO_CONVERSION);
}

EImageColorSpace EImageColorSpace_stringToEnum(const std::string& dataType)
{
const std::string type = boost::to_lower_copy(dataType);

if(type == "auto")
return EImageColorSpace::AUTO;
if(type == "linear")
return EImageColorSpace::LINEAR;
if(type == "srgb")
return EImageColorSpace::SRGB;
if(type == "aces2065-1")
return EImageColorSpace::ACES2065_1;
if(type == "acescg")
return EImageColorSpace::ACEScg;
if(type == "lab")
return EImageColorSpace::LAB;
if(type == "xyz")
return EImageColorSpace::XYZ;
if(type == "no_conversion")
return EImageColorSpace::NO_CONVERSION;

throw std::out_of_range("Invalid EImageColorSpace: " + dataType);
}

std::string EImageColorSpace_enumToString(const EImageColorSpace dataType)
{
switch(dataType)
{
case EImageColorSpace::AUTO:
return "auto";
case EImageColorSpace::LINEAR:
return "linear";
case EImageColorSpace::SRGB:
return "srgb";
case EImageColorSpace::ACES2065_1:
return "aces2065-1";
case EImageColorSpace::ACEScg:
return "acescg";
case EImageColorSpace::LAB:
return "lab";
case EImageColorSpace::XYZ:
return "xyz";
case EImageColorSpace::NO_CONVERSION:
return "no_conversion";
}
throw std::out_of_range("Invalid EImageColorSpace enum");
}

std::string EImageColorSpace_enumToOIIOString(const EImageColorSpace colorSpace)
{
switch(colorSpace)
{
case EImageColorSpace::SRGB: return "sRGB";
case EImageColorSpace::LINEAR: return "Linear";
case EImageColorSpace::ACES2065_1: return "aces2065-1";
case EImageColorSpace::ACEScg: return "ACEScg";
default: ;
}
throw std::out_of_range("No string defined for EImageColorSpace to OIIO conversion: " +
std::to_string(int(colorSpace)));
}

EImageColorSpace EImageColorSpace_OIIOstringToEnum(const std::string& colorspace)
{
if (colorspace == "Linear") return EImageColorSpace::LINEAR;
if (colorspace == "sRGB") return EImageColorSpace::SRGB;
if (colorspace == "aces2065-1") return EImageColorSpace::ACES2065_1;
if (colorspace == "ACEScg") return EImageColorSpace::ACEScg;

throw std::out_of_range("No EImageColorSpace defined for string: " + colorspace);
}

bool EImageColorSpace_isSupportedOIIOEnum(const EImageColorSpace& colorspace)
{
switch(colorspace)
{
case EImageColorSpace::SRGB: return true;
case EImageColorSpace::LINEAR: return true;
case EImageColorSpace::ACES2065_1: return true;
case EImageColorSpace::ACEScg: return true;
default: return false;
}
}

bool EImageColorSpace_isSupportedOIIOstring(const std::string& colorspace)
{
if (colorspace == "Linear") return true;
if (colorspace == "sRGB") return true;
if (colorspace == "aces2065-1") return true;
if (colorspace == "ACEScg") return true;
return false;
}

std::ostream& operator<<(std::ostream& os, EImageColorSpace dataType)
{
return os << EImageColorSpace_enumToString(dataType);
}

std::istream& operator>>(std::istream& in, EImageColorSpace& dataType)
{
std::string token;
in >> token;
dataType = EImageColorSpace_stringToEnum(token);
return in;
}

}
}
Loading