1# Copyright 2014 The Android Open Source Project 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14"""Verifies RAW sensitivity burst.""" 15 16 17import logging 18import os.path 19import matplotlib 20from matplotlib import pylab 21from mobly import test_runner 22 23import its_base_test 24import camera_properties_utils 25import capture_request_utils 26import image_processing_utils 27import its_session_utils 28 29_GR_PLANE_IDX = 1 # GR plane index in RGGB data 30_IMG_STATS_GRID = 9 # find used to find the center 11.11% 31_NAME = os.path.splitext(os.path.basename(__file__))[0] 32_NUM_STEPS = 5 33_VAR_THRESH = 1.01 # each shot must be 1% noisier than previous 34 35 36def define_raw_stats_fmt(props): 37 """Defines the format using camera props active array width and height.""" 38 aax = props['android.sensor.info.preCorrectionActiveArraySize']['left'] 39 aay = props['android.sensor.info.preCorrectionActiveArraySize']['top'] 40 aaw = props['android.sensor.info.preCorrectionActiveArraySize']['right'] - aax 41 aah = props[ 42 'android.sensor.info.preCorrectionActiveArraySize']['bottom'] - aay 43 44 return {'format': 'rawStats', 45 'gridWidth': aaw // _IMG_STATS_GRID, 46 'gridHeight': aah // _IMG_STATS_GRID} 47 48 49class RawSensitivityBurstTest(its_base_test.ItsBaseTest): 50 """Captures a set of RAW images with increasing sensitivity & measures noise. 51 52 Sensitivity range (gain) is determined from camera properties and limited to 53 the analog sensitivity range as captures are RAW only in a burst. Digital 54 sensitivity range from props['android.sensor.info.sensitivityRange'] is not 55 used. 56 57 Uses RawStats capture format to speed up processing. RawStats defines a grid 58 over the RAW image and returns average and variance of requested areas. 59 white_level is found from camera to normalize variance values from RawStats. 60 61 Noise (image variance) of center patch should increase with increasing 62 sensitivity. 63 """ 64 65 def test_raw_sensitivity_burst(self): 66 logging.debug('Starting %s', _NAME) 67 with its_session_utils.ItsSession( 68 device_id=self.dut.serial, 69 camera_id=self.camera_id, 70 hidden_physical_id=self.hidden_physical_id) as cam: 71 props = cam.get_camera_properties() 72 props = cam.override_with_hidden_physical_camera_props(props) 73 camera_properties_utils.skip_unless( 74 camera_properties_utils.raw16(props) and 75 camera_properties_utils.manual_sensor(props) and 76 camera_properties_utils.read_3a(props) and 77 camera_properties_utils.per_frame_control(props) and 78 not camera_properties_utils.mono_camera(props)) 79 80 # Load chart for scene 81 its_session_utils.load_scene( 82 cam, props, self.scene, self.tablet, self.chart_distance) 83 84 # Find sensitivity range and create capture requests 85 sens_min, _ = props['android.sensor.info.sensitivityRange'] 86 sens_max = props['android.sensor.maxAnalogSensitivity'] 87 sens_step = (sens_max - sens_min) // _NUM_STEPS 88 sens_ae, exp_ae, _, _, f_dist = cam.do_3a(get_results=True) 89 sens_exp_prod = sens_ae * exp_ae 90 reqs = [] 91 settings = [] 92 for sens in range(sens_min, sens_max, sens_step): 93 exp = int(sens_exp_prod / float(sens)) 94 req = capture_request_utils.manual_capture_request(sens, exp, f_dist) 95 reqs.append(req) 96 settings.append((sens, exp)) 97 98 # Get rawStats capture format 99 fmt = define_raw_stats_fmt(props) 100 101 # Do captures 102 caps = cam.do_capture(reqs, fmt) 103 104 # Extract variances from each shot 105 variances = [] 106 for i, cap in enumerate(caps): 107 (sens, exp) = settings[i] 108 109 # Find white_level for RawStats normalization 110 white_level = float(props['android.sensor.info.whiteLevel']) 111 _, var_image = image_processing_utils.unpack_rawstats_capture(cap) 112 cfa_idxs = image_processing_utils.get_canonical_cfa_order(props) 113 var = var_image[_IMG_STATS_GRID//2, _IMG_STATS_GRID//2, 114 cfa_idxs[_GR_PLANE_IDX]]/white_level**2 115 variances.append(var) 116 logging.debug('s=%d, e=%d, var=%e', sens, exp, var) 117 118 # Create a plot 119 x = range(len(variances)) 120 pylab.figure(_NAME) 121 pylab.plot(x, variances, '-ro') 122 pylab.xticks(x) 123 pylab.ticklabel_format(style='sci', axis='y', scilimits=(-6, -6)) 124 pylab.xlabel('Setting Combination') 125 pylab.ylabel('Image Center Patch Variance') 126 pylab.title(_NAME) 127 matplotlib.pyplot.savefig( 128 '%s_variances.png' % os.path.join(self.log_path, _NAME)) 129 130 # Asserts that each shot is noisier than previous 131 for i in x[0:-1]: 132 e_msg = 'variances [i]: %.5f, [i+1]: %.5f, THRESH: %.2f' % ( 133 variances[i], variances[i+1], _VAR_THRESH) 134 assert variances[i] < variances[i+1] / _VAR_THRESH, e_msg 135 136if __name__ == '__main__': 137 test_runner.main() 138