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