xref: /aosp_15_r20/cts/apps/CameraITS/utils/preview_processing_utils.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"""Utility functions for verifying preview stabilization.
15*b7c941bbSAndroid Build Coastguard Worker"""
16*b7c941bbSAndroid Build Coastguard Worker
17*b7c941bbSAndroid Build Coastguard Workerimport cv2
18*b7c941bbSAndroid Build Coastguard Workerimport logging
19*b7c941bbSAndroid Build Coastguard Workerimport os
20*b7c941bbSAndroid Build Coastguard Workerimport threading
21*b7c941bbSAndroid Build Coastguard Workerimport time
22*b7c941bbSAndroid Build Coastguard Worker
23*b7c941bbSAndroid Build Coastguard Workerimport numpy as np
24*b7c941bbSAndroid Build Coastguard Worker
25*b7c941bbSAndroid Build Coastguard Workerimport its_session_utils
26*b7c941bbSAndroid Build Coastguard Workerimport sensor_fusion_utils
27*b7c941bbSAndroid Build Coastguard Workerimport video_processing_utils
28*b7c941bbSAndroid Build Coastguard Worker
29*b7c941bbSAndroid Build Coastguard Worker_AREA_720P_VIDEO = 1280 * 720
30*b7c941bbSAndroid Build Coastguard Worker_ASPECT_RATIO_16_9 = 16/9  # determine if preview fmt > 16:9
31*b7c941bbSAndroid Build Coastguard Worker_ASPECT_TOL = 0.01
32*b7c941bbSAndroid Build Coastguard Worker_GREEN_TOL = 200  # 200 out of 255 Green value in RGB
33*b7c941bbSAndroid Build Coastguard Worker_GREEN_PERCENT = 95
34*b7c941bbSAndroid Build Coastguard Worker_HIGH_RES_SIZE = '3840x2160'  # Resolution for 4K quality
35*b7c941bbSAndroid Build Coastguard Worker_IMG_FORMAT = 'png'
36*b7c941bbSAndroid Build Coastguard Worker_MIN_PHONE_MOVEMENT_ANGLE = 5  # degrees
37*b7c941bbSAndroid Build Coastguard Worker_NATURAL_ORIENTATION_PORTRAIT = (90, 270)  # orientation in "normal position"
38*b7c941bbSAndroid Build Coastguard Worker_NUM_ROTATIONS = 24
39*b7c941bbSAndroid Build Coastguard Worker_PREVIEW_DURATION = 400  # milliseconds
40*b7c941bbSAndroid Build Coastguard Worker_PREVIEW_MAX_TESTED_AREA = 1920 * 1440
41*b7c941bbSAndroid Build Coastguard Worker_PREVIEW_MIN_TESTED_AREA = 320 * 240
42*b7c941bbSAndroid Build Coastguard Worker_PREVIEW_STABILIZATION_FACTOR = 0.7  # 70% of gyro movement allowed
43*b7c941bbSAndroid Build Coastguard Worker_RED_BLUE_TOL = 20  # 20 out of 255 Red or Blue value in RGB
44*b7c941bbSAndroid Build Coastguard Worker_SKIP_INITIAL_FRAMES = 15
45*b7c941bbSAndroid Build Coastguard Worker_START_FRAME = 30  # give 3A some frames to warm up
46*b7c941bbSAndroid Build Coastguard Worker_VIDEO_DELAY_TIME = 5.5  # seconds
47*b7c941bbSAndroid Build Coastguard Worker_VIDEO_DURATION = 5.5  # seconds
48*b7c941bbSAndroid Build Coastguard Worker
49*b7c941bbSAndroid Build Coastguard Worker
50*b7c941bbSAndroid Build Coastguard Workerdef get_720p_or_above_size(supported_preview_sizes):
51*b7c941bbSAndroid Build Coastguard Worker  """Returns the smallest size above or equal to 720p in preview and video.
52*b7c941bbSAndroid Build Coastguard Worker
53*b7c941bbSAndroid Build Coastguard Worker  If the largest preview size is under 720P, returns the largest value.
54*b7c941bbSAndroid Build Coastguard Worker
55*b7c941bbSAndroid Build Coastguard Worker  Args:
56*b7c941bbSAndroid Build Coastguard Worker    supported_preview_sizes: list; preview sizes.
57*b7c941bbSAndroid Build Coastguard Worker      e.g. ['1920x960', '1600x1200', '1920x1080']
58*b7c941bbSAndroid Build Coastguard Worker  Returns:
59*b7c941bbSAndroid Build Coastguard Worker    smallest size >= 720p video format
60*b7c941bbSAndroid Build Coastguard Worker  """
61*b7c941bbSAndroid Build Coastguard Worker
62*b7c941bbSAndroid Build Coastguard Worker  size_to_area = lambda s: int(s.split('x')[0])*int(s.split('x')[1])
63*b7c941bbSAndroid Build Coastguard Worker  smallest_area = float('inf')
64*b7c941bbSAndroid Build Coastguard Worker  smallest_720p_or_above_size = ''
65*b7c941bbSAndroid Build Coastguard Worker  largest_supported_preview_size = ''
66*b7c941bbSAndroid Build Coastguard Worker  largest_area = 0
67*b7c941bbSAndroid Build Coastguard Worker  for size in supported_preview_sizes:
68*b7c941bbSAndroid Build Coastguard Worker    area = size_to_area(size)
69*b7c941bbSAndroid Build Coastguard Worker    if smallest_area > area >= _AREA_720P_VIDEO:
70*b7c941bbSAndroid Build Coastguard Worker      smallest_area = area
71*b7c941bbSAndroid Build Coastguard Worker      smallest_720p_or_above_size = size
72*b7c941bbSAndroid Build Coastguard Worker    else:
73*b7c941bbSAndroid Build Coastguard Worker      if area > largest_area:
74*b7c941bbSAndroid Build Coastguard Worker        largest_area = area
75*b7c941bbSAndroid Build Coastguard Worker        largest_supported_preview_size = size
76*b7c941bbSAndroid Build Coastguard Worker
77*b7c941bbSAndroid Build Coastguard Worker  if largest_area > _AREA_720P_VIDEO:
78*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Smallest 720p or above size: %s',
79*b7c941bbSAndroid Build Coastguard Worker                  smallest_720p_or_above_size)
80*b7c941bbSAndroid Build Coastguard Worker    return smallest_720p_or_above_size
81*b7c941bbSAndroid Build Coastguard Worker  else:
82*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Largest supported preview size: %s',
83*b7c941bbSAndroid Build Coastguard Worker                  largest_supported_preview_size)
84*b7c941bbSAndroid Build Coastguard Worker    return largest_supported_preview_size
85*b7c941bbSAndroid Build Coastguard Worker
86*b7c941bbSAndroid Build Coastguard Worker
87*b7c941bbSAndroid Build Coastguard Workerdef collect_data(cam, tablet_device, preview_size, stabilize, rot_rig,
88*b7c941bbSAndroid Build Coastguard Worker                 zoom_ratio=None, fps_range=None, hlg10=False, ois=False):
89*b7c941bbSAndroid Build Coastguard Worker  """Capture a new set of data from the device.
90*b7c941bbSAndroid Build Coastguard Worker
91*b7c941bbSAndroid Build Coastguard Worker  Captures camera preview frames while the user is moving the device in
92*b7c941bbSAndroid Build Coastguard Worker  the prescribed manner.
93*b7c941bbSAndroid Build Coastguard Worker
94*b7c941bbSAndroid Build Coastguard Worker  Args:
95*b7c941bbSAndroid Build Coastguard Worker    cam: camera object.
96*b7c941bbSAndroid Build Coastguard Worker    tablet_device: boolean; based on config file.
97*b7c941bbSAndroid Build Coastguard Worker    preview_size: str; preview stream resolution. ex. '1920x1080'
98*b7c941bbSAndroid Build Coastguard Worker    stabilize: boolean; whether preview stabilization is ON.
99*b7c941bbSAndroid Build Coastguard Worker    rot_rig: dict with 'cntl' and 'ch' defined.
100*b7c941bbSAndroid Build Coastguard Worker    zoom_ratio: float; static zoom ratio. None if default zoom.
101*b7c941bbSAndroid Build Coastguard Worker    fps_range: list; target fps range.
102*b7c941bbSAndroid Build Coastguard Worker    hlg10: boolean; whether to capture hlg10 output.
103*b7c941bbSAndroid Build Coastguard Worker    ois: boolean; whether optical image stabilization is ON.
104*b7c941bbSAndroid Build Coastguard Worker  Returns:
105*b7c941bbSAndroid Build Coastguard Worker    recording object; a dictionary containing output path, video size, etc.
106*b7c941bbSAndroid Build Coastguard Worker  """
107*b7c941bbSAndroid Build Coastguard Worker
108*b7c941bbSAndroid Build Coastguard Worker  output_surfaces = cam.preview_surface(preview_size, hlg10)
109*b7c941bbSAndroid Build Coastguard Worker  return collect_data_with_surfaces(cam, tablet_device, output_surfaces,
110*b7c941bbSAndroid Build Coastguard Worker                                    stabilize, rot_rig, zoom_ratio,
111*b7c941bbSAndroid Build Coastguard Worker                                    fps_range, ois)
112*b7c941bbSAndroid Build Coastguard Worker
113*b7c941bbSAndroid Build Coastguard Worker
114*b7c941bbSAndroid Build Coastguard Workerdef collect_data_with_surfaces(cam, tablet_device, output_surfaces,
115*b7c941bbSAndroid Build Coastguard Worker                               stabilize, rot_rig, zoom_ratio=None,
116*b7c941bbSAndroid Build Coastguard Worker                               fps_range=None, ois=False):
117*b7c941bbSAndroid Build Coastguard Worker  """Capture a new set of data from the device.
118*b7c941bbSAndroid Build Coastguard Worker
119*b7c941bbSAndroid Build Coastguard Worker  Captures camera preview frames while the user is moving the device in
120*b7c941bbSAndroid Build Coastguard Worker  the prescribed manner.
121*b7c941bbSAndroid Build Coastguard Worker
122*b7c941bbSAndroid Build Coastguard Worker  Args:
123*b7c941bbSAndroid Build Coastguard Worker    cam: camera object.
124*b7c941bbSAndroid Build Coastguard Worker    tablet_device: boolean; based on config file.
125*b7c941bbSAndroid Build Coastguard Worker    output_surfaces: list of dict; The list of output surfaces configured for
126*b7c941bbSAndroid Build Coastguard Worker      the recording. Only the first surface is used for recording; the rest are
127*b7c941bbSAndroid Build Coastguard Worker      configured, but not requested.
128*b7c941bbSAndroid Build Coastguard Worker    stabilize: boolean; whether preview stabilization is ON.
129*b7c941bbSAndroid Build Coastguard Worker    rot_rig: dict with 'cntl' and 'ch' defined.
130*b7c941bbSAndroid Build Coastguard Worker    zoom_ratio: float; static zoom ratio. None if default zoom.
131*b7c941bbSAndroid Build Coastguard Worker    fps_range: list; target fps range.
132*b7c941bbSAndroid Build Coastguard Worker    ois: boolean; whether optical image stabilization is ON.
133*b7c941bbSAndroid Build Coastguard Worker  Returns:
134*b7c941bbSAndroid Build Coastguard Worker    recording object; a dictionary containing output path, video size, etc.
135*b7c941bbSAndroid Build Coastguard Worker  """
136*b7c941bbSAndroid Build Coastguard Worker
137*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Starting sensor event collection')
138*b7c941bbSAndroid Build Coastguard Worker  serial_port = None
139*b7c941bbSAndroid Build Coastguard Worker  if rot_rig['cntl'].lower() == sensor_fusion_utils.ARDUINO_STRING.lower():
140*b7c941bbSAndroid Build Coastguard Worker    # identify port
141*b7c941bbSAndroid Build Coastguard Worker    serial_port = sensor_fusion_utils.serial_port_def(
142*b7c941bbSAndroid Build Coastguard Worker        sensor_fusion_utils.ARDUINO_STRING)
143*b7c941bbSAndroid Build Coastguard Worker    # send test cmd to Arduino until cmd returns properly
144*b7c941bbSAndroid Build Coastguard Worker    sensor_fusion_utils.establish_serial_comm(serial_port)
145*b7c941bbSAndroid Build Coastguard Worker  # Start camera vibration
146*b7c941bbSAndroid Build Coastguard Worker  if tablet_device:
147*b7c941bbSAndroid Build Coastguard Worker    servo_speed = sensor_fusion_utils.ARDUINO_SERVO_SPEED_STABILIZATION_TABLET
148*b7c941bbSAndroid Build Coastguard Worker  else:
149*b7c941bbSAndroid Build Coastguard Worker    servo_speed = sensor_fusion_utils.ARDUINO_SERVO_SPEED_STABILIZATION
150*b7c941bbSAndroid Build Coastguard Worker  p = threading.Thread(
151*b7c941bbSAndroid Build Coastguard Worker      target=sensor_fusion_utils.rotation_rig,
152*b7c941bbSAndroid Build Coastguard Worker      args=(
153*b7c941bbSAndroid Build Coastguard Worker          rot_rig['cntl'],
154*b7c941bbSAndroid Build Coastguard Worker          rot_rig['ch'],
155*b7c941bbSAndroid Build Coastguard Worker          _NUM_ROTATIONS,
156*b7c941bbSAndroid Build Coastguard Worker          sensor_fusion_utils.ARDUINO_ANGLES_STABILIZATION,
157*b7c941bbSAndroid Build Coastguard Worker          servo_speed,
158*b7c941bbSAndroid Build Coastguard Worker          sensor_fusion_utils.ARDUINO_MOVE_TIME_STABILIZATION,
159*b7c941bbSAndroid Build Coastguard Worker          serial_port,
160*b7c941bbSAndroid Build Coastguard Worker      ),
161*b7c941bbSAndroid Build Coastguard Worker  )
162*b7c941bbSAndroid Build Coastguard Worker  p.start()
163*b7c941bbSAndroid Build Coastguard Worker
164*b7c941bbSAndroid Build Coastguard Worker  cam.start_sensor_events()
165*b7c941bbSAndroid Build Coastguard Worker  # Allow time for rig to start moving
166*b7c941bbSAndroid Build Coastguard Worker  time.sleep(_VIDEO_DELAY_TIME)
167*b7c941bbSAndroid Build Coastguard Worker
168*b7c941bbSAndroid Build Coastguard Worker  # Record video and return recording object
169*b7c941bbSAndroid Build Coastguard Worker  min_fps = fps_range[0] if (fps_range is not None) else None
170*b7c941bbSAndroid Build Coastguard Worker  max_fps = fps_range[1] if (fps_range is not None) else None
171*b7c941bbSAndroid Build Coastguard Worker  recording_obj = cam.do_preview_recording_multiple_surfaces(
172*b7c941bbSAndroid Build Coastguard Worker      output_surfaces, _VIDEO_DURATION, stabilize, ois, zoom_ratio=zoom_ratio,
173*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_min=min_fps, ae_target_fps_max=max_fps)
174*b7c941bbSAndroid Build Coastguard Worker
175*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Recorded output path: %s', recording_obj['recordedOutputPath'])
176*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Tested quality: %s', recording_obj['quality'])
177*b7c941bbSAndroid Build Coastguard Worker
178*b7c941bbSAndroid Build Coastguard Worker  # Wait for vibration to stop
179*b7c941bbSAndroid Build Coastguard Worker  p.join()
180*b7c941bbSAndroid Build Coastguard Worker
181*b7c941bbSAndroid Build Coastguard Worker  return recording_obj
182*b7c941bbSAndroid Build Coastguard Worker
183*b7c941bbSAndroid Build Coastguard Worker
184*b7c941bbSAndroid Build Coastguard Workerdef verify_preview_stabilization(recording_obj, gyro_events,
185*b7c941bbSAndroid Build Coastguard Worker                                 test_name, log_path, facing, zoom_ratio=None):
186*b7c941bbSAndroid Build Coastguard Worker  """Verify the returned recording is properly stabilized.
187*b7c941bbSAndroid Build Coastguard Worker
188*b7c941bbSAndroid Build Coastguard Worker  Args:
189*b7c941bbSAndroid Build Coastguard Worker    recording_obj: Camcorder recording object.
190*b7c941bbSAndroid Build Coastguard Worker    gyro_events: Gyroscope events collected while recording.
191*b7c941bbSAndroid Build Coastguard Worker    test_name: Name of the test.
192*b7c941bbSAndroid Build Coastguard Worker    log_path: Path for the log file.
193*b7c941bbSAndroid Build Coastguard Worker    facing: Facing of the camera device.
194*b7c941bbSAndroid Build Coastguard Worker    zoom_ratio: Static zoom ratio. None if default zoom.
195*b7c941bbSAndroid Build Coastguard Worker
196*b7c941bbSAndroid Build Coastguard Worker  Returns:
197*b7c941bbSAndroid Build Coastguard Worker    A dictionary containing the maximum gyro angle, the maximum camera angle,
198*b7c941bbSAndroid Build Coastguard Worker    and a failure message if the recorded video isn't properly stablilized.
199*b7c941bbSAndroid Build Coastguard Worker  """
200*b7c941bbSAndroid Build Coastguard Worker
201*b7c941bbSAndroid Build Coastguard Worker  file_name = recording_obj['recordedOutputPath'].split('/')[-1]
202*b7c941bbSAndroid Build Coastguard Worker  logging.debug('recorded file name: %s', file_name)
203*b7c941bbSAndroid Build Coastguard Worker  video_size = recording_obj['videoSize']
204*b7c941bbSAndroid Build Coastguard Worker  logging.debug('video size: %s', video_size)
205*b7c941bbSAndroid Build Coastguard Worker
206*b7c941bbSAndroid Build Coastguard Worker  # Get all frames from the video
207*b7c941bbSAndroid Build Coastguard Worker  file_list = video_processing_utils.extract_all_frames_from_video(
208*b7c941bbSAndroid Build Coastguard Worker      log_path, file_name, _IMG_FORMAT
209*b7c941bbSAndroid Build Coastguard Worker  )
210*b7c941bbSAndroid Build Coastguard Worker  frames = []
211*b7c941bbSAndroid Build Coastguard Worker
212*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Number of frames %d', len(file_list))
213*b7c941bbSAndroid Build Coastguard Worker  for file in file_list:
214*b7c941bbSAndroid Build Coastguard Worker    img_bgr = cv2.imread(os.path.join(log_path, file))
215*b7c941bbSAndroid Build Coastguard Worker    img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
216*b7c941bbSAndroid Build Coastguard Worker    frames.append(img_rgb / 255)
217*b7c941bbSAndroid Build Coastguard Worker  frame_h, frame_w, _ = frames[0].shape
218*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Frame size %d x %d', frame_w, frame_h)
219*b7c941bbSAndroid Build Coastguard Worker
220*b7c941bbSAndroid Build Coastguard Worker  # Extract camera rotations
221*b7c941bbSAndroid Build Coastguard Worker  if zoom_ratio:
222*b7c941bbSAndroid Build Coastguard Worker    zoom_ratio_suffix = f'{zoom_ratio:.1f}'
223*b7c941bbSAndroid Build Coastguard Worker  else:
224*b7c941bbSAndroid Build Coastguard Worker    zoom_ratio_suffix = '1'
225*b7c941bbSAndroid Build Coastguard Worker  file_name_stem = (
226*b7c941bbSAndroid Build Coastguard Worker      f'{os.path.join(log_path, test_name)}_{video_size}_{zoom_ratio_suffix}x')
227*b7c941bbSAndroid Build Coastguard Worker  cam_rots = sensor_fusion_utils.get_cam_rotations(
228*b7c941bbSAndroid Build Coastguard Worker      frames[_START_FRAME:],
229*b7c941bbSAndroid Build Coastguard Worker      facing,
230*b7c941bbSAndroid Build Coastguard Worker      frame_h,
231*b7c941bbSAndroid Build Coastguard Worker      file_name_stem,
232*b7c941bbSAndroid Build Coastguard Worker      _START_FRAME,
233*b7c941bbSAndroid Build Coastguard Worker      stabilized_video=True
234*b7c941bbSAndroid Build Coastguard Worker  )
235*b7c941bbSAndroid Build Coastguard Worker  sensor_fusion_utils.plot_camera_rotations(cam_rots, _START_FRAME,
236*b7c941bbSAndroid Build Coastguard Worker                                            video_size, file_name_stem)
237*b7c941bbSAndroid Build Coastguard Worker  max_camera_angle = sensor_fusion_utils.calc_max_rotation_angle(
238*b7c941bbSAndroid Build Coastguard Worker      cam_rots, 'Camera')
239*b7c941bbSAndroid Build Coastguard Worker
240*b7c941bbSAndroid Build Coastguard Worker  # Extract gyro rotations
241*b7c941bbSAndroid Build Coastguard Worker  sensor_fusion_utils.plot_gyro_events(
242*b7c941bbSAndroid Build Coastguard Worker      gyro_events, f'{test_name}_{video_size}_{zoom_ratio_suffix}x',
243*b7c941bbSAndroid Build Coastguard Worker      log_path)
244*b7c941bbSAndroid Build Coastguard Worker  gyro_rots = sensor_fusion_utils.conv_acceleration_to_movement(
245*b7c941bbSAndroid Build Coastguard Worker      gyro_events, _VIDEO_DELAY_TIME)
246*b7c941bbSAndroid Build Coastguard Worker  max_gyro_angle = sensor_fusion_utils.calc_max_rotation_angle(
247*b7c941bbSAndroid Build Coastguard Worker      gyro_rots, 'Gyro')
248*b7c941bbSAndroid Build Coastguard Worker  logging.debug(
249*b7c941bbSAndroid Build Coastguard Worker      'Max deflection (degrees) %s: video: %.3f, gyro: %.3f ratio: %.4f',
250*b7c941bbSAndroid Build Coastguard Worker      video_size, max_camera_angle, max_gyro_angle,
251*b7c941bbSAndroid Build Coastguard Worker      max_camera_angle / max_gyro_angle)
252*b7c941bbSAndroid Build Coastguard Worker
253*b7c941bbSAndroid Build Coastguard Worker  # Assert phone is moved enough during test
254*b7c941bbSAndroid Build Coastguard Worker  if max_gyro_angle < _MIN_PHONE_MOVEMENT_ANGLE:
255*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError(
256*b7c941bbSAndroid Build Coastguard Worker        f'Phone not moved enough! Movement: {max_gyro_angle}, '
257*b7c941bbSAndroid Build Coastguard Worker        f'THRESH: {_MIN_PHONE_MOVEMENT_ANGLE} degrees')
258*b7c941bbSAndroid Build Coastguard Worker
259*b7c941bbSAndroid Build Coastguard Worker  w_x_h = video_size.split('x')
260*b7c941bbSAndroid Build Coastguard Worker  if int(w_x_h[0])/int(w_x_h[1]) > _ASPECT_RATIO_16_9:
261*b7c941bbSAndroid Build Coastguard Worker    preview_stabilization_factor = _PREVIEW_STABILIZATION_FACTOR * 1.1
262*b7c941bbSAndroid Build Coastguard Worker  else:
263*b7c941bbSAndroid Build Coastguard Worker    preview_stabilization_factor = _PREVIEW_STABILIZATION_FACTOR
264*b7c941bbSAndroid Build Coastguard Worker
265*b7c941bbSAndroid Build Coastguard Worker  failure_msg = None
266*b7c941bbSAndroid Build Coastguard Worker  if max_camera_angle >= max_gyro_angle * preview_stabilization_factor:
267*b7c941bbSAndroid Build Coastguard Worker    failure_msg = (
268*b7c941bbSAndroid Build Coastguard Worker        f'{video_size} preview not stabilized enough! '
269*b7c941bbSAndroid Build Coastguard Worker        f'Max preview angle:  {max_camera_angle:.3f}, '
270*b7c941bbSAndroid Build Coastguard Worker        f'Max gyro angle: {max_gyro_angle:.3f}, '
271*b7c941bbSAndroid Build Coastguard Worker        f'ratio: {max_camera_angle/max_gyro_angle:.3f} '
272*b7c941bbSAndroid Build Coastguard Worker        f'THRESH: {preview_stabilization_factor}.')
273*b7c941bbSAndroid Build Coastguard Worker  # Delete saved frames if the format is a PASS
274*b7c941bbSAndroid Build Coastguard Worker  else:
275*b7c941bbSAndroid Build Coastguard Worker    for file in file_list:
276*b7c941bbSAndroid Build Coastguard Worker      try:
277*b7c941bbSAndroid Build Coastguard Worker        os.remove(os.path.join(log_path, file))
278*b7c941bbSAndroid Build Coastguard Worker      except FileNotFoundError:
279*b7c941bbSAndroid Build Coastguard Worker        logging.debug('File Not Found: %s', str(file))
280*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Format %s passes, frame images removed', video_size)
281*b7c941bbSAndroid Build Coastguard Worker
282*b7c941bbSAndroid Build Coastguard Worker  return {'gyro': max_gyro_angle, 'cam': max_camera_angle,
283*b7c941bbSAndroid Build Coastguard Worker          'failure': failure_msg}
284*b7c941bbSAndroid Build Coastguard Worker
285*b7c941bbSAndroid Build Coastguard Worker
286*b7c941bbSAndroid Build Coastguard Workerdef collect_preview_data_with_zoom(cam, preview_size, zoom_start,
287*b7c941bbSAndroid Build Coastguard Worker                                   zoom_end, step_size, recording_duration_ms,
288*b7c941bbSAndroid Build Coastguard Worker                                   padded_frames=False):
289*b7c941bbSAndroid Build Coastguard Worker  """Captures a preview video from the device.
290*b7c941bbSAndroid Build Coastguard Worker
291*b7c941bbSAndroid Build Coastguard Worker  Captures camera preview frames from the passed device.
292*b7c941bbSAndroid Build Coastguard Worker
293*b7c941bbSAndroid Build Coastguard Worker  Args:
294*b7c941bbSAndroid Build Coastguard Worker    cam: camera object.
295*b7c941bbSAndroid Build Coastguard Worker    preview_size: str; preview resolution. ex. '1920x1080'.
296*b7c941bbSAndroid Build Coastguard Worker    zoom_start: (float) is the starting zoom ratio during recording.
297*b7c941bbSAndroid Build Coastguard Worker    zoom_end: (float) is the ending zoom ratio during recording.
298*b7c941bbSAndroid Build Coastguard Worker    step_size: (float) is the step for zoom ratio during recording.
299*b7c941bbSAndroid Build Coastguard Worker    recording_duration_ms: preview recording duration in ms.
300*b7c941bbSAndroid Build Coastguard Worker    padded_frames: boolean; Whether to add additional frames at the beginning
301*b7c941bbSAndroid Build Coastguard Worker      and end of recording to workaround issue with MediaRecorder.
302*b7c941bbSAndroid Build Coastguard Worker
303*b7c941bbSAndroid Build Coastguard Worker  Returns:
304*b7c941bbSAndroid Build Coastguard Worker    recording object as described by cam.do_preview_recording_with_dynamic_zoom.
305*b7c941bbSAndroid Build Coastguard Worker  """
306*b7c941bbSAndroid Build Coastguard Worker  recording_obj = cam.do_preview_recording_with_dynamic_zoom(
307*b7c941bbSAndroid Build Coastguard Worker      preview_size,
308*b7c941bbSAndroid Build Coastguard Worker      stabilize=False,
309*b7c941bbSAndroid Build Coastguard Worker      sweep_zoom=(zoom_start, zoom_end, step_size, recording_duration_ms),
310*b7c941bbSAndroid Build Coastguard Worker      padded_frames=padded_frames
311*b7c941bbSAndroid Build Coastguard Worker  )
312*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Recorded output path: %s', recording_obj['recordedOutputPath'])
313*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Tested quality: %s', recording_obj['quality'])
314*b7c941bbSAndroid Build Coastguard Worker  return recording_obj
315*b7c941bbSAndroid Build Coastguard Worker
316*b7c941bbSAndroid Build Coastguard Worker
317*b7c941bbSAndroid Build Coastguard Workerdef is_aspect_ratio_match(size_str, target_ratio):
318*b7c941bbSAndroid Build Coastguard Worker  """Checks if a resolution string matches the target aspect ratio."""
319*b7c941bbSAndroid Build Coastguard Worker  width, height = map(int, size_str.split('x'))
320*b7c941bbSAndroid Build Coastguard Worker  return abs(width / height - target_ratio) < _ASPECT_TOL
321*b7c941bbSAndroid Build Coastguard Worker
322*b7c941bbSAndroid Build Coastguard Worker
323*b7c941bbSAndroid Build Coastguard Workerdef get_max_preview_test_size(cam, camera_id, aspect_ratio=None,
324*b7c941bbSAndroid Build Coastguard Worker                              max_tested_area=_PREVIEW_MAX_TESTED_AREA):
325*b7c941bbSAndroid Build Coastguard Worker  """Finds the max preview size to be tested.
326*b7c941bbSAndroid Build Coastguard Worker
327*b7c941bbSAndroid Build Coastguard Worker  If the device supports the _HIGH_RES_SIZE preview size then
328*b7c941bbSAndroid Build Coastguard Worker  it uses that for testing, otherwise uses the max supported
329*b7c941bbSAndroid Build Coastguard Worker  preview size capped at max_tested_area.
330*b7c941bbSAndroid Build Coastguard Worker
331*b7c941bbSAndroid Build Coastguard Worker  Args:
332*b7c941bbSAndroid Build Coastguard Worker    cam: camera object
333*b7c941bbSAndroid Build Coastguard Worker    camera_id: str; camera device id under test
334*b7c941bbSAndroid Build Coastguard Worker    aspect_ratio: preferred aspect_ratio For example: '4/3'
335*b7c941bbSAndroid Build Coastguard Worker    max_tested_area: area of max preview resolution
336*b7c941bbSAndroid Build Coastguard Worker
337*b7c941bbSAndroid Build Coastguard Worker  Returns:
338*b7c941bbSAndroid Build Coastguard Worker    preview_test_size: str; wxh resolution of the size to be tested
339*b7c941bbSAndroid Build Coastguard Worker  """
340*b7c941bbSAndroid Build Coastguard Worker  resolution_to_area = lambda s: int(s.split('x')[0])*int(s.split('x')[1])
341*b7c941bbSAndroid Build Coastguard Worker  supported_preview_sizes = cam.get_all_supported_preview_sizes(
342*b7c941bbSAndroid Build Coastguard Worker      camera_id, filter_recordable=True)
343*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Resolutions supported by preview and MediaRecorder: %s',
344*b7c941bbSAndroid Build Coastguard Worker                supported_preview_sizes)
345*b7c941bbSAndroid Build Coastguard Worker
346*b7c941bbSAndroid Build Coastguard Worker  if aspect_ratio is None:
347*b7c941bbSAndroid Build Coastguard Worker    supported_preview_sizes = [size for size in supported_preview_sizes
348*b7c941bbSAndroid Build Coastguard Worker                               if resolution_to_area(size)
349*b7c941bbSAndroid Build Coastguard Worker                               >= video_processing_utils.LOWEST_RES_TESTED_AREA]
350*b7c941bbSAndroid Build Coastguard Worker  else:
351*b7c941bbSAndroid Build Coastguard Worker    supported_preview_sizes = [size for size in supported_preview_sizes
352*b7c941bbSAndroid Build Coastguard Worker                               if resolution_to_area(size)
353*b7c941bbSAndroid Build Coastguard Worker                               >= video_processing_utils.LOWEST_RES_TESTED_AREA
354*b7c941bbSAndroid Build Coastguard Worker                               and is_aspect_ratio_match(size, aspect_ratio)]
355*b7c941bbSAndroid Build Coastguard Worker
356*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Supported preview resolutions: %s', supported_preview_sizes)
357*b7c941bbSAndroid Build Coastguard Worker
358*b7c941bbSAndroid Build Coastguard Worker  if _HIGH_RES_SIZE in supported_preview_sizes:
359*b7c941bbSAndroid Build Coastguard Worker    preview_test_size = _HIGH_RES_SIZE
360*b7c941bbSAndroid Build Coastguard Worker  else:
361*b7c941bbSAndroid Build Coastguard Worker    capped_supported_preview_sizes = [
362*b7c941bbSAndroid Build Coastguard Worker        size
363*b7c941bbSAndroid Build Coastguard Worker        for size in supported_preview_sizes
364*b7c941bbSAndroid Build Coastguard Worker        if (
365*b7c941bbSAndroid Build Coastguard Worker            resolution_to_area(size) <= max_tested_area
366*b7c941bbSAndroid Build Coastguard Worker            and resolution_to_area(size) >= _PREVIEW_MIN_TESTED_AREA
367*b7c941bbSAndroid Build Coastguard Worker        )
368*b7c941bbSAndroid Build Coastguard Worker    ]
369*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Capped preview resolutions: %s',
370*b7c941bbSAndroid Build Coastguard Worker                  capped_supported_preview_sizes)
371*b7c941bbSAndroid Build Coastguard Worker    preview_test_size = capped_supported_preview_sizes[-1]
372*b7c941bbSAndroid Build Coastguard Worker
373*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Selected preview resolution: %s', preview_test_size)
374*b7c941bbSAndroid Build Coastguard Worker
375*b7c941bbSAndroid Build Coastguard Worker  return preview_test_size
376*b7c941bbSAndroid Build Coastguard Worker
377*b7c941bbSAndroid Build Coastguard Worker
378*b7c941bbSAndroid Build Coastguard Workerdef get_max_extension_preview_test_size(cam, camera_id, extension):
379*b7c941bbSAndroid Build Coastguard Worker  """Finds the max preview size for an extension to be tested.
380*b7c941bbSAndroid Build Coastguard Worker
381*b7c941bbSAndroid Build Coastguard Worker  If the device supports the _HIGH_RES_SIZE preview size then
382*b7c941bbSAndroid Build Coastguard Worker  it uses that for testing, otherwise uses the max supported
383*b7c941bbSAndroid Build Coastguard Worker  preview size capped at _PREVIEW_MAX_TESTED_AREA.
384*b7c941bbSAndroid Build Coastguard Worker
385*b7c941bbSAndroid Build Coastguard Worker  Args:
386*b7c941bbSAndroid Build Coastguard Worker    cam: camera object
387*b7c941bbSAndroid Build Coastguard Worker    camera_id: str; camera device id under test
388*b7c941bbSAndroid Build Coastguard Worker    extension: int; camera extension mode under test
389*b7c941bbSAndroid Build Coastguard Worker
390*b7c941bbSAndroid Build Coastguard Worker  Returns:
391*b7c941bbSAndroid Build Coastguard Worker    preview_test_size: str; wxh resolution of the size to be tested
392*b7c941bbSAndroid Build Coastguard Worker  """
393*b7c941bbSAndroid Build Coastguard Worker  resolution_to_area = lambda s: int(s.split('x')[0])*int(s.split('x')[1])
394*b7c941bbSAndroid Build Coastguard Worker  supported_preview_sizes = (
395*b7c941bbSAndroid Build Coastguard Worker      cam.get_supported_extension_preview_sizes(camera_id, extension))
396*b7c941bbSAndroid Build Coastguard Worker  supported_preview_sizes = [size for size in supported_preview_sizes
397*b7c941bbSAndroid Build Coastguard Worker                             if resolution_to_area(size)
398*b7c941bbSAndroid Build Coastguard Worker                             >= video_processing_utils.LOWEST_RES_TESTED_AREA]
399*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Supported preview resolutions for extension %d: %s',
400*b7c941bbSAndroid Build Coastguard Worker                extension, supported_preview_sizes)
401*b7c941bbSAndroid Build Coastguard Worker
402*b7c941bbSAndroid Build Coastguard Worker  if _HIGH_RES_SIZE in supported_preview_sizes:
403*b7c941bbSAndroid Build Coastguard Worker    preview_test_size = _HIGH_RES_SIZE
404*b7c941bbSAndroid Build Coastguard Worker  else:
405*b7c941bbSAndroid Build Coastguard Worker    capped_supported_preview_sizes = [
406*b7c941bbSAndroid Build Coastguard Worker        size
407*b7c941bbSAndroid Build Coastguard Worker        for size in supported_preview_sizes
408*b7c941bbSAndroid Build Coastguard Worker        if (
409*b7c941bbSAndroid Build Coastguard Worker            resolution_to_area(size) <= _PREVIEW_MAX_TESTED_AREA
410*b7c941bbSAndroid Build Coastguard Worker            and resolution_to_area(size) >= _PREVIEW_MIN_TESTED_AREA
411*b7c941bbSAndroid Build Coastguard Worker        )
412*b7c941bbSAndroid Build Coastguard Worker    ]
413*b7c941bbSAndroid Build Coastguard Worker    preview_test_size = capped_supported_preview_sizes[-1]
414*b7c941bbSAndroid Build Coastguard Worker
415*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Selected preview resolution: %s', preview_test_size)
416*b7c941bbSAndroid Build Coastguard Worker
417*b7c941bbSAndroid Build Coastguard Worker  return preview_test_size
418*b7c941bbSAndroid Build Coastguard Worker
419*b7c941bbSAndroid Build Coastguard Worker
420*b7c941bbSAndroid Build Coastguard Workerdef mirror_preview_image_by_sensor_orientation(
421*b7c941bbSAndroid Build Coastguard Worker    sensor_orientation, input_preview_img):
422*b7c941bbSAndroid Build Coastguard Worker  """If testing front camera, mirror preview image to match camera capture.
423*b7c941bbSAndroid Build Coastguard Worker
424*b7c941bbSAndroid Build Coastguard Worker  Preview are flipped on device's natural orientation, so for sensor
425*b7c941bbSAndroid Build Coastguard Worker  orientation 90 or 270, it is up or down. Sensor orientation 0 or 180
426*b7c941bbSAndroid Build Coastguard Worker  is left or right.
427*b7c941bbSAndroid Build Coastguard Worker
428*b7c941bbSAndroid Build Coastguard Worker  Args:
429*b7c941bbSAndroid Build Coastguard Worker    sensor_orientation: integer; display orientation in natural position.
430*b7c941bbSAndroid Build Coastguard Worker    input_preview_img: numpy array; image extracted from preview recording.
431*b7c941bbSAndroid Build Coastguard Worker  Returns:
432*b7c941bbSAndroid Build Coastguard Worker    output_preview_img: numpy array; flipped according to natural orientation.
433*b7c941bbSAndroid Build Coastguard Worker  """
434*b7c941bbSAndroid Build Coastguard Worker  if sensor_orientation in _NATURAL_ORIENTATION_PORTRAIT:
435*b7c941bbSAndroid Build Coastguard Worker    # Opencv expects a numpy array but np.flip generates a 'view' which
436*b7c941bbSAndroid Build Coastguard Worker    # doesn't work with opencv. ndarray.copy forces copy instead of view.
437*b7c941bbSAndroid Build Coastguard Worker    output_preview_img = np.ndarray.copy(np.flipud(input_preview_img))
438*b7c941bbSAndroid Build Coastguard Worker    logging.debug(
439*b7c941bbSAndroid Build Coastguard Worker        'Found sensor orientation %d, flipping up down', sensor_orientation)
440*b7c941bbSAndroid Build Coastguard Worker  else:
441*b7c941bbSAndroid Build Coastguard Worker    output_preview_img = np.ndarray.copy(np.fliplr(input_preview_img))
442*b7c941bbSAndroid Build Coastguard Worker    logging.debug(
443*b7c941bbSAndroid Build Coastguard Worker        'Found sensor orientation %d, flipping left right', sensor_orientation)
444*b7c941bbSAndroid Build Coastguard Worker
445*b7c941bbSAndroid Build Coastguard Worker  return output_preview_img
446*b7c941bbSAndroid Build Coastguard Worker
447*b7c941bbSAndroid Build Coastguard Worker
448*b7c941bbSAndroid Build Coastguard Workerdef is_image_green(image_path):
449*b7c941bbSAndroid Build Coastguard Worker  """Checks if an image is mostly green.
450*b7c941bbSAndroid Build Coastguard Worker
451*b7c941bbSAndroid Build Coastguard Worker  Checks if an image is mostly green by ensuring green is dominant
452*b7c941bbSAndroid Build Coastguard Worker  and red/blue values are low.
453*b7c941bbSAndroid Build Coastguard Worker
454*b7c941bbSAndroid Build Coastguard Worker  Args:
455*b7c941bbSAndroid Build Coastguard Worker    image_path: str; The path to the image file.
456*b7c941bbSAndroid Build Coastguard Worker
457*b7c941bbSAndroid Build Coastguard Worker  Returns:
458*b7c941bbSAndroid Build Coastguard Worker    bool: True if mostly green, False otherwise.
459*b7c941bbSAndroid Build Coastguard Worker  """
460*b7c941bbSAndroid Build Coastguard Worker
461*b7c941bbSAndroid Build Coastguard Worker  image = cv2.imread(image_path)
462*b7c941bbSAndroid Build Coastguard Worker
463*b7c941bbSAndroid Build Coastguard Worker  green_pixels = ((image[:, :, 1] > _GREEN_TOL) &
464*b7c941bbSAndroid Build Coastguard Worker                  (image[:, :, 0] < _RED_BLUE_TOL) &
465*b7c941bbSAndroid Build Coastguard Worker                  (image[:, :, 2] < _RED_BLUE_TOL)).sum()
466*b7c941bbSAndroid Build Coastguard Worker
467*b7c941bbSAndroid Build Coastguard Worker  green_percentage = (green_pixels / (image.shape[0] * image.shape[1])) * 100
468*b7c941bbSAndroid Build Coastguard Worker
469*b7c941bbSAndroid Build Coastguard Worker  if green_percentage >= _GREEN_PERCENT:
470*b7c941bbSAndroid Build Coastguard Worker    return True
471*b7c941bbSAndroid Build Coastguard Worker  else:
472*b7c941bbSAndroid Build Coastguard Worker    return False
473*b7c941bbSAndroid Build Coastguard Worker
474*b7c941bbSAndroid Build Coastguard Worker
475*b7c941bbSAndroid Build Coastguard Workerdef preview_over_zoom_range(dut, cam, preview_size, z_min, z_max, z_step_size,
476*b7c941bbSAndroid Build Coastguard Worker                            log_path):
477*b7c941bbSAndroid Build Coastguard Worker  """Captures a preview video from the device over zoom range.
478*b7c941bbSAndroid Build Coastguard Worker
479*b7c941bbSAndroid Build Coastguard Worker  Captures camera preview frames at various zoom level in zoom range.
480*b7c941bbSAndroid Build Coastguard Worker
481*b7c941bbSAndroid Build Coastguard Worker  Args:
482*b7c941bbSAndroid Build Coastguard Worker    dut: device under test
483*b7c941bbSAndroid Build Coastguard Worker    cam: camera object
484*b7c941bbSAndroid Build Coastguard Worker    preview_size: str; preview resolution. ex. '1920x1080'
485*b7c941bbSAndroid Build Coastguard Worker    z_min: minimum zoom for preview capture
486*b7c941bbSAndroid Build Coastguard Worker    z_max: maximum zoom for preview capture
487*b7c941bbSAndroid Build Coastguard Worker    z_step_size: zoom step size from min to max
488*b7c941bbSAndroid Build Coastguard Worker    log_path: str; path for video file directory
489*b7c941bbSAndroid Build Coastguard Worker
490*b7c941bbSAndroid Build Coastguard Worker  Returns:
491*b7c941bbSAndroid Build Coastguard Worker    capture_results: total capture results of each frame
492*b7c941bbSAndroid Build Coastguard Worker    file_list: file name for each frame
493*b7c941bbSAndroid Build Coastguard Worker  """
494*b7c941bbSAndroid Build Coastguard Worker  logging.debug('z_min : %.2f, z_max = %.2f, z_step_size = %.2f',
495*b7c941bbSAndroid Build Coastguard Worker                z_min, z_max, z_step_size)
496*b7c941bbSAndroid Build Coastguard Worker
497*b7c941bbSAndroid Build Coastguard Worker  # Converge 3A
498*b7c941bbSAndroid Build Coastguard Worker  cam.do_3a()
499*b7c941bbSAndroid Build Coastguard Worker
500*b7c941bbSAndroid Build Coastguard Worker  # recording preview
501*b7c941bbSAndroid Build Coastguard Worker  # TODO: b/350821827 - encode time stamps in camera frames instead of
502*b7c941bbSAndroid Build Coastguard Worker  #                     padded green frams
503*b7c941bbSAndroid Build Coastguard Worker  # MediaRecorder on some devices drop last few frames. To solve this issue
504*b7c941bbSAndroid Build Coastguard Worker  # add green frames as padding at the end of recorded camera frames. This way
505*b7c941bbSAndroid Build Coastguard Worker  # green buffer frames would be droped by MediaRecorder instead of actual
506*b7c941bbSAndroid Build Coastguard Worker  # frames. Later these green padded frames are removed.
507*b7c941bbSAndroid Build Coastguard Worker  preview_rec_obj = collect_preview_data_with_zoom(
508*b7c941bbSAndroid Build Coastguard Worker      cam, preview_size, z_min, z_max, z_step_size,
509*b7c941bbSAndroid Build Coastguard Worker      _PREVIEW_DURATION, padded_frames=True)
510*b7c941bbSAndroid Build Coastguard Worker
511*b7c941bbSAndroid Build Coastguard Worker  preview_file_name = its_session_utils.pull_file_from_dut(
512*b7c941bbSAndroid Build Coastguard Worker      dut, preview_rec_obj['recordedOutputPath'], log_path)
513*b7c941bbSAndroid Build Coastguard Worker
514*b7c941bbSAndroid Build Coastguard Worker  logging.debug('recorded video size : %s',
515*b7c941bbSAndroid Build Coastguard Worker                str(preview_rec_obj['videoSize']))
516*b7c941bbSAndroid Build Coastguard Worker
517*b7c941bbSAndroid Build Coastguard Worker  # Extract frames as png from mp4 preview recording
518*b7c941bbSAndroid Build Coastguard Worker  file_list = video_processing_utils.extract_all_frames_from_video(
519*b7c941bbSAndroid Build Coastguard Worker      log_path, preview_file_name, _IMG_FORMAT
520*b7c941bbSAndroid Build Coastguard Worker  )
521*b7c941bbSAndroid Build Coastguard Worker
522*b7c941bbSAndroid Build Coastguard Worker  first_camera_frame_idx = 0
523*b7c941bbSAndroid Build Coastguard Worker  last_camera_frame_idx = len(file_list)
524*b7c941bbSAndroid Build Coastguard Worker
525*b7c941bbSAndroid Build Coastguard Worker  # Find index of the first-non green frame
526*b7c941bbSAndroid Build Coastguard Worker  for (idx, file_name) in enumerate(file_list):
527*b7c941bbSAndroid Build Coastguard Worker    file_path = os.path.join(log_path, file_name)
528*b7c941bbSAndroid Build Coastguard Worker    if is_image_green(file_path):
529*b7c941bbSAndroid Build Coastguard Worker      its_session_utils.remove_file(file_path)
530*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Removed green file %s', file_name)
531*b7c941bbSAndroid Build Coastguard Worker    else:
532*b7c941bbSAndroid Build Coastguard Worker      logging.debug('First camera frame: %s', file_name)
533*b7c941bbSAndroid Build Coastguard Worker      first_camera_frame_idx = idx
534*b7c941bbSAndroid Build Coastguard Worker      break
535*b7c941bbSAndroid Build Coastguard Worker
536*b7c941bbSAndroid Build Coastguard Worker  # Find index of last non-green frame
537*b7c941bbSAndroid Build Coastguard Worker  for (idx, file_name) in reversed(list(enumerate(file_list))):
538*b7c941bbSAndroid Build Coastguard Worker    file_path = os.path.join(log_path, file_name)
539*b7c941bbSAndroid Build Coastguard Worker    if is_image_green(file_path):
540*b7c941bbSAndroid Build Coastguard Worker      its_session_utils.remove_file(file_path)
541*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Removed green file %s', file_name)
542*b7c941bbSAndroid Build Coastguard Worker    else:
543*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Last camera frame: %s', file_name)
544*b7c941bbSAndroid Build Coastguard Worker      last_camera_frame_idx = idx
545*b7c941bbSAndroid Build Coastguard Worker      break
546*b7c941bbSAndroid Build Coastguard Worker
547*b7c941bbSAndroid Build Coastguard Worker  logging.debug('start idx = %d -- end idx = %d', first_camera_frame_idx,
548*b7c941bbSAndroid Build Coastguard Worker                last_camera_frame_idx)
549*b7c941bbSAndroid Build Coastguard Worker  file_list = file_list[first_camera_frame_idx:last_camera_frame_idx+1]
550*b7c941bbSAndroid Build Coastguard Worker
551*b7c941bbSAndroid Build Coastguard Worker  # Raise error if capture result and frame count doesn't match
552*b7c941bbSAndroid Build Coastguard Worker  capture_results = preview_rec_obj['captureMetadata']
553*b7c941bbSAndroid Build Coastguard Worker  extra_capture_result_count = len(capture_results) - len(file_list)
554*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Number of frames %d', len(file_list))
555*b7c941bbSAndroid Build Coastguard Worker  if extra_capture_result_count != 0:
556*b7c941bbSAndroid Build Coastguard Worker    its_session_utils.remove_frame_files(log_path)
557*b7c941bbSAndroid Build Coastguard Worker    e_msg = (f'Number of CaptureResult ({len(capture_results)}) '
558*b7c941bbSAndroid Build Coastguard Worker             f'vs number of Frames ({len(file_list)}) count mismatch.'
559*b7c941bbSAndroid Build Coastguard Worker             ' Retry Test.')
560*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError(e_msg)
561*b7c941bbSAndroid Build Coastguard Worker
562*b7c941bbSAndroid Build Coastguard Worker  # skip frames which might not have 3A converged
563*b7c941bbSAndroid Build Coastguard Worker  capture_results = capture_results[_SKIP_INITIAL_FRAMES:]
564*b7c941bbSAndroid Build Coastguard Worker  skipped_files = file_list[:_SKIP_INITIAL_FRAMES]
565*b7c941bbSAndroid Build Coastguard Worker  file_list = file_list[_SKIP_INITIAL_FRAMES:]
566*b7c941bbSAndroid Build Coastguard Worker
567*b7c941bbSAndroid Build Coastguard Worker  # delete skipped files
568*b7c941bbSAndroid Build Coastguard Worker  for file_name in skipped_files:
569*b7c941bbSAndroid Build Coastguard Worker    its_session_utils.remove_file(os.path.join(log_path, file_name))
570*b7c941bbSAndroid Build Coastguard Worker
571*b7c941bbSAndroid Build Coastguard Worker  return capture_results, file_list
572