1# Copyright 2019 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 camera will produce full black & full white images.""" 15 16 17import logging 18import os.path 19import matplotlib 20from matplotlib import pylab 21 22 23from mobly import test_runner 24import numpy as np 25 26import its_base_test 27import camera_properties_utils 28import capture_request_utils 29import image_processing_utils 30import its_session_utils 31 32CH_FULL_SCALE = 255 33CH_THRESH_BLACK = 6 34CH_THRESH_WHITE = CH_FULL_SCALE - 6 35CH_TOL_WHITE = 2 36COLOR_PLANES = ['R', 'G', 'B'] 37NAME = os.path.splitext(os.path.basename(__file__))[0] 38PATCH_H = 0.1 39PATCH_W = 0.1 40PATCH_X = 0.45 41PATCH_Y = 0.45 42VGA_WIDTH, VGA_HEIGHT = 640, 480 43 44 45def do_img_capture(cam, s, e, fmt, latency, cap_name, log_path): 46 """Do the image captures with the defined parameters. 47 48 Args: 49 cam: its_session open for camera 50 s: sensitivity for request 51 e: exposure in ns for request 52 fmt: format of request 53 latency: number of frames for sync latency of request 54 cap_name: string to define the capture 55 log_path: path for plot directory 56 57 Returns: 58 means values of center patch from capture 59 """ 60 61 req = capture_request_utils.manual_capture_request(s, e) 62 cap = its_session_utils.do_capture_with_latency(cam, req, latency, fmt) 63 img = image_processing_utils.convert_capture_to_rgb_image(cap) 64 image_processing_utils.write_image( 65 img, '%s_%s.jpg' % (os.path.join(log_path, NAME), cap_name)) 66 patch = image_processing_utils.get_image_patch( 67 img, PATCH_X, PATCH_Y, PATCH_W, PATCH_H) 68 means = image_processing_utils.compute_image_means(patch) 69 means = [m * CH_FULL_SCALE for m in means] 70 logging.debug('%s pixel means: %s', cap_name, str(means)) 71 r_exp = cap['metadata']['android.sensor.exposureTime'] 72 r_iso = cap['metadata']['android.sensor.sensitivity'] 73 logging.debug('%s shot write values: sens = %d, exp time = %.4fms', 74 cap_name, s, (e / 1000000.0)) 75 logging.debug('%s shot read values: sens = %d, exp time = %.4fms', 76 cap_name, r_iso, (r_exp / 1000000.0)) 77 return means 78 79 80class BlackWhiteTest(its_base_test.ItsBaseTest): 81 """Test that device will prodoce full black + white images. 82 """ 83 84 def test_black_white(self): 85 r_means = [] 86 g_means = [] 87 b_means = [] 88 89 with its_session_utils.ItsSession( 90 device_id=self.dut.serial, 91 camera_id=self.camera_id, 92 hidden_physical_id=self.hidden_physical_id) as cam: 93 props = cam.get_camera_properties() 94 props = cam.override_with_hidden_physical_camera_props(props) 95 96 # Check SKIP conditions 97 camera_properties_utils.skip_unless( 98 camera_properties_utils.manual_sensor(props)) 99 100 # Load chart for scene 101 its_session_utils.load_scene( 102 cam, props, self.scene, self.tablet, self.chart_distance) 103 104 # Initialize params for requests 105 latency = camera_properties_utils.sync_latency(props) 106 fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT} 107 expt_range = props['android.sensor.info.exposureTimeRange'] 108 sens_range = props['android.sensor.info.sensitivityRange'] 109 log_path = self.log_path 110 111 # Take shot with very low ISO and exp time: expect it to be black 112 s = sens_range[0] 113 e = expt_range[0] 114 black_means = do_img_capture(cam, s, e, fmt, latency, 'black', log_path) 115 r_means.append(black_means[0]) 116 g_means.append(black_means[1]) 117 b_means.append(black_means[2]) 118 119 # Take shot with very high ISO and exp time: expect it to be white. 120 s = sens_range[1] 121 e = expt_range[1] 122 white_means = do_img_capture(cam, s, e, fmt, latency, 'white', log_path) 123 r_means.append(white_means[0]) 124 g_means.append(white_means[1]) 125 b_means.append(white_means[2]) 126 127 # Draw plot 128 pylab.title('test_black_white') 129 pylab.plot([0, 1], r_means, '-ro') 130 pylab.plot([0, 1], g_means, '-go') 131 pylab.plot([0, 1], b_means, '-bo') 132 pylab.xlabel('Capture Number') 133 pylab.ylabel('Output Values [0:255]') 134 pylab.ylim([0, 255]) 135 matplotlib.pyplot.savefig('%s_plot_means.png' % ( 136 os.path.join(log_path, NAME))) 137 138 # Assert blacks below CH_THRESH_BLACK 139 for ch, mean in enumerate(black_means): 140 e_msg = '%s black: %.1f, THRESH: %.f' % ( 141 COLOR_PLANES[ch], mean, CH_THRESH_BLACK) 142 assert mean < CH_THRESH_BLACK, e_msg 143 144 # Assert whites above CH_THRESH_WHITE 145 for ch, mean in enumerate(white_means): 146 e_msg = '%s white: %.1f, THRESH: %.f' % ( 147 COLOR_PLANES[ch], mean, CH_THRESH_WHITE) 148 assert mean > CH_THRESH_WHITE, e_msg 149 150 # Assert channels saturate evenly (was test_channel_saturation) 151 e_msg = 'ch saturation not equal! RGB: %s, ATOL: %.f' % ( 152 str(white_means), CH_TOL_WHITE) 153 assert np.isclose( 154 np.amin(white_means), np.amax(white_means), atol=CH_TOL_WHITE), e_msg 155 156if __name__ == '__main__': 157 test_runner.main() 158 159