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 manual burst capture consistency.""" 15 16 17import logging 18import os.path 19from matplotlib import pylab 20import matplotlib.pyplot 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 target_exposure_utils 30 31_API_LEVEL_30 = 30 32_BURST_LEN = 50 33_COLORS = ('R', 'G', 'B') 34_NAME = os.path.splitext(os.path.basename(__file__))[0] 35_NUM_BURSTS = 2 36_PATCH_H = 0.1 # center 10% 37_PATCH_W = 0.1 38_PATCH_X = 0.5 - _PATCH_W/2 39_PATCH_Y = 0.5 - _PATCH_H/2 40_SPREAD_THRESH = 0.03 41_SPREAD_THRESH_API_LEVEL_30 = 0.02 42 43_NUM_FRAMES = _BURST_LEN * _NUM_BURSTS 44 45 46class BurstSamenessManualTest(its_base_test.ItsBaseTest): 47 """Take long bursts of images and check that they're all identical. 48 49 Assumes a static scene. Can be used to idenfity if there are sporadic 50 frames that are processed differently or have artifacts. Uses manual 51 capture settings. 52 """ 53 54 def test_burst_sameness_manual(self): 55 logging.debug('Starting %s', _NAME) 56 with its_session_utils.ItsSession( 57 device_id=self.dut.serial, 58 camera_id=self.camera_id, 59 hidden_physical_id=self.hidden_physical_id) as cam: 60 props = cam.get_camera_properties() 61 props = cam.override_with_hidden_physical_camera_props(props) 62 log_path = self.log_path 63 name_with_path = os.path.join(log_path, _NAME) 64 65 # check SKIP conditions 66 camera_properties_utils.skip_unless( 67 camera_properties_utils.compute_target_exposure(props) and 68 camera_properties_utils.per_frame_control(props)) 69 70 # Load chart for scene 71 its_session_utils.load_scene( 72 cam, props, self.scene, self.tablet, 73 its_session_utils.CHART_DISTANCE_NO_SCALING) 74 75 # Capture at the smallest resolution 76 _, fmt = capture_request_utils.get_fastest_manual_capture_settings(props) 77 e, s = target_exposure_utils.get_target_exposure_combos( 78 log_path, cam)['minSensitivity'] 79 req = capture_request_utils.manual_capture_request(s, e) 80 w, h = fmt['width'], fmt['height'] 81 82 # Capture bursts of YUV shots. 83 # Get the mean values of a center patch for each. 84 # Also build a 4D array, imgs, which is an array of all RGB images. 85 r_means = [] 86 g_means = [] 87 b_means = [] 88 imgs = np.empty([_NUM_FRAMES, h, w, 3]) 89 for j in range(_NUM_BURSTS): 90 caps = cam.do_capture([req]*_BURST_LEN, [fmt]) 91 for i, cap in enumerate(caps): 92 n = j*_BURST_LEN + i 93 imgs[n] = image_processing_utils.convert_capture_to_rgb_image(cap) 94 patch = image_processing_utils.get_image_patch( 95 imgs[n], _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H) 96 means = image_processing_utils.compute_image_means(patch) 97 r_means.append(means[0]) 98 g_means.append(means[1]) 99 b_means.append(means[2]) 100 101 # Save first frame for setup debug 102 image_processing_utils.write_image( 103 imgs[0], f'{name_with_path}_frame000.jpg') 104 105 # Plot RGB means vs frames 106 frames = range(_NUM_FRAMES) 107 pylab.figure(_NAME) 108 pylab.title(_NAME) 109 pylab.plot(frames, r_means, '-ro') 110 pylab.plot(frames, g_means, '-go') 111 pylab.plot(frames, b_means, '-bo') 112 pylab.ylim([0, 1]) 113 pylab.xlabel('frame number') 114 pylab.ylabel('RGB avg [0, 1]') 115 matplotlib.pyplot.savefig(f'{name_with_path}_plot_means.png') 116 117 # determine spread_thresh 118 spread_thresh = _SPREAD_THRESH 119 if its_session_utils.get_first_api_level( 120 self.dut.serial) >= _API_LEVEL_30: 121 spread_thresh = _SPREAD_THRESH_API_LEVEL_30 122 123 # PASS/FAIL based on center patch similarity 124 for plane, means in enumerate([r_means, g_means, b_means]): 125 spread = max(means) - min(means) 126 logging.debug('%s spread: %.5f', _COLORS[plane], spread) 127 if spread > spread_thresh: 128 # Save all frames if FAIL 129 logging.debug('Dumping all images') 130 for i in range(1, _NUM_FRAMES): 131 image_processing_utils.write_image( 132 imgs[i], f'{name_with_path}_frame{i:03d}.jpg') 133 raise AssertionError(f'{_COLORS[plane]} spread > THRESH. spread: ' 134 f'{spread}, THRESH: {spread_thresh:.2f}') 135 136if __name__ == '__main__': 137 test_runner.main() 138