1# Copyright 2014 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 android.scaler.cropRegion param works.""" 15 16 17import logging 18import os.path 19 20from mobly import test_runner 21import numpy as np 22 23import its_base_test 24import camera_properties_utils 25import capture_request_utils 26import image_processing_utils 27import its_session_utils 28import target_exposure_utils 29 30# 5 regions specified in normalized (x, y, w, h) coords. 31CROP_REGIONS = [(0.0, 0.0, 0.5, 0.5), # top-left 32 (0.5, 0.0, 0.5, 0.5), # top-right 33 (0.0, 0.5, 0.5, 0.5), # bottom-left 34 (0.5, 0.5, 0.5, 0.5), # bottom-right 35 (0.25, 0.25, 0.5, 0.5)] # center 36MIN_DIGITAL_ZOOM_THRESH = 2 37NAME = os.path.splitext(os.path.basename(__file__))[0] 38 39 40class CropRegionsTest(its_base_test.ItsBaseTest): 41 """Test that crop regions works.""" 42 43 def test_crop_regions(self): 44 logging.debug('Starting %s', NAME) 45 with its_session_utils.ItsSession( 46 device_id=self.dut.serial, 47 camera_id=self.camera_id, 48 hidden_physical_id=self.hidden_physical_id) as cam: 49 props = cam.get_camera_properties() 50 props = cam.override_with_hidden_physical_camera_props(props) 51 log_path = self.log_path 52 53 # check SKIP conditions 54 camera_properties_utils.skip_unless( 55 camera_properties_utils.compute_target_exposure(props) and 56 camera_properties_utils.freeform_crop(props) and 57 camera_properties_utils.per_frame_control(props)) 58 59 # Load chart for scene 60 its_session_utils.load_scene( 61 cam, props, self.scene, self.tablet, self.chart_distance) 62 63 a = props['android.sensor.info.activeArraySize'] 64 ax, ay = a['left'], a['top'] 65 aw, ah = a['right'] - a['left'], a['bottom'] - a['top'] 66 e, s = target_exposure_utils.get_target_exposure_combos( 67 props, cam)['minSensitivity'] 68 logging.debug('Active sensor region (%d,%d %dx%d)', ax, ay, aw, ah) 69 70 # Uses a 2x digital zoom. 71 max_digital_zoom = capture_request_utils.get_max_digital_zoom(props) 72 e_msg = 'Max digital zoom: %d, THRESH: %d' % (max_digital_zoom, 73 MIN_DIGITAL_ZOOM_THRESH) 74 assert max_digital_zoom >= MIN_DIGITAL_ZOOM_THRESH, e_msg 75 76 # Capture a full frame. 77 req = capture_request_utils.manual_capture_request(s, e) 78 cap_full = cam.do_capture(req) 79 img_full = image_processing_utils.convert_capture_to_rgb_image(cap_full) 80 wfull, hfull = cap_full['width'], cap_full['height'] 81 image_processing_utils.write_image(img_full, '%s_full_%dx%d.jpg' % ( 82 os.path.join(log_path, NAME), wfull, hfull)) 83 84 # Capture a burst of crop region frames. 85 # Note that each region is 1/2x1/2 of the full frame, and is digitally 86 # zoomed into the full size output image, so must be downscaled (below) 87 # by 2x when compared to a tile of the full image. 88 reqs = [] 89 for x, y, w, h in CROP_REGIONS: 90 req = capture_request_utils.manual_capture_request(s, e) 91 req['android.scaler.cropRegion'] = { 92 'top': int(ah * y), 93 'left': int(aw * x), 94 'right': int(aw * (x + w)), 95 'bottom': int(ah * (y + h))} 96 reqs.append(req) 97 caps_regions = cam.do_capture(reqs) 98 match_failed = False 99 for i, cap in enumerate(caps_regions): 100 a = cap['metadata']['android.scaler.cropRegion'] 101 ax, ay = a['left'], a['top'] 102 aw, ah = a['right'] - a['left'], a['bottom'] - a['top'] 103 104 # Match this crop image against each of the five regions of 105 # the full image, to find the best match (which should be 106 # the region that corresponds to this crop image). 107 img_crop = image_processing_utils.convert_capture_to_rgb_image(cap) 108 img_crop = image_processing_utils.downscale_image(img_crop, 2) 109 image_processing_utils.write_image(img_crop, '%s_crop%d.jpg' % ( 110 os.path.join(log_path, NAME), i)) 111 min_diff = None 112 min_diff_region = None 113 for j, (x, y, w, h) in enumerate(CROP_REGIONS): 114 tile_full = image_processing_utils.get_image_patch( 115 img_full, x, y, w, h) 116 wtest = min(tile_full.shape[1], aw) 117 htest = min(tile_full.shape[0], ah) 118 tile_full = tile_full[0:htest:, 0:wtest:, ::] 119 tile_crop = img_crop[0:htest:, 0:wtest:, ::] 120 image_processing_utils.write_image( 121 tile_full, '%s_fullregion%d.jpg' % ( 122 os.path.join(log_path, NAME), j)) 123 diff = np.fabs(tile_full - tile_crop).mean() 124 if min_diff is None or diff < min_diff: 125 min_diff = diff 126 min_diff_region = j 127 if i != min_diff_region: 128 match_failed = True 129 logging.debug('Crop image %d (%d,%d %dx%d) best match with region %d', 130 i, ax, ay, aw, ah, min_diff_region) 131 132 assert not match_failed 133 134if __name__ == '__main__': 135 test_runner.main() 136 137