1# Copyright 2018 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.control.availableEffects that are supported.""" 15 16 17import logging 18import os.path 19from mobly import test_runner 20import numpy as np 21 22import its_base_test 23import camera_properties_utils 24import capture_request_utils 25import image_processing_utils 26import its_session_utils 27 28# android.control.availableEffects possible values 29_EFFECTS = {0: 'OFF', 30 1: 'MONO', 31 2: 'NEGATIVE', 32 3: 'SOLARIZE', 33 4: 'SEPIA', 34 5: 'POSTERIZE', 35 6: 'WHITEBOARD', 36 7: 'BLACKBOARD', 37 8: 'AQUA'} 38_MONO_UV_SPREAD_MAX = 2 # max spread for U & V channels [0:255] for mono image 39_NAME = os.path.splitext(os.path.basename(__file__))[0] 40_VGA_W, _VGA_H = 640, 480 41_YUV_MAX = 255 # normalization number for YUV images [0:1] --> [0:255] 42_YUV_UV_SPREAD_ATOL = 10 # min spread for U & V channels [0:255] for color img 43_YUV_Y_SPREAD_ATOL = 50 # min spread for Y channel [0:255] for color image 44 45 46class EffectsTest(its_base_test.ItsBaseTest): 47 """Test effects. 48 49 Test: capture frame for supported camera effects and check if generated 50 correctly. Note we only check effects OFF and MONO currently. Other effects 51 do not have standardized definitions, so they are not tested. 52 However, the test saves images for all supported effects. 53 """ 54 55 def test_effects(self): 56 logging.debug('Starting %s', _NAME) 57 with its_session_utils.ItsSession( 58 device_id=self.dut.serial, 59 camera_id=self.camera_id, 60 hidden_physical_id=self.hidden_physical_id) as cam: 61 props = cam.get_camera_properties() 62 props = cam.override_with_hidden_physical_camera_props(props) 63 mono_camera = camera_properties_utils.mono_camera(props) 64 65 # Load chart for scene. 66 its_session_utils.load_scene( 67 cam, props, self.scene, self.tablet, self.chart_distance) 68 69 # Determine available effects and run test(s) 70 effects = props['android.control.availableEffects'] 71 camera_properties_utils.skip_unless(effects != [0]) 72 cam.do_3a(mono_camera=mono_camera) 73 logging.debug('Supported effects: %s', str(effects)) 74 failed = [] 75 for effect in effects: 76 req = capture_request_utils.auto_capture_request() 77 req['android.control.effectMode'] = effect 78 fmt = {'format': 'yuv', 'width': _VGA_W, 'height': _VGA_H} 79 cap = cam.do_capture(req, fmt) 80 81 # Save image of each effect 82 img = image_processing_utils.convert_capture_to_rgb_image( 83 cap, props=props) 84 img_name = (f'{os.path.join(self.log_path,_NAME)}_' 85 f'{_EFFECTS[effect]}.jpg') 86 image_processing_utils.write_image(img, img_name) 87 88 # Simple checks 89 if effect == 0: 90 logging.debug('Checking effects OFF...') 91 y, u, v = image_processing_utils.convert_capture_to_planes( 92 cap, props) 93 y_min, y_max = np.amin(y)*_YUV_MAX, np.amax(y)*_YUV_MAX 94 e_msg = (f'Y_range: {y_min:.2f},{y_max:.2f} ' 95 f'THRESH: {_YUV_Y_SPREAD_ATOL}; ') 96 if (y_max-y_min) < _YUV_Y_SPREAD_ATOL: 97 failed.append({'effect': _EFFECTS[effect], 'error': e_msg}) 98 if not mono_camera: 99 u_min, u_max = np.amin(u) * _YUV_MAX, np.amax(u) * _YUV_MAX 100 v_min, v_max = np.amin(v) * _YUV_MAX, np.amax(v) * _YUV_MAX 101 e_msg += (f'U_range: {u_min:.2f},{u_max:.2f} ' 102 f'THRESH: {_YUV_UV_SPREAD_ATOL}; ') 103 e_msg += (f'V_range: {v_min:.2f},{v_max:.2f} ' 104 f'THRESH: {_YUV_UV_SPREAD_ATOL}') 105 if ((u_max - u_min) < _YUV_UV_SPREAD_ATOL or 106 (v_max - v_min) < _YUV_UV_SPREAD_ATOL): 107 failed.append({'effect': _EFFECTS[effect], 'error': e_msg}) 108 elif effect == 1: 109 logging.debug('Checking MONO effect...') 110 _, u, v = image_processing_utils.convert_capture_to_planes( 111 cap, props) 112 u_min, u_max = np.amin(u)*_YUV_MAX, np.amax(u)*_YUV_MAX 113 v_min, v_max = np.amin(v)*_YUV_MAX, np.amax(v)*_YUV_MAX 114 e_msg = (f'U_range: {u_min:.2f},{u_max:.2f}; ' 115 f'V_range: {v_min:.2f},{v_max:.2f}; ' 116 f'ATOL: {_MONO_UV_SPREAD_MAX}') 117 if ((u_max - u_min) > _MONO_UV_SPREAD_MAX or 118 (v_max - v_min) > _MONO_UV_SPREAD_MAX): 119 failed.append({'effect': _EFFECTS[effect], 'error': e_msg}) 120 if failed: 121 logging.debug('Failed effects:') 122 for fail in failed: 123 logging.debug(' %s: %s', fail['effect'], fail['error']) 124 raise AssertionError(f'{_NAME} failed. See test_log.DEBUG for errors.') 125 126if __name__ == '__main__': 127 test_runner.main() 128