CameraITS: extract chart from scene once for scene3
Change-Id: I20f90e59c6895ec5cefd25bf01704d94d11626fa
diff --git a/apps/CameraITS/pymodules/its/cv2image.py b/apps/CameraITS/pymodules/its/cv2image.py
index 83e654e..9a8c93d 100644
--- a/apps/CameraITS/pymodules/its/cv2image.py
+++ b/apps/CameraITS/pymodules/its/cv2image.py
@@ -12,27 +12,24 @@
# See the License for the specific language governing permissions and
# limitations under the License.
-import matplotlib
-matplotlib.use('Agg')
-
-import its.error
-from matplotlib import pylab
-import sys
-from PIL import Image
-import numpy
-import math
-import unittest
-import cStringIO
-import scipy.stats
-import copy
-import cv2
import os
+import unittest
+
+import cv2
+import its.device
+import its.error
+import numpy
+
+VGA_HEIGHT = 480
+VGA_WIDTH = 640
+
def scale_img(img, scale=1.0):
"""Scale and image based on a real number scale factor."""
dim = (int(img.shape[1]*scale), int(img.shape[0]*scale))
return cv2.resize(img.copy(), dim, interpolation=cv2.INTER_AREA)
+
class Chart(object):
"""Definition for chart object.
@@ -57,6 +54,9 @@
self._scale_start = scale_start
self._scale_stop = scale_stop
self._scale_step = scale_step
+ self.xnorm, self.ynorm, self.wnorm, self.hnorm = its.image.chart_located_per_argv()
+ if not self.xnorm:
+ self.locate()
def _calc_scale_factors(self, cam, props, fmt, s, e, fd):
"""Take an image with s, e, & fd to find the chart location.
@@ -95,8 +95,14 @@
print 'Chart/image scale factor = %.2f' % scale_factor
return template, img_3a, scale_factor
- def locate(self, cam, props, fmt, s, e, fd):
- """Find the chart in the image.
+ def locate(self, cam=None, props=None, fmt=None, s=0, e=0, fd=0):
+ """Find the chart in the image, and append location to chart object.
+
+ The values appended are:
+ xnorm: float; [0, 1] left loc of chart in scene
+ ynorm: float; [0, 1] top loc of chart in scene
+ wnorm: float; [0, 1] width of chart in scene
+ hnorm: float; [0, 1] height of chart in scene
Args:
cam: An open device session
@@ -107,15 +113,21 @@
e: Exposure time for the AF request as defined in
android.sensor.exposureTime
fd: float; autofocus lens position
-
- Returns:
- xnorm: float; [0, 1] left loc of chart in scene
- ynorm: float; [0, 1] top loc of chart in scene
- wnorm: float; [0, 1] width of chart in scene
- hnorm: float; [0, 1] height of chart in scene
"""
- chart, scene, s_factor = self._calc_scale_factors(cam, props, fmt,
- s, e, fd)
+ if cam:
+ chart, scene, s_factor = self._calc_scale_factors(cam, props, fmt,
+ s, e, fd)
+ else:
+ with its.device.ItsSession() as cam:
+ props = cam.get_camera_properties()
+ fmt = {'format': 'yuv', 'width': VGA_WIDTH,
+ 'height': VGA_HEIGHT}
+
+ # Get sensitivity, exposure time, and focus distance with 3A.
+ s, e, _, _, fd = cam.do_3a(get_results=True)
+
+ chart, scene, s_factor = self._calc_scale_factors(cam, props,
+ fmt, s, e, fd)
scale_start = self._scale_start * s_factor
scale_stop = self._scale_stop * s_factor
scale_step = self._scale_step * s_factor
@@ -142,26 +154,36 @@
# determine if optimization results are valid
opt_values = [x[0] for x in max_match]
if 2.0*min(opt_values) > max(opt_values):
- estring = ('Unable to find chart in scene!\n'
+ estring = ('Warning: unable to find chart in scene!\n'
'Check camera distance and self-reported '
'pixel pitch, focal length and hyperfocal distance.')
- raise its.error.Error(estring)
- # find max and draw bbox
- match_index = max_match.index(max(max_match, key=lambda x: x[0]))
- scale = scale_start + scale_step * match_index
- print 'Optimum scale factor: %.3f' % scale
- top_left_scaled = max_match[match_index][1]
- h, w = chart.shape
- bottom_right_scaled = (top_left_scaled[0] + w, top_left_scaled[1] + h)
- top_left = (int(top_left_scaled[0]/scale),
- int(top_left_scaled[1]/scale))
- bottom_right = (int(bottom_right_scaled[0]/scale),
- int(bottom_right_scaled[1]/scale))
- wnorm = float((bottom_right[0]) - top_left[0]) / scene.shape[1]
- hnorm = float((bottom_right[1]) - top_left[1]) / scene.shape[0]
- xnorm = float(top_left[0]) / scene.shape[1]
- ynorm = float(top_left[1]) / scene.shape[0]
- return xnorm, ynorm, wnorm, hnorm
+ print estring
+ self.wnorm = 1.0
+ self.hnorm = 1.0
+ self.xnorm = 0.0
+ self.ynorm = 0.0
+ else:
+ if (max(opt_values) == opt_values[0] or
+ max(opt_values) == opt_values[len(opt_values)-1]):
+ estring = ('Warning: chart is at extreme range of locator '
+ 'check.\n')
+ print estring
+ # find max and draw bbox
+ match_index = max_match.index(max(max_match, key=lambda x: x[0]))
+ scale = scale_start + scale_step * match_index
+ print 'Optimum scale factor: %.3f' % scale
+ top_left_scaled = max_match[match_index][1]
+ h, w = chart.shape
+ bottom_right_scaled = (top_left_scaled[0] + w,
+ top_left_scaled[1] + h)
+ top_left = (int(top_left_scaled[0]/scale),
+ int(top_left_scaled[1]/scale))
+ bottom_right = (int(bottom_right_scaled[0]/scale),
+ int(bottom_right_scaled[1]/scale))
+ self.wnorm = float((bottom_right[0]) - top_left[0]) / scene.shape[1]
+ self.hnorm = float((bottom_right[1]) - top_left[1]) / scene.shape[0]
+ self.xnorm = float(top_left[0]) / scene.shape[1]
+ self.ynorm = float(top_left[1]) / scene.shape[0]
class __UnitTest(unittest.TestCase):
@@ -186,7 +208,8 @@
blur = cv2.blur(chart, (j, j))
blur = blur[:, :, numpy.newaxis]
sharpness[j] = (yuv_full_scale *
- its.image.compute_image_sharpness(blur / white_level))
+ its.image.compute_image_sharpness(blur /
+ white_level))
self.assertTrue(numpy.isclose(sharpness[2]/sharpness[4],
numpy.sqrt(2), atol=0.1))
self.assertTrue(numpy.isclose(sharpness[4]/sharpness[8],
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index d47a995..960ca59 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -765,6 +765,25 @@
"""
return (img - numpy.amin(img))/(numpy.amax(img) - numpy.amin(img))
+
+def chart_located_per_argv():
+ """Determine if chart already located outside of test.
+
+ If chart info provided, return location and size. If not, return None.
+
+ Args:
+ None
+ Returns:
+ chart_loc: float converted xnorm,ynorm,wnorm,hnorm from argv text.
+ argv is of form 'chart_loc=0.45,0.45,0.1,0.1'
+ """
+ for s in sys.argv[1:]:
+ if s[:10] == "chart_loc=" and len(s) > 10:
+ chart_loc = s[10:].split(",")
+ return map(float, chart_loc)
+ return None, None, None, None
+
+
def flip_mirror_img_per_argv(img):
"""Flip/mirror an image if "flip" or "mirror" is in argv
diff --git a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
index cd563be..68575ee 100644
--- a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
+++ b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
@@ -37,19 +37,20 @@
CHART_SCALE_STEP = 0.025
-def test_lens_movement_reporting(cam, props, fmt, sensitivity, exp, af_fd):
+def test_lens_movement_reporting(cam, props, fmt, gain, exp, af_fd, chart):
"""Return fd, sharpness, lens state of the output images.
Args:
cam: An open device session.
props: Properties of cam
fmt: dict; capture format
- sensitivity: Sensitivity for the 3A request as defined in
+ gain: Sensitivity for the 3A request as defined in
android.sensor.sensitivity
exp: Exposure time for the 3A request as defined in
android.sensor.exposureTime
af_fd: Focus distance for the 3A request as defined in
android.lens.focusDistance
+ chart: Object that contains chart information
Returns:
Object containing reported sharpness of the output image, keyed by
@@ -57,15 +58,6 @@
'sharpness'
"""
- # initialize chart class
- chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
- CHART_SCALE_START, CHART_SCALE_STOP,
- CHART_SCALE_STEP)
-
- # find chart location
- xnorm, ynorm, wnorm, hnorm = chart.locate(cam, props, fmt, sensitivity,
- exp, af_fd)
-
# initialize variables and take data sets
data_set = {}
white_level = int(props['android.sensor.info.whiteLevel'])
@@ -74,7 +66,7 @@
fds = sorted(fds * NUM_IMGS)
reqs = []
for i, fd in enumerate(fds):
- reqs.append(its.objects.manual_capture_request(sensitivity, exp))
+ reqs.append(its.objects.manual_capture_request(gain, exp))
reqs[i]['android.lens.focusDistance'] = fd
caps = cam.do_capture(reqs, fmt)
for i, cap in enumerate(caps):
@@ -93,11 +85,11 @@
print ' lens moving %r' % data['lens_moving']
y, _, _ = its.image.convert_capture_to_planes(cap, props)
y = its.image.flip_mirror_img_per_argv(y)
- chart = its.image.normalize_img(its.image.get_image_patch(y,
- xnorm, ynorm,
- wnorm, hnorm))
- its.image.write_image(chart, '%s_i=%d_chart.jpg' % (NAME, i))
- data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+ chart.img = its.image.normalize_img(its.image.get_image_patch(
+ y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+ its.image.write_image(chart.img, '%s_i=%d_chart.jpg' % (NAME, i))
+ data['sharpness'] = white_level*its.image.compute_image_sharpness(
+ chart.img)
print 'Chart sharpness: %.1f\n' % data['sharpness']
data_set[i] = data
return data_set
@@ -110,6 +102,11 @@
"""
print '\nStarting test_lens_movement_reporting.py'
+ # initialize chart class
+ chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
+ CHART_SCALE_START, CHART_SCALE_STOP,
+ CHART_SCALE_STEP)
+
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
its.caps.skip_unless(not its.caps.fixed_focus(props))
@@ -121,7 +118,7 @@
s, e, _, _, fd = cam.do_3a(get_results=True)
# Get sharpness for each focal distance
- d = test_lens_movement_reporting(cam, props, fmt, s, e, fd)
+ d = test_lens_movement_reporting(cam, props, fmt, s, e, fd, chart)
for k in sorted(d):
print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
'sharpness: %.1f \tlens_moving: %r \t'
diff --git a/apps/CameraITS/tests/scene3/test_lens_position.py b/apps/CameraITS/tests/scene3/test_lens_position.py
index f850e3d..6fb09e6 100644
--- a/apps/CameraITS/tests/scene3/test_lens_position.py
+++ b/apps/CameraITS/tests/scene3/test_lens_position.py
@@ -38,7 +38,7 @@
CHART_SCALE_STEP = 0.025
-def test_lens_position(cam, props, fmt, sensitivity, exp, af_fd):
+def test_lens_position(cam, props, fmt, sensitivity, exp, chart):
"""Return fd, sharpness, lens state of the output images.
Args:
@@ -49,8 +49,7 @@
android.sensor.sensitivity
exp: Exposure time for the 3A request as defined in
android.sensor.exposureTime
- af_fd: Focus distance for the 3A request as defined in
- android.lens.focusDistance
+ chart: Object with chart properties
Returns:
Dictionary of results for different focal distance captures
@@ -58,15 +57,6 @@
d_static, d_moving
"""
- # initialize chart class
- chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
- CHART_SCALE_START, CHART_SCALE_STOP,
- CHART_SCALE_STEP)
-
- # find chart location
- xnorm, ynorm, wnorm, hnorm = chart.locate(cam, props, fmt, sensitivity,
- exp, af_fd)
-
# initialize variables and take data sets
data_static = {}
data_moving = {}
@@ -89,11 +79,11 @@
print ' focus distance (diopters): %.3f' % data['fd']
print ' current lens location (diopters): %.3f' % data['loc']
y, _, _ = its.image.convert_capture_to_planes(cap, props)
- chart = its.image.normalize_img(its.image.get_image_patch(y,
- xnorm, ynorm,
- wnorm, hnorm))
- its.image.write_image(chart, '%s_stat_i=%d_chart.jpg' % (NAME, i))
- data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+ chart.img = its.image.normalize_img(its.image.get_image_patch(
+ y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+ its.image.write_image(chart.img, '%s_stat_i=%d_chart.jpg' % (NAME, i))
+ data['sharpness'] = white_level*its.image.compute_image_sharpness(
+ chart.img)
print 'Chart sharpness: %.1f\n' % data['sharpness']
data_static[i] = data
# take moving data set
@@ -116,11 +106,11 @@
print ' current lens location (diopters): %.3f' % data['loc']
y, _, _ = its.image.convert_capture_to_planes(cap, props)
y = its.image.flip_mirror_img_per_argv(y)
- chart = its.image.normalize_img(its.image.get_image_patch(y,
- xnorm, ynorm,
- wnorm, hnorm))
- its.image.write_image(chart, '%s_move_i=%d_chart.jpg' % (NAME, i))
- data['sharpness'] = white_level*its.image.compute_image_sharpness(chart)
+ chart.img = its.image.normalize_img(its.image.get_image_patch(
+ y, chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm))
+ its.image.write_image(chart.img, '%s_move_i=%d_chart.jpg' % (NAME, i))
+ data['sharpness'] = white_level*its.image.compute_image_sharpness(
+ chart.img)
print 'Chart sharpness: %.1f\n' % data['sharpness']
data_moving[i] = data
return data_static, data_moving
@@ -128,19 +118,23 @@
def main():
"""Test if focus position is properly reported for moving lenses."""
-
print '\nStarting test_lens_position.py'
+ # initialize chart class
+ chart = its.cv2image.Chart(CHART_FILE, CHART_HEIGHT, CHART_DISTANCE,
+ CHART_SCALE_START, CHART_SCALE_STOP,
+ CHART_SCALE_STEP)
+
with its.device.ItsSession() as cam:
props = cam.get_camera_properties()
its.caps.skip_unless(not its.caps.fixed_focus(props))
its.caps.skip_unless(its.caps.lens_calibrated(props))
fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
- # Get proper sensitivity, exposure time, and focus distance with 3A.
- s, e, _, _, fd = cam.do_3a(get_results=True)
+ # Get proper sensitivity and exposure time with 3A
+ s, e, _, _, _ = cam.do_3a(get_results=True)
# Get sharpness for each focal distance
- d_stat, d_move = test_lens_position(cam, props, fmt, s, e, fd)
+ d_stat, d_move = test_lens_position(cam, props, fmt, s, e, chart)
print 'Lens stationary'
for k in sorted(d_stat):
print ('i: %d\tfd: %.3f\tlens location (diopters): %.3f \t'
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index a6fc759..20ff1cd 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -21,13 +21,24 @@
import sys
import its.caps
+import its.cv2image
import its.device
+import its.image
from its.device import ItsSession
CHART_DELAY = 1 # seconds
+CHART_DISTANCE = 30.0 # cm
+CHART_HEIGHT = 13.5 # cm
+CHART_SCALE_START = 0.65
+CHART_SCALE_STOP = 1.35
+CHART_SCALE_STEP = 0.025
FACING_EXTERNAL = 2
NUM_TRYS = 2
+SCENE3_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its',
+ 'test_images', 'ISO12233.png')
SKIP_RET_CODE = 101 # note this must be same as tests/scene*/test_*
+VGA_HEIGHT = 480
+VGA_WIDTH = 640
def evaluate_socket_failure(err_file_path):
@@ -294,6 +305,14 @@
valid_scene_code = subprocess.call(cmd, cwd=topdir)
assert valid_scene_code == 0
print "Start running ITS on camera %s, %s" % (camera_id, scene)
+ # Extract chart from scene for scene3 once up front
+ chart_loc_arg = ''
+ if scene == 'scene3':
+ chart = its.cv2image.Chart(SCENE3_FILE, CHART_HEIGHT,
+ CHART_DISTANCE, CHART_SCALE_START,
+ CHART_SCALE_STOP, CHART_SCALE_STEP)
+ chart_loc_arg = 'chart_loc=%.2f,%.2f,%.2f,%.2f' % (chart.xnorm,
+ chart.ynorm, chart.wnorm, chart.hnorm)
# Run each test, capturing stdout and stderr.
for (testname, testpath) in tests:
if auto_scene_switch:
@@ -325,7 +344,7 @@
test_code = skip_code
if skip_code is not SKIP_RET_CODE:
cmd = ['python', os.path.join(os.getcwd(), testpath)]
- cmd += sys.argv[1:] + [camera_id_arg]
+ cmd += sys.argv[1:] + [camera_id_arg] + [chart_loc_arg]
with open(outpath, 'w') as fout, open(errpath, 'w') as ferr:
test_code = subprocess.call(
cmd, stderr=ferr, stdout=fout, cwd=outdir)