• 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_ATOL = 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    pylab.title(f'{_NAME}')
142    for j in range(_NUM_COLORS):
143      pylab.plot(_NR_MODES_LIST, snrs[j], '-'+'rgb'[j]+'o')
144    pylab.xlabel(f'{str(_NR_MODES)[1:-1]}')  # strip '{' '}' off string
145    pylab.ylabel('SNR (dB)')
146    pylab.xticks(_NR_MODES_LIST)
147    matplotlib.pyplot.savefig(f'{name_with_log_path}_plot_SNRs.png')
148
149    if nr_modes_reported != _NR_MODES_LIST:
150      raise AssertionError(f'{nr_modes_reported} != {_NR_MODES_LIST}')
151
152    for j in range(_NUM_COLORS):
153      # Higher SNR is better
154      # Verify OFF is not better than FAST
155      if (snrs[j][_NR_MODES['OFF']] >= snrs[j][_NR_MODES['FAST']] +
156          _SNR_ATOL):
157        raise AssertionError(
158            f"{_COLORS[j]} OFF: {snrs[j][_NR_MODES['OFF']]:.3f}, "
159            f"FAST: {snrs[j][_NR_MODES['FAST']]:.3f}, ATOL: {_SNR_ATOL}")
160
161      # Verify FAST is not better than HQ
162      if (snrs[j][_NR_MODES['FAST']] >= snrs[j][_NR_MODES['HQ']] +
163          _SNR_ATOL):
164        raise AssertionError(
165            f"{_COLORS[j]} FAST: {snrs[j][_NR_MODES['FAST']]:.3f}, "
166            f"HQ: {snrs[j][_NR_MODES['HQ']]:.3f}, ATOL: {_SNR_ATOL}")
167
168      # Verify HQ is better than OFF
169      if snrs[j][_NR_MODES['HQ']] <= snrs[j][_NR_MODES['OFF']]:
170        raise AssertionError(
171            f"{_COLORS[j]} OFF: {snrs[j][_NR_MODES['OFF']]:.3f}, "
172            f"HQ: {snrs[j][_NR_MODES['HQ']]:.3f}")
173
174      if camera_properties_utils.noise_reduction_mode(props, _NR_MODES['MIN']):
175        # Verify OFF is not better than MINIMAL
176        if not(snrs[j][_NR_MODES['OFF']] < snrs[j][_NR_MODES['MIN']] +
177               _SNR_ATOL):
178          raise AssertionError(
179              f"{_COLORS[j]} OFF: {snrs[j][_NR_MODES['OFF']]:.3f}, "
180              f"MIN: {snrs[j][_NR_MODES['MIN']]:.3f}, ATOL: {_SNR_ATOL}")
181
182        # Verify MINIMAL is not better than HQ
183        if not (snrs[j][_NR_MODES['MIN']] < snrs[j][_NR_MODES['HQ']] +
184                _SNR_ATOL):
185          raise AssertionError(
186              f"{_COLORS[j]} MIN: {snrs[j][_NR_MODES['MIN']]:.3f}, "
187              f"HQ: {snrs[j][_NR_MODES['HQ']]:.3f}, ATOL: {_SNR_ATOL}")
188
189        # Verify ZSL is close to MINIMAL
190        if camera_properties_utils.noise_reduction_mode(
191            props, _NR_MODES['ZSL']):
192          if not math.isclose(snrs[j][_NR_MODES['ZSL']],
193                              snrs[j][_NR_MODES['MIN']],
194                              abs_tol=_SNR_ATOL):
195            raise AssertionError(
196                f"{_COLORS[j]} ZSL: {snrs[j][_NR_MODES['ZSL']]:.3f}, "
197                f"MIN: {snrs[j][_NR_MODES['MIN']]:.3f}, ATOL: {_SNR_ATOL}")
198
199      elif camera_properties_utils.noise_reduction_mode(
200          props, _NR_MODES['ZSL']):
201        # Verify ZSL is close to OFF
202        if not math.isclose(
203            snrs[j][_NR_MODES['ZSL']], snrs[j][_NR_MODES['OFF']],
204            abs_tol=_SNR_ATOL):
205          raise AssertionError(
206              f"{_COLORS[j]} OFF: {snrs[j][_NR_MODES['OFF']]:.3f}, "
207              f"ZSL: {snrs[j][_NR_MODES['ZSL']]:.3f}, ATOL: {_SNR_ATOL}")
208
209
210if __name__ == '__main__':
211  test_runner.main()
212