1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2014 The Android Open Source Project 2*b7c941bbSAndroid Build Coastguard Worker# 3*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License"); 4*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License. 5*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at 6*b7c941bbSAndroid Build Coastguard Worker# 7*b7c941bbSAndroid Build Coastguard Worker# http://www.apache.org/licenses/LICENSE-2.0 8*b7c941bbSAndroid Build Coastguard Worker# 9*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software 10*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS, 11*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and 13*b7c941bbSAndroid Build Coastguard Worker# limitations under the License. 14*b7c941bbSAndroid Build Coastguard Worker"""CameraITS script to generate noise models.""" 15*b7c941bbSAndroid Build Coastguard Worker 16*b7c941bbSAndroid Build Coastguard Workerimport logging 17*b7c941bbSAndroid Build Coastguard Workerimport math 18*b7c941bbSAndroid Build Coastguard Workerimport os.path 19*b7c941bbSAndroid Build Coastguard Workerimport pathlib 20*b7c941bbSAndroid Build Coastguard Workerimport pickle 21*b7c941bbSAndroid Build Coastguard Workerimport tempfile 22*b7c941bbSAndroid Build Coastguard Workerimport textwrap 23*b7c941bbSAndroid Build Coastguard Worker 24*b7c941bbSAndroid Build Coastguard Workerimport capture_read_noise_utils 25*b7c941bbSAndroid Build Coastguard Workerimport its_base_test 26*b7c941bbSAndroid Build Coastguard Workerimport its_session_utils 27*b7c941bbSAndroid Build Coastguard Workerfrom matplotlib import pyplot as plt 28*b7c941bbSAndroid Build Coastguard Workerimport matplotlib.ticker 29*b7c941bbSAndroid Build Coastguard Workerfrom mobly import test_runner 30*b7c941bbSAndroid Build Coastguard Workerimport noise_model_constants 31*b7c941bbSAndroid Build Coastguard Workerimport noise_model_utils 32*b7c941bbSAndroid Build Coastguard Workerimport numpy as np 33*b7c941bbSAndroid Build Coastguard Worker 34*b7c941bbSAndroid Build Coastguard Worker_IS_QUAD_BAYER = False # A manual flag to choose standard or quad Bayer noise 35*b7c941bbSAndroid Build Coastguard Worker # model generation. 36*b7c941bbSAndroid Build Coastguard Workerif _IS_QUAD_BAYER: 37*b7c941bbSAndroid Build Coastguard Worker _COLOR_CHANNEL_NAMES = noise_model_constants.QUAD_BAYER_COLORS 38*b7c941bbSAndroid Build Coastguard Worker _PLOT_COLORS = noise_model_constants.QUAD_BAYER_PLOT_COLORS 39*b7c941bbSAndroid Build Coastguard Worker _TILE_SIZE = 64 # Tile size to compute mean/variance. Large tiles may have 40*b7c941bbSAndroid Build Coastguard Worker # their variance corrupted by low freq image changes. 41*b7c941bbSAndroid Build Coastguard Worker _STATS_FORMAT = 'raw10QuadBayerStats' # rawQuadBayerStats|raw10QuadBayerStats 42*b7c941bbSAndroid Build Coastguard Worker _READ_NOISE_RAW_FORMAT = 'raw10QuadBayer' # rawQuadBayer|raw10QuadBayer 43*b7c941bbSAndroid Build Coastguard Workerelse: 44*b7c941bbSAndroid Build Coastguard Worker _COLOR_CHANNEL_NAMES = noise_model_constants.BAYER_COLORS 45*b7c941bbSAndroid Build Coastguard Worker _PLOT_COLORS = noise_model_constants.BAYER_PLOT_COLORS 46*b7c941bbSAndroid Build Coastguard Worker _TILE_SIZE = 32 # Tile size to compute mean/variance. Large tiles may have 47*b7c941bbSAndroid Build Coastguard Worker # their variance corrupted by low freq image changes. 48*b7c941bbSAndroid Build Coastguard Worker _STATS_FORMAT = 'rawStats' # rawStats|raw10Stats 49*b7c941bbSAndroid Build Coastguard Worker _READ_NOISE_RAW_FORMAT = 'raw' # raw|raw10 50*b7c941bbSAndroid Build Coastguard Worker 51*b7c941bbSAndroid Build Coastguard Worker_STATS_CONFIG = { 52*b7c941bbSAndroid Build Coastguard Worker 'format': _STATS_FORMAT, 53*b7c941bbSAndroid Build Coastguard Worker 'gridWidth': _TILE_SIZE, 54*b7c941bbSAndroid Build Coastguard Worker 'gridHeight': _TILE_SIZE, 55*b7c941bbSAndroid Build Coastguard Worker} 56*b7c941bbSAndroid Build Coastguard Worker_BRACKET_MAX = 8 # Exposure bracketing range in stops 57*b7c941bbSAndroid Build Coastguard Worker_BRACKET_FACTOR = math.pow(2, _BRACKET_MAX) 58*b7c941bbSAndroid Build Coastguard Worker_ISO_MAX_VALUE = None # ISO range max value, uses sensor max if None 59*b7c941bbSAndroid Build Coastguard Worker_ISO_MIN_VALUE = None # ISO range min value, uses sensor min if None 60*b7c941bbSAndroid Build Coastguard Worker_MAX_SCALE_FUDGE = 1.1 61*b7c941bbSAndroid Build Coastguard Worker_MAX_SIGNAL_VALUE = 0.25 # Maximum value to allow mean of the tiles to go. 62*b7c941bbSAndroid Build Coastguard Worker_NAME = os.path.basename(__file__).split('.')[0] 63*b7c941bbSAndroid Build Coastguard Worker_NAME_READ_NOISE = os.path.join(tempfile.gettempdir(), 'CameraITS/ReadNoise') 64*b7c941bbSAndroid Build Coastguard Worker_NAME_READ_NOISE_FILE = 'read_noise_results.pkl' 65*b7c941bbSAndroid Build Coastguard Worker_STATS_FILE_NAME = 'stats.pkl' 66*b7c941bbSAndroid Build Coastguard Worker_OUTLIER_MEDIAN_ABS_DEVS = 10 # Defines the number of Median Absolute 67*b7c941bbSAndroid Build Coastguard Worker # Deviations that constitutes acceptable data 68*b7c941bbSAndroid Build Coastguard Worker_READ_NOISE_STEPS_PER_STOP = 12 # Sensitivities per stop to sample for read 69*b7c941bbSAndroid Build Coastguard Worker # noise 70*b7c941bbSAndroid Build Coastguard Worker_REMOVE_VAR_OUTLIERS = False # When True, filters the variance to remove 71*b7c941bbSAndroid Build Coastguard Worker # outliers 72*b7c941bbSAndroid Build Coastguard Worker_STEPS_PER_STOP = 3 # How many sensitivities per stop to sample. 73*b7c941bbSAndroid Build Coastguard Worker_ISO_MULTIPLIER = math.pow(2, 1.0 / _STEPS_PER_STOP) 74*b7c941bbSAndroid Build Coastguard Worker_TILE_CROP_N = 0 # Number of tiles to crop from edge of image. Usually 0. 75*b7c941bbSAndroid Build Coastguard Worker_TWO_STAGE_MODEL = False # Require read noise data prior to running noise model 76*b7c941bbSAndroid Build Coastguard Worker_ZOOM_RATIO = 1 # Zoom target to be used while running the model 77*b7c941bbSAndroid Build Coastguard Worker_FIG_DPI = 100 # DPI for plotting noise model figures. 78*b7c941bbSAndroid Build Coastguard Worker_BAYER_COLORS_FOR_NOISE_PROFILE = tuple( 79*b7c941bbSAndroid Build Coastguard Worker map(str.lower, noise_model_constants.BAYER_COLORS) 80*b7c941bbSAndroid Build Coastguard Worker) 81*b7c941bbSAndroid Build Coastguard Worker_QUAD_BAYER_COLORS_FOR_NOISE_PROFILE = tuple( 82*b7c941bbSAndroid Build Coastguard Worker map(str.lower, noise_model_constants.QUAD_BAYER_COLORS) 83*b7c941bbSAndroid Build Coastguard Worker) 84*b7c941bbSAndroid Build Coastguard Worker 85*b7c941bbSAndroid Build Coastguard Worker 86*b7c941bbSAndroid Build Coastguard Workerclass DngNoiseModel(its_base_test.ItsBaseTest): 87*b7c941bbSAndroid Build Coastguard Worker """Create DNG noise model. 88*b7c941bbSAndroid Build Coastguard Worker 89*b7c941bbSAndroid Build Coastguard Worker Captures RAW images with increasing analog gains to create the model. 90*b7c941bbSAndroid Build Coastguard Worker """ 91*b7c941bbSAndroid Build Coastguard Worker 92*b7c941bbSAndroid Build Coastguard Worker def _create_noise_model_code(self, noise_model, sens_min, sens_max, 93*b7c941bbSAndroid Build Coastguard Worker sens_max_analog, file_path): 94*b7c941bbSAndroid Build Coastguard Worker """Creates the C file for the noise model. 95*b7c941bbSAndroid Build Coastguard Worker 96*b7c941bbSAndroid Build Coastguard Worker Args: 97*b7c941bbSAndroid Build Coastguard Worker noise_model: Noise model parameters. 98*b7c941bbSAndroid Build Coastguard Worker sens_min: The minimum sensitivity value. 99*b7c941bbSAndroid Build Coastguard Worker sens_max: The maximum sensitivity value. 100*b7c941bbSAndroid Build Coastguard Worker sens_max_analog: The maximum analog sensitivity value. 101*b7c941bbSAndroid Build Coastguard Worker file_path: The path to the noise model file. 102*b7c941bbSAndroid Build Coastguard Worker """ 103*b7c941bbSAndroid Build Coastguard Worker # Generate individual noise model components. 104*b7c941bbSAndroid Build Coastguard Worker scale_a, scale_b, offset_a, offset_b = zip(*noise_model) 105*b7c941bbSAndroid Build Coastguard Worker digital_gain_cdef = ( 106*b7c941bbSAndroid Build Coastguard Worker f'(sens / {sens_max_analog:.1f}) < 1.0 ? ' 107*b7c941bbSAndroid Build Coastguard Worker f'1.0 : (sens / {sens_max_analog:.1f})' 108*b7c941bbSAndroid Build Coastguard Worker ) 109*b7c941bbSAndroid Build Coastguard Worker 110*b7c941bbSAndroid Build Coastguard Worker with open(file_path, 'w') as text_file: 111*b7c941bbSAndroid Build Coastguard Worker scale_a_str = ','.join([str(i) for i in scale_a]) 112*b7c941bbSAndroid Build Coastguard Worker scale_b_str = ','.join([str(i) for i in scale_b]) 113*b7c941bbSAndroid Build Coastguard Worker offset_a_str = ','.join([str(i) for i in offset_a]) 114*b7c941bbSAndroid Build Coastguard Worker offset_b_str = ','.join([str(i) for i in offset_b]) 115*b7c941bbSAndroid Build Coastguard Worker # pylint: disable=line-too-long 116*b7c941bbSAndroid Build Coastguard Worker code = textwrap.dedent(f"""\ 117*b7c941bbSAndroid Build Coastguard Worker /* Generated test code to dump a table of data for external validation 118*b7c941bbSAndroid Build Coastguard Worker * of the noise model parameters. 119*b7c941bbSAndroid Build Coastguard Worker */ 120*b7c941bbSAndroid Build Coastguard Worker #include <stdio.h> 121*b7c941bbSAndroid Build Coastguard Worker #include <assert.h> 122*b7c941bbSAndroid Build Coastguard Worker double compute_noise_model_entry_S(int plane, int sens); 123*b7c941bbSAndroid Build Coastguard Worker double compute_noise_model_entry_O(int plane, int sens); 124*b7c941bbSAndroid Build Coastguard Worker int main(void) {{ 125*b7c941bbSAndroid Build Coastguard Worker for (int plane = 0; plane < {len(scale_a)}; plane++) {{ 126*b7c941bbSAndroid Build Coastguard Worker for (int sens = {sens_min}; sens <= {sens_max}; sens += 100) {{ 127*b7c941bbSAndroid Build Coastguard Worker double o = compute_noise_model_entry_O(plane, sens); 128*b7c941bbSAndroid Build Coastguard Worker double s = compute_noise_model_entry_S(plane, sens); 129*b7c941bbSAndroid Build Coastguard Worker printf("%d,%d,%lf,%lf\\n", plane, sens, o, s); 130*b7c941bbSAndroid Build Coastguard Worker }} 131*b7c941bbSAndroid Build Coastguard Worker }} 132*b7c941bbSAndroid Build Coastguard Worker return 0; 133*b7c941bbSAndroid Build Coastguard Worker }} 134*b7c941bbSAndroid Build Coastguard Worker 135*b7c941bbSAndroid Build Coastguard Worker /* Generated functions to map a given sensitivity to the O and S noise 136*b7c941bbSAndroid Build Coastguard Worker * model parameters in the DNG noise model. The planes are in 137*b7c941bbSAndroid Build Coastguard Worker * R, Gr, Gb, B order. 138*b7c941bbSAndroid Build Coastguard Worker */ 139*b7c941bbSAndroid Build Coastguard Worker double compute_noise_model_entry_S(int plane, int sens) {{ 140*b7c941bbSAndroid Build Coastguard Worker static double noise_model_A[] = {{ {scale_a_str:s} }}; 141*b7c941bbSAndroid Build Coastguard Worker static double noise_model_B[] = {{ {scale_b_str:s} }}; 142*b7c941bbSAndroid Build Coastguard Worker double A = noise_model_A[plane]; 143*b7c941bbSAndroid Build Coastguard Worker double B = noise_model_B[plane]; 144*b7c941bbSAndroid Build Coastguard Worker double s = A * sens + B; 145*b7c941bbSAndroid Build Coastguard Worker return s < 0.0 ? 0.0 : s; 146*b7c941bbSAndroid Build Coastguard Worker }} 147*b7c941bbSAndroid Build Coastguard Worker 148*b7c941bbSAndroid Build Coastguard Worker double compute_noise_model_entry_O(int plane, int sens) {{ 149*b7c941bbSAndroid Build Coastguard Worker static double noise_model_C[] = {{ {offset_a_str:s} }}; 150*b7c941bbSAndroid Build Coastguard Worker static double noise_model_D[] = {{ {offset_b_str:s} }}; 151*b7c941bbSAndroid Build Coastguard Worker double digital_gain = {digital_gain_cdef:s}; 152*b7c941bbSAndroid Build Coastguard Worker double C = noise_model_C[plane]; 153*b7c941bbSAndroid Build Coastguard Worker double D = noise_model_D[plane]; 154*b7c941bbSAndroid Build Coastguard Worker double o = C * sens * sens + D * digital_gain * digital_gain; 155*b7c941bbSAndroid Build Coastguard Worker return o < 0.0 ? 0.0 : o; 156*b7c941bbSAndroid Build Coastguard Worker }} 157*b7c941bbSAndroid Build Coastguard Worker """) 158*b7c941bbSAndroid Build Coastguard Worker 159*b7c941bbSAndroid Build Coastguard Worker text_file.write(code) 160*b7c941bbSAndroid Build Coastguard Worker 161*b7c941bbSAndroid Build Coastguard Worker def _create_noise_profile_code(self, noise_model, color_channels, file_path): 162*b7c941bbSAndroid Build Coastguard Worker """Creates the noise profile C++ file. 163*b7c941bbSAndroid Build Coastguard Worker 164*b7c941bbSAndroid Build Coastguard Worker Args: 165*b7c941bbSAndroid Build Coastguard Worker noise_model: Noise model parameters. 166*b7c941bbSAndroid Build Coastguard Worker color_channels: Color channels in canonical order. 167*b7c941bbSAndroid Build Coastguard Worker file_path: The path to the noise profile C++ file. 168*b7c941bbSAndroid Build Coastguard Worker """ 169*b7c941bbSAndroid Build Coastguard Worker # Generate individual noise model components. 170*b7c941bbSAndroid Build Coastguard Worker scale_a, scale_b, offset_a, offset_b = zip(*noise_model) 171*b7c941bbSAndroid Build Coastguard Worker num_channels = noise_model.shape[0] 172*b7c941bbSAndroid Build Coastguard Worker params = [] 173*b7c941bbSAndroid Build Coastguard Worker for ch, color in enumerate(color_channels): 174*b7c941bbSAndroid Build Coastguard Worker prefix = f'.noise_coefficients_{color} = {{' 175*b7c941bbSAndroid Build Coastguard Worker spaces = ' ' * len(prefix) 176*b7c941bbSAndroid Build Coastguard Worker suffix = '},' if ch != num_channels - 1 else '}' 177*b7c941bbSAndroid Build Coastguard Worker params.append(textwrap.dedent(f""" 178*b7c941bbSAndroid Build Coastguard Worker {prefix}.gradient_slope = {scale_a[ch]}, 179*b7c941bbSAndroid Build Coastguard Worker {spaces}.offset_slope = {scale_b[ch]}, 180*b7c941bbSAndroid Build Coastguard Worker {spaces}.gradient_intercept = {offset_a[ch]}, 181*b7c941bbSAndroid Build Coastguard Worker {spaces}.offset_intercept = {offset_b[ch]}{suffix}""")) 182*b7c941bbSAndroid Build Coastguard Worker 183*b7c941bbSAndroid Build Coastguard Worker with open(file_path, 'w') as text_file: 184*b7c941bbSAndroid Build Coastguard Worker # pylint: disable=line-too-long 185*b7c941bbSAndroid Build Coastguard Worker code_comment = textwrap.dedent("""\ 186*b7c941bbSAndroid Build Coastguard Worker /* noise_profile.cc 187*b7c941bbSAndroid Build Coastguard Worker Note: gradient_slope --> gradient of API s_measured parameter 188*b7c941bbSAndroid Build Coastguard Worker offset_slope --> o_model of API s_measured parameter 189*b7c941bbSAndroid Build Coastguard Worker gradient_intercept--> gradient of API o_measured parameter 190*b7c941bbSAndroid Build Coastguard Worker offset_intercept --> o_model of API o_measured parameter 191*b7c941bbSAndroid Build Coastguard Worker Note: SENSOR_NOISE_PROFILE in Android Developers doc uses 192*b7c941bbSAndroid Build Coastguard Worker N(x) = sqrt(Sx + O), where 'S' is 's_measured' & 'O' is 'o_measured' 193*b7c941bbSAndroid Build Coastguard Worker */ 194*b7c941bbSAndroid Build Coastguard Worker """) 195*b7c941bbSAndroid Build Coastguard Worker params_str = textwrap.indent(''.join(params), ' ' * 4) 196*b7c941bbSAndroid Build Coastguard Worker code_params = '.profile = {' + params_str + '},' 197*b7c941bbSAndroid Build Coastguard Worker code = code_comment + code_params 198*b7c941bbSAndroid Build Coastguard Worker text_file.write(code) 199*b7c941bbSAndroid Build Coastguard Worker 200*b7c941bbSAndroid Build Coastguard Worker def _create_noise_model_and_profile_code(self, noise_model, sens_min, 201*b7c941bbSAndroid Build Coastguard Worker sens_max, sens_max_analog, log_path): 202*b7c941bbSAndroid Build Coastguard Worker """Creates the code file with noise model parameters. 203*b7c941bbSAndroid Build Coastguard Worker 204*b7c941bbSAndroid Build Coastguard Worker Args: 205*b7c941bbSAndroid Build Coastguard Worker noise_model: Noise model parameters. 206*b7c941bbSAndroid Build Coastguard Worker sens_min: The minimum sensitivity value. 207*b7c941bbSAndroid Build Coastguard Worker sens_max: The maximum sensitivity value. 208*b7c941bbSAndroid Build Coastguard Worker sens_max_analog: The maximum analog sensitivity value. 209*b7c941bbSAndroid Build Coastguard Worker log_path: The path to the log file. 210*b7c941bbSAndroid Build Coastguard Worker """ 211*b7c941bbSAndroid Build Coastguard Worker noise_model_utils.check_noise_model_shape(noise_model) 212*b7c941bbSAndroid Build Coastguard Worker # Create noise model code with noise model parameters. 213*b7c941bbSAndroid Build Coastguard Worker self._create_noise_model_code( 214*b7c941bbSAndroid Build Coastguard Worker noise_model, 215*b7c941bbSAndroid Build Coastguard Worker sens_min, 216*b7c941bbSAndroid Build Coastguard Worker sens_max, 217*b7c941bbSAndroid Build Coastguard Worker sens_max_analog, 218*b7c941bbSAndroid Build Coastguard Worker os.path.join(log_path, 'noise_model.c'), 219*b7c941bbSAndroid Build Coastguard Worker ) 220*b7c941bbSAndroid Build Coastguard Worker 221*b7c941bbSAndroid Build Coastguard Worker num_channels = noise_model.shape[0] 222*b7c941bbSAndroid Build Coastguard Worker is_quad_bayer = ( 223*b7c941bbSAndroid Build Coastguard Worker num_channels == noise_model_constants.NUM_QUAD_BAYER_CHANNELS 224*b7c941bbSAndroid Build Coastguard Worker ) 225*b7c941bbSAndroid Build Coastguard Worker if is_quad_bayer: 226*b7c941bbSAndroid Build Coastguard Worker # Average noise model parameters of every four channels. 227*b7c941bbSAndroid Build Coastguard Worker avg_noise_model = noise_model.reshape(-1, 4, noise_model.shape[1]).mean( 228*b7c941bbSAndroid Build Coastguard Worker axis=1 229*b7c941bbSAndroid Build Coastguard Worker ) 230*b7c941bbSAndroid Build Coastguard Worker # Create noise model code with average noise model parameters. 231*b7c941bbSAndroid Build Coastguard Worker self._create_noise_model_code( 232*b7c941bbSAndroid Build Coastguard Worker avg_noise_model, 233*b7c941bbSAndroid Build Coastguard Worker sens_min, 234*b7c941bbSAndroid Build Coastguard Worker sens_max, 235*b7c941bbSAndroid Build Coastguard Worker sens_max_analog, 236*b7c941bbSAndroid Build Coastguard Worker os.path.join(log_path, 'noise_model_avg.c'), 237*b7c941bbSAndroid Build Coastguard Worker ) 238*b7c941bbSAndroid Build Coastguard Worker # Create noise profile code with average noise model parameters. 239*b7c941bbSAndroid Build Coastguard Worker self._create_noise_profile_code( 240*b7c941bbSAndroid Build Coastguard Worker avg_noise_model, 241*b7c941bbSAndroid Build Coastguard Worker _BAYER_COLORS_FOR_NOISE_PROFILE, 242*b7c941bbSAndroid Build Coastguard Worker os.path.join(log_path, 'noise_profile_avg.cc'), 243*b7c941bbSAndroid Build Coastguard Worker ) 244*b7c941bbSAndroid Build Coastguard Worker # Create noise profile code with noise model parameters. 245*b7c941bbSAndroid Build Coastguard Worker self._create_noise_profile_code( 246*b7c941bbSAndroid Build Coastguard Worker noise_model, 247*b7c941bbSAndroid Build Coastguard Worker _QUAD_BAYER_COLORS_FOR_NOISE_PROFILE, 248*b7c941bbSAndroid Build Coastguard Worker os.path.join(log_path, 'noise_profile.cc'), 249*b7c941bbSAndroid Build Coastguard Worker ) 250*b7c941bbSAndroid Build Coastguard Worker 251*b7c941bbSAndroid Build Coastguard Worker else: 252*b7c941bbSAndroid Build Coastguard Worker # Create noise profile code with noise model parameters. 253*b7c941bbSAndroid Build Coastguard Worker self._create_noise_profile_code( 254*b7c941bbSAndroid Build Coastguard Worker noise_model, 255*b7c941bbSAndroid Build Coastguard Worker _BAYER_COLORS_FOR_NOISE_PROFILE, 256*b7c941bbSAndroid Build Coastguard Worker os.path.join(log_path, 'noise_profile.cc'), 257*b7c941bbSAndroid Build Coastguard Worker ) 258*b7c941bbSAndroid Build Coastguard Worker 259*b7c941bbSAndroid Build Coastguard Worker def _plot_stats_and_noise_model_fittings( 260*b7c941bbSAndroid Build Coastguard Worker self, iso_to_stats_dict, measured_models, noise_model, sens_max_analog, 261*b7c941bbSAndroid Build Coastguard Worker folder_path_prefix): 262*b7c941bbSAndroid Build Coastguard Worker """Plots the stats (means, vars_) and noise models fittings. 263*b7c941bbSAndroid Build Coastguard Worker 264*b7c941bbSAndroid Build Coastguard Worker Args: 265*b7c941bbSAndroid Build Coastguard Worker iso_to_stats_dict: A dictionary mapping ISO to a list of tuples of 266*b7c941bbSAndroid Build Coastguard Worker exposure time in milliseconds, mean values, and variance values. 267*b7c941bbSAndroid Build Coastguard Worker measured_models: A list of measured noise models for each ISO value. 268*b7c941bbSAndroid Build Coastguard Worker noise_model: A numpy array of global noise model parameters for all ISO 269*b7c941bbSAndroid Build Coastguard Worker values. 270*b7c941bbSAndroid Build Coastguard Worker sens_max_analog: The maximum analog sensitivity value. 271*b7c941bbSAndroid Build Coastguard Worker folder_path_prefix: The prefix of path to save figures. 272*b7c941bbSAndroid Build Coastguard Worker 273*b7c941bbSAndroid Build Coastguard Worker Raises: 274*b7c941bbSAndroid Build Coastguard Worker ValueError: If the noise model shape is invalid. 275*b7c941bbSAndroid Build Coastguard Worker """ 276*b7c941bbSAndroid Build Coastguard Worker noise_model_utils.check_noise_model_shape(noise_model) 277*b7c941bbSAndroid Build Coastguard Worker # Separate individual noise model components. 278*b7c941bbSAndroid Build Coastguard Worker scale_a, scale_b, offset_a, offset_b = zip(*noise_model) 279*b7c941bbSAndroid Build Coastguard Worker 280*b7c941bbSAndroid Build Coastguard Worker iso_pidx_to_measured_model_dict = {} 281*b7c941bbSAndroid Build Coastguard Worker num_channels = noise_model.shape[0] 282*b7c941bbSAndroid Build Coastguard Worker for pidx in range(num_channels): 283*b7c941bbSAndroid Build Coastguard Worker for iso, s_measured, o_measured in measured_models[pidx]: 284*b7c941bbSAndroid Build Coastguard Worker iso_pidx_to_measured_model_dict[(iso, pidx)] = (s_measured, o_measured) 285*b7c941bbSAndroid Build Coastguard Worker 286*b7c941bbSAndroid Build Coastguard Worker isos = np.asarray(sorted(iso_to_stats_dict.keys())) 287*b7c941bbSAndroid Build Coastguard Worker digital_gains = noise_model_utils.compute_digital_gains( 288*b7c941bbSAndroid Build Coastguard Worker isos, sens_max_analog 289*b7c941bbSAndroid Build Coastguard Worker ) 290*b7c941bbSAndroid Build Coastguard Worker 291*b7c941bbSAndroid Build Coastguard Worker x_range = [0, _MAX_SIGNAL_VALUE] 292*b7c941bbSAndroid Build Coastguard Worker for iso, digital_gain in zip(isos, digital_gains): 293*b7c941bbSAndroid Build Coastguard Worker logging.info('Plotting stats and noise model for ISO %d.', iso) 294*b7c941bbSAndroid Build Coastguard Worker fig, subplots = noise_model_utils.create_stats_figure( 295*b7c941bbSAndroid Build Coastguard Worker iso, _COLOR_CHANNEL_NAMES 296*b7c941bbSAndroid Build Coastguard Worker ) 297*b7c941bbSAndroid Build Coastguard Worker 298*b7c941bbSAndroid Build Coastguard Worker xmax = 0 299*b7c941bbSAndroid Build Coastguard Worker stats_per_plane = [[] for _ in range(num_channels)] 300*b7c941bbSAndroid Build Coastguard Worker for exposure_ms, means, vars_ in iso_to_stats_dict[iso]: 301*b7c941bbSAndroid Build Coastguard Worker exposure_norm = noise_model_constants.COLOR_NORM(np.log2(exposure_ms)) 302*b7c941bbSAndroid Build Coastguard Worker exposure_color = noise_model_constants.RAINBOW_CMAP(exposure_norm) 303*b7c941bbSAndroid Build Coastguard Worker for pidx in range(num_channels): 304*b7c941bbSAndroid Build Coastguard Worker means_p = means[pidx] 305*b7c941bbSAndroid Build Coastguard Worker vars_p = vars_[pidx] 306*b7c941bbSAndroid Build Coastguard Worker 307*b7c941bbSAndroid Build Coastguard Worker if means_p.size > 0 and vars_p.size > 0: 308*b7c941bbSAndroid Build Coastguard Worker subplots[pidx].plot( 309*b7c941bbSAndroid Build Coastguard Worker means_p, 310*b7c941bbSAndroid Build Coastguard Worker vars_p, 311*b7c941bbSAndroid Build Coastguard Worker color=exposure_color, 312*b7c941bbSAndroid Build Coastguard Worker marker='.', 313*b7c941bbSAndroid Build Coastguard Worker markeredgecolor=exposure_color, 314*b7c941bbSAndroid Build Coastguard Worker markersize=1, 315*b7c941bbSAndroid Build Coastguard Worker linestyle='None', 316*b7c941bbSAndroid Build Coastguard Worker alpha=0.5, 317*b7c941bbSAndroid Build Coastguard Worker ) 318*b7c941bbSAndroid Build Coastguard Worker 319*b7c941bbSAndroid Build Coastguard Worker stats_per_plane[pidx].extend(list(zip(means_p, vars_p))) 320*b7c941bbSAndroid Build Coastguard Worker xmax = max(xmax, max(means_p)) 321*b7c941bbSAndroid Build Coastguard Worker 322*b7c941bbSAndroid Build Coastguard Worker iso_sq = iso ** 2 323*b7c941bbSAndroid Build Coastguard Worker digital_gain_sq = digital_gain ** 2 324*b7c941bbSAndroid Build Coastguard Worker for pidx in range(num_channels): 325*b7c941bbSAndroid Build Coastguard Worker # Add the final noise model to subplots. 326*b7c941bbSAndroid Build Coastguard Worker s_model = scale_a[pidx] * iso * digital_gain + scale_b[pidx] 327*b7c941bbSAndroid Build Coastguard Worker o_model = (offset_a[pidx] * iso_sq + offset_b[pidx]) * digital_gain_sq 328*b7c941bbSAndroid Build Coastguard Worker 329*b7c941bbSAndroid Build Coastguard Worker plot_color = _PLOT_COLORS[pidx] 330*b7c941bbSAndroid Build Coastguard Worker subplots[pidx].plot( 331*b7c941bbSAndroid Build Coastguard Worker x_range, 332*b7c941bbSAndroid Build Coastguard Worker [o_model, s_model * _MAX_SIGNAL_VALUE + o_model], 333*b7c941bbSAndroid Build Coastguard Worker color=plot_color, 334*b7c941bbSAndroid Build Coastguard Worker linestyle='-', 335*b7c941bbSAndroid Build Coastguard Worker label='Model', 336*b7c941bbSAndroid Build Coastguard Worker alpha=0.5, 337*b7c941bbSAndroid Build Coastguard Worker ) 338*b7c941bbSAndroid Build Coastguard Worker 339*b7c941bbSAndroid Build Coastguard Worker # Add the noise model measured by captures with current iso to subplots. 340*b7c941bbSAndroid Build Coastguard Worker if (iso, pidx) not in iso_pidx_to_measured_model_dict: 341*b7c941bbSAndroid Build Coastguard Worker continue 342*b7c941bbSAndroid Build Coastguard Worker 343*b7c941bbSAndroid Build Coastguard Worker s_measured, o_measured = iso_pidx_to_measured_model_dict[(iso, pidx)] 344*b7c941bbSAndroid Build Coastguard Worker 345*b7c941bbSAndroid Build Coastguard Worker subplots[pidx].plot( 346*b7c941bbSAndroid Build Coastguard Worker x_range, 347*b7c941bbSAndroid Build Coastguard Worker [o_measured, s_measured * _MAX_SIGNAL_VALUE + o_measured], 348*b7c941bbSAndroid Build Coastguard Worker color=plot_color, 349*b7c941bbSAndroid Build Coastguard Worker linestyle='--', 350*b7c941bbSAndroid Build Coastguard Worker label='Linear fit', 351*b7c941bbSAndroid Build Coastguard Worker ) 352*b7c941bbSAndroid Build Coastguard Worker 353*b7c941bbSAndroid Build Coastguard Worker ymax = (o_measured + s_measured * xmax) * _MAX_SCALE_FUDGE 354*b7c941bbSAndroid Build Coastguard Worker subplots[pidx].set_xlim(xmin=0, xmax=xmax) 355*b7c941bbSAndroid Build Coastguard Worker subplots[pidx].set_ylim(ymin=0, ymax=ymax) 356*b7c941bbSAndroid Build Coastguard Worker subplots[pidx].legend() 357*b7c941bbSAndroid Build Coastguard Worker 358*b7c941bbSAndroid Build Coastguard Worker fig.savefig( 359*b7c941bbSAndroid Build Coastguard Worker f'{folder_path_prefix}_samples_iso{iso:04d}.png', dpi=_FIG_DPI 360*b7c941bbSAndroid Build Coastguard Worker ) 361*b7c941bbSAndroid Build Coastguard Worker 362*b7c941bbSAndroid Build Coastguard Worker def _plot_noise_model_single_plane( 363*b7c941bbSAndroid Build Coastguard Worker self, pidx, plot, sens, measured_params, modeled_params): 364*b7c941bbSAndroid Build Coastguard Worker """Plots the noise model for one color plane specified by pidx. 365*b7c941bbSAndroid Build Coastguard Worker 366*b7c941bbSAndroid Build Coastguard Worker Args: 367*b7c941bbSAndroid Build Coastguard Worker pidx: The index of the color plane in Bayer pattern. 368*b7c941bbSAndroid Build Coastguard Worker plot: The ax to plot on. 369*b7c941bbSAndroid Build Coastguard Worker sens: The sensitivity of the sensor. 370*b7c941bbSAndroid Build Coastguard Worker measured_params: The measured parameters. 371*b7c941bbSAndroid Build Coastguard Worker modeled_params: The modeled parameters. 372*b7c941bbSAndroid Build Coastguard Worker """ 373*b7c941bbSAndroid Build Coastguard Worker color_channel = _COLOR_CHANNEL_NAMES[pidx] 374*b7c941bbSAndroid Build Coastguard Worker measured_label = f'{color_channel}-Measured' 375*b7c941bbSAndroid Build Coastguard Worker model_label = f'{color_channel}-Model' 376*b7c941bbSAndroid Build Coastguard Worker 377*b7c941bbSAndroid Build Coastguard Worker plot_color = _PLOT_COLORS[pidx] 378*b7c941bbSAndroid Build Coastguard Worker # Plot the measured parameters. 379*b7c941bbSAndroid Build Coastguard Worker plot.loglog( 380*b7c941bbSAndroid Build Coastguard Worker sens, 381*b7c941bbSAndroid Build Coastguard Worker measured_params, 382*b7c941bbSAndroid Build Coastguard Worker color=plot_color, 383*b7c941bbSAndroid Build Coastguard Worker marker='+', 384*b7c941bbSAndroid Build Coastguard Worker markeredgecolor=plot_color, 385*b7c941bbSAndroid Build Coastguard Worker linestyle='None', 386*b7c941bbSAndroid Build Coastguard Worker base=10, 387*b7c941bbSAndroid Build Coastguard Worker label=measured_label, 388*b7c941bbSAndroid Build Coastguard Worker ) 389*b7c941bbSAndroid Build Coastguard Worker # Plot the modeled parameters. 390*b7c941bbSAndroid Build Coastguard Worker plot.loglog( 391*b7c941bbSAndroid Build Coastguard Worker sens, 392*b7c941bbSAndroid Build Coastguard Worker modeled_params, 393*b7c941bbSAndroid Build Coastguard Worker color=plot_color, 394*b7c941bbSAndroid Build Coastguard Worker marker='o', 395*b7c941bbSAndroid Build Coastguard Worker markeredgecolor=plot_color, 396*b7c941bbSAndroid Build Coastguard Worker linestyle='None', 397*b7c941bbSAndroid Build Coastguard Worker base=10, 398*b7c941bbSAndroid Build Coastguard Worker label=model_label, 399*b7c941bbSAndroid Build Coastguard Worker alpha=0.3, 400*b7c941bbSAndroid Build Coastguard Worker ) 401*b7c941bbSAndroid Build Coastguard Worker 402*b7c941bbSAndroid Build Coastguard Worker def _plot_noise_model(self, isos, measured_models, noise_model, 403*b7c941bbSAndroid Build Coastguard Worker sens_max_analog, name_with_log_path): 404*b7c941bbSAndroid Build Coastguard Worker """Plot the noise model for a given set of ISO values. 405*b7c941bbSAndroid Build Coastguard Worker 406*b7c941bbSAndroid Build Coastguard Worker The read noise model is defined by the following equation: 407*b7c941bbSAndroid Build Coastguard Worker f(x) = s_model * x + o_model 408*b7c941bbSAndroid Build Coastguard Worker where we have: 409*b7c941bbSAndroid Build Coastguard Worker s_model = scale_a * analog_gain * digital_gain + scale_b is the 410*b7c941bbSAndroid Build Coastguard Worker multiplicative factor, 411*b7c941bbSAndroid Build Coastguard Worker o_model = (offset_a * analog_gain^2 + offset_b) * digital_gain^2 412*b7c941bbSAndroid Build Coastguard Worker is the offset term. 413*b7c941bbSAndroid Build Coastguard Worker 414*b7c941bbSAndroid Build Coastguard Worker Args: 415*b7c941bbSAndroid Build Coastguard Worker isos: A list of ISO values. 416*b7c941bbSAndroid Build Coastguard Worker measured_models: A list of measured models, each of which is a tuple of 417*b7c941bbSAndroid Build Coastguard Worker (sens, s_measured, o_measured). 418*b7c941bbSAndroid Build Coastguard Worker noise_model: Noise model parameters of each plane, each of which is a 419*b7c941bbSAndroid Build Coastguard Worker tuple of (scale_a, scale_b, offset_a, offset_b). 420*b7c941bbSAndroid Build Coastguard Worker sens_max_analog: The maximum analog gain. 421*b7c941bbSAndroid Build Coastguard Worker name_with_log_path: The name of the file to save the logs to. 422*b7c941bbSAndroid Build Coastguard Worker """ 423*b7c941bbSAndroid Build Coastguard Worker noise_model_utils.check_noise_model_shape(noise_model) 424*b7c941bbSAndroid Build Coastguard Worker 425*b7c941bbSAndroid Build Coastguard Worker # Plot noise model parameters. 426*b7c941bbSAndroid Build Coastguard Worker fig, axes = plt.subplots(4, 2, figsize=(22, 17)) 427*b7c941bbSAndroid Build Coastguard Worker s_plots, o_plots = axes[:, 0], axes[:, 1] 428*b7c941bbSAndroid Build Coastguard Worker num_channels = noise_model.shape[0] 429*b7c941bbSAndroid Build Coastguard Worker is_quad_bayer = ( 430*b7c941bbSAndroid Build Coastguard Worker num_channels == noise_model_constants.NUM_QUAD_BAYER_CHANNELS 431*b7c941bbSAndroid Build Coastguard Worker ) 432*b7c941bbSAndroid Build Coastguard Worker for pidx, measured_model in enumerate(measured_models): 433*b7c941bbSAndroid Build Coastguard Worker # Grab the sensitivities and line parameters of each sensitivity. 434*b7c941bbSAndroid Build Coastguard Worker sens, s_measured, o_measured = zip(*measured_model) 435*b7c941bbSAndroid Build Coastguard Worker sens = np.asarray(sens) 436*b7c941bbSAndroid Build Coastguard Worker sens_sq = np.square(sens) 437*b7c941bbSAndroid Build Coastguard Worker scale_a, scale_b, offset_a, offset_b = noise_model[pidx] 438*b7c941bbSAndroid Build Coastguard Worker # Plot noise model components with the values predicted by the model. 439*b7c941bbSAndroid Build Coastguard Worker digital_gains = noise_model_utils.compute_digital_gains( 440*b7c941bbSAndroid Build Coastguard Worker sens, sens_max_analog 441*b7c941bbSAndroid Build Coastguard Worker ) 442*b7c941bbSAndroid Build Coastguard Worker 443*b7c941bbSAndroid Build Coastguard Worker # s_model = scale_a * analog_gain * digital_gain + scale_b, 444*b7c941bbSAndroid Build Coastguard Worker # o_model = (offset_a * analog_gain^2 + offset_b) * digital_gain^2. 445*b7c941bbSAndroid Build Coastguard Worker s_model = scale_a * sens * digital_gains + scale_b 446*b7c941bbSAndroid Build Coastguard Worker o_model = (offset_a * sens_sq + offset_b) * np.square(digital_gains) 447*b7c941bbSAndroid Build Coastguard Worker if is_quad_bayer: 448*b7c941bbSAndroid Build Coastguard Worker s_plot, o_plot = s_plots[pidx // 4], o_plots[pidx // 4] 449*b7c941bbSAndroid Build Coastguard Worker else: 450*b7c941bbSAndroid Build Coastguard Worker s_plot, o_plot = s_plots[pidx], o_plots[pidx] 451*b7c941bbSAndroid Build Coastguard Worker 452*b7c941bbSAndroid Build Coastguard Worker self._plot_noise_model_single_plane( 453*b7c941bbSAndroid Build Coastguard Worker pidx, s_plot, sens, s_measured, s_model) 454*b7c941bbSAndroid Build Coastguard Worker self._plot_noise_model_single_plane( 455*b7c941bbSAndroid Build Coastguard Worker pidx, o_plot, sens, o_measured, o_model) 456*b7c941bbSAndroid Build Coastguard Worker 457*b7c941bbSAndroid Build Coastguard Worker # Set figure attributes after plotting noise model parameters. 458*b7c941bbSAndroid Build Coastguard Worker for s_plot, o_plot in zip(s_plots, o_plots): 459*b7c941bbSAndroid Build Coastguard Worker s_plot.set_xlabel('ISO') 460*b7c941bbSAndroid Build Coastguard Worker s_plot.set_ylabel('S') 461*b7c941bbSAndroid Build Coastguard Worker 462*b7c941bbSAndroid Build Coastguard Worker o_plot.set_xlabel('ISO') 463*b7c941bbSAndroid Build Coastguard Worker o_plot.set_ylabel('O') 464*b7c941bbSAndroid Build Coastguard Worker 465*b7c941bbSAndroid Build Coastguard Worker for sub_plot in (s_plot, o_plot): 466*b7c941bbSAndroid Build Coastguard Worker sub_plot.set_xticks(isos) 467*b7c941bbSAndroid Build Coastguard Worker # No minor ticks. 468*b7c941bbSAndroid Build Coastguard Worker sub_plot.xaxis.set_minor_locator(matplotlib.ticker.NullLocator()) 469*b7c941bbSAndroid Build Coastguard Worker sub_plot.xaxis.set_major_formatter(matplotlib.ticker.ScalarFormatter()) 470*b7c941bbSAndroid Build Coastguard Worker sub_plot.legend() 471*b7c941bbSAndroid Build Coastguard Worker 472*b7c941bbSAndroid Build Coastguard Worker fig.suptitle('Noise model: N(x) = sqrt(Sx + O)', x=0.54, y=0.99) 473*b7c941bbSAndroid Build Coastguard Worker plt.tight_layout() 474*b7c941bbSAndroid Build Coastguard Worker fig.savefig(f'{name_with_log_path}.png', dpi=_FIG_DPI) 475*b7c941bbSAndroid Build Coastguard Worker 476*b7c941bbSAndroid Build Coastguard Worker def test_dng_noise_model_generation(self): 477*b7c941bbSAndroid Build Coastguard Worker """Calibrates standard Bayer or quad Bayer noise model. 478*b7c941bbSAndroid Build Coastguard Worker 479*b7c941bbSAndroid Build Coastguard Worker def requires 'test' in name to actually run. 480*b7c941bbSAndroid Build Coastguard Worker This function: 481*b7c941bbSAndroid Build Coastguard Worker * Calibrates read noise (optional). 482*b7c941bbSAndroid Build Coastguard Worker * Captures stats images of different ISO values and exposure times. 483*b7c941bbSAndroid Build Coastguard Worker * Measures linear fittings for each ISO value. 484*b7c941bbSAndroid Build Coastguard Worker * Computes and validates overall noise model parameters. 485*b7c941bbSAndroid Build Coastguard Worker * Plots noise model parameters figures. 486*b7c941bbSAndroid Build Coastguard Worker * Plots stats samples, linear fittings and model fittings. 487*b7c941bbSAndroid Build Coastguard Worker * Saves the read noise plot and csv data (optional). 488*b7c941bbSAndroid Build Coastguard Worker * Generates noise model and noise profile code. 489*b7c941bbSAndroid Build Coastguard Worker """ 490*b7c941bbSAndroid Build Coastguard Worker read_noise_file_path = capture_read_noise_utils.calibrate_read_noise( 491*b7c941bbSAndroid Build Coastguard Worker self.dut.serial, 492*b7c941bbSAndroid Build Coastguard Worker self.camera_id, 493*b7c941bbSAndroid Build Coastguard Worker self.hidden_physical_id, 494*b7c941bbSAndroid Build Coastguard Worker _NAME_READ_NOISE, 495*b7c941bbSAndroid Build Coastguard Worker _NAME_READ_NOISE_FILE, 496*b7c941bbSAndroid Build Coastguard Worker _READ_NOISE_STEPS_PER_STOP, 497*b7c941bbSAndroid Build Coastguard Worker raw_format=_READ_NOISE_RAW_FORMAT, 498*b7c941bbSAndroid Build Coastguard Worker is_two_stage_model=_TWO_STAGE_MODEL, 499*b7c941bbSAndroid Build Coastguard Worker ) 500*b7c941bbSAndroid Build Coastguard Worker 501*b7c941bbSAndroid Build Coastguard Worker # Begin DNG Noise Model Calibration 502*b7c941bbSAndroid Build Coastguard Worker with its_session_utils.ItsSession( 503*b7c941bbSAndroid Build Coastguard Worker device_id=self.dut.serial, 504*b7c941bbSAndroid Build Coastguard Worker camera_id=self.camera_id, 505*b7c941bbSAndroid Build Coastguard Worker hidden_physical_id=self.hidden_physical_id) as cam: 506*b7c941bbSAndroid Build Coastguard Worker props = cam.get_camera_properties() 507*b7c941bbSAndroid Build Coastguard Worker props = cam.override_with_hidden_physical_camera_props(props) 508*b7c941bbSAndroid Build Coastguard Worker log_path = self.log_path 509*b7c941bbSAndroid Build Coastguard Worker name_with_log_path = os.path.join(log_path, _NAME) 510*b7c941bbSAndroid Build Coastguard Worker logging.info('Starting %s for camera %s', _NAME, cam.get_camera_name()) 511*b7c941bbSAndroid Build Coastguard Worker 512*b7c941bbSAndroid Build Coastguard Worker # Get basic properties we need. 513*b7c941bbSAndroid Build Coastguard Worker sens_min, sens_max = props['android.sensor.info.sensitivityRange'] 514*b7c941bbSAndroid Build Coastguard Worker sens_max_analog = props['android.sensor.maxAnalogSensitivity'] 515*b7c941bbSAndroid Build Coastguard Worker # Maximum sensitivity for measuring noise model. 516*b7c941bbSAndroid Build Coastguard Worker sens_max_meas = sens_max_analog 517*b7c941bbSAndroid Build Coastguard Worker 518*b7c941bbSAndroid Build Coastguard Worker # Change the ISO min and/or max values if specified 519*b7c941bbSAndroid Build Coastguard Worker if _ISO_MIN_VALUE is not None: 520*b7c941bbSAndroid Build Coastguard Worker sens_min = _ISO_MIN_VALUE 521*b7c941bbSAndroid Build Coastguard Worker if _ISO_MAX_VALUE is not None: 522*b7c941bbSAndroid Build Coastguard Worker sens_max_meas = _ISO_MAX_VALUE 523*b7c941bbSAndroid Build Coastguard Worker 524*b7c941bbSAndroid Build Coastguard Worker logging.info('Sensitivity range: [%d, %d]', sens_min, sens_max) 525*b7c941bbSAndroid Build Coastguard Worker logging.info('Max analog sensitivity: %d', sens_max_analog) 526*b7c941bbSAndroid Build Coastguard Worker logging.info( 527*b7c941bbSAndroid Build Coastguard Worker 'Sensitivity range for measurement: [%d, %d]', 528*b7c941bbSAndroid Build Coastguard Worker sens_min, sens_max_meas, 529*b7c941bbSAndroid Build Coastguard Worker ) 530*b7c941bbSAndroid Build Coastguard Worker 531*b7c941bbSAndroid Build Coastguard Worker offset_a, offset_b = None, None 532*b7c941bbSAndroid Build Coastguard Worker read_noise_data = None 533*b7c941bbSAndroid Build Coastguard Worker if _TWO_STAGE_MODEL: 534*b7c941bbSAndroid Build Coastguard Worker # Check if read noise results exist for this device and camera 535*b7c941bbSAndroid Build Coastguard Worker if not os.path.exists(read_noise_file_path): 536*b7c941bbSAndroid Build Coastguard Worker raise AssertionError( 537*b7c941bbSAndroid Build Coastguard Worker 'Read noise results file does not exist for this device. Run' 538*b7c941bbSAndroid Build Coastguard Worker ' capture_read_noise_file_path script to gather read noise data' 539*b7c941bbSAndroid Build Coastguard Worker ' for current sensor' 540*b7c941bbSAndroid Build Coastguard Worker ) 541*b7c941bbSAndroid Build Coastguard Worker 542*b7c941bbSAndroid Build Coastguard Worker with open(read_noise_file_path, 'rb') as f: 543*b7c941bbSAndroid Build Coastguard Worker read_noise_data = pickle.load(f) 544*b7c941bbSAndroid Build Coastguard Worker 545*b7c941bbSAndroid Build Coastguard Worker offset_a, offset_b = ( 546*b7c941bbSAndroid Build Coastguard Worker capture_read_noise_utils.get_read_noise_coefficients( 547*b7c941bbSAndroid Build Coastguard Worker read_noise_data, 548*b7c941bbSAndroid Build Coastguard Worker sens_min, 549*b7c941bbSAndroid Build Coastguard Worker sens_max_meas, 550*b7c941bbSAndroid Build Coastguard Worker ) 551*b7c941bbSAndroid Build Coastguard Worker ) 552*b7c941bbSAndroid Build Coastguard Worker 553*b7c941bbSAndroid Build Coastguard Worker iso_to_stats_dict = noise_model_utils.capture_stats_images( 554*b7c941bbSAndroid Build Coastguard Worker cam, 555*b7c941bbSAndroid Build Coastguard Worker props, 556*b7c941bbSAndroid Build Coastguard Worker _STATS_CONFIG, 557*b7c941bbSAndroid Build Coastguard Worker sens_min, 558*b7c941bbSAndroid Build Coastguard Worker sens_max_meas, 559*b7c941bbSAndroid Build Coastguard Worker _ZOOM_RATIO, 560*b7c941bbSAndroid Build Coastguard Worker _TILE_CROP_N, 561*b7c941bbSAndroid Build Coastguard Worker _MAX_SIGNAL_VALUE, 562*b7c941bbSAndroid Build Coastguard Worker _ISO_MULTIPLIER, 563*b7c941bbSAndroid Build Coastguard Worker _BRACKET_MAX, 564*b7c941bbSAndroid Build Coastguard Worker _BRACKET_FACTOR, 565*b7c941bbSAndroid Build Coastguard Worker self.log_path, 566*b7c941bbSAndroid Build Coastguard Worker stats_file_name=_STATS_FILE_NAME, 567*b7c941bbSAndroid Build Coastguard Worker is_remove_var_outliers=_REMOVE_VAR_OUTLIERS, 568*b7c941bbSAndroid Build Coastguard Worker outlier_median_abs_deviations=_OUTLIER_MEDIAN_ABS_DEVS, 569*b7c941bbSAndroid Build Coastguard Worker is_debug_mode=self.debug_mode, 570*b7c941bbSAndroid Build Coastguard Worker ) 571*b7c941bbSAndroid Build Coastguard Worker 572*b7c941bbSAndroid Build Coastguard Worker measured_models, samples = noise_model_utils.measure_linear_noise_models( 573*b7c941bbSAndroid Build Coastguard Worker iso_to_stats_dict, 574*b7c941bbSAndroid Build Coastguard Worker _COLOR_CHANNEL_NAMES, 575*b7c941bbSAndroid Build Coastguard Worker ) 576*b7c941bbSAndroid Build Coastguard Worker 577*b7c941bbSAndroid Build Coastguard Worker noise_model = noise_model_utils.compute_noise_model( 578*b7c941bbSAndroid Build Coastguard Worker samples, 579*b7c941bbSAndroid Build Coastguard Worker sens_max_analog, 580*b7c941bbSAndroid Build Coastguard Worker offset_a, 581*b7c941bbSAndroid Build Coastguard Worker offset_b, 582*b7c941bbSAndroid Build Coastguard Worker _TWO_STAGE_MODEL, 583*b7c941bbSAndroid Build Coastguard Worker ) 584*b7c941bbSAndroid Build Coastguard Worker 585*b7c941bbSAndroid Build Coastguard Worker noise_model_utils.validate_noise_model( 586*b7c941bbSAndroid Build Coastguard Worker noise_model, 587*b7c941bbSAndroid Build Coastguard Worker _COLOR_CHANNEL_NAMES, 588*b7c941bbSAndroid Build Coastguard Worker sens_min, 589*b7c941bbSAndroid Build Coastguard Worker ) 590*b7c941bbSAndroid Build Coastguard Worker 591*b7c941bbSAndroid Build Coastguard Worker self._plot_noise_model( 592*b7c941bbSAndroid Build Coastguard Worker sorted(iso_to_stats_dict.keys()), 593*b7c941bbSAndroid Build Coastguard Worker measured_models, 594*b7c941bbSAndroid Build Coastguard Worker noise_model, 595*b7c941bbSAndroid Build Coastguard Worker sens_max_analog, 596*b7c941bbSAndroid Build Coastguard Worker name_with_log_path, 597*b7c941bbSAndroid Build Coastguard Worker ) 598*b7c941bbSAndroid Build Coastguard Worker 599*b7c941bbSAndroid Build Coastguard Worker self._plot_stats_and_noise_model_fittings( 600*b7c941bbSAndroid Build Coastguard Worker iso_to_stats_dict, 601*b7c941bbSAndroid Build Coastguard Worker measured_models, 602*b7c941bbSAndroid Build Coastguard Worker noise_model, 603*b7c941bbSAndroid Build Coastguard Worker sens_max_analog, 604*b7c941bbSAndroid Build Coastguard Worker name_with_log_path, 605*b7c941bbSAndroid Build Coastguard Worker ) 606*b7c941bbSAndroid Build Coastguard Worker 607*b7c941bbSAndroid Build Coastguard Worker # If 2-Stage model is enabled, save the read noise graph and csv data 608*b7c941bbSAndroid Build Coastguard Worker if _TWO_STAGE_MODEL: 609*b7c941bbSAndroid Build Coastguard Worker # Save the linear plot of the read noise data 610*b7c941bbSAndroid Build Coastguard Worker filename = f'{pathlib.Path(_NAME_READ_NOISE_FILE).stem}.png' 611*b7c941bbSAndroid Build Coastguard Worker file_path = os.path.join(log_path, filename) 612*b7c941bbSAndroid Build Coastguard Worker capture_read_noise_utils.plot_read_noise_data( 613*b7c941bbSAndroid Build Coastguard Worker read_noise_data, 614*b7c941bbSAndroid Build Coastguard Worker sens_min, 615*b7c941bbSAndroid Build Coastguard Worker sens_max_meas, 616*b7c941bbSAndroid Build Coastguard Worker file_path, 617*b7c941bbSAndroid Build Coastguard Worker _COLOR_CHANNEL_NAMES, 618*b7c941bbSAndroid Build Coastguard Worker _PLOT_COLORS, 619*b7c941bbSAndroid Build Coastguard Worker ) 620*b7c941bbSAndroid Build Coastguard Worker 621*b7c941bbSAndroid Build Coastguard Worker # Save the data as a csv file 622*b7c941bbSAndroid Build Coastguard Worker filename = f'{pathlib.Path(_NAME_READ_NOISE_FILE).stem}.csv' 623*b7c941bbSAndroid Build Coastguard Worker file_path = os.path.join(log_path, filename) 624*b7c941bbSAndroid Build Coastguard Worker capture_read_noise_utils.save_read_noise_data_as_csv( 625*b7c941bbSAndroid Build Coastguard Worker read_noise_data, 626*b7c941bbSAndroid Build Coastguard Worker sens_min, 627*b7c941bbSAndroid Build Coastguard Worker sens_max_meas, 628*b7c941bbSAndroid Build Coastguard Worker file_path, 629*b7c941bbSAndroid Build Coastguard Worker _COLOR_CHANNEL_NAMES, 630*b7c941bbSAndroid Build Coastguard Worker ) 631*b7c941bbSAndroid Build Coastguard Worker 632*b7c941bbSAndroid Build Coastguard Worker # Generate the noise model file. 633*b7c941bbSAndroid Build Coastguard Worker self._create_noise_model_and_profile_code( 634*b7c941bbSAndroid Build Coastguard Worker noise_model, 635*b7c941bbSAndroid Build Coastguard Worker sens_min, 636*b7c941bbSAndroid Build Coastguard Worker sens_max, 637*b7c941bbSAndroid Build Coastguard Worker sens_max_analog, 638*b7c941bbSAndroid Build Coastguard Worker log_path, 639*b7c941bbSAndroid Build Coastguard Worker ) 640*b7c941bbSAndroid Build Coastguard Worker 641*b7c941bbSAndroid Build Coastguard Worker 642*b7c941bbSAndroid Build Coastguard Workerif __name__ == '__main__': 643*b7c941bbSAndroid Build Coastguard Worker test_runner.main() 644