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 29EFFECTS = {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'} 38MONO_UV_SPREAD_MAX = 2 # max spread for U & V channels [0:255] for mono image 39NAME = os.path.splitext(os.path.basename(__file__))[0] 40VGA_W, VGA_H = 640, 480 41YUV_MAX = 255 # normalization number for YUV images [0:1] --> [0:255] 42YUV_UV_SPREAD_ATOL = 10 # min spread for U & V channels [0:255] for color image 43YUV_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 = '%s_%s.jpg' % (os.path.join(self.log_path, 85 NAME), EFFECTS[effect]) 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(cap, props) 92 y_min, y_max = np.amin(y)*YUV_MAX, np.amax(y)*YUV_MAX 93 e_msg = 'Y_range: %.2f,%.2f THRESH: %d; ' % ( 94 y_min, y_max, YUV_Y_SPREAD_ATOL) 95 if (y_max-y_min) < YUV_Y_SPREAD_ATOL: 96 failed.append({'effect': EFFECTS[effect], 'error': e_msg}) 97 if not mono_camera: 98 u_min, u_max = np.amin(u) * YUV_MAX, np.amax(u) * YUV_MAX 99 v_min, v_max = np.amin(v) * YUV_MAX, np.amax(v) * YUV_MAX 100 e_msg += 'U_range: %.2f,%.2f THRESH: %d; ' % ( 101 u_min, u_max, YUV_UV_SPREAD_ATOL) 102 e_msg += 'V_range: %.2f,%.2f THRESH: %d' % ( 103 v_min, v_max, YUV_UV_SPREAD_ATOL) 104 if ((u_max - u_min) < YUV_UV_SPREAD_ATOL or 105 (v_max - v_min) < YUV_UV_SPREAD_ATOL): 106 failed.append({'effect': EFFECTS[effect], 'error': e_msg}) 107 elif effect == 1: 108 logging.debug('Checking MONO effect...') 109 _, u, v = image_processing_utils.convert_capture_to_planes(cap, props) 110 u_min, u_max = np.amin(u)*YUV_MAX, np.amax(u)*YUV_MAX 111 v_min, v_max = np.amin(v)*YUV_MAX, np.amax(v)*YUV_MAX 112 e_msg = 'U_range: %.2f,%.2f; V_range: %.2f,%.2f; TOL: %d' % ( 113 u_min, u_max, v_min, v_max, MONO_UV_SPREAD_MAX) 114 if ((u_max - u_min) > MONO_UV_SPREAD_MAX or 115 (v_max - v_min) > MONO_UV_SPREAD_MAX): 116 failed.append({'effect': EFFECTS[effect], 'error': e_msg}) 117 if failed: 118 logging.debug('Failed effects:') 119 for fail in failed: 120 logging.debug(' %s: %s', fail['effect'], fail['error']) 121 raise AssertionError(f'{NAME} failed. See test_log.DEBUG for errors.') 122 123if __name__ == '__main__': 124 test_runner.main() 125