• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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