xref: /aosp_15_r20/cts/apps/CameraITS/tools/check_alignment.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2024 The Android Open Source Project
2*b7c941bbSAndroid Build Coastguard Worker#
3*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*b7c941bbSAndroid Build Coastguard Worker#
7*b7c941bbSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*b7c941bbSAndroid Build Coastguard Worker#
9*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*b7c941bbSAndroid Build Coastguard Worker# limitations under the License.
14*b7c941bbSAndroid Build Coastguard Worker"""Tool to check camera alignment to test chart."""
15*b7c941bbSAndroid Build Coastguard Worker
16*b7c941bbSAndroid Build Coastguard Workerimport logging
17*b7c941bbSAndroid Build Coastguard Workerimport os.path
18*b7c941bbSAndroid Build Coastguard Worker
19*b7c941bbSAndroid Build Coastguard Workerimport capture_request_utils
20*b7c941bbSAndroid Build Coastguard Workerimport image_processing_utils
21*b7c941bbSAndroid Build Coastguard Workerimport its_base_test
22*b7c941bbSAndroid Build Coastguard Workerimport its_session_utils
23*b7c941bbSAndroid Build Coastguard Workerfrom mobly import test_runner
24*b7c941bbSAndroid Build Coastguard Workerimport opencv_processing_utils
25*b7c941bbSAndroid Build Coastguard Worker
26*b7c941bbSAndroid Build Coastguard Worker_CIRCLE_COLOR = 0  # [0: black, 255: white]
27*b7c941bbSAndroid Build Coastguard Worker_CIRCLE_MIN_AREA = 0.01  # 1% of image size
28*b7c941bbSAndroid Build Coastguard Worker_FMT = 'JPEG'
29*b7c941bbSAndroid Build Coastguard Worker_NAME = os.path.basename(__file__).split('.')[0]
30*b7c941bbSAndroid Build Coastguard Worker_SCENE = 'scene4'  # Using scene with circle
31*b7c941bbSAndroid Build Coastguard Worker
32*b7c941bbSAndroid Build Coastguard Worker
33*b7c941bbSAndroid Build Coastguard Workerdef _circle_and_image_center_offset(cam, props, name_with_log_path):
34*b7c941bbSAndroid Build Coastguard Worker  """Find offset between circle center and image center.
35*b7c941bbSAndroid Build Coastguard Worker
36*b7c941bbSAndroid Build Coastguard Worker  Args:
37*b7c941bbSAndroid Build Coastguard Worker    cam: its_session_utils.ItsSession object
38*b7c941bbSAndroid Build Coastguard Worker    props: camera properties object
39*b7c941bbSAndroid Build Coastguard Worker    name_with_log_path: path to saved data
40*b7c941bbSAndroid Build Coastguard Worker
41*b7c941bbSAndroid Build Coastguard Worker  Returns:
42*b7c941bbSAndroid Build Coastguard Worker    x_offset: circle center's x-position vs. image center
43*b7c941bbSAndroid Build Coastguard Worker    y_offset: circle center's y-position vs. image center
44*b7c941bbSAndroid Build Coastguard Worker  """
45*b7c941bbSAndroid Build Coastguard Worker
46*b7c941bbSAndroid Build Coastguard Worker  # Take a single JPEG capture
47*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Using %s for reference', _FMT)
48*b7c941bbSAndroid Build Coastguard Worker  fmt = capture_request_utils.get_largest_format('jpeg', props)
49*b7c941bbSAndroid Build Coastguard Worker  req = capture_request_utils.auto_capture_request()
50*b7c941bbSAndroid Build Coastguard Worker  cap = cam.do_capture(req, fmt)
51*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Captured %s %dx%d', _FMT, cap['width'], cap['height'])
52*b7c941bbSAndroid Build Coastguard Worker  img = image_processing_utils.convert_capture_to_rgb_image(cap, props)
53*b7c941bbSAndroid Build Coastguard Worker  size = (cap['height'], cap['width'])
54*b7c941bbSAndroid Build Coastguard Worker
55*b7c941bbSAndroid Build Coastguard Worker  # Get image size
56*b7c941bbSAndroid Build Coastguard Worker  w = size[1]
57*b7c941bbSAndroid Build Coastguard Worker  h = size[0]
58*b7c941bbSAndroid Build Coastguard Worker  img_name = f'{name_with_log_path}_{_FMT}_w{w}_h{h}.png'
59*b7c941bbSAndroid Build Coastguard Worker  image_processing_utils.write_image(img, img_name, True)
60*b7c941bbSAndroid Build Coastguard Worker
61*b7c941bbSAndroid Build Coastguard Worker  # Find circle.
62*b7c941bbSAndroid Build Coastguard Worker  img *= 255  # cv2 needs images between [0,255]
63*b7c941bbSAndroid Build Coastguard Worker  circle = opencv_processing_utils.find_circle(
64*b7c941bbSAndroid Build Coastguard Worker      img, img_name, _CIRCLE_MIN_AREA, _CIRCLE_COLOR)
65*b7c941bbSAndroid Build Coastguard Worker  opencv_processing_utils.append_circle_center_to_img(circle, img, img_name)
66*b7c941bbSAndroid Build Coastguard Worker
67*b7c941bbSAndroid Build Coastguard Worker  # Determine final return values.
68*b7c941bbSAndroid Build Coastguard Worker  x_offset, y_offset = circle['x_offset'], circle['y_offset']
69*b7c941bbSAndroid Build Coastguard Worker
70*b7c941bbSAndroid Build Coastguard Worker  return x_offset, y_offset
71*b7c941bbSAndroid Build Coastguard Worker
72*b7c941bbSAndroid Build Coastguard Worker
73*b7c941bbSAndroid Build Coastguard Workerclass CheckAlignmentTest(its_base_test.ItsBaseTest):
74*b7c941bbSAndroid Build Coastguard Worker  """Create a single capture that checks for scene center vs. circle center.
75*b7c941bbSAndroid Build Coastguard Worker  """
76*b7c941bbSAndroid Build Coastguard Worker
77*b7c941bbSAndroid Build Coastguard Worker  def test_check_alignment(self):
78*b7c941bbSAndroid Build Coastguard Worker    with its_session_utils.ItsSession(
79*b7c941bbSAndroid Build Coastguard Worker        device_id=self.dut.serial,
80*b7c941bbSAndroid Build Coastguard Worker        camera_id=self.camera_id,
81*b7c941bbSAndroid Build Coastguard Worker        hidden_physical_id=self.hidden_physical_id) as cam:
82*b7c941bbSAndroid Build Coastguard Worker      props = cam.get_camera_properties()
83*b7c941bbSAndroid Build Coastguard Worker      props = cam.override_with_hidden_physical_camera_props(props)
84*b7c941bbSAndroid Build Coastguard Worker      name_with_log_path = os.path.join(self.log_path, _NAME)
85*b7c941bbSAndroid Build Coastguard Worker      logging.info('Starting %s for camera %s', _NAME, cam.get_camera_name())
86*b7c941bbSAndroid Build Coastguard Worker
87*b7c941bbSAndroid Build Coastguard Worker      # Load chart for scene
88*b7c941bbSAndroid Build Coastguard Worker      its_session_utils.copy_scenes_to_tablet(_SCENE, self.tablet.serial)
89*b7c941bbSAndroid Build Coastguard Worker      its_session_utils.load_scene(
90*b7c941bbSAndroid Build Coastguard Worker          cam, props, _SCENE, self.tablet, self.chart_distance)
91*b7c941bbSAndroid Build Coastguard Worker
92*b7c941bbSAndroid Build Coastguard Worker      # Find offset between circle center and image center
93*b7c941bbSAndroid Build Coastguard Worker      x_offset, y_offset = _circle_and_image_center_offset(
94*b7c941bbSAndroid Build Coastguard Worker          cam, props, name_with_log_path)
95*b7c941bbSAndroid Build Coastguard Worker      logging.info('Circle center position wrt to image center: %.3fx%.3f',
96*b7c941bbSAndroid Build Coastguard Worker                   x_offset, y_offset)
97*b7c941bbSAndroid Build Coastguard Worker
98*b7c941bbSAndroid Build Coastguard Workerif __name__ == '__main__':
99*b7c941bbSAndroid Build Coastguard Worker  test_runner.main()
100*b7c941bbSAndroid Build Coastguard Worker
101