Skip to content

Commit 79ab03e

Browse files
committed
More tests in prepare_transform_spectral function
Signed-off-by: Aleksandr Motsjonov <[email protected]>
1 parent 07036e1 commit 79ab03e

File tree

1 file changed

+349
-0
lines changed

1 file changed

+349
-0
lines changed

tests/test_image_converter.cpp

Lines changed: 349 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1211,6 +1211,350 @@ void test_missing_illuminant_data()
12111211
output.find( "Error: No matching light source" ) != std::string::npos );
12121212
}
12131213

1214+
/// Tests that conversion fails when specified illuminant type is not found in illuminant data (should fail)
1215+
void test_illuminant_type_not_found()
1216+
{
1217+
std::cout << std::endl << "test_illuminant_type_not_found()" << std::endl;
1218+
1219+
// Create test directory with database
1220+
TestDirectory test_dir;
1221+
1222+
// Create camera data (so camera lookup succeeds)
1223+
test_dir.create_test_data_file(
1224+
"camera",
1225+
{ { "manufacturer", "Blackmagic" }, { "model", "Cinema Camera" } } );
1226+
1227+
// Create training data (so training data loading succeeds)
1228+
test_dir.create_test_data_file( "training", { { "illuminant", "D65" } } );
1229+
1230+
// Create observer data (so observer data loading succeeds)
1231+
test_dir.create_test_data_file( "cmf", { { "illuminant", "D65" } } );
1232+
1233+
// Create illuminant data with a specific illuminant type (e.g., "D65")
1234+
// but we'll request a different type that doesn't exist
1235+
test_dir.create_test_data_file( "illuminant", { { "illuminant", "D65" } } );
1236+
1237+
// Create a mock ImageSpec with camera metadata
1238+
OIIO::ImageSpec image_spec;
1239+
image_spec.width = 100;
1240+
image_spec.height = 100;
1241+
image_spec.nchannels = 3;
1242+
image_spec.format = OIIO::TypeDesc::UINT8;
1243+
image_spec["cameraMake"] = "Blackmagic";
1244+
image_spec["cameraModel"] = "Cinema Camera";
1245+
1246+
// Configure settings with illuminant that doesn't exist in the database
1247+
ImageConverter::Settings settings;
1248+
settings.database_directories = { test_dir.get_database_path() };
1249+
settings.illuminant = "A"; // Request illuminant "A" which doesn't exist
1250+
settings.verbosity = 1;
1251+
1252+
// Test: Request an illuminant type that doesn't exist in the illuminant data
1253+
std::vector<double> WB_multipliers;
1254+
std::vector<std::vector<double>> IDT_matrix;
1255+
std::vector<std::vector<double>> CAT_matrix;
1256+
1257+
// Capture stderr output to verify error messages
1258+
bool success;
1259+
std::string output = capture_stderr( [&]() {
1260+
// This should fail because illuminant "A" is not found
1261+
success = prepare_transform_spectral(
1262+
image_spec, settings, WB_multipliers, IDT_matrix, CAT_matrix );
1263+
} );
1264+
1265+
// Should fail
1266+
OIIO_CHECK_ASSERT( !success );
1267+
1268+
// Assert on the expected error message
1269+
OIIO_CHECK_ASSERT(
1270+
output.find( "Failed to find illuminant type = 'a'." ) !=
1271+
std::string::npos );
1272+
}
1273+
1274+
/// Tests that auto-detection of illuminant works with 4-channel WB_multipliers and verbosity output
1275+
void test_auto_detect_illuminant_with_wb_multipliers()
1276+
{
1277+
std::cout << std::endl
1278+
<< "test_auto_detect_illuminant_with_wb_multipliers()"
1279+
<< std::endl;
1280+
1281+
// Create test directory with database
1282+
TestDirectory test_dir;
1283+
1284+
// Create camera data (so camera lookup succeeds)
1285+
test_dir.create_test_data_file(
1286+
"camera",
1287+
{ { "manufacturer", "Blackmagic" }, { "model", "Cinema Camera" } } );
1288+
1289+
// Create training data (so training data loading succeeds)
1290+
test_dir.create_test_data_file( "training", { { "illuminant", "D65" } } );
1291+
1292+
// Create observer data (so observer data loading succeeds)
1293+
test_dir.create_test_data_file( "cmf", { { "illuminant", "D65" } } );
1294+
1295+
// Create illuminant data (so illuminant data loading succeeds)
1296+
test_dir.create_test_data_file( "illuminant", { { "illuminant", "D65" } } );
1297+
1298+
// Create a mock ImageSpec with camera metadata
1299+
OIIO::ImageSpec image_spec;
1300+
image_spec.width = 100;
1301+
image_spec.height = 100;
1302+
image_spec.nchannels = 3;
1303+
image_spec.format = OIIO::TypeDesc::UINT8;
1304+
image_spec["cameraMake"] = "Blackmagic";
1305+
image_spec["cameraModel"] = "Cinema Camera";
1306+
1307+
// Configure settings with empty illuminant (to trigger auto-detection)
1308+
// and verbosity > 0 (to trigger the "Found illuminant:" message)
1309+
ImageConverter::Settings settings;
1310+
settings.database_directories = { test_dir.get_database_path() };
1311+
settings.illuminant = ""; // Empty to trigger auto-detection
1312+
settings.verbosity = 1; // > 0 to trigger the output message
1313+
1314+
// Provide WB_multipliers with size 4 to exercise the 4-channel path
1315+
std::vector<double> WB_multipliers = { 1.5, 1.0, 1.2, 1.0 }; // 4 channels
1316+
std::vector<std::vector<double>> IDT_matrix;
1317+
std::vector<std::vector<double>> CAT_matrix;
1318+
1319+
bool success;
1320+
std::string output = capture_stderr( [&]() {
1321+
// This should succeed and auto-detect the illuminant
1322+
// This will exercise the 4-channel WB_multipliers path (when WB_multipliers.size() == 4)
1323+
// and the verbosity output path (when verbosity > 0 and illuminant is found)
1324+
success = prepare_transform_spectral(
1325+
image_spec, settings, WB_multipliers, IDT_matrix, CAT_matrix );
1326+
} );
1327+
1328+
// Should succeed
1329+
OIIO_CHECK_ASSERT( success );
1330+
1331+
// With the current mocked input (WB_multipliers = {1.5, 1.0, 1.2, 1.0}),
1332+
OIIO_CHECK_ASSERT(
1333+
output.find( "Found illuminant: '2000k'." ) != std::string::npos );
1334+
}
1335+
1336+
/// Tests that auto-detection extracts white balance from RAW metadata when WB_multipliers is not provided
1337+
void test_auto_detect_illuminant_from_raw_metadata()
1338+
{
1339+
std::cout << std::endl
1340+
<< "test_auto_detect_illuminant_from_raw_metadata()" << std::endl;
1341+
1342+
// Create test directory with database
1343+
TestDirectory test_dir;
1344+
1345+
// Create camera data (so camera lookup succeeds)
1346+
test_dir.create_test_data_file(
1347+
"camera",
1348+
{ { "manufacturer", "Blackmagic" }, { "model", "Cinema Camera" } } );
1349+
1350+
// Create training data (so training data loading succeeds)
1351+
test_dir.create_test_data_file( "training", { { "illuminant", "D65" } } );
1352+
1353+
// Create observer data (so observer data loading succeeds)
1354+
test_dir.create_test_data_file( "cmf", { { "illuminant", "D65" } } );
1355+
1356+
// Create illuminant data (so illuminant data loading succeeds)
1357+
test_dir.create_test_data_file( "illuminant", { { "illuminant", "D65" } } );
1358+
1359+
// Use direct library method to control WB_multipliers and force the else path
1360+
// The exec method populates WB_multipliers from metadata, so it takes the if branch.
1361+
// To test the else branch (extract from raw:pre_mul), we need WB_multipliers.size() != 4.
1362+
1363+
// Create a mock ImageSpec with camera metadata and raw:pre_mul attribute
1364+
OIIO::ImageSpec image_spec;
1365+
image_spec.width = 100;
1366+
image_spec.height = 100;
1367+
image_spec.nchannels = 3;
1368+
image_spec.format = OIIO::TypeDesc::UINT8;
1369+
image_spec["cameraMake"] = "Blackmagic";
1370+
image_spec["cameraModel"] = "Cinema Camera";
1371+
1372+
// Add raw:pre_mul attribute to simulate RAW metadata extraction path
1373+
float pre_mul[4] = { 1.5f, 1.0f, 1.2f, 1.0f };
1374+
image_spec.attribute(
1375+
"raw:pre_mul", OIIO::TypeDesc( OIIO::TypeDesc::FLOAT, 4 ), pre_mul );
1376+
1377+
// Configure settings with empty illuminant (to trigger auto-detection)
1378+
// and verbosity > 0 (to trigger the "Found illuminant:" message)
1379+
ImageConverter::Settings settings;
1380+
settings.database_directories = { test_dir.get_database_path() };
1381+
settings.illuminant = ""; // Empty to trigger auto-detection
1382+
settings.verbosity = 1; // > 0 to trigger the output message
1383+
1384+
// Provide empty WB_multipliers to trigger extraction from raw:pre_mul
1385+
// This exercises the path where WB_multipliers.size() != 4
1386+
std::vector<double>
1387+
WB_multipliers; // Empty - will trigger raw:pre_mul extraction
1388+
std::vector<std::vector<double>> IDT_matrix;
1389+
std::vector<std::vector<double>> CAT_matrix;
1390+
1391+
bool success;
1392+
std::string output = capture_stderr( [&]() {
1393+
// This should succeed and auto-detect the illuminant from raw:pre_mul
1394+
// This exercises the extraction path when WB_multipliers is not provided
1395+
success = prepare_transform_spectral(
1396+
image_spec, settings, WB_multipliers, IDT_matrix, CAT_matrix );
1397+
} );
1398+
1399+
// Should succeed
1400+
OIIO_CHECK_ASSERT( success );
1401+
1402+
// Verify the "Found illuminant:" message appears
1403+
OIIO_CHECK_ASSERT(
1404+
output.find( "Found illuminant: '2000k'." ) != std::string::npos );
1405+
}
1406+
1407+
/// Tests that auto-detection normalizes white balance multipliers when min_val > 0 and != 1
1408+
void test_auto_detect_illuminant_with_normalization()
1409+
{
1410+
std::cout << std::endl
1411+
<< "test_auto_detect_illuminant_with_normalization()"
1412+
<< std::endl;
1413+
1414+
// Create test directory with database
1415+
TestDirectory test_dir;
1416+
1417+
// Create camera data (so camera lookup succeeds)
1418+
test_dir.create_test_data_file(
1419+
"camera",
1420+
{ { "manufacturer", "Blackmagic" }, { "model", "Cinema Camera" } } );
1421+
1422+
// Create training data (so training data loading succeeds)
1423+
test_dir.create_test_data_file( "training", { { "illuminant", "D65" } } );
1424+
1425+
// Create observer data (so observer data loading succeeds)
1426+
test_dir.create_test_data_file( "cmf", { { "illuminant", "D65" } } );
1427+
1428+
// Create illuminant data (so illuminant data loading succeeds)
1429+
test_dir.create_test_data_file( "illuminant", { { "illuminant", "D65" } } );
1430+
1431+
// Create a mock ImageSpec with camera metadata and raw:pre_mul attribute
1432+
OIIO::ImageSpec image_spec;
1433+
image_spec.width = 100;
1434+
image_spec.height = 100;
1435+
image_spec.nchannels = 3;
1436+
image_spec.format = OIIO::TypeDesc::UINT8;
1437+
image_spec["cameraMake"] = "Blackmagic";
1438+
image_spec["cameraModel"] = "Cinema Camera";
1439+
1440+
// Add raw:pre_mul attribute with values where min_val > 0 and != 1
1441+
// Using values like {2.0, 1.5, 1.8, 1.5} where min=1.5, which is > 0 and != 1
1442+
// This will trigger the normalization path
1443+
float pre_mul[4] = { 2.0f, 1.5f, 1.8f, 1.5f };
1444+
image_spec.attribute(
1445+
"raw:pre_mul", OIIO::TypeDesc( OIIO::TypeDesc::FLOAT, 4 ), pre_mul );
1446+
1447+
// Configure settings with empty illuminant (to trigger auto-detection)
1448+
// and verbosity > 0 (to trigger the "Found illuminant:" message)
1449+
ImageConverter::Settings settings;
1450+
settings.database_directories = { test_dir.get_database_path() };
1451+
settings.illuminant = ""; // Empty to trigger auto-detection
1452+
settings.verbosity = 1; // > 0 to trigger the output message
1453+
1454+
// Provide empty WB_multipliers to trigger extraction from raw:pre_mul
1455+
// This exercises the path where WB_multipliers.size() != 4
1456+
std::vector<double>
1457+
WB_multipliers; // Empty - will trigger raw:pre_mul extraction
1458+
std::vector<std::vector<double>> IDT_matrix;
1459+
std::vector<std::vector<double>> CAT_matrix;
1460+
1461+
bool success;
1462+
std::string output = capture_stderr( [&]() {
1463+
// This should succeed and auto-detect the illuminant from raw:pre_mul
1464+
// The normalization path will be exercised when min_val > 0 and != 1
1465+
success = prepare_transform_spectral(
1466+
image_spec, settings, WB_multipliers, IDT_matrix, CAT_matrix );
1467+
} );
1468+
1469+
// Should succeed
1470+
OIIO_CHECK_ASSERT( success );
1471+
1472+
// Verify the "Found illuminant:" message appears
1473+
OIIO_CHECK_ASSERT(
1474+
output.find( "Found illuminant: '1500k'." ) != std::string::npos );
1475+
}
1476+
1477+
/// Tests that prepare_transform_spectral fails when IDT matrix calculation fails
1478+
void test_prepare_transform_spectral_idt_calculation_fail()
1479+
{
1480+
std::cout << std::endl
1481+
<< "test_prepare_transform_spectral_idt_calculation_fail()"
1482+
<< std::endl;
1483+
1484+
// Create test directory with database
1485+
TestDirectory test_dir;
1486+
1487+
// Create camera data (so camera lookup succeeds)
1488+
test_dir.create_test_data_file(
1489+
"camera",
1490+
{ { "manufacturer", "Blackmagic" }, { "model", "Cinema Camera" } } );
1491+
1492+
// Create observer data (so observer data loading succeeds)
1493+
test_dir.create_test_data_file( "cmf", { { "illuminant", "D65" } } );
1494+
1495+
// Create illuminant data (so illuminant data loading succeeds)
1496+
test_dir.create_test_data_file( "illuminant", { { "illuminant", "D65" } } );
1497+
1498+
// Create training data with minimal structure that causes curve fitting to fail
1499+
// We need to create a file that loads but causes optimization to fail
1500+
std::string training_dir = test_dir.get_database_path() + "/training";
1501+
std::filesystem::create_directories( training_dir );
1502+
std::string training_file = training_dir + "/training_spectral.json";
1503+
1504+
// Create training data with only one patch and minimal wavelengths
1505+
// This should pass initial validation but cause curve fitting to fail
1506+
nlohmann::json training_json;
1507+
training_json["header"]["illuminant"] = "D65";
1508+
training_json["units"] = "relative";
1509+
training_json["index"] = { { "main", { "patch1" } } };
1510+
1511+
nlohmann::json data_main;
1512+
// Add only a few wavelengths - insufficient for proper curve fitting
1513+
data_main["380"] = { 0.1 };
1514+
data_main["385"] = { 0.1 };
1515+
data_main["390"] = { 0.1 };
1516+
training_json["data"]["main"] = data_main;
1517+
1518+
std::ofstream training_out( training_file );
1519+
training_out << training_json.dump( 4 );
1520+
training_out.close();
1521+
1522+
// Create a mock ImageSpec with camera metadata
1523+
OIIO::ImageSpec image_spec;
1524+
image_spec.width = 100;
1525+
image_spec.height = 100;
1526+
image_spec.nchannels = 3;
1527+
image_spec.format = OIIO::TypeDesc::UINT8;
1528+
image_spec["cameraMake"] = "Blackmagic";
1529+
image_spec["cameraModel"] = "Cinema Camera";
1530+
1531+
// Configure settings with illuminant specified
1532+
ImageConverter::Settings settings;
1533+
settings.database_directories = { test_dir.get_database_path() };
1534+
settings.illuminant = "D65";
1535+
settings.verbosity = 1;
1536+
1537+
// Provide WB_multipliers
1538+
std::vector<double> WB_multipliers = { 1.5, 1.0, 1.2 };
1539+
std::vector<std::vector<double>> IDT_matrix;
1540+
std::vector<std::vector<double>> CAT_matrix;
1541+
1542+
bool success;
1543+
std::string output = capture_stderr( [&]() {
1544+
// This should fail when trying to calculate IDT matrix
1545+
success = prepare_transform_spectral(
1546+
image_spec, settings, WB_multipliers, IDT_matrix, CAT_matrix );
1547+
} );
1548+
1549+
// Should fail
1550+
OIIO_CHECK_ASSERT( !success );
1551+
1552+
// Verify the error message about failed IDT matrix calculation
1553+
OIIO_CHECK_ASSERT(
1554+
output.find( "Failed to calculate the input transform matrix." ) !=
1555+
std::string::npos );
1556+
}
1557+
12141558
void assert_success_conversion( const std::string &output )
12151559
{
12161560
// Assert that the command succeeded (no error messages)
@@ -1587,12 +1931,17 @@ int main( int, char ** )
15871931
test_missing_training_data();
15881932
test_missing_observer_data();
15891933
test_missing_illuminant_data();
1934+
test_illuminant_type_not_found();
1935+
test_auto_detect_illuminant_with_wb_multipliers();
1936+
test_auto_detect_illuminant_from_raw_metadata();
1937+
test_auto_detect_illuminant_with_normalization();
15901938

15911939
test_spectral_conversion_success();
15921940
test_rawtoaces_spectral_mode_complete_success_with_custom_camera_info();
15931941

15941942
test_prepare_transform_spectral_wb_calculation_fail_due_to_invalid_illuminant_data();
15951943
test_prepare_transform_spectral_wb_calculation_fail_due_to_invalid_camera_data();
1944+
test_prepare_transform_spectral_idt_calculation_fail();
15961945

15971946
test_rawtoaces_spectral_mode_complete_success_with_default_illuminant_warning();
15981947
test_illuminant_ignored_with_metadata_wb();

0 commit comments

Comments
 (0)