1# Copyright 2015 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 RAW12 and YUV images are similar.""" 15 16 17import logging 18import os.path 19from mobly import test_runner 20 21import its_base_test 22import camera_properties_utils 23import capture_request_utils 24import image_processing_utils 25import its_session_utils 26import target_exposure_utils 27 28MAX_IMG_SIZE = (1920, 1080) 29NAME = os.path.splitext(os.path.basename(__file__))[0] 30PATCH_H = 0.1 # center 10% 31PATCH_W = 0.1 32PATCH_X = 0.5 - PATCH_W/2 33PATCH_Y = 0.5 - PATCH_H/2 34THRESHOLD_MAX_RMS_DIFF = 0.035 35 36 37class YuvPlusRaw12Test(its_base_test.ItsBaseTest): 38 """Test capturing a single frame as both RAW12 and YUV outputs.""" 39 40 def test_yuv_plus_raw12(self): 41 logging.debug('Starting %s', NAME) 42 with its_session_utils.ItsSession( 43 device_id=self.dut.serial, 44 camera_id=self.camera_id, 45 hidden_physical_id=self.hidden_physical_id) as cam: 46 props = cam.get_camera_properties() 47 props = cam.override_with_hidden_physical_camera_props(props) 48 log_path = self.log_path 49 50 # check SKIP conditions 51 camera_properties_utils.skip_unless( 52 camera_properties_utils.compute_target_exposure(props) and 53 camera_properties_utils.raw12(props) and 54 camera_properties_utils.per_frame_control(props) and 55 not camera_properties_utils.mono_camera(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 # Use a manual request with a linear tonemap so that the YUV and RAW 62 # look the same (once converted by the image_processing_utils module). 63 e, s = target_exposure_utils.get_target_exposure_combos( 64 log_path, cam)['midExposureTime'] 65 req = capture_request_utils.manual_capture_request( 66 s, e, 0.0, True, props) 67 68 mode = req['android.shading.mode'] 69 logging.debug('shading mode: %d', mode) 70 71 max_raw12_size = capture_request_utils.get_available_output_sizes( 72 'raw12', props)[0] 73 w, h = capture_request_utils.get_available_output_sizes( 74 'yuv', props, MAX_IMG_SIZE, max_raw12_size)[0] 75 out_surfaces = [{'format': 'raw12'}, 76 {'format': 'yuv', 'width': w, 'height': h}] 77 cap_raw, cap_yuv = cam.do_capture(req, out_surfaces) 78 79 img = image_processing_utils.convert_capture_to_rgb_image(cap_yuv) 80 image_processing_utils.write_image(img, '%s_shading=%d_yuv.jpg' % ( 81 os.path.join(log_path, NAME), mode), True) 82 patch = image_processing_utils.get_image_patch( 83 img, PATCH_X, PATCH_Y, PATCH_W, PATCH_H) 84 rgb_means_yuv = image_processing_utils.compute_image_means(patch) 85 86 # RAW shots are 1/2 x 1/2 smaller after conversion to RGB, but patch 87 # cropping is relative. 88 img = image_processing_utils.convert_capture_to_rgb_image( 89 cap_raw, props=props) 90 image_processing_utils.write_image(img, '%s_shading=%d_raw12.jpg' % ( 91 os.path.join(log_path, NAME), mode), True) 92 patch = image_processing_utils.get_image_patch( 93 img, PATCH_X, PATCH_Y, PATCH_W, PATCH_H) 94 rgb_means_raw = image_processing_utils.compute_image_means(patch) 95 96 rms_diff = image_processing_utils.compute_image_rms_difference( 97 rgb_means_yuv, rgb_means_raw) 98 msg = f'RMS diff: {rms_diff:.4f}' 99 logging.debug('%s', msg) 100 if rms_diff >= THRESHOLD_MAX_RMS_DIFF: 101 raise AssertionError(msg + f', spec: {THRESHOLD_MAX_RMS_DIFF}') 102 103if __name__ == '__main__': 104 test_runner.main() 105 106