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 31API_LEVEL_30 = 30 32BURST_LEN = 50 33COLORS = ['R', 'G', 'B'] 34NAME = os.path.splitext(os.path.basename(__file__))[0] 35NUM_BURSTS = 5 36PATCH_H = 0.1 # center 10% 37PATCH_W = 0.1 38PATCH_X = 0.5 - PATCH_W/2 39PATCH_Y = 0.5 - PATCH_H/2 40SPREAD_THRESH = 0.03 41SPREAD_THRESH_API_LEVEL_30 = 0.02 42 43NUM_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 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, self.chart_distance) 72 73 # Capture at the smallest resolution 74 _, fmt = capture_request_utils.get_fastest_manual_capture_settings(props) 75 e, s = target_exposure_utils.get_target_exposure_combos( 76 log_path, cam)['minSensitivity'] 77 req = capture_request_utils.manual_capture_request(s, e) 78 w, h = fmt['width'], fmt['height'] 79 80 # Capture bursts of YUV shots. 81 # Get the mean values of a center patch for each. 82 # Also build a 4D array, imgs, which is an array of all RGB images. 83 r_means = [] 84 g_means = [] 85 b_means = [] 86 imgs = np.empty([NUM_FRAMES, h, w, 3]) 87 for j in range(NUM_BURSTS): 88 caps = cam.do_capture([req]*BURST_LEN, [fmt]) 89 for i, cap in enumerate(caps): 90 n = j*BURST_LEN + i 91 imgs[n] = image_processing_utils.convert_capture_to_rgb_image(cap) 92 patch = image_processing_utils.get_image_patch( 93 imgs[n], PATCH_X, PATCH_Y, PATCH_W, PATCH_H) 94 means = image_processing_utils.compute_image_means(patch) 95 r_means.append(means[0]) 96 g_means.append(means[1]) 97 b_means.append(means[2]) 98 99 # Save first frame for setup debug 100 image_processing_utils.write_image( 101 imgs[0], '%s_frame000.jpg' % os.path.join(log_path, NAME)) 102 103 # Save all frames if debug 104 if self.debug_mode: 105 logging.debug('Dumping all images') 106 for i in range(1, NUM_FRAMES): 107 image_processing_utils.write_image( 108 imgs[i], '%s_frame%03d.jpg'%(os.path.join(log_path, NAME), i)) 109 110 # Plot RGB means vs frames 111 frames = range(NUM_FRAMES) 112 pylab.figure(NAME) 113 pylab.title(NAME) 114 pylab.plot(frames, r_means, '-ro') 115 pylab.plot(frames, g_means, '-go') 116 pylab.plot(frames, b_means, '-bo') 117 pylab.ylim([0, 1]) 118 pylab.xlabel('frame number') 119 pylab.ylabel('RGB avg [0, 1]') 120 matplotlib.pyplot.savefig( 121 '%s_plot_means.png' % os.path.join(log_path, NAME)) 122 123 # determine spread_thresh 124 spread_thresh = SPREAD_THRESH 125 if its_session_utils.get_first_api_level(self.dut.serial) >= API_LEVEL_30: 126 spread_thresh = SPREAD_THRESH_API_LEVEL_30 127 128 # PASS/FAIL based on center patch similarity. 129 for plane, means in enumerate([r_means, g_means, b_means]): 130 spread = max(means) - min(means) 131 msg = '%s spread: %.5f, spread_thresh: %.2f' % ( 132 COLORS[plane], spread, spread_thresh) 133 logging.debug('%s', msg) 134 assert spread < spread_thresh, msg 135 136if __name__ == '__main__': 137 test_runner.main() 138 139