1# Copyright 2024 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"""Tool to check camera alignment to test chart."""
15
16import logging
17import os.path
18
19import capture_request_utils
20import image_processing_utils
21import its_base_test
22import its_session_utils
23from mobly import test_runner
24import opencv_processing_utils
25
26_CIRCLE_COLOR = 0  # [0: black, 255: white]
27_CIRCLE_MIN_AREA = 0.01  # 1% of image size
28_FMT = 'JPEG'
29_NAME = os.path.basename(__file__).split('.')[0]
30_SCENE = 'scene4'  # Using scene with circle
31
32
33def _circle_and_image_center_offset(cam, props, name_with_log_path):
34  """Find offset between circle center and image center.
35
36  Args:
37    cam: its_session_utils.ItsSession object
38    props: camera properties object
39    name_with_log_path: path to saved data
40
41  Returns:
42    x_offset: circle center's x-position vs. image center
43    y_offset: circle center's y-position vs. image center
44  """
45
46  # Take a single JPEG capture
47  logging.debug('Using %s for reference', _FMT)
48  fmt = capture_request_utils.get_largest_jpeg_format(props)
49  req = capture_request_utils.auto_capture_request()
50  cap = cam.do_capture(req, fmt)
51  logging.debug('Captured %s %dx%d', _FMT, cap['width'], cap['height'])
52  img = image_processing_utils.convert_capture_to_rgb_image(cap, props)
53  size = (cap['height'], cap['width'])
54
55  # Get image size
56  w = size[1]
57  h = size[0]
58  img_name = f'{name_with_log_path}_{_FMT}_w{w}_h{h}.png'
59  image_processing_utils.write_image(img, img_name, True)
60
61  # Find circle.
62  img *= 255  # cv2 needs images between [0,255]
63  circle = opencv_processing_utils.find_circle(
64      img, img_name, _CIRCLE_MIN_AREA, _CIRCLE_COLOR)
65  opencv_processing_utils.append_circle_center_to_img(circle, img, img_name)
66
67  # Determine final return values.
68  x_offset, y_offset = circle['x_offset'], circle['y_offset']
69
70  return x_offset, y_offset
71
72
73class CheckAlignmentTest(its_base_test.ItsBaseTest):
74  """Create a single capture that checks for scene center vs. circle center.
75  """
76
77  def test_check_alignment(self):
78    with its_session_utils.ItsSession(
79        device_id=self.dut.serial,
80        camera_id=self.camera_id) as cam:
81      props = cam.get_camera_properties()
82      props = cam.override_with_hidden_physical_camera_props(props)
83      name_with_log_path = os.path.join(self.log_path, _NAME)
84      logging.info('Starting %s for camera %s', _NAME, cam.get_camera_name())
85
86      # Load chart for scene
87      its_session_utils.copy_scenes_to_tablet(_SCENE, self.tablet.serial)
88      its_session_utils.load_scene(
89          cam, props, _SCENE, self.tablet, self.chart_distance)
90
91      # Find offset between circle center and image center
92      x_offset, y_offset = _circle_and_image_center_offset(
93          cam, props, name_with_log_path)
94      logging.info('Circle center position wrt to image center: %.3fx%.3f',
95                   x_offset, y_offset)
96
97if __name__ == '__main__':
98  test_runner.main()
99
100