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 android.edge.mode works properly.""" 15 16 17import logging 18import os 19import matplotlib 20from matplotlib import pylab 21from mobly import test_runner 22import numpy as np 23 24import its_base_test 25import camera_properties_utils 26import capture_request_utils 27import image_processing_utils 28import its_session_utils 29import opencv_processing_utils 30 31_EDGE_MODES = {'OFF': 0, 'FAST': 1, 'HQ': 2} 32_NAME = os.path.splitext(os.path.basename(__file__))[0] 33_NUM_SAMPLES = 4 34_SHARPNESS_RTOL = 0.1 35 36 37def plot_results(modes, sharpness_values, name_with_log_path): 38 """Plot the results. 39 40 Args: 41 modes: integer edge mode values 42 sharpness_values: float values of sharpness 43 name_with_log_path: file name with log_path for save location 44 """ 45 pylab.figure(_NAME) 46 pylab.suptitle(_NAME) 47 pylab.title(str(_EDGE_MODES)) 48 pylab.xlabel('Edge Enhancement Mode') 49 pylab.ylabel('Image Sharpness') 50 pylab.xticks(modes) 51 pylab.plot(modes, sharpness_values, '-ro') 52 matplotlib.pyplot.savefig(f'{name_with_log_path}_plot.png') 53 54 55def do_capture_and_determine_sharpness( 56 cam, edge_mode, sensitivity, exp, fd, out_surface, chart, 57 name_with_log_path): 58 """Return sharpness of the output image and the capture result metadata. 59 60 Processes a capture request with a given edge mode, sensitivity, exposure 61 time, focus distance, output surface parameter. 62 63 Args: 64 cam: An open device session. 65 edge_mode: Edge mode for the request as defined in android.edge.mode 66 sensitivity: Sensitivity for the request as defined in 67 android.sensor.sensitivity 68 exp: Exposure time for the request as defined in 69 android.sensor.exposureTime. 70 fd: Focus distance for the request as defined in 71 android.lens.focusDistance 72 out_surface: Specifications of the output image format and size. 73 chart: object that contains chart information 74 name_with_log_path: file name with log_path to write result images 75 76 Returns: 77 Object containing reported edge mode and the sharpness of the output 78 image, keyed by the following strings: 79 edge_mode 80 sharpness 81 """ 82 83 req = capture_request_utils.manual_capture_request(sensitivity, exp) 84 req['android.lens.focusDistance'] = fd 85 req['android.edge.mode'] = edge_mode 86 87 sharpness_list = [] 88 caps = cam.do_capture([req]*_NUM_SAMPLES, [out_surface], repeat_request=req) 89 for n, cap in enumerate(caps): 90 y, _, _ = image_processing_utils.convert_capture_to_planes(cap) 91 chart.img = image_processing_utils.get_image_patch( 92 y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm) 93 if n == 0: 94 image_processing_utils.write_image( 95 chart.img, f'{name_with_log_path}_edge={edge_mode}.jpg') 96 edge_mode_res = cap['metadata']['android.edge.mode'] 97 sharpness_list.append( 98 image_processing_utils.compute_image_sharpness(chart.img)*255) 99 logging.debug('edge mode: %d, sharpness values: %s', 100 edge_mode_res, sharpness_list) 101 return {'edge_mode': edge_mode_res, 'sharpness': np.mean(sharpness_list)} 102 103 104class EdgeEnhancementTest(its_base_test.ItsBaseTest): 105 """Test that the android.edge.mode param is applied correctly. 106 107 Capture non-reprocess images for each edge mode and calculate their 108 sharpness as a baseline. 109 """ 110 111 def test_edge_enhancement(self): 112 with its_session_utils.ItsSession( 113 device_id=self.dut.serial, 114 camera_id=self.camera_id, 115 hidden_physical_id=self.hidden_physical_id) as cam: 116 props = cam.get_camera_properties() 117 props = cam.override_with_hidden_physical_camera_props(props) 118 name_with_log_path = os.path.join(self.log_path, _NAME) 119 120 # Check skip conditions 121 camera_properties_utils.skip_unless( 122 camera_properties_utils.read_3a(props) and 123 camera_properties_utils.per_frame_control(props) and 124 camera_properties_utils.edge_mode(props, 0)) 125 126 # Load chart for scene 127 its_session_utils.load_scene( 128 cam, props, self.scene, self.tablet, self.chart_distance) 129 130 # Initialize chart class and locate chart in scene 131 chart = opencv_processing_utils.Chart( 132 cam, props, self.log_path, distance=self.chart_distance) 133 134 # Define format 135 fmt = 'yuv' 136 size = capture_request_utils.get_available_output_sizes(fmt, props)[0] 137 out_surface = {'width': size[0], 'height': size[1], 'format': fmt} 138 logging.debug('%s: %dx%d', fmt, size[0], size[1]) 139 140 # Get proper sensitivity, exposure time, and focus distance. 141 mono_camera = camera_properties_utils.mono_camera(props) 142 s, e, _, _, fd = cam.do_3a(get_results=True, mono_camera=mono_camera) 143 logging.debug('iso: %d, exp: %d, fd: %.3f', s, e, fd) 144 145 # Get the sharpness for each edge mode for regular requests 146 sharpness_regular = [] 147 edge_mode_reported_regular = [] 148 for edge_mode in _EDGE_MODES.values(): 149 # Skip unavailable modes 150 if not camera_properties_utils.edge_mode(props, edge_mode): 151 edge_mode_reported_regular.append(edge_mode) 152 sharpness_regular.append(0) 153 continue 154 155 ret = do_capture_and_determine_sharpness( 156 cam, edge_mode, s, e, fd, out_surface, chart, name_with_log_path) 157 edge_mode_reported_regular.append(ret['edge_mode']) 158 sharpness_regular.append(ret['sharpness']) 159 160 logging.debug('Reported edge modes: %s', edge_mode_reported_regular) 161 logging.debug('Sharpness with EE mode [0,1,2,3]: %s', 162 str(sharpness_regular)) 163 plot_results( 164 edge_mode_reported_regular, sharpness_regular, name_with_log_path) 165 166 logging.debug('Verify HQ is sharper than OFF') 167 if (sharpness_regular[_EDGE_MODES['HQ']] <= 168 sharpness_regular[_EDGE_MODES['OFF']]): 169 raise AssertionError( 170 f"HQ: {sharpness_regular[_EDGE_MODES['HQ']]:.3f}, " 171 f"OFF: {sharpness_regular[_EDGE_MODES['OFF']]:.3f}") 172 173 logging.debug('Verify OFF is not sharper than FAST') 174 if (sharpness_regular[_EDGE_MODES['FAST']] <= 175 sharpness_regular[_EDGE_MODES['OFF']]*(1.0-_SHARPNESS_RTOL)): 176 raise AssertionError( 177 f"FAST: {sharpness_regular[_EDGE_MODES['FAST']]:.3f}, " 178 f"OFF: {sharpness_regular[_EDGE_MODES['OFF']]:.3f}, " 179 f"RTOL: {_SHARPNESS_RTOL}") 180 181 logging.debug('Verify FAST is not sharper than HQ') 182 if (sharpness_regular[_EDGE_MODES['HQ']] <= 183 sharpness_regular[_EDGE_MODES['FAST']]*(1.0-_SHARPNESS_RTOL)): 184 raise AssertionError( 185 f"HQ: {sharpness_regular[_EDGE_MODES['HQ']]:.3f}, " 186 f"FAST: {sharpness_regular[_EDGE_MODES['FAST']]:.3f}, " 187 f"RTOL: {_SHARPNESS_RTOL}") 188 189if __name__ == '__main__': 190 test_runner.main() 191