• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 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"""Verify zoom ratio scales circle sizes correctly."""
15
16
17import logging
18import os.path
19
20import camera_properties_utils
21import capture_request_utils
22import image_processing_utils
23import its_base_test
24import its_session_utils
25from mobly import test_runner
26import numpy as np
27import zoom_capture_utils
28
29_CIRCLE_COLOR = 0  # [0: black, 255: white]
30_CIRCLE_AR_RTOL = 0.15  # contour width vs height (aspect ratio)
31_CIRCLISH_RTOL = 0.05  # contour area vs ideal circle area pi*((w+h)/4)**2
32_JPEG_STR = 'jpg'
33_MIN_AREA_RATIO = 0.00015  # based on 2000/(4000x3000) pixels
34_MIN_CIRCLE_PTS = 25
35_MIN_FOCUS_DIST_TOL = 0.80  # allow charts a little closer than min
36_NAME = os.path.splitext(os.path.basename(__file__))[0]
37_NUM_STEPS = 10
38_TEST_FORMATS = ['yuv']  # list so can be appended for newer Android versions
39_TEST_REQUIRED_MPC = 33
40_ZOOM_MIN_THRESH = 2.0
41
42
43class ZoomTest(its_base_test.ItsBaseTest):
44  """Test the camera zoom behavior.
45  """
46
47  def test_zoom(self):
48    with its_session_utils.ItsSession(
49        device_id=self.dut.serial,
50        camera_id=self.camera_id,
51        hidden_physical_id=self.hidden_physical_id) as cam:
52      props = cam.get_camera_properties()
53      props = cam.override_with_hidden_physical_camera_props(props)
54      camera_properties_utils.skip_unless(
55          camera_properties_utils.zoom_ratio_range(props))
56
57      # Load chart for scene
58      its_session_utils.load_scene(
59          cam, props, self.scene, self.tablet, self.chart_distance)
60
61      # Determine test zoom range
62      z_range = props['android.control.zoomRatioRange']
63      debug = self.debug_mode
64      z_min, z_max = float(z_range[0]), float(z_range[1])
65      camera_properties_utils.skip_unless(z_max >= z_min * _ZOOM_MIN_THRESH)
66      z_max = min(z_max, zoom_capture_utils.ZOOM_MAX_THRESH * z_min)
67      z_list = np.arange(z_min, z_max, (z_max - z_min) / (_NUM_STEPS - 1))
68      z_list = np.append(z_list, z_max)
69      logging.debug('Testing zoom range: %s', str(z_list))
70
71      # Check media performance class
72      media_performance_class = its_session_utils.get_media_performance_class(
73          self.dut.serial)
74      if (media_performance_class >= _TEST_REQUIRED_MPC and
75          cam.is_primary_camera() and
76          cam.has_ultrawide_camera(facing=props['android.lens.facing']) and
77          int(z_min) >= 1):
78        raise AssertionError(
79            f'With primary camera {self.camera_id}, '
80            f'MPC >= {_TEST_REQUIRED_MPC}, and '
81            'an ultrawide camera facing in the same direction as the primary, '
82            'zoom_ratio minimum must be less than 1.0. '
83            f'Found media performance class {media_performance_class} '
84            f'and minimum zoom {z_min}.')
85
86      # set TOLs based on camera and test rig params
87      if camera_properties_utils.logical_multi_camera(props):
88        test_tols, size = zoom_capture_utils.get_test_tols_and_cap_size(
89            cam, props, self.chart_distance, debug)
90      else:
91        test_tols = {}
92        fls = props['android.lens.info.availableFocalLengths']
93        for fl in fls:
94          test_tols[fl] = (zoom_capture_utils.RADIUS_RTOL,
95                           zoom_capture_utils.OFFSET_RTOL)
96        yuv_size = capture_request_utils.get_largest_yuv_format(props)
97        size = [yuv_size['width'], yuv_size['height']]
98      logging.debug('capture size: %s', str(size))
99      logging.debug('test TOLs: %s', str(test_tols))
100
101      # determine vendor API level and test_formats to test
102      test_formats = _TEST_FORMATS
103      vendor_api_level = its_session_utils.get_vendor_api_level(self.dut.serial)
104      if vendor_api_level >= its_session_utils.ANDROID14_API_LEVEL:
105        test_formats.append(_JPEG_STR)
106
107      # do captures over zoom range and find circles with cv2
108      img_name_stem = f'{os.path.join(self.log_path, _NAME)}'
109      req = capture_request_utils.auto_capture_request()
110      test_failed = False
111      for fmt in test_formats:
112        logging.debug('testing %s format', fmt)
113        test_data = {}
114        for i, z in enumerate(z_list):
115          req['android.control.zoomRatio'] = z
116          cam.do_3a(zoom_ratio=z)
117          cap = cam.do_capture(
118              req, {'format': fmt, 'width': size[0], 'height': size[1]})
119
120          img = image_processing_utils.convert_capture_to_rgb_image(
121              cap, props=props)
122          img_name = f'{img_name_stem}_{fmt}_{round(z, 2)}.{_JPEG_STR}'
123          image_processing_utils.write_image(img, img_name)
124
125          # determine radius tolerance of capture
126          cap_fl = cap['metadata']['android.lens.focalLength']
127          radius_tol, offset_tol = test_tols.get(
128              cap_fl,
129              (zoom_capture_utils.RADIUS_RTOL, zoom_capture_utils.OFFSET_RTOL)
130          )
131
132          # Find the center circle in img
133          circle = zoom_capture_utils.get_center_circle(img, img_name, size, z,
134                                                        z_list[0], debug)
135          # Zoom is too large to find center circle
136          if circle is None:
137            break
138          test_data[i] = {'z': z, 'circle': circle, 'r_tol': radius_tol,
139                          'o_tol': offset_tol, 'fl': cap_fl}
140
141        if not zoom_capture_utils.verify_zoom_results(
142            test_data, size, z_max, z_min):
143          test_failed = True
144
145    if test_failed:
146      raise AssertionError(f'{_NAME} failed! Check test_log.DEBUG for errors')
147
148if __name__ == '__main__':
149  test_runner.main()
150