# Copyright 2016 The Android Open Source Project # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """CameraITS test to check test patterns generation.""" import logging import os from mobly import test_runner import numpy as np import its_base_test import camera_properties_utils import capture_request_utils import image_processing_utils import its_session_utils NAME = os.path.basename(__file__).split('.')[0] CHECKED_PATTERNS = [1, 2, 5] # [SOLID_COLOR, COLOR_BARS, BLACK] COLOR_BAR_ORDER = ['WHITE', 'YELLOW', 'CYAN', 'GREEN', 'MAGENTA', 'RED', 'BLUE', 'BLACK'] COLOR_CHECKER = {'BLACK': [0, 0, 0], 'RED': [1, 0, 0], 'GREEN': [0, 1, 0], 'BLUE': [0, 0, 1], 'MAGENTA': [1, 0, 1], 'CYAN': [0, 1, 1], 'YELLOW': [1, 1, 0], 'WHITE': [1, 1, 1]} CH_TOL = 2E-3 # 1/2 DN in [0:1] def check_solid_color(cap, props): """Checks for solid color test pattern. Args: cap: capture element props: capture properties Returns: True/False """ logging.debug('Checking solid TestPattern...') r, gr, gb, b = image_processing_utils.convert_capture_to_planes(cap, props) r_tile = image_processing_utils.get_image_patch(r, 0.0, 0.0, 1.0, 1.0) gr_tile = image_processing_utils.get_image_patch(gr, 0.0, 0.0, 1.0, 1.0) gb_tile = image_processing_utils.get_image_patch(gb, 0.0, 0.0, 1.0, 1.0) b_tile = image_processing_utils.get_image_patch(b, 0.0, 0.0, 1.0, 1.0) var_max = max( np.amax(r_tile), np.amax(gr_tile), np.amax(gb_tile), np.amax(b_tile)) var_min = min( np.amin(r_tile), np.amin(gr_tile), np.amin(gb_tile), np.amin(b_tile)) white_level = int(props['android.sensor.info.whiteLevel']) logging.debug('pixel min: %.f, pixel max: %.f', white_level * var_min, white_level * var_max) return np.isclose(var_max, var_min, atol=CH_TOL) def check_color_bars(cap, props, mirror=False): """Checks for color bar test pattern.Compute avg of bars and compare to ideal. Args: cap: capture element props: capture properties mirror: boolean; whether to mirror image or not Returns: True/False """ logging.debug('Checking color bar TestPattern...') delta = 0.0005 num_bars = len(COLOR_BAR_ORDER) color_match = [] img = image_processing_utils.convert_capture_to_rgb_image(cap, props=props) if mirror: logging.debug('Image mirrored') img = np.fliplr(img) for i, color in enumerate(COLOR_BAR_ORDER): tile = image_processing_utils.get_image_patch(img, float(i) / num_bars + delta, 0.0, 1.0 / num_bars - 2 * delta, 1.0) color_match.append( np.allclose( image_processing_utils.compute_image_means(tile), COLOR_CHECKER[color], atol=CH_TOL)) logging.debug(COLOR_BAR_ORDER) logging.debug(color_match) return all(color_match) def check_pattern(cap, props, pattern): """Checks for pattern correctness. Args: cap: capture element props: capture properties pattern (int): valid number for pattern Returns: True/False """ if pattern == 1 or pattern == 5: # solid color or black return check_solid_color(cap, props) elif pattern == 2: # color bars striped = check_color_bars(cap, props, mirror=False) # check mirrored version in case image rotated from sensor orientation if not striped: striped = check_color_bars(cap, props, mirror=True) return striped else: logging.debug('No specific test for TestPattern: %d', pattern) return True def test_test_patterns_impl(cam, props, af_fd, name): """Image sensor test patterns implementation. Args: cam: An open device session. props: Properties of cam af_fd: Focus distance name: Path to save the captured image. """ avail_patterns = props['android.sensor.availableTestPatternModes'] logging.debug('avail_patterns: %s', avail_patterns) sens_min, _ = props['android.sensor.info.sensitivityRange'] exposure = min(props['android.sensor.info.exposureTimeRange']) for pattern in CHECKED_PATTERNS: if pattern in avail_patterns: req = capture_request_utils.manual_capture_request( int(sens_min), exposure) req['android.lens.focusDistance'] = af_fd req['android.sensor.testPatternMode'] = pattern fmt = {'format': 'raw'} cap = cam.do_capture(req, fmt) img = image_processing_utils.convert_capture_to_rgb_image( cap, props=props) # Save pattern image_processing_utils.write_image(img, '%s_%d.jpg' % (name, pattern), True) # Check pattern for correctness assert check_pattern(cap, props, pattern) else: logging.debug('%d not in android.sensor.availableTestPatternModes.', (pattern)) class TestPatterns(its_base_test.ItsBaseTest): """Test pattern generation test. Test: Capture frames for each valid test pattern and check if generated correctly. android.sensor.testPatternMode 0: OFF 1: SOLID_COLOR 2: COLOR_BARS 3: COLOR_BARS_FADE_TO_GREY 4: PN9 5: BLACK (test/system only) """ def test_test_patterns(self): logging.debug('Starting %s', NAME) with its_session_utils.ItsSession( device_id=self.dut.serial, camera_id=self.camera_id, hidden_physical_id=self.hidden_physical_id) as cam: props = cam.get_camera_properties() props = cam.override_with_hidden_physical_camera_props(props) camera_properties_utils.skip_unless( camera_properties_utils.raw16(props) and camera_properties_utils.manual_sensor(props) and camera_properties_utils.per_frame_control(props)) # For test pattern, use min_fd focus_distance = props['android.lens.info.minimumFocusDistance'] name = os.path.join(self.log_path, NAME) test_test_patterns_impl(cam, props, focus_distance, name) if __name__ == '__main__': test_runner.main()