| # Copyright 2013 The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| """Verifies android.noiseReduction.mode parameters is applied when set.""" |
| |
| |
| import logging |
| import os.path |
| import matplotlib |
| from matplotlib import pylab |
| from mobly import test_runner |
| import numpy as np |
| |
| import its_base_test |
| import camera_properties_utils |
| import capture_request_utils |
| import image_processing_utils |
| import its_session_utils |
| import target_exposure_utils |
| |
| COLORS = ['R', 'G', 'B'] |
| NAME = os.path.splitext(os.path.basename(__file__))[0] |
| NR_MODES = {'OFF': 0, 'FAST': 1, 'HQ': 2, 'MIN': 3, 'ZSL': 4} |
| NR_MODES_LIST = list(NR_MODES.values()) |
| NUM_COLORS = len(COLORS) |
| NUM_FRAMES_PER_MODE = 4 |
| PATCH_H = 0.1 # center 10% |
| PATCH_W = 0.1 |
| PATCH_X = 0.5 - PATCH_W/2 |
| PATCH_Y = 0.5 - PATCH_H/2 |
| SNR_TOLERANCE = 3 # unit in dB |
| |
| |
| class ParamNoiseReductionTest(its_base_test.ItsBaseTest): |
| """Test that the android.noiseReduction.mode param is applied when set. |
| |
| Capture images with the camera dimly lit. |
| |
| Capture images with low gain and noise redcution off, and use the |
| variance of these captures as the baseline. |
| |
| Use high analog gain on remaining tests to ensure captured images are noisy. |
| """ |
| |
| def test_param_noise_reduction(self): |
| logging.debug('Starting %s', NAME) |
| logging.debug('NR_MODES: %s', str(NR_MODES)) |
| with its_session_utils.ItsSession( |
| device_id=self.dut.serial, |
| camera_id=self.camera_id, |
| hidden_physical_id=self.hidden_physical_id) as cam: |
| props = cam.get_camera_properties() |
| props = cam.override_with_hidden_physical_camera_props(props) |
| log_path = self.log_path |
| |
| # check SKIP conditions |
| camera_properties_utils.skip_unless( |
| camera_properties_utils.compute_target_exposure(props) and |
| camera_properties_utils.per_frame_control(props) and |
| camera_properties_utils.noise_reduction_mode(props, 0)) |
| |
| # Load chart for scene |
| its_session_utils.load_scene( |
| cam, props, self.scene, self.tablet, self.chart_distance) |
| |
| snrs = [[], [], []] # List of SNRs for R,G,B |
| ref_snr = [] # Reference (baseline) SNR for each of R,G,B |
| nr_modes_reported = [] |
| |
| # NR mode 0 with low gain |
| e, s = target_exposure_utils.get_target_exposure_combos( |
| log_path, cam)['minSensitivity'] |
| req = capture_request_utils.manual_capture_request(s, e) |
| req['android.noiseReduction.mode'] = 0 |
| cap = cam.do_capture(req) |
| rgb_image = image_processing_utils.convert_capture_to_rgb_image(cap) |
| image_processing_utils.write_image( |
| rgb_image, '%s_low_gain.jpg' % os.path.join(log_path, NAME)) |
| rgb_patch = image_processing_utils.get_image_patch( |
| rgb_image, PATCH_X, PATCH_Y, PATCH_W, PATCH_H) |
| ref_snr = image_processing_utils.compute_image_snrs(rgb_patch) |
| logging.debug('Ref SNRs: %s', str(ref_snr)) |
| |
| e, s = target_exposure_utils.get_target_exposure_combos( |
| log_path, cam)['maxSensitivity'] |
| for mode in NR_MODES_LIST: |
| # Skip unavailable modes |
| if not camera_properties_utils.noise_reduction_mode(props, mode): |
| nr_modes_reported.append(mode) |
| for channel in range(NUM_COLORS): |
| snrs[channel].append(0) |
| continue |
| |
| rgb_snr_list = [] |
| # Capture several images to account for per frame noise variations |
| for n in range(NUM_FRAMES_PER_MODE): |
| req = capture_request_utils.manual_capture_request(s, e) |
| req['android.noiseReduction.mode'] = mode |
| cap = cam.do_capture(req) |
| rgb_image = image_processing_utils.convert_capture_to_rgb_image(cap) |
| if n == 0: |
| nr_modes_reported.append( |
| cap['metadata']['android.noiseReduction.mode']) |
| image_processing_utils.write_image( |
| rgb_image, '%s_high_gain_nr=%d.jpg' % ( |
| os.path.join(log_path, NAME), mode)) |
| rgb_patch = image_processing_utils.get_image_patch( |
| rgb_image, PATCH_X, PATCH_Y, PATCH_W, PATCH_H) |
| rgb_snrs = image_processing_utils.compute_image_snrs(rgb_patch) |
| rgb_snr_list.append(rgb_snrs) |
| |
| r_snrs = [rgb[0] for rgb in rgb_snr_list] |
| g_snrs = [rgb[1] for rgb in rgb_snr_list] |
| b_snrs = [rgb[2] for rgb in rgb_snr_list] |
| rgb_snrs = [np.mean(r_snrs), np.mean(g_snrs), np.mean(b_snrs)] |
| logging.debug('NR mode %s SNRs', mode) |
| logging.debug('R SNR: %.2f, Min: %.2f, Max: %.2f', |
| rgb_snrs[0], min(r_snrs), max(r_snrs)) |
| logging.debug('G SNR: %.2f, Min: %.2f, Max: %.2f', |
| rgb_snrs[1], min(g_snrs), max(g_snrs)) |
| logging.debug('B SNR: %.2f, Min: %.2f, Max: %.2f', |
| rgb_snrs[2], min(b_snrs), max(b_snrs)) |
| |
| for chan in range(NUM_COLORS): |
| snrs[chan].append(rgb_snrs[chan]) |
| |
| # Draw plot |
| pylab.figure(NAME) |
| for j in range(NUM_COLORS): |
| pylab.plot(NR_MODES_LIST, snrs[j], '-'+'rgb'[j]+'o') |
| pylab.xlabel('Noise Reduction Mode') |
| pylab.ylabel('SNR (dB)') |
| pylab.xticks(NR_MODES_LIST) |
| matplotlib.pyplot.savefig('%s_plot_SNRs.png' % os.path.join(log_path, NAME)) |
| |
| assert nr_modes_reported == NR_MODES_LIST |
| |
| for j in range(NUM_COLORS): |
| # Higher SNR is better |
| # Verify OFF is not better than FAST |
| e_msg = '%s OFF: %.3f, FAST: %.3f, TOL: %.3f' % ( |
| COLORS[j], snrs[j][NR_MODES['OFF']], snrs[j][NR_MODES['FAST']], |
| SNR_TOLERANCE) |
| assert (snrs[j][NR_MODES['OFF']] < snrs[j][NR_MODES['FAST']] + |
| SNR_TOLERANCE), e_msg |
| |
| # Verify FAST is not better than HQ |
| e_msg = '%s FAST: %.3f, HQ: %.3f, TOL: %.3f' % ( |
| COLORS[j], snrs[j][NR_MODES['FAST']], snrs[j][NR_MODES['HQ']], |
| SNR_TOLERANCE) |
| assert (snrs[j][NR_MODES['FAST']] < snrs[j][NR_MODES['HQ']] + |
| SNR_TOLERANCE), e_msg |
| |
| # Verify HQ is better than OFF |
| e_msg = '%s OFF: %.3f, HQ: %.3f' % ( |
| COLORS[j], snrs[j][NR_MODES['OFF']], snrs[j][NR_MODES['HQ']]) |
| assert snrs[j][NR_MODES['HQ']] > snrs[j][NR_MODES['OFF']], e_msg |
| |
| if camera_properties_utils.noise_reduction_mode(props, NR_MODES['MIN']): |
| # Verify OFF is not better than MINIMAL |
| e_msg = '%s OFF: %.3f, MIN: %.3f, TOL: %.3f' % ( |
| COLORS[j], snrs[j][NR_MODES['OFF']], snrs[j][NR_MODES['MIN']], |
| SNR_TOLERANCE) |
| assert (snrs[j][NR_MODES['OFF']] < snrs[j][NR_MODES['MIN']] + |
| SNR_TOLERANCE), e_msg |
| |
| # Verify MINIMAL is not better than HQ |
| e_msg = '%s MIN: %.3f, HQ: %.3f, TOL: %.3f' % ( |
| COLORS[j], snrs[j][NR_MODES['MIN']], snrs[j][NR_MODES['HQ']], |
| SNR_TOLERANCE) |
| assert (snrs[j][NR_MODES['MIN']] < snrs[j][NR_MODES['HQ']] + |
| SNR_TOLERANCE), e_msg |
| |
| if camera_properties_utils.noise_reduction_mode(props, NR_MODES['ZSL']): |
| # Verify ZSL is close to MINIMAL |
| e_msg = '%s ZSL: %.3f, MIN: %.3f, TOL: %.3f' % ( |
| COLORS[j], snrs[j][NR_MODES['ZSL']], snrs[j][NR_MODES['MIN']], |
| SNR_TOLERANCE) |
| assert np.isclose(snrs[j][NR_MODES['ZSL']], snrs[j][NR_MODES['MIN']], |
| atol=SNR_TOLERANCE), e_msg |
| elif camera_properties_utils.noise_reduction_mode(props, NR_MODES['ZSL']): |
| # Verify ZSL is close to OFF |
| e_msg = '%s OFF: %.3f, ZSL: %.3f, TOL: %.3f' % ( |
| COLORS[j], snrs[j][NR_MODES['OFF']], snrs[j][NR_MODES['ZSL']], |
| SNR_TOLERANCE) |
| assert np.isclose(snrs[j][NR_MODES['ZSL']], snrs[j][NR_MODES['OFF']], |
| atol=SNR_TOLERANCE), e_msg |
| |
| if __name__ == '__main__': |
| test_runner.main() |
| |