1# Copyright 2023 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 landscape to portrait override works as intended."""
15
16
17import logging
18import os
19
20from mobly import test_runner
21
22import its_base_test
23import camera_properties_utils
24import its_session_utils
25import opencv_processing_utils
26
27
28_NAME = os.path.splitext(os.path.basename(__file__))[0]
29_OPT_VAL_THRESHOLD = 0.5
30_ENABLED = 'enabled'
31_DISABLED = 'disabled'
32
33
34def _get_true_sensor_orientation(cam, camera_id):
35  """Returns a camera's properties and its true sensor orientation.
36
37  Args:
38    cam: The camera object
39    camera_id: The id of the camera
40
41  Returns:
42    Pair of camera properties and sensor orientation.
43  """
44  props = cam.get_camera_properties_by_id(camera_id,
45                                          override_to_portrait=False)
46  props = cam.override_with_hidden_physical_camera_props(props)
47  sensor_orientation = camera_properties_utils.sensor_orientation(props)
48  return (props, sensor_orientation)
49
50
51def _create_image_folder(log_path, suffix):
52  """Creates a new folder for storing debugging images and returns its path.
53
54  Args:
55    log_path: The root log path for LandscapeToPortraitTest
56    suffix: The suffix string for the image folder ("enabled" or "disabled")
57
58  Returns:
59    The newly created log path string, a subfolder of LandscapeToPortraitTest
60  """
61  image_path = os.path.join(log_path, f'test_landscape_to_portrait_{suffix}')
62  os.mkdir(image_path)
63  return image_path
64
65
66def _verify_opt_val(chart):
67  """Checks that the opt_val for template matching is sufficient to pass.
68
69  Args:
70    chart: The chart for the test
71  """
72  if chart.opt_val is None:
73    raise AssertionError('Unable to find a template match!')
74  elif chart.opt_val < _OPT_VAL_THRESHOLD:
75    raise AssertionError('Poor template match (opt val: '
76                         f'{chart.opt_val:.3f} thresh: {_OPT_VAL_THRESHOLD}'
77                         '). Check jpeg output.')
78
79
80class LandscapeToPortraitTest(its_base_test.ItsBaseTest):
81  """Test the landscape to portrait override.
82
83  Note the test does not require a specific target but does perform
84  both automatic and manual captures so it requires a fixed scene
85  where 3A can converge.
86  """
87
88  def test_landscape_to_portrait_enabled(self):
89    logging.debug('Starting %s: %s', _NAME, _ENABLED)
90    with its_session_utils.ItsSession(
91        device_id=self.dut.serial,
92        camera_id=self.camera_id,
93        hidden_physical_id=self.hidden_physical_id,
94        override_to_portrait=True) as cam:
95
96      # Read the properties with overrideToPortrait off to find the real sensor
97      # orientation
98      props, sensor_orientation = _get_true_sensor_orientation(
99          cam, self.camera_id)
100
101      # check SKIP conditions
102      camera_properties_utils.skip_unless(
103          cam.is_landscape_to_portrait_enabled() and
104          (sensor_orientation == 0 or sensor_orientation == 180))
105
106      # Load chart for scene
107      its_session_utils.load_scene(
108          cam, props, self.scene, self.tablet, self.chart_distance)
109
110      # Initialize chart class and locate chart in scene
111      # Make a separate log dir for the chart images, otherwise they'll get
112      # overwritten
113      image_path = _create_image_folder(self.log_path, _ENABLED)
114      chart = opencv_processing_utils.Chart(
115          cam, props, image_path, distance=self.chart_distance, rotation=90)
116
117      _verify_opt_val(chart)
118
119  def test_landscape_to_portrait_disabled(self):
120    logging.debug('Starting %s: %s', _NAME, _DISABLED)
121    with its_session_utils.ItsSession(
122        device_id=self.dut.serial,
123        camera_id=self.camera_id,
124        hidden_physical_id=self.hidden_physical_id) as cam:
125
126      # Read the properties with overrideToPortrait off to find the real sensor
127      # orientation
128      props, sensor_orientation = _get_true_sensor_orientation(
129          cam, self.camera_id)
130
131      # check SKIP conditions.
132      camera_properties_utils.skip_unless(
133          sensor_orientation == 0 or sensor_orientation == 180)
134
135      # Load chart for scene
136      its_session_utils.load_scene(
137          cam, props, self.scene, self.tablet, self.chart_distance)
138
139      # Initialize chart class and locate chart in scene
140      # Make a separate log dir for the chart images, otherwise they'll get
141      # overwritten
142      image_path = _create_image_folder(self.log_path, _DISABLED)
143      chart = opencv_processing_utils.Chart(
144          cam, props, image_path, distance=self.chart_distance)
145
146      _verify_opt_val(chart)
147
148if __name__ == '__main__':
149  test_runner.main()
150