• 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 and YUV images are similar."""
15
16
17import logging
18import os.path
19from mobly import test_runner
20
21import its_base_test
22import camera_properties_utils
23import capture_request_utils
24import image_processing_utils
25import its_session_utils
26
27_MAX_IMG_SIZE = (1920, 1080)
28_NAME = os.path.splitext(os.path.basename(__file__))[0]
29_PATCH_H = 0.1  # center 10%
30_PATCH_W = 0.1
31_PATCH_X = 0.5 - _PATCH_W/2
32_PATCH_Y = 0.5 - _PATCH_H/2
33_THRESHOLD_MAX_RMS_DIFF = 0.035
34
35
36def convert_and_compare_captures(cap_raw, cap_yuv, props,
37                                 log_path_with_name, raw_fmt):
38  """Helper function to convert and compare RAW and YUV captures.
39
40  Args:
41   cap_raw: capture request object with RAW/RAW10/RAW12 format specified
42   cap_yuv: capture capture request object with YUV format specified
43   props: object from its_session_utils.get_camera_properties().
44   log_path_with_name: logging path where artifacts should be stored.
45   raw_fmt: string 'raw', 'raw10', or 'raw12' to include in file name
46  Returns:
47    string "PASS" if test passed, else message for AssertionError.
48  """
49  shading_mode = cap_raw['metadata']['android.shading.mode']
50  control_af_mode = cap_raw['metadata']['android.control.afMode']
51  focus_distance = cap_raw['metadata']['android.lens.focusDistance']
52  logging.debug('%s capture AF mode: %s', raw_fmt, control_af_mode)
53  logging.debug('%s capture focus distance: %s', raw_fmt, focus_distance)
54  logging.debug('%s capture shading mode: %d', raw_fmt, shading_mode)
55
56  img = image_processing_utils.convert_capture_to_rgb_image(cap_yuv)
57  image_processing_utils.write_image(
58      img, f'{log_path_with_name}_shading={shading_mode}_yuv.jpg', True)
59  patch = image_processing_utils.get_image_patch(
60      img, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H)
61  rgb_means_yuv = image_processing_utils.compute_image_means(patch)
62
63  # RAW shots are 1/2 x 1/2 smaller after conversion to RGB, but patch
64  # cropping is relative.
65  img = image_processing_utils.convert_capture_to_rgb_image(
66      cap_raw, props=props)
67  image_processing_utils.write_image(
68      img, f'{log_path_with_name}_shading={shading_mode}_{raw_fmt}.jpg', True)
69  patch = image_processing_utils.get_image_patch(
70      img, _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H)
71  rgb_means_raw = image_processing_utils.compute_image_means(patch)
72
73  rms_diff = image_processing_utils.compute_image_rms_difference_1d(
74      rgb_means_yuv, rgb_means_raw)
75  msg = f'{raw_fmt} diff: {rms_diff:.4f}'
76  logging.debug('%s', msg)
77  if rms_diff >= _THRESHOLD_MAX_RMS_DIFF:
78    return f'{msg}, spec: {_THRESHOLD_MAX_RMS_DIFF}'
79  else:
80    return 'PASS'
81
82
83class YuvPlusRawTest(its_base_test.ItsBaseTest):
84  """Test capturing a single frame as both YUV and various RAW formats.
85
86  Tests RAW, RAW10 and RAW12 as available.
87  """
88
89  def test_yuv_plus_raw(self):
90    failure_messages = []
91    logging.debug('Starting %s', _NAME)
92    with its_session_utils.ItsSession(
93        device_id=self.dut.serial,
94        camera_id=self.camera_id,
95        hidden_physical_id=self.hidden_physical_id) as cam:
96      props = cam.get_camera_properties()
97      props = cam.override_with_hidden_physical_camera_props(props)
98      log_path = os.path.join(self.log_path, _NAME)
99
100      # check SKIP conditions
101      camera_properties_utils.skip_unless(
102          camera_properties_utils.raw_output(props) and
103          camera_properties_utils.linear_tonemap(props) and
104          not camera_properties_utils.mono_camera(props))
105
106      # Load chart for scene
107      its_session_utils.load_scene(
108          cam, props, self.scene, self.tablet,
109          its_session_utils.CHART_DISTANCE_NO_SCALING)
110
111      # determine compatible RAW formats
112      raw_formats = []
113      if camera_properties_utils.raw16(props):
114        raw_formats.append('raw')
115      else:
116        logging.debug('Skipping test_yuv_plus_raw')
117      if camera_properties_utils.raw10(props):
118        raw_formats.append('raw10')
119      else:
120        logging.debug('Skipping test_yuv_plus_raw10')
121      if camera_properties_utils.raw12(props):
122        raw_formats.append('raw12')
123      else:
124        logging.debug('Skipping test_yuv_plus_raw12')
125
126      for raw_fmt in raw_formats:
127        req = capture_request_utils.auto_capture_request(
128            linear_tonemap=True, props=props, do_af=False)
129        max_raw_size = capture_request_utils.get_available_output_sizes(
130            raw_fmt, props)[0]
131        if capture_request_utils.is_common_aspect_ratio(max_raw_size):
132          w, h = capture_request_utils.get_available_output_sizes(
133              'yuv', props, _MAX_IMG_SIZE, max_raw_size)[0]
134        else:
135          w, h = capture_request_utils.get_available_output_sizes(
136              'yuv', props, max_size=_MAX_IMG_SIZE)[0]
137        out_surfaces = [{'format': raw_fmt},
138                        {'format': 'yuv', 'width': w, 'height': h}]
139        cam.do_3a(do_af=False)
140        cap_raw, cap_yuv = cam.do_capture(req, out_surfaces)
141        msg = convert_and_compare_captures(cap_raw, cap_yuv, props,
142                                           log_path, raw_fmt)
143        if msg != 'PASS':
144          failure_messages.append(msg)
145
146      if failure_messages:
147        raise AssertionError('\n'.join(failure_messages))
148
149
150if __name__ == '__main__':
151  test_runner.main()
152
153