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
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)

set( RAWTOACES_MAJOR_VERSION 2 )
set( RAWTOACES_MINOR_VERSION 1 )
set( RAWTOACES_PATCH_VERSION 0 )
set( RAWTOACES_PATCH_VERSION 1 )
set( RAWTOACES_VERSION ${RAWTOACES_MAJOR_VERSION}.${RAWTOACES_MINOR_VERSION}.${RAWTOACES_PATCH_VERSION} )

set(RAWTOACES_CORE_LIB "rawtoaces_core")
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ A help message with a description of all command line options can be obtained by
--highlight-mode VAL 0 = clip, 1 = unclip, 2 = blend, 3..9 = rebuild. (default: 0)
--crop-box X Y W H Apply custom crop. If not present, the default crop is applied, which should match the crop of the in-camera JPEG.
--crop-mode STR Cropping mode. Supported options: 'none' (write out the full sensor area), 'soft' (write out full image, mark the crop as the display window), 'hard' (write out only the crop area). (default: soft)
--flip VAL If not 0, override the orientation specified in the metadata. 1..8 correspond to EXIF orientation codes (3 = 180 deg, 6 = 90 deg CCW, 8 = 90 deg CW.) (default: 0)
--flip VAL If not -1, override the orientation specified in the metadata. 1..8 correspond to EXIF orientation codes (0 = none, 3 = 180 deg, 6 = 90 deg CCW, 8 = 90 deg CW.) (default: -1)
--denoise-threshold VAL Wavelet denoising threshold (default: 0)
--demosaic STR Demosaicing algorithm. Supported options: 'linear', 'VNG', 'PPG', 'AHD', 'DCB', 'AHD-Mod', 'AFD', 'VCD', 'Mixed', 'LMMSE', 'AMaZE', 'DHT', 'AAHD', 'AHD'. (default: AHD)
Benchmarking and debugging:
Expand Down
15 changes: 14 additions & 1 deletion docs/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,17 @@
Release 2.1.0 (March ?? 2026) -- compared to 2.0.0
Release 2.1.1 (June ?? 2026) -- compared to 2.1.0
--------------------------------------------------------


**This version is API-compatible and ABI-compatible with the previous version.**

### Changes:

- *fix*: fix colour tint when processing DNG images [#280](https://github.com/AcademySoftwareFoundation/rawtoaces/pull/280)
- *fix*: allow custom white-balancing weights in DNG mode [#272](https://github.com/AcademySoftwareFoundation/rawtoaces/pull/272)
- *fix*: fix default orientation and box white balance [#268](https://github.com/AcademySoftwareFoundation/rawtoaces/pull/268)
- *fix*: add homebrew location to default DB search path [#264](https://github.com/AcademySoftwareFoundation/rawtoaces/pull/264)

Release 2.1.0 (March 18 2026) -- compared to 2.0.0
--------------------------------------------------------


Expand Down
1 change: 1 addition & 0 deletions docs/CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ by first name.
- Miaoqi Zhu (@miaoqi)
- Mikael Sundell (@mikaelsundell)
- Pavan Madduri (@pmady)
- Rémi Achard (@remia)
- Reto (@retokromer)
- Scott Dyer (@scottdyer)
- Sean Cooper (@scoopxyz)
Expand Down
6 changes: 3 additions & 3 deletions include/rawtoaces/image_converter.h
Original file line number Diff line number Diff line change
Expand Up @@ -188,10 +188,10 @@ class ImageConverter
/// 0 = clip, 1 = unclip, 2 = blend, 3..9 = rebuild.
int highlight_mode = 0;

/// If not 0, override the orientation specified in the metadata.
/// If not -1, override the orientation specified in the metadata.
/// 1..8 correspond to EXIF orientation codes
/// (3 = 180 deg, 6 = 90 deg CCW, 8 = 90 deg CW.)
int flip = 0;
/// (0 = none, 3 = 180 deg, 6 = 90 deg CCW, 8 = 90 deg CW.)
int flip = -1;

/// Apply custom crop. If not specified (all values are zeroes),
/// the default crop is applied, which should match the crop of the
Expand Down
4 changes: 2 additions & 2 deletions src/docs/api/python/image_converter.rst
Original file line number Diff line number Diff line change
Expand Up @@ -121,9 +121,9 @@ ImageConverter

.. py:attribute:: int flip

If not 0, override the orientation specified in the metadata.
If not -1, override the orientation specified in the metadata.
1..8 correspond to EXIF orientation codes
(3 = 180 deg, 6 = 90 deg CCW, 8 = 90 deg CW.)
(0 = none, 3 = 180 deg, 6 = 90 deg CCW, 8 = 90 deg CW.)

.. py:attribute:: float denoise_threshold

Expand Down
37 changes: 28 additions & 9 deletions src/rawtoaces_core/mathOps.h
Original file line number Diff line number Diff line change
Expand Up @@ -414,35 +414,54 @@ template <typename T> vector<T> XYZ_to_uv( const vector<T> &XYZ )

template <typename T>
std::vector<std::vector<T>> calculate_CAT(
const std::vector<T> &src_white_XYZ, const std::vector<T> &dst_white_XYZ )
const std::vector<T> &src_white_XYZ,
const std::vector<T> &dst_white_XYZ,
bool use_bradford )
{
assert( src_white_XYZ.size() == 3 );
assert( dst_white_XYZ.size() == 3 );

// clang-format off
// Color Adaptation Matrices - CAT02 (default)

// Color Adaptation Matrices - Bradford
static const std::vector<std::vector<double>> Bradford = {
{ 0.8951, 0.2664, -0.1614 },
{ -0.7502, 1.7135, 0.0367 },
{ 0.0389, -0.0685, 1.0296 }
};

static const std::vector<std::vector<double>> Bradford_inv = {
{ 0.98699290546671225, -0.14705425642099007, 0.15996265166373122 },
{ 0.43230526972339445, 0.51836027153677744, 0.049291228212855594 },
{ -0.0085286645751773294, 0.040042821654084869, 0.96848669578754998 }
};

// Color Adaptation Matrices - CAT02
static const std::vector<std::vector<double>> CAT02 = {
{ 0.7328, 0.4296, -0.1624 },
{ -0.7036, 1.6975, 0.0061 },
{ 0.0030, 0.0136, 0.9834 }
};

static const std::vector<std::vector<double>> CAT02_inv = {
{ 1.0961238208355142, -0.27886900021828726, 0.18274517938277304 },
{ 1.0961238208355142, -0.27886900021828726, 0.18274517938277304 },
{ 0.45436904197535921, 0.47353315430741177, 0.072097803717229125 },
{ -0.0096276087384293551, -0.0056980312161134198, 1.0153256399545427 }
{ -0.0096276087384293551, -0.0056980312161134198, 1.0153256399545427 }
};
// clang-format on

std::vector<double> src_white_LMS = mulVector( src_white_XYZ, CAT02 );
std::vector<double> dst_white_LMS = mulVector( dst_white_XYZ, CAT02 );
const auto &M1 = use_bradford ? Bradford : CAT02;
const auto &M2 = use_bradford ? Bradford_inv : CAT02_inv;

std::vector<double> src_white_LMS = mulVector( src_white_XYZ, M1 );
std::vector<double> dst_white_LMS = mulVector( dst_white_XYZ, M1 );

std::vector<std::vector<double>> mat( 3, std::vector<double>( 3, 0 ) );
for ( size_t i = 0; i < 3; i++ )
mat[i][i] = dst_white_LMS[i] / src_white_LMS[i];

mat = mulVector( mat, transposeVec( CAT02 ) );
mat = mulVector( CAT02_inv, transposeVec( mat ) );
mat = mulVector( mat, transposeVec( M1 ) );
mat = mulVector( M2, transposeVec( mat ) );
return mat;
}

Expand Down
85 changes: 56 additions & 29 deletions src/rawtoaces_core/rawtoaces_core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,8 +259,16 @@ SpectralSolver::collect_data_files( const std::string &type ) const
}
else
{
std::cerr << "Warning: Database location '" << directory
<< "' is not a directory." << std::endl;
if ( std::filesystem::exists( directory ) )
{
std::cerr << "Warning: Database location '" << directory
<< "' is not a directory." << std::endl;
}
else
{
std::cerr << "Warning: Database location '" << directory
<< "' does not exist." << std::endl;
}
}
}
return result;
Expand Down Expand Up @@ -589,7 +597,8 @@ std::vector<std::vector<double>> calculate_XYZ(
( observer_z * illuminant_spectrum ).integrate() / y;

XYZ = mulVector(
XYZ, calculate_CAT( source_white_point, reference_white_point ) );
XYZ,
calculate_CAT( source_white_point, reference_white_point, false ) );

return XYZ;
}
Expand Down Expand Up @@ -1155,9 +1164,6 @@ vector<double> matrix_RGB_to_XYZ( const double chromaticities[][2] )
/// optimization, then calculates the white point either from the neutral RGB
/// values or from the calibration illuminant's color temperature.
///
/// The function also applies baseline exposure compensation and normalizes
/// the white point to ensure proper color scaling in the transformation pipeline.
///
/// @param metadata Camera metadata containing calibration and exposure information
/// @param out_camera_to_XYZ_matrix Output camera to XYZ transformation matrix
/// @param out_camera_XYZ_white_point Output camera white point in XYZ space
Expand All @@ -1171,9 +1177,6 @@ void get_camera_XYZ_matrix_and_white_point(
invertV( find_XYZ_to_camera_matrix( metadata, metadata.neutral_RGB ) );
assert( std::fabs( sumVector( out_camera_to_XYZ_matrix ) - 0.0 ) > 1e-09 );

scaleVector(
out_camera_to_XYZ_matrix, std::pow( 2.0, metadata.baseline_exposure ) );

if ( metadata.neutral_RGB.size() > 0 )
{
out_camera_XYZ_white_point =
Expand Down Expand Up @@ -1202,38 +1205,62 @@ vector<vector<double>> MetadataSolver::calculate_CAT_matrix()
vector<double> output_XYZ_white_point =
mulVector( output_RGB_to_XYZ_matrix, deviceWhiteV );
vector<vector<double>> CAT_matrix =
calculate_CAT( camera_XYZ_white_point, output_XYZ_white_point );
calculate_CAT( camera_XYZ_white_point, output_XYZ_white_point, true );

return CAT_matrix;
}

vector<vector<double>> MetadataSolver::calculate_IDT_matrix()
/// Colour space transform from XYZ D60 to ACES
const std::vector<std::vector<double>> &get_XYZ_D60_to_ACES()
{
// 1. Obtains the CAT matrix for white point adaptation
vector<vector<double>> CAT_matrix = calculate_CAT_matrix();
// clang-format off
static const std::vector<std::vector<double>> XYZ_D60_to_ACES = {
{ 1.0498110175, 0.0000000000, -0.0000974845 },
{ -0.4959030231, 1.3733130458, 0.0982400361 },
{ 0.0000000000, 0.0000000000, 0.9912520182 }
};
// clang-format on

// 2. Converts the CAT matrix to a flattened format for matrix multiplication
vector<double> XYZ_D65_acesrgb( 9 ), CAT( 9 );
for ( size_t i = 0; i < 3; i++ )
for ( size_t j = 0; j < 3; j++ )
{
XYZ_D65_acesrgb[i * 3 + j] = XYZ_D65_acesrgb_3[i][j];
CAT[i * 3 + j] = CAT_matrix[i][j];
}
return XYZ_D60_to_ACES;
}

// 3. Multiplies the D65 ACES RGB to XYZ matrix with the CAT matrix
vector<double> matrix = mulVector( XYZ_D65_acesrgb, CAT, 3 );
std::vector<std::vector<double>> MetadataSolver::calculate_IDT_matrix()
{
std::vector<double> camera_to_XYZ_matrix;
std::vector<double> camera_XYZ_white_point;

get_camera_XYZ_matrix_and_white_point(
_metadata, camera_to_XYZ_matrix, camera_XYZ_white_point );

// 4. Reshapes the result into a 3×3 transformation matrix
vector<vector<double>> DNG_IDT_matrix( 3, vector<double>( 3 ) );
assert( camera_to_XYZ_matrix.size() == 9 );
assert( camera_XYZ_white_point.size() == 3 );

std::vector<double> deviceWhiteV( 3, 1.0 );
std::vector<double> output_RGB_to_XYZ_matrix =
matrix_RGB_to_XYZ( chromaticitiesACES );
std::vector<double> output_XYZ_white_point =
mulVector( output_RGB_to_XYZ_matrix, deviceWhiteV );
std::vector<std::vector<double>> CAT_matrix =
calculate_CAT( camera_XYZ_white_point, output_XYZ_white_point, true );

std::vector<vector<double>> camera_to_XYZ( 3, vector<double>( 3 ) );
for ( size_t i = 0; i < 3; i++ )
for ( size_t j = 0; j < 3; j++ )
DNG_IDT_matrix[i][j] = matrix[i * 3 + j];
camera_to_XYZ[i][j] = camera_to_XYZ_matrix[i * 3 + j];

// The camera_to_XYZ_matrix expects camera raw values, but the pixels
// we get from libraw are white-balanced. Undo the white-balancing as
// the first step.
std::vector<std::vector<double>> result( 3, std::vector<double>( 3 ) );
result[0][0] = _metadata.neutral_RGB[0];
result[1][1] = _metadata.neutral_RGB[1];
result[2][2] = _metadata.neutral_RGB[2];

// 5. Validates the matrix properties (non-zero determinant)
assert( std::fabs( sumVectorM( DNG_IDT_matrix ) - 0.0 ) > 1e-09 );
result = mulVector( camera_to_XYZ, result );
result = mulVector( CAT_matrix, transposeVec( result ) );
result = mulVector( get_XYZ_D60_to_ACES(), transposeVec( result ) );

return DNG_IDT_matrix;
return result;
}

/// Cost function operator for Ceres optimization of IDT matrix parameters.
Expand Down
Loading
Loading