ITS: create noise_profile.cc for tools/dng_noise_model.py
bug: 192413663
Change-Id: Ib129a06551128197dab98ceb115eb9398d9b8d48
diff --git a/apps/CameraITS/tools/dng_noise_model.py b/apps/CameraITS/tools/dng_noise_model.py
index a70ef8f..080c4e1 100644
--- a/apps/CameraITS/tools/dng_noise_model.py
+++ b/apps/CameraITS/tools/dng_noise_model.py
@@ -111,6 +111,38 @@
text_file.write('%s' % code)
text_file.close()
+ # Creates the noise profile C++ file
+ code = textwrap.dedent(f"""\
+ /* noise_profile.cc
+ Note: gradient_slope --> gradient of API slope parameter
+ offset_slope --> offset of API slope parameter
+ gradient_intercept--> gradient of API intercept parameter
+ offset_intercept --> offset of API intercept parameter
+ Note: SENSOR_NOISE_PROFILE in Android Developers doc uses
+ N(x) = sqrt(Sx + O), where 'S' is 'slope' & 'O' is 'intercept'
+ */
+ .noise_profile =
+ {{.noise_coefficients_r = {{.gradient_slope = {noise_model_a[0]},
+ .offset_slope = {noise_model_b[0]},
+ .gradient_intercept = {noise_model_c[0]},
+ .offset_intercept = {noise_model_d[0]}}},
+ .noise_coefficients_gr = {{.gradient_slope = {noise_model_a[1]},
+ .offset_slope = {noise_model_b[1]},
+ .gradient_intercept = {noise_model_c[1]},
+ .offset_intercept = {noise_model_d[1]}}},
+ .noise_coefficients_gb = {{.gradient_slope = {noise_model_a[2]},
+ .offset_slope = {noise_model_b[2]},
+ .gradient_intercept = {noise_model_c[2]},
+ .offset_intercept = {noise_model_d[2]}}},
+ .noise_coefficients_b = {{.gradient_slope = {noise_model_a[3]},
+ .offset_slope = {noise_model_b[3]},
+ .gradient_intercept = {noise_model_c[3]},
+ .offset_intercept = {noise_model_d[3]}}}}},
+ """)
+ text_file = open(os.path.join(log_path, 'noise_profile.cc'), 'w')
+ text_file.write('%s' % code)
+ text_file.close()
+
class DngNoiseModel(its_base_test.ItsBaseTest):
"""Create DNG noise model.
@@ -279,17 +311,17 @@
iso *= math.pow(2, 1.0/_STEPS_PER_STOP)
# Do model plots
- (fig, (plt_slope, plt_intercept)) = plt.subplots(2, 1, figsize=(11, 8.5))
- plt_slope.set_title('Noise model')
- plt_slope.set_ylabel('Slope')
- plt_intercept.set_xlabel('ISO')
- plt_intercept.set_ylabel('Intercept')
+ (fig, (plt_s, plt_o)) = plt.subplots(2, 1, figsize=(11, 8.5))
+ plt_s.set_title('Noise model: N(x) = sqrt(Sx + O)')
+ plt_s.set_ylabel('S')
+ plt_o.set_xlabel('ISO')
+ plt_o.set_ylabel('O')
noise_model = []
for (pidx, p) in enumerate(measured_models):
# Grab the sensitivities and line parameters from each sensitivity.
- slp_measured = [e[1] for e in measured_models[pidx]]
- int_measured = [e[2] for e in measured_models[pidx]]
+ s_measured = [e[1] for e in measured_models[pidx]]
+ o_measured = [e[2] for e in measured_models[pidx]]
sens = np.asarray([e[0] for e in measured_models[pidx]])
sens_sq = np.square(sens)
@@ -314,30 +346,34 @@
# Divide the whole system by gains*means.
f = lambda x, a, b, c, d: (c*(x[0]**2)+d+(x[1])*a*x[0]+(x[1])*b)/(x[0])
result, _ = scipy.optimize.curve_fit(f, (gains, means), vars_/(gains))
-
- a_p, b_p, c_p, d_p = result[0:4]
+ # result[0:4] = s_gradient, s_offset, o_gradient, o_offset
+ # Note 'S' and 'O' are the API terms for the 2 model params.
+ # The noise_profile.cc uses 'slope' for 'S' and 'intercept' for 'O'.
+ # 'gradient' and 'offset' are used to describe the linear fit
+ # parameters for 'S' and 'O'.
noise_model.append(result[0:4])
# Plot noise model components with the values predicted by the model.
- slp_model = result[0]*sens + result[1]
- int_model = result[2]*sens_sq + result[3]*np.square(np.maximum(
+ s_model = result[0]*sens + result[1]
+ o_model = result[2]*sens_sq + result[3]*np.square(np.maximum(
sens/sens_max_analog, 1))
- plt_slope.loglog(sens, slp_measured, 'rgkb'[pidx]+'+', base=10,
- label='Measured')
- plt_slope.loglog(sens, slp_model, 'rgkb'[pidx]+'o', base=10,
- label='Model', alpha=0.3)
- plt_intercept.loglog(sens, int_measured, 'rgkb'[pidx]+'+', base=10,
- label='Measured')
- plt_intercept.loglog(sens, int_model, 'rgkb'[pidx]+'o', base=10,
- label='Model', alpha=0.3)
- plt_slope.legend()
- plt_slope.set_xticks(isos)
- plt_slope.set_xticklabels(isos)
+ plt_s.loglog(sens, s_measured, 'rgkb'[pidx]+'+', base=10,
+ label='Measured')
+ plt_s.loglog(sens, s_model, 'rgkb'[pidx]+'o', base=10,
+ label='Model', alpha=0.3)
+ plt_o.loglog(sens, o_measured, 'rgkb'[pidx]+'+', base=10,
+ label='Measured')
+ plt_o.loglog(sens, o_model, 'rgkb'[pidx]+'o', base=10,
+ label='Model', alpha=0.3)
+ plt_s.legend()
+ plt_s.set_xticks(isos)
+ plt_s.set_xticklabels(isos)
- plt_intercept.set_xticks(isos)
- plt_intercept.set_xticklabels(isos)
- plt_intercept.legend()
+ plt_o.set_xticks([])
+ plt_o.set_xticks(isos)
+ plt_o.set_xticklabels(isos)
+ plt_o.legend()
fig.savefig(f'{name_with_log_path}.png')
# Generate individual noise model components
@@ -345,30 +381,29 @@
*noise_model)
# Add models to subplots and re-save
- for [s, fig] in plots: # re-step through figs...
- dig_gain = max(s/sens_max_analog, 1)
+ for [iso, fig] in plots: # re-step through figs...
+ dig_gain = max(iso/sens_max_analog, 1)
fig.gca()
for (pidx, p) in enumerate(measured_models):
- slope = noise_model_a[pidx]*s + noise_model_b[pidx]
- intercept = noise_model_c[pidx]*s**2 + noise_model_d[pidx]*dig_gain**2
- color_plane_plots[s][pidx].plot(
- [0, _MAX_SIGNAL_VALUE],
- [intercept, intercept+slope*_MAX_SIGNAL_VALUE],
+ s = noise_model_a[pidx]*iso + noise_model_b[pidx]
+ o = noise_model_c[pidx]*iso**2 + noise_model_d[pidx]*dig_gain**2
+ color_plane_plots[iso][pidx].plot(
+ [0, _MAX_SIGNAL_VALUE], [o, o+s*_MAX_SIGNAL_VALUE],
'rgkb'[pidx]+'-', label='Model', alpha=0.5)
- color_plane_plots[s][pidx].legend(loc='upper left')
- fig.savefig(f'{name_with_log_path}_samples_iso{s:04d}.png')
+ color_plane_plots[iso][pidx].legend(loc='upper left')
+ fig.savefig(f'{name_with_log_path}_samples_iso{iso:04d}.png')
- # Validity checks on model: read noise > 0, positive slope.
+ # Validity checks on model: read noise > 0, positive intercept gradient.
for i, _ in enumerate(_BAYER_LIST):
read_noise = noise_model_c[i] * sens_min * sens_min + noise_model_d[i]
if read_noise <= 0:
raise AssertionError(f'{_BAYER_LIST[i]} model min ISO noise < 0! '
- f'C: {noise_model_c[i]:.4e}, '
- f'D: {noise_model_d[i]:.4e}, '
+ f'API intercept gradient: {noise_model_c[i]:.4e}, '
+ f'API intercept offset: {noise_model_d[i]:.4e}, '
f'read_noise: {read_noise:.4e}')
if noise_model_c[i] <= 0:
- raise AssertionError(f'{_BAYER_LIST[i]} model slope is negative. '
- f' slope={noise_model_c[i]:.4e}')
+ raise AssertionError(f'{_BAYER_LIST[i]} model API intercept gradient '
+ f'is negative: {noise_model_c[i]:.4e}')
# Generate the noise model file.
create_noise_model_code(