You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
212 lines
9.4 KiB
212 lines
9.4 KiB
# Copyright 2014 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.
|
|
|
|
# --------------------------------------------------------------------------- #
|
|
# The Google Python style guide should be used for scripts: #
|
|
# http://google-styleguide.googlecode.com/svn/trunk/pyguide.html #
|
|
# --------------------------------------------------------------------------- #
|
|
|
|
# The ITS modules that are in the utils directory. To see formatted
|
|
# docs, use the "pydoc" command:
|
|
#
|
|
# > pydoc image_processing_utils
|
|
#
|
|
import capture_request_utils
|
|
import image_processing_utils
|
|
import its_base_test
|
|
import its_session_utils
|
|
|
|
# Standard Python modules.
|
|
import logging
|
|
import os.path
|
|
|
|
# Modules from the numpy, scipy, and matplotlib libraries. These are used for
|
|
# the image processing code, and images are represented as numpy arrays.
|
|
from matplotlib import pylab
|
|
import numpy
|
|
import matplotlib
|
|
import matplotlib.pyplot
|
|
|
|
# Module for Mobly
|
|
from mobly import test_runner
|
|
|
|
# A convention in each script is to use the filename (without the extension)
|
|
# as the name of the test, when printing results to the screen or dumping files.
|
|
_NAME = os.path.basename(__file__).split('.')[0]
|
|
|
|
|
|
# Each script has a class definition
|
|
class TutorialTest(its_base_test.ItsBaseTest):
|
|
"""Test the validity of some metadata entries.
|
|
|
|
Looks at the capture results and at the camera characteristics objects.
|
|
Script uses a config.yml file in the CameraITS directory.
|
|
A sample config.yml file:
|
|
TestBeds:
|
|
- Name: TEST_BED_TUTORIAL
|
|
Controllers:
|
|
AndroidDevice:
|
|
- serial: 03281FDD40008Y
|
|
label: dut
|
|
TestParams:
|
|
camera: "1"
|
|
scene: "0"
|
|
|
|
A sample script call:
|
|
python tests/tutorial.py --config config.yml
|
|
|
|
"""
|
|
|
|
def test_tutorial(self):
|
|
# Each script has a string description of what it does. This is the first
|
|
# entry inside the main function.
|
|
"""Tutorial script to show how to use the ITS infrastructure."""
|
|
|
|
# The standard way to open a session with a connected camera device. This
|
|
# creates a cam object which encapsulates the session and which is active
|
|
# within the scope of the 'with' block; when the block exits, the camera
|
|
# session is closed. The device and camera are defined in the config.yml
|
|
# file.
|
|
with its_session_utils.ItsSession(
|
|
device_id=self.dut.serial,
|
|
camera_id=self.camera_id,
|
|
hidden_physical_id=self.hidden_physical_id) as cam:
|
|
|
|
# Append the log_path to store images in the proper location.
|
|
# Images will be stored in the test output folder:
|
|
# /tmp/logs/mobly/$TEST_BED_NAME/$DATE/TutorialTest
|
|
file_name = os.path.join(self.log_path, _NAME)
|
|
|
|
# Get the static properties of the camera device. Returns a Python
|
|
# associative array object; print it to the console.
|
|
props = cam.get_camera_properties()
|
|
logging.debug('props\n%s', str(props))
|
|
|
|
# Grab a YUV frame with manual exposure of sensitivity = 200, exposure
|
|
# duration = 50ms.
|
|
req = capture_request_utils.manual_capture_request(200, 50*1000*1000)
|
|
cap = cam.do_capture(req)
|
|
|
|
# Print the properties of the captured frame; width and height are
|
|
# integers, and the metadata is a Python associative array object.
|
|
# logging.info will be printed to screen & test_log.INFO
|
|
# logging.debug to test_log.DEBUG in /tmp/logs/mobly/... directory
|
|
logging.info('Captured image width: %d, height: %d',
|
|
cap['width'], cap['height'])
|
|
logging.debug('metadata\n%s', str(cap['metadata']))
|
|
|
|
# The captured image is YUV420. Convert to RGB, and save as a file.
|
|
rgbimg = image_processing_utils.convert_capture_to_rgb_image(cap)
|
|
image_processing_utils.write_image(rgbimg, '%s_rgb_1.jpg' % file_name)
|
|
|
|
# Can also get the Y,U,V planes separately; save these to greyscale
|
|
# files.
|
|
yimg, uimg, vimg = image_processing_utils.convert_capture_to_planes(cap)
|
|
image_processing_utils.write_image(yimg, '%s_y_plane_1.jpg' % file_name)
|
|
image_processing_utils.write_image(uimg, '%s_u_plane_1.jpg' % file_name)
|
|
image_processing_utils.write_image(vimg, '%s_v_plane_1.jpg' % file_name)
|
|
|
|
# Run 3A on the device. In this case, just use the entire image as the
|
|
# 3A region, and run each of AWB,AE,AF. Can also change the region and
|
|
# specify independently for each of AE,AWB,AF whether it should run.
|
|
#
|
|
# NOTE: This may fail, if the camera isn't pointed at a reasonable
|
|
# target scene. If it fails, the script will end. The logcat messages
|
|
# can be inspected to see the status of 3A running on the device.
|
|
#
|
|
# If this keeps on failing, try also rebooting the device before
|
|
# running the test.
|
|
sens, exp, gains, xform, focus = cam.do_3a(get_results=True)
|
|
logging.info('AE: sensitivity %d, exposure %dms', sens, exp/1000000.0)
|
|
logging.info('AWB: gains %s', str(gains))
|
|
logging.info('AWB: transform %s', str(xform))
|
|
logging.info('AF: distance %.4f', focus)
|
|
|
|
# Grab a new manual frame, using the 3A values, and convert it to RGB
|
|
# and save it to a file too. Note that the 'req' object is just a
|
|
# Python dictionary that is pre-populated by the capture_request_utils
|
|
# functions (in this case a default manual capture), and the key/value
|
|
# pairs in the object can be used to set any field of the capture
|
|
# request. Here, the AWB gains and transform (CCM) are being used.
|
|
# Note that the CCM transform is in a rational format in capture
|
|
# requests, meaning it is an object with integer numerators and
|
|
# denominators. The 3A routine returns simple floats instead, however,
|
|
# so a conversion from float to rational must be performed.
|
|
req = capture_request_utils.manual_capture_request(sens, exp)
|
|
xform_rat = capture_request_utils.float_to_rational(xform)
|
|
|
|
req['android.colorCorrection.transform'] = xform_rat
|
|
req['android.colorCorrection.gains'] = gains
|
|
cap = cam.do_capture(req)
|
|
rgbimg = image_processing_utils.convert_capture_to_rgb_image(cap)
|
|
image_processing_utils.write_image(rgbimg, f'{file_name}_rgb_2.jpg')
|
|
|
|
# log the actual capture request object that was used.
|
|
logging.debug('req: %s', str(req))
|
|
|
|
# Images are numpy arrays. The dimensions are (h,w,3) when indexing,
|
|
# in the case of RGB images. Greyscale images are (h,w,1). Pixels are
|
|
# generally float32 values in the [0,1] range, however some of the
|
|
# helper functions in image_processing_utils deal with the packed YUV420
|
|
# and other formats of images that come from the device (and convert
|
|
# them to float32).
|
|
# Print the dimensions of the image, and the top-left pixel value,
|
|
# which is an array of 3 floats.
|
|
logging.info('RGB image dimensions: %s', str(rgbimg.shape))
|
|
logging.info('RGB image top-left pixel: %s', str(rgbimg[0, 0]))
|
|
|
|
# Grab a center tile from the image; this returns a new image. Save
|
|
# this tile image. In this case, the tile is the middle 10% x 10%
|
|
# rectangle.
|
|
tile = image_processing_utils.get_image_patch(
|
|
rgbimg, 0.45, 0.45, 0.1, 0.1)
|
|
image_processing_utils.write_image(tile, f'{file_name}_rgb_2_tile.jpg')
|
|
|
|
# Compute the mean values of the center tile image.
|
|
rgb_means = image_processing_utils.compute_image_means(tile)
|
|
logging.info('RGB means: %s', str(rgb_means))
|
|
|
|
# Apply a lookup table to the image, and save the new version. The LUT
|
|
# is basically a tonemap, and can be used to implement a gamma curve.
|
|
# In this case, the LUT is used to double the value of each pixel.
|
|
lut = numpy.array([2*i for i in range(65536)])
|
|
rgbimg_lut = image_processing_utils.apply_lut_to_image(rgbimg, lut)
|
|
image_processing_utils.write_image(
|
|
rgbimg_lut, f'{file_name}_rgb_2_lut.jpg')
|
|
|
|
# Compute a histogram of the luma image, in 256 buckets.
|
|
yimg, _, _ = image_processing_utils.convert_capture_to_planes(cap)
|
|
hist, _ = numpy.histogram(yimg*255, 256, (0, 256))
|
|
|
|
# Plot the histogram using matplotlib, and save as a PNG image.
|
|
pylab.plot(range(256), hist.tolist())
|
|
pylab.xlabel('Luma DN')
|
|
pylab.ylabel('Pixel count')
|
|
pylab.title('Histogram of luma channel of captured image')
|
|
matplotlib.pyplot.savefig(f'{file_name}_histogram.png')
|
|
|
|
# Capture a frame to be returned as a JPEG. Load it as an RGB image,
|
|
# then save it back as a JPEG.
|
|
cap = cam.do_capture(req, cam.CAP_JPEG)
|
|
rgbimg = image_processing_utils.convert_capture_to_rgb_image(cap)
|
|
image_processing_utils.write_image(rgbimg, f'{file_name}_jpg.jpg')
|
|
r, _, _ = image_processing_utils.convert_capture_to_planes(cap)
|
|
image_processing_utils.write_image(r, f'{file_name}_r.jpg')
|
|
|
|
# This is the standard boilerplate in each test that allows the script to both
|
|
# be executed directly and imported as a module.
|
|
if __name__ == '__main__':
|
|
test_runner.main()
|
|
|