1# Copyright 2013 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 android.noiseReduction.mode parameters is applied when set.""" 15 16 17import logging 18import os.path 19import matplotlib 20from matplotlib import pylab 21from mobly import test_runner 22import numpy as np 23 24import its_base_test 25import camera_properties_utils 26import capture_request_utils 27import image_processing_utils 28import its_session_utils 29import target_exposure_utils 30 31COLORS = ['R', 'G', 'B'] 32NAME = os.path.splitext(os.path.basename(__file__))[0] 33NR_MODES = {'OFF': 0, 'FAST': 1, 'HQ': 2, 'MIN': 3, 'ZSL': 4} 34NR_MODES_LIST = list(NR_MODES.values()) 35NUM_COLORS = len(COLORS) 36NUM_FRAMES_PER_MODE = 4 37PATCH_H = 0.1 # center 10% 38PATCH_W = 0.1 39PATCH_X = 0.5 - PATCH_W/2 40PATCH_Y = 0.5 - PATCH_H/2 41SNR_TOLERANCE = 3 # unit in dB 42 43 44class ParamNoiseReductionTest(its_base_test.ItsBaseTest): 45 """Test that the android.noiseReduction.mode param is applied when set. 46 47 Capture images with the camera dimly lit. 48 49 Capture images with low gain and noise redcution off, and use the 50 variance of these captures as the baseline. 51 52 Use high analog gain on remaining tests to ensure captured images are noisy. 53 """ 54 55 def test_param_noise_reduction(self): 56 logging.debug('Starting %s', NAME) 57 logging.debug('NR_MODES: %s', str(NR_MODES)) 58 with its_session_utils.ItsSession( 59 device_id=self.dut.serial, 60 camera_id=self.camera_id, 61 hidden_physical_id=self.hidden_physical_id) as cam: 62 props = cam.get_camera_properties() 63 props = cam.override_with_hidden_physical_camera_props(props) 64 log_path = self.log_path 65 66 # check SKIP conditions 67 camera_properties_utils.skip_unless( 68 camera_properties_utils.compute_target_exposure(props) and 69 camera_properties_utils.per_frame_control(props) and 70 camera_properties_utils.noise_reduction_mode(props, 0)) 71 72 # Load chart for scene 73 its_session_utils.load_scene( 74 cam, props, self.scene, self.tablet, self.chart_distance) 75 76 snrs = [[], [], []] # List of SNRs for R,G,B 77 ref_snr = [] # Reference (baseline) SNR for each of R,G,B 78 nr_modes_reported = [] 79 80 # NR mode 0 with low gain 81 e, s = target_exposure_utils.get_target_exposure_combos( 82 log_path, cam)['minSensitivity'] 83 req = capture_request_utils.manual_capture_request(s, e) 84 req['android.noiseReduction.mode'] = 0 85 cap = cam.do_capture(req) 86 rgb_image = image_processing_utils.convert_capture_to_rgb_image(cap) 87 image_processing_utils.write_image( 88 rgb_image, '%s_low_gain.jpg' % os.path.join(log_path, NAME)) 89 rgb_patch = image_processing_utils.get_image_patch( 90 rgb_image, PATCH_X, PATCH_Y, PATCH_W, PATCH_H) 91 ref_snr = image_processing_utils.compute_image_snrs(rgb_patch) 92 logging.debug('Ref SNRs: %s', str(ref_snr)) 93 94 e, s = target_exposure_utils.get_target_exposure_combos( 95 log_path, cam)['maxSensitivity'] 96 for mode in NR_MODES_LIST: 97 # Skip unavailable modes 98 if not camera_properties_utils.noise_reduction_mode(props, mode): 99 nr_modes_reported.append(mode) 100 for channel in range(NUM_COLORS): 101 snrs[channel].append(0) 102 continue 103 104 rgb_snr_list = [] 105 # Capture several images to account for per frame noise variations 106 for n in range(NUM_FRAMES_PER_MODE): 107 req = capture_request_utils.manual_capture_request(s, e) 108 req['android.noiseReduction.mode'] = mode 109 cap = cam.do_capture(req) 110 rgb_image = image_processing_utils.convert_capture_to_rgb_image(cap) 111 if n == 0: 112 nr_modes_reported.append( 113 cap['metadata']['android.noiseReduction.mode']) 114 image_processing_utils.write_image( 115 rgb_image, '%s_high_gain_nr=%d.jpg' % ( 116 os.path.join(log_path, NAME), mode)) 117 rgb_patch = image_processing_utils.get_image_patch( 118 rgb_image, PATCH_X, PATCH_Y, PATCH_W, PATCH_H) 119 rgb_snrs = image_processing_utils.compute_image_snrs(rgb_patch) 120 rgb_snr_list.append(rgb_snrs) 121 122 r_snrs = [rgb[0] for rgb in rgb_snr_list] 123 g_snrs = [rgb[1] for rgb in rgb_snr_list] 124 b_snrs = [rgb[2] for rgb in rgb_snr_list] 125 rgb_snrs = [np.mean(r_snrs), np.mean(g_snrs), np.mean(b_snrs)] 126 logging.debug('NR mode %s SNRs', mode) 127 logging.debug('R SNR: %.2f, Min: %.2f, Max: %.2f', 128 rgb_snrs[0], min(r_snrs), max(r_snrs)) 129 logging.debug('G SNR: %.2f, Min: %.2f, Max: %.2f', 130 rgb_snrs[1], min(g_snrs), max(g_snrs)) 131 logging.debug('B SNR: %.2f, Min: %.2f, Max: %.2f', 132 rgb_snrs[2], min(b_snrs), max(b_snrs)) 133 134 for chan in range(NUM_COLORS): 135 snrs[chan].append(rgb_snrs[chan]) 136 137 # Draw plot 138 pylab.figure(NAME) 139 for j in range(NUM_COLORS): 140 pylab.plot(NR_MODES_LIST, snrs[j], '-'+'rgb'[j]+'o') 141 pylab.xlabel('Noise Reduction Mode') 142 pylab.ylabel('SNR (dB)') 143 pylab.xticks(NR_MODES_LIST) 144 matplotlib.pyplot.savefig('%s_plot_SNRs.png' % os.path.join(log_path, NAME)) 145 146 assert nr_modes_reported == NR_MODES_LIST 147 148 for j in range(NUM_COLORS): 149 # Higher SNR is better 150 # Verify OFF is not better than FAST 151 e_msg = '%s OFF: %.3f, FAST: %.3f, TOL: %.3f' % ( 152 COLORS[j], snrs[j][NR_MODES['OFF']], snrs[j][NR_MODES['FAST']], 153 SNR_TOLERANCE) 154 assert (snrs[j][NR_MODES['OFF']] < snrs[j][NR_MODES['FAST']] + 155 SNR_TOLERANCE), e_msg 156 157 # Verify FAST is not better than HQ 158 e_msg = '%s FAST: %.3f, HQ: %.3f, TOL: %.3f' % ( 159 COLORS[j], snrs[j][NR_MODES['FAST']], snrs[j][NR_MODES['HQ']], 160 SNR_TOLERANCE) 161 assert (snrs[j][NR_MODES['FAST']] < snrs[j][NR_MODES['HQ']] + 162 SNR_TOLERANCE), e_msg 163 164 # Verify HQ is better than OFF 165 e_msg = '%s OFF: %.3f, HQ: %.3f' % ( 166 COLORS[j], snrs[j][NR_MODES['OFF']], snrs[j][NR_MODES['HQ']]) 167 assert snrs[j][NR_MODES['HQ']] > snrs[j][NR_MODES['OFF']], e_msg 168 169 if camera_properties_utils.noise_reduction_mode(props, NR_MODES['MIN']): 170 # Verify OFF is not better than MINIMAL 171 e_msg = '%s OFF: %.3f, MIN: %.3f, TOL: %.3f' % ( 172 COLORS[j], snrs[j][NR_MODES['OFF']], snrs[j][NR_MODES['MIN']], 173 SNR_TOLERANCE) 174 assert (snrs[j][NR_MODES['OFF']] < snrs[j][NR_MODES['MIN']] + 175 SNR_TOLERANCE), e_msg 176 177 # Verify MINIMAL is not better than HQ 178 e_msg = '%s MIN: %.3f, HQ: %.3f, TOL: %.3f' % ( 179 COLORS[j], snrs[j][NR_MODES['MIN']], snrs[j][NR_MODES['HQ']], 180 SNR_TOLERANCE) 181 assert (snrs[j][NR_MODES['MIN']] < snrs[j][NR_MODES['HQ']] + 182 SNR_TOLERANCE), e_msg 183 184 if camera_properties_utils.noise_reduction_mode(props, NR_MODES['ZSL']): 185 # Verify ZSL is close to MINIMAL 186 e_msg = '%s ZSL: %.3f, MIN: %.3f, TOL: %.3f' % ( 187 COLORS[j], snrs[j][NR_MODES['ZSL']], snrs[j][NR_MODES['MIN']], 188 SNR_TOLERANCE) 189 assert np.isclose(snrs[j][NR_MODES['ZSL']], snrs[j][NR_MODES['MIN']], 190 atol=SNR_TOLERANCE), e_msg 191 elif camera_properties_utils.noise_reduction_mode(props, NR_MODES['ZSL']): 192 # Verify ZSL is close to OFF 193 e_msg = '%s OFF: %.3f, ZSL: %.3f, TOL: %.3f' % ( 194 COLORS[j], snrs[j][NR_MODES['OFF']], snrs[j][NR_MODES['ZSL']], 195 SNR_TOLERANCE) 196 assert np.isclose(snrs[j][NR_MODES['ZSL']], snrs[j][NR_MODES['OFF']], 197 atol=SNR_TOLERANCE), e_msg 198 199if __name__ == '__main__': 200 test_runner.main() 201 202