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 with its_session_utils.ItsSession( 56 device_id=self.dut.serial, 57 camera_id=self.camera_id, 58 hidden_physical_id=self.hidden_physical_id) as cam: 59 props = cam.get_camera_properties() 60 props = cam.override_with_hidden_physical_camera_props(props) 61 log_path = self.log_path 62 name_with_path = os.path.join(log_path, _NAME) 63 64 # check SKIP conditions 65 camera_properties_utils.skip_unless( 66 camera_properties_utils.compute_target_exposure(props) and 67 camera_properties_utils.per_frame_control(props)) 68 69 # Load chart for scene 70 its_session_utils.load_scene( 71 cam, props, self.scene, self.tablet, 72 its_session_utils.CHART_DISTANCE_NO_SCALING) 73 74 # Capture at the smallest resolution 75 _, fmt = capture_request_utils.get_fastest_manual_capture_settings(props) 76 e, s = target_exposure_utils.get_target_exposure_combos( 77 log_path, cam)['minSensitivity'] 78 req = capture_request_utils.manual_capture_request(s, e) 79 w, h = fmt['width'], fmt['height'] 80 81 # Capture bursts of YUV shots. 82 # Get the mean values of a center patch for each. 83 # Also build a 4D array, imgs, which is an array of all RGB images. 84 r_means = [] 85 g_means = [] 86 b_means = [] 87 imgs = np.empty([_NUM_FRAMES, h, w, 3]) 88 for j in range(_NUM_BURSTS): 89 caps = cam.do_capture([req]*_BURST_LEN, [fmt]) 90 for i, cap in enumerate(caps): 91 n = j*_BURST_LEN + i 92 imgs[n] = image_processing_utils.convert_capture_to_rgb_image(cap) 93 patch = image_processing_utils.get_image_patch( 94 imgs[n], _PATCH_X, _PATCH_Y, _PATCH_W, _PATCH_H) 95 means = image_processing_utils.compute_image_means(patch) 96 r_means.append(means[0]) 97 g_means.append(means[1]) 98 b_means.append(means[2]) 99 100 # Save first frame for setup debug 101 image_processing_utils.write_image( 102 imgs[0], f'{name_with_path}_frame000.jpg') 103 104 # Plot RGB means vs frames 105 frames = range(_NUM_FRAMES) 106 pylab.figure(_NAME) 107 pylab.title(_NAME) 108 pylab.plot(frames, r_means, '-ro') 109 pylab.plot(frames, g_means, '-go') 110 pylab.plot(frames, b_means, '-bo') 111 pylab.ylim([0, 1]) 112 pylab.xlabel('frame number') 113 pylab.ylabel('RGB avg [0, 1]') 114 matplotlib.pyplot.savefig(f'{name_with_path}_plot_means.png') 115 116 # determine spread_thresh 117 spread_thresh = _SPREAD_THRESH 118 if its_session_utils.get_first_api_level( 119 self.dut.serial) >= _API_LEVEL_30: 120 spread_thresh = _SPREAD_THRESH_API_LEVEL_30 121 122 # PASS/FAIL based on center patch similarity 123 for plane, means in enumerate([r_means, g_means, b_means]): 124 spread = max(means) - min(means) 125 logging.debug('%s spread: %.5f', _COLORS[plane], spread) 126 if spread > spread_thresh: 127 # Save all frames if FAIL 128 logging.debug('Dumping all images') 129 for i in range(1, _NUM_FRAMES): 130 image_processing_utils.write_image( 131 imgs[i], f'{name_with_path}_frame{i:03d}.jpg') 132 raise AssertionError(f'{_COLORS[plane]} spread > THRESH. spread: ' 133 f'{spread:.4f}, THRESH: {spread_thresh:.2f}') 134 135if __name__ == '__main__': 136 test_runner.main() 137