Skip to content

Commit c991bf9

Browse files
committed
Refactorizing all
1 parent 8d6def8 commit c991bf9

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1697
-1251
lines changed

CMakeLists.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,23 @@ set(CORE_SOURCES
6767
src/core/analysis/RawProcessor.cpp
6868
src/core/arguments/ArgumentManager.cpp
6969
src/core/arguments/ChartOptionsParser.cpp
70+
src/core/arguments/parsing/ArgumentRegistry.cpp
71+
src/core/arguments/parsing/CliParser.cpp
72+
src/core/arguments/parsing/OptionsConverter.cpp
7073
src/core/engine/Engine.cpp
7174
src/core/engine/Initialization.cpp
72-
src/core/engine/Processing.cpp
75+
src/core/engine/PatchAnalysisStrategy.cpp
76+
src/core/engine/processing/CornerDetectionHandler.cpp
77+
src/core/engine/processing/Processing.cpp
78+
src/core/engine/processing/ResultAggregator.cpp
7379
src/core/engine/Reporting.cpp
7480
src/core/engine/Validation.cpp
7581
src/core/graphics/ChartGenerator.cpp
82+
src/core/graphics/detection/ChartCornerDetector.cpp
83+
src/core/graphics/drawing/CurveDrawer.cpp
84+
src/core/graphics/drawing/LabelDrawer.cpp
7685
src/core/graphics/FontManager.cpp
86+
src/core/graphics/geometry/KeystoneCorrection.cpp
7787
src/core/graphics/ImageProcessing.cpp
7888
src/core/graphics/PlotBase.cpp
7989
src/core/graphics/PlotData.cpp

src/core/arguments/ArgumentManager.cpp

Lines changed: 16 additions & 212 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,13 @@
44
* @brief Implements the centralized argument management system.
55
*/
66
#include "ArgumentManager.hpp"
7-
#include "Constants.hpp"
8-
#include "../graphics/Constants.hpp"
9-
#include "../utils/PlatformUtils.hpp"
10-
#include "ArgumentsOptions.hpp"
11-
#include <CLI/CLI.hpp>
7+
#include "parsing/ArgumentRegistry.hpp"
8+
#include "parsing/CliParser.hpp"
9+
#include "parsing/OptionsConverter.hpp"
1210
#include <libintl.h>
1311

1412
#define _(string) gettext(string)
1513

16-
// --- ArgumentManager Implementation ---
17-
1814
ArgumentManager& ArgumentManager::Instance()
1915
{
2016
static ArgumentManager instance;
@@ -23,224 +19,32 @@ ArgumentManager& ArgumentManager::Instance()
2319

2420
ArgumentManager::ArgumentManager()
2521
{
26-
RegisterAllArguments();
27-
// Populate the values map with defaults immediately upon creation.
22+
// 1. Get all argument definitions from the registry.
23+
m_descriptors = DynaRange::Arguments::Parsing::ArgumentRegistry::RegisterAll();
24+
25+
// 2. Populate the values map with defaults immediately upon creation.
2826
for (const auto& [name, desc] : m_descriptors) {
2927
m_values[name] = desc.default_value;
3028
}
3129
}
3230

33-
void ArgumentManager::RegisterAllArguments()
34-
{
35-
if (m_is_registered)
36-
return;
37-
38-
using namespace DynaRange::Arguments::Constants;
39-
40-
m_descriptors[BlackLevel] = { BlackLevel, "B", _("Camera RAW black level"), ArgType::Double, DEFAULT_BLACK_LEVEL };
41-
m_descriptors[BlackFile] = { BlackFile, "b", _("Totally dark RAW file ideally shot at base ISO"), ArgType::String, std::string("") };
42-
m_descriptors[SaturationLevel] = { SaturationLevel, "S", _("Camera RAW saturation level"), ArgType::Double, DEFAULT_SATURATION_LEVEL };
43-
m_descriptors[SaturationFile] = { SaturationFile, "s", _("Totally clipped RAW file ideally shot at base ISO"), ArgType::String, std::string("") };
44-
m_descriptors[InputFiles] = { InputFiles, "i", _("Input RAW files shot over the test chart ideally for every ISO"), ArgType::StringVector, std::vector<std::string> {}, false };
45-
m_descriptors[PatchRatio] = { PatchRatio, "r", _("Relative patch width/height used to compute signal and noise readings (default=0.5)"), ArgType::Double, DEFAULT_PATCH_RATIO, false, 0.0, 1.0 };
46-
m_descriptors[SnrThresholdDb] = { SnrThresholdDb, "d", _("SNR threshold(s) list in dB for DR calculation (default=0 12 being 0dB=\"Engineering DR\" and 12dB=\"Photographic DR\")"), ArgType::DoubleVector, std::vector<double> { 12.0, 0.0 } };
47-
m_descriptors[DrNormalizationMpx] = { DrNormalizationMpx, "m", _("Number of Mpx for DR normalization (default=8Mpx, no normalization=per pixel DR=0Mpx)"), ArgType::Double, DEFAULT_DR_NORMALIZATION_MPX };
48-
m_descriptors[PolyFit] = { PolyFit, "f", _("Polynomic order to fit the SNR curve (default=3)"), ArgType::Int, DEFAULT_POLY_ORDER, false, 2, 3 };
49-
m_descriptors[OutputFile] = { OutputFile, "o", _("Output CSV text file(s) with all results..."), ArgType::String, std::string(DEFAULT_OUTPUT_FILENAME) };
50-
m_descriptors[PlotFormat] = { PlotFormat, "p", _("Export SNR curves plot in PNG/SVG format (default format=PNG)"), ArgType::String, std::string("PNG") };
51-
m_descriptors[PlotParams] = { PlotParams, "P", _("Export SNR curves with SCL 1-3 info (default=1 1 1 3)"), ArgType::IntVector, std::vector<int> { 1, 1, 1, 3 } };
52-
m_descriptors[PrintPatches] = { PrintPatches, "g", _("Save keystone/ETTR/gamma corrected test chart in PNG format indicating the grid of patches used for all calculations (default=\"printpatches.png\")"), ArgType::String, std::string("") };
53-
m_descriptors[RawChannels] = { RawChannels, "w", _("Specify with 0/1 boolean values for which RAW channel(s) the calculations (SNR curves, DR) will be carried out (default=0 0 0 0 1)"), ArgType::IntVector, std::vector<int> { 0, 0, 0, 0, 1 } };
54-
m_descriptors[GeneratePlot] = { GeneratePlot, "", "", ArgType::Flag, false };
55-
56-
// Internal flags
57-
m_descriptors[SnrThresholdIsDefault] = { SnrThresholdIsDefault, "", "", ArgType::Flag, true };
58-
m_descriptors[BlackLevelIsDefault] = { BlackLevelIsDefault, "", "", ArgType::Flag, true };
59-
m_descriptors[SaturationLevelIsDefault] = { SaturationLevelIsDefault, "", "", ArgType::Flag, true };
60-
61-
// Chart arguments
62-
m_descriptors[Chart] = { Chart, "c", _("specify format of test chart (default DIMX=1920, W=3, H=2)"), ArgType::IntVector, std::vector<int>() };
63-
m_descriptors[ChartColour] = { ChartColour, "C", _("Create test chart in PNG format ranging colours..."), ArgType::StringVector, std::vector<std::string>() };
64-
m_descriptors[CreateChartMode] = { CreateChartMode, "", "", ArgType::Flag, false };
65-
m_descriptors[ChartCoords] = { ChartCoords, "x", _("Test chart defined by 4 corners: tl, bl, br, tr"), ArgType::DoubleVector, std::vector<double>() };
66-
m_descriptors[ChartPatches] = { ChartPatches, "M", _("Specify number of patches over rows (M) and columns (N) (default M=4, N=6)"), ArgType::IntVector, std::vector<int>() };
67-
m_is_registered = true;
68-
}
69-
7031
void ArgumentManager::ParseCli(int argc, char* argv[])
7132
{
72-
using namespace DynaRange::Arguments::Constants;
73-
CLI::App app { _("Calculates the dynamic range from a series of RAW images.") };
74-
auto fmt = app.get_formatter();
75-
fmt->column_width(35);
76-
77-
ProgramOptions temp_opts;
78-
std::vector<double> temp_snr_thresholds;
79-
std::vector<int> temp_raw_channels;
80-
std::string temp_plot_format;
81-
std::vector<int> temp_plot_params;
82-
83-
// --- Define all options ---
84-
auto chart_opt = app.add_option("-c,--chart", temp_opts.chart_params, m_descriptors.at(Chart).help_text)->expected(5);
85-
auto chart_colour_opt = app.add_option("-C,--chart-colour", temp_opts.chart_colour_params, m_descriptors.at(ChartColour).help_text)->expected(0, 4);
86-
auto chart_patches_opt = app.add_option("-M,--chart-patches", temp_opts.chart_patches, m_descriptors.at(ChartPatches).help_text)->expected(2);
87-
auto chart_coords_opt = app.add_option("-x,--chart-coords", temp_opts.chart_coords, m_descriptors.at(ChartCoords).help_text)->expected(8);
88-
auto input_opt = app.add_option("-i,--input-files", temp_opts.input_files, m_descriptors.at(InputFiles).help_text);
33+
// 1. Instantiate the parser and parse the command line.
34+
DynaRange::Arguments::Parsing::CliParser parser;
35+
auto parsed_values = parser.Parse(argc, argv, m_descriptors);
8936

90-
auto black_file_opt = app.add_option("-b,--black-file", temp_opts.dark_file_path, m_descriptors.at(BlackFile).help_text)->check(CLI::ExistingFile);
91-
auto black_level_opt = app.add_option("-B,--black-level", temp_opts.dark_value, m_descriptors.at(BlackLevel).help_text);
92-
auto sat_file_opt = app.add_option("-s,--saturation-file", temp_opts.sat_file_path, m_descriptors.at(SaturationFile).help_text)->check(CLI::ExistingFile);
93-
auto sat_level_opt = app.add_option("-S,--saturation-level", temp_opts.saturation_value, m_descriptors.at(SaturationLevel).help_text);
94-
95-
auto output_opt = app.add_option("-o,--output-file", temp_opts.output_filename, m_descriptors.at(OutputFile).help_text);
96-
auto snr_opt = app.add_option("-d,--snrthreshold-db", temp_snr_thresholds, m_descriptors.at(SnrThresholdDb).help_text);
97-
auto dr_norm_opt = app.add_option("-m,--drnormalization-mpx", temp_opts.dr_normalization_mpx, m_descriptors.at(DrNormalizationMpx).help_text);
98-
auto poly_fit_opt = app.add_option("-f,--poly-fit", temp_opts.poly_order, m_descriptors.at(PolyFit).help_text)
99-
->check(CLI::IsMember(std::vector<int>(std::begin(VALID_POLY_ORDERS), std::end(VALID_POLY_ORDERS))));
100-
auto patch_ratio_opt = app.add_option("-r,--patch-ratio", temp_opts.patch_ratio, m_descriptors.at(PatchRatio).help_text)->check(CLI::Range(0.0, 1.0));
101-
102-
auto plot_format_opt = app.add_option("-p,--plot-format", temp_plot_format, m_descriptors.at(PlotFormat).help_text);
103-
auto plot_params_opt = app.add_option("-P,--plot-params", temp_plot_params, m_descriptors.at(PlotParams).help_text)->expected(4);
104-
105-
auto print_patch_opt = app.add_option("-g,--print-patches", temp_opts.print_patch_filename, m_descriptors.at(PrintPatches).help_text)->expected(0, 1)->default_str("chartpatches.png");
106-
auto raw_channel_opt = app.add_option("-w,--raw-channels", temp_raw_channels, m_descriptors.at(RawChannels).help_text)->expected(5);
107-
108-
// --- Single Parse Pass ---
109-
try {
110-
app.parse(argc, argv);
111-
if (chart_opt->count() == 0 && chart_colour_opt->count() == 0 && input_opt->count() == 0) {
112-
throw CLI::RequiredError(_("--input-files is required unless creating a chart with --chart or --chart-colour."));
113-
}
114-
} catch (const CLI::ParseError& e) {
115-
exit(app.exit(e));
116-
}
117-
118-
// --- Store Parsed Values ---
119-
if (chart_opt->count() > 0 || chart_colour_opt->count() > 0) {
120-
m_values[CreateChartMode] = true;
121-
}
122-
123-
if (plot_format_opt->count() > 0 || plot_params_opt->count() > 0) {
124-
m_values[GeneratePlot] = true;
125-
}
126-
127-
if (chart_opt->count() > 0)
128-
m_values[Chart] = temp_opts.chart_params;
129-
if (chart_colour_opt->count() > 0)
130-
m_values[ChartColour] = temp_opts.chart_colour_params;
131-
if (chart_patches_opt->count() > 0)
132-
m_values[ChartPatches] = temp_opts.chart_patches;
133-
if (chart_coords_opt->count() > 0)
134-
m_values[ChartCoords] = temp_opts.chart_coords;
135-
136-
if (black_file_opt->count() > 0) {
137-
m_values[BlackFile] = temp_opts.dark_file_path;
138-
m_values[BlackLevelIsDefault] = false;
139-
}
140-
if (black_level_opt->count() > 0) {
141-
m_values[BlackLevel] = temp_opts.dark_value;
142-
m_values[BlackLevelIsDefault] = false;
143-
}
144-
145-
if (sat_file_opt->count() > 0) {
146-
m_values[SaturationFile] = temp_opts.sat_file_path;
147-
m_values[SaturationLevelIsDefault] = false;
148-
}
149-
if (sat_level_opt->count() > 0) {
150-
m_values[SaturationLevel] = temp_opts.saturation_value;
151-
m_values[SaturationLevelIsDefault] = false;
152-
}
153-
154-
if (raw_channel_opt->count() > 0) {
155-
m_values[RawChannels] = temp_raw_channels;
156-
}
157-
158-
if (output_opt->count() > 0)
159-
m_values[OutputFile] = temp_opts.output_filename;
160-
if (dr_norm_opt->count() > 0)
161-
m_values[DrNormalizationMpx] = temp_opts.dr_normalization_mpx;
162-
if (poly_fit_opt->count() > 0)
163-
m_values[PolyFit] = temp_opts.poly_order;
164-
if (patch_ratio_opt->count() > 0)
165-
m_values[PatchRatio] = temp_opts.patch_ratio;
166-
167-
if (plot_format_opt->count() > 0)
168-
m_values[PlotFormat] = temp_plot_format;
169-
if (plot_params_opt->count() > 0)
170-
m_values[PlotParams] = temp_plot_params;
171-
172-
if (print_patch_opt->count() > 0)
173-
m_values[PrintPatches] = temp_opts.print_patch_filename;
174-
m_values[InputFiles] = PlatformUtils::ExpandWildcards(temp_opts.input_files);
175-
if (snr_opt->count() > 0) {
176-
m_values[SnrThresholdDb] = temp_snr_thresholds;
177-
m_values[SnrThresholdIsDefault] = false;
37+
// 2. Merge the parsed values into the manager's state.
38+
// This overwrites defaults with any values provided by the user.
39+
for (const auto& [name, value] : parsed_values) {
40+
m_values[name] = value;
17841
}
17942
}
18043

18144
ProgramOptions ArgumentManager::ToProgramOptions()
18245
{
183-
using namespace DynaRange::Arguments::Constants;
184-
ProgramOptions opts;
185-
186-
opts.create_chart_mode = Get<bool>(CreateChartMode);
187-
opts.chart_params = Get<std::vector<int>>(Chart);
188-
opts.chart_colour_params = Get<std::vector<std::string>>(ChartColour);
189-
opts.chart_coords = Get<std::vector<double>>(ChartCoords);
190-
opts.chart_patches = Get<std::vector<int>>(ChartPatches);
191-
opts.dark_value = Get<double>(BlackLevel);
192-
opts.saturation_value = Get<double>(SaturationLevel);
193-
opts.dark_file_path = Get<std::string>(BlackFile);
194-
opts.sat_file_path = Get<std::string>(SaturationFile);
195-
opts.output_filename = Get<std::string>(OutputFile);
196-
opts.input_files = Get<std::vector<std::string>>(InputFiles);
197-
opts.poly_order = Get<int>(PolyFit);
198-
opts.dr_normalization_mpx = Get<double>(DrNormalizationMpx);
199-
opts.patch_ratio = Get<double>(PatchRatio);
200-
201-
opts.generate_plot = Get<bool>(GeneratePlot);
202-
if (opts.generate_plot) {
203-
std::string format_str = Get<std::string>(PlotFormat);
204-
std::transform(format_str.begin(), format_str.end(), format_str.begin(), ::toupper);
205-
if (format_str == "SVG")
206-
opts.plot_format = DynaRange::Graphics::Constants::PlotOutputFormat::SVG;
207-
else if (format_str == "PDF")
208-
opts.plot_format = DynaRange::Graphics::Constants::PlotOutputFormat::PDF;
209-
else
210-
opts.plot_format = DynaRange::Graphics::Constants::PlotOutputFormat::PNG;
211-
212-
auto params_vec = Get<std::vector<int>>(PlotParams);
213-
if (params_vec.size() == 4) {
214-
opts.plot_details.show_scatters = (params_vec[0] != 0);
215-
opts.plot_details.show_curve = (params_vec[1] != 0);
216-
opts.plot_details.show_labels = (params_vec[2] != 0);
217-
opts.plot_command_mode = params_vec[3];
218-
} else { // Fallback to default if vector is wrong size
219-
opts.plot_details = { true, true, true };
220-
opts.plot_command_mode = 3;
221-
}
222-
}
223-
224-
opts.print_patch_filename = Get<std::string>(PrintPatches);
225-
opts.black_level_is_default = Get<bool>(BlackLevelIsDefault);
226-
opts.saturation_level_is_default = Get<bool>(SaturationLevelIsDefault);
227-
228-
if (Get<bool>(SnrThresholdIsDefault)) {
229-
opts.snr_thresholds_db = { 12.0, 0.0 };
230-
} else {
231-
opts.snr_thresholds_db = Get<std::vector<double>>(SnrThresholdDb);
232-
}
233-
234-
auto channels_vec = Get<std::vector<int>>(RawChannels);
235-
if (channels_vec.size() == 5) {
236-
opts.raw_channels.R = (channels_vec[0] != 0);
237-
opts.raw_channels.G1 = (channels_vec[1] != 0);
238-
opts.raw_channels.G2 = (channels_vec[2] != 0);
239-
opts.raw_channels.B = (channels_vec[3] != 0);
240-
opts.raw_channels.AVG = (channels_vec[4] != 0);
241-
}
242-
243-
return opts;
46+
// Delegate the conversion to the specialized converter.
47+
return DynaRange::Arguments::Parsing::OptionsConverter::ToProgramOptions(m_values);
24448
}
24549

24650
void ArgumentManager::Set(const std::string& long_name, std::any value)

src/core/arguments/ArgumentManager.hpp

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
#include <any>
99
#include <map>
1010
#include <optional>
11-
#include <stdexcept> // For std::runtime_error
11+
#include <stdexcept>
1212
#include <string>
1313

1414
enum class ArgType { Int, Double, String, StringVector, IntVector, DoubleVector, Flag };
@@ -50,9 +50,6 @@ class ArgumentManager {
5050
ArgumentManager(const ArgumentManager&) = delete;
5151
ArgumentManager& operator=(const ArgumentManager&) = delete;
5252

53-
void RegisterAllArguments();
54-
5553
std::map<std::string, ArgumentDescriptor> m_descriptors;
5654
std::map<std::string, std::any> m_values;
57-
bool m_is_registered = false;
5855
};
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// File: src/core/arguments/parsing/ArgumentRegistry.cpp
2+
/**
3+
* @file ArgumentRegistry.cpp
4+
* @brief Implements the argument registry for the application.
5+
*/
6+
#include "ArgumentRegistry.hpp"
7+
#include "../Constants.hpp"
8+
#include "../ArgumentsOptions.hpp"
9+
#include <libintl.h>
10+
11+
#define _(string) gettext(string)
12+
13+
namespace DynaRange::Arguments::Parsing {
14+
15+
std::map<std::string, ArgumentDescriptor> ArgumentRegistry::RegisterAll()
16+
{
17+
std::map<std::string, ArgumentDescriptor> descriptors;
18+
using namespace DynaRange::Arguments::Constants;
19+
20+
descriptors[BlackLevel] = { BlackLevel, "B", _("Camera RAW black level"), ArgType::Double, DEFAULT_BLACK_LEVEL };
21+
descriptors[BlackFile] = { BlackFile, "b", _("Totally dark RAW file ideally shot at base ISO"), ArgType::String, std::string("") };
22+
descriptors[SaturationLevel] = { SaturationLevel, "S", _("Camera RAW saturation level"), ArgType::Double, DEFAULT_SATURATION_LEVEL };
23+
descriptors[SaturationFile] = { SaturationFile, "s", _("Totally clipped RAW file ideally shot at base ISO"), ArgType::String, std::string("") };
24+
descriptors[InputFiles] = { InputFiles, "i", _("Input RAW files shot over the test chart ideally for every ISO"), ArgType::StringVector, std::vector<std::string> {}, false };
25+
descriptors[PatchRatio] = { PatchRatio, "r", _("Relative patch width/height used to compute signal and noise readings (default=0.5)"), ArgType::Double, DEFAULT_PATCH_RATIO, false, 0.0, 1.0 };
26+
descriptors[SnrThresholdDb] = { SnrThresholdDb, "d", _("SNR threshold(s) list in dB for DR calculation (default=0 12 being 0dB=\"Engineering DR\" and 12dB=\"Photographic DR\")"), ArgType::DoubleVector, std::vector<double> { 12.0, 0.0 } };
27+
descriptors[DrNormalizationMpx] = { DrNormalizationMpx, "m", _("Number of Mpx for DR normalization (default=8Mpx, no normalization=per pixel DR=0Mpx)"), ArgType::Double, DEFAULT_DR_NORMALIZATION_MPX };
28+
descriptors[PolyFit] = { PolyFit, "f", _("Polynomic order to fit the SNR curve (default=3)"), ArgType::Int, DEFAULT_POLY_ORDER, false, 2, 3 };
29+
descriptors[OutputFile] = { OutputFile, "o", _("Output CSV text file(s) with all results..."), ArgType::String, std::string(DEFAULT_OUTPUT_FILENAME) };
30+
descriptors[PlotFormat] = { PlotFormat, "p", _("Export SNR curves plot in PNG/SVG format (default format=PNG)"), ArgType::String, std::string("PNG") };
31+
descriptors[PlotParams] = { PlotParams, "P", _("Export SNR curves with SCL 1-3 info (default=1 1 1 3)"), ArgType::IntVector, std::vector<int> { 1, 1, 1, 3 } };
32+
descriptors[PrintPatches] = { PrintPatches, "g", _("Save keystone/ETTR/gamma corrected test chart in PNG format indicating the grid of patches used for all calculations (default=\"printpatches.png\")"), ArgType::String, std::string("") };
33+
descriptors[RawChannels] = { RawChannels, "w", _("Specify with 0/1 boolean values for which RAW channel(s) the calculations (SNR curves, DR) will be carried out (default=0 0 0 0 1)"), ArgType::IntVector, std::vector<int> { 0, 0, 0, 0, 1 } };
34+
descriptors[GeneratePlot] = { GeneratePlot, "", "", ArgType::Flag, false };
35+
36+
// Chart arguments
37+
descriptors[Chart] = { Chart, "c", _("specify format of test chart (default DIMX=1920, W=3, H=2)"), ArgType::IntVector, std::vector<int>() };
38+
descriptors[ChartColour] = { ChartColour, "C", _("Create test chart in PNG format ranging colours..."), ArgType::StringVector, std::vector<std::string>() };
39+
descriptors[ChartCoords] = { ChartCoords, "x", _("Test chart defined by 4 corners: tl, bl, br, tr"), ArgType::DoubleVector, std::vector<double>() };
40+
descriptors[ChartPatches] = { ChartPatches, "M", _("Specify number of patches over rows (M) and columns (N) (default M=4, N=6)"), ArgType::IntVector, std::vector<int>() };
41+
42+
// Internal flags
43+
descriptors[CreateChartMode] = { CreateChartMode, "", "", ArgType::Flag, false };
44+
descriptors[SnrThresholdIsDefault] = { SnrThresholdIsDefault, "", "", ArgType::Flag, true };
45+
descriptors[BlackLevelIsDefault] = { BlackLevelIsDefault, "", "", ArgType::Flag, true };
46+
descriptors[SaturationLevelIsDefault] = { SaturationLevelIsDefault, "", "", ArgType::Flag, true };
47+
48+
return descriptors;
49+
}
50+
51+
} // namespace DynaRange::Arguments::Parsing

0 commit comments

Comments
 (0)