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