CameraITS: add test to detect img flip, mirror or 180 degree rotation
Change-Id: I2da4425daf63916b0a24a82dd735d0afb9ca2e67
diff --git a/apps/CameraITS/pymodules/its/cv2image.py b/apps/CameraITS/pymodules/its/cv2image.py
index 9a8c93d..a34d4ce 100644
--- a/apps/CameraITS/pymodules/its/cv2image.py
+++ b/apps/CameraITS/pymodules/its/cv2image.py
@@ -30,6 +30,18 @@
return cv2.resize(img.copy(), dim, interpolation=cv2.INTER_AREA)
+def gray_scale_img(img):
+ """Return gray scale version of image."""
+ if len(img.shape) == 2:
+ img_gray = img.copy()
+ elif len(img.shape) == 3:
+ if img.shape[2] == 1:
+ img_gray = img[:, :, 0].copy()
+ else:
+ img_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)
+ return img_gray
+
+
class Chart(object):
"""Definition for chart object.
@@ -54,7 +66,7 @@
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()
+ self.xnorm, self.ynorm, self.wnorm, self.hnorm, self.scale = its.image.chart_located_per_argv()
if not self.xnorm:
self.locate()
@@ -79,7 +91,7 @@
req['android.lens.focusDistance'] = fd
cap_chart = its.image.stationary_lens_cap(cam, req, fmt)
img_3a = its.image.convert_capture_to_rgb_image(cap_chart, props)
- img_3a = its.image.flip_mirror_img_per_argv(img_3a)
+ img_3a = its.image.rotate_img_per_argv(img_3a)
its.image.write_image(img_3a, 'af_scene.jpg')
template = cv2.imread(self._file, cv2.IMREAD_ANYDEPTH)
focal_l = cap_chart['metadata']['android.lens.focalLength']
@@ -103,6 +115,7 @@
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
+ scale: float; scale factor to extract chart
Args:
cam: An open device session
@@ -131,17 +144,12 @@
scale_start = self._scale_start * s_factor
scale_stop = self._scale_stop * s_factor
scale_step = self._scale_step * s_factor
+ self.scale = s_factor
max_match = []
# check for normalized image
if numpy.amax(scene) <= 1.0:
scene = (scene * 255.0).astype(numpy.uint8)
- if len(scene.shape) == 2:
- scene_gray = scene.copy()
- elif len(scene.shape) == 3:
- if scene.shape[2] == 1:
- scene_gray = scene[:, :, 0]
- else:
- scene_gray = cv2.cvtColor(scene.copy(), cv2.COLOR_RGB2GRAY)
+ scene_gray = gray_scale_img(scene)
print 'Finding chart in scene...'
for scale in numpy.arange(scale_start, scale_stop, scale_step):
scene_scaled = scale_img(scene_gray, scale)
@@ -170,16 +178,16 @@
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
+ self.scale = scale_start + scale_step * match_index
+ print 'Optimum scale factor: %.3f' % self.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))
+ top_left = (int(top_left_scaled[0]/self.scale),
+ int(top_left_scaled[1]/self.scale))
+ bottom_right = (int(bottom_right_scaled[0]/self.scale),
+ int(bottom_right_scaled[1]/self.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]
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 960ca59..d4f51f6 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -626,7 +626,10 @@
ytile = math.ceil(ynorm * hfull)
wtile = math.floor(wnorm * wfull)
htile = math.floor(hnorm * hfull)
- return img[ytile:ytile+htile,xtile:xtile+wtile,:].copy()
+ if len(img.shape)==2:
+ return img[ytile:ytile+htile,xtile:xtile+wtile].copy()
+ else:
+ return img[ytile:ytile+htile,xtile:xtile+wtile,:].copy()
def compute_image_means(img):
"""Calculate the mean of each color channel in the image.
@@ -755,6 +758,7 @@
[gy, gx] = numpy.gradient(luma)
return numpy.average(numpy.sqrt(gy*gy + gx*gx))
+
def normalize_img(img):
"""Normalize the image values to between 0 and 1.
@@ -774,31 +778,30 @@
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'
+ chart_loc: float converted xnorm,ynorm,wnorm,hnorm,scale from argv text.
+ argv is of form 'chart_loc=0.45,0.45,0.1,0.1,1.0'
"""
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
+ return None, None, None, None, None
-def flip_mirror_img_per_argv(img):
- """Flip/mirror an image if "flip" or "mirror" is in argv
+def rotate_img_per_argv(img):
+ """Rotate an image 180 degrees if "rotate" is in argv
Args:
img: 2-D numpy array of image values
Returns:
- Flip/mirrored image
+ Rotated image
"""
img_out = img
- if "flip" in sys.argv:
- img_out = numpy.flipud(img_out)
- if "mirror" in sys.argv:
- img_out = numpy.fliplr(img_out)
+ if "rotate180" in sys.argv:
+ img_out = numpy.fliplr(numpy.flipud(img_out))
return img_out
+
def stationary_lens_cap(cam, req, fmt):
"""Take up to NUM_TRYS caps and save the 1st one with lens stationary.
@@ -823,6 +826,7 @@
raise its.error.Error('Cannot settle lens after %d trys!' % trys)
return cap[NUM_FRAMES-1]
+
class __UnitTest(unittest.TestCase):
"""Run a suite of unit tests on this module.
"""
diff --git a/apps/CameraITS/tests/scene2/test_faces.py b/apps/CameraITS/tests/scene2/test_faces.py
index 4e30fc1..2acce85 100644
--- a/apps/CameraITS/tests/scene2/test_faces.py
+++ b/apps/CameraITS/tests/scene2/test_faces.py
@@ -60,7 +60,7 @@
# but it should detect at least one face in last frame
if i == NUM_TEST_FRAMES - 1:
img = its.image.convert_capture_to_rgb_image(cap, props=props)
- img = its.image.flip_mirror_img_per_argv(img)
+ img = its.image.rotate_img_per_argv(img)
img_name = "%s_fd_mode_%s.jpg" % (NAME, fd_mode)
its.image.write_image(img, img_name)
if len(faces) == 0:
diff --git a/apps/CameraITS/tests/scene3/test_flip_mirror.py b/apps/CameraITS/tests/scene3/test_flip_mirror.py
new file mode 100644
index 0000000..86d96f1
--- /dev/null
+++ b/apps/CameraITS/tests/scene3/test_flip_mirror.py
@@ -0,0 +1,130 @@
+# 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.
+
+import os
+import cv2
+
+import its.caps
+import its.cv2image
+import its.device
+import its.image
+import its.objects
+import numpy as np
+
+NAME = os.path.basename(__file__).split('.')[0]
+CHART_FILE = os.path.join(os.environ['CAMERA_ITS_TOP'], 'pymodules', 'its',
+ 'test_images', 'ISO12233.png')
+CHART_HEIGHT = 13.5 # cm
+CHART_DISTANCE = 30.0 # cm
+CHART_SCALE_START = 0.65
+CHART_SCALE_STOP = 1.35
+CHART_SCALE_STEP = 0.025
+CHART_ORIENTATIONS = ['nominal', 'flip', 'mirror', 'rotate']
+VGA_WIDTH = 640
+VGA_HEIGHT = 480
+(X_CROP, Y_CROP) = (0.5, 0.5) # crop center area of ISO12233 chart
+
+
+def test_flip_mirror(cam, props, fmt, chart):
+ """Return if image is flipped or mirrored.
+
+ Args:
+ cam (class): An open device session
+ props (class): Properties of cam
+ fmt (dict): Capture format
+ chart (class): Object with chart properties
+
+ Returns:
+ boolean: True if flipped, False if not
+ """
+
+ # determine if in debug mode
+ debug = its.caps.debug_mode()
+
+ # get a local copy of the chart template
+ template = cv2.imread(CHART_FILE, cv2.IMREAD_ANYDEPTH)
+
+ # take img, crop chart, scale and prep for cv2 template match
+ req = its.objects.auto_capture_request()
+ cap = cam.do_capture(req, fmt)
+ y, _, _ = its.image.convert_capture_to_planes(cap, props)
+ y = its.image.rotate_img_per_argv(y)
+ patch = its.image.get_image_patch(y, chart.xnorm, chart.ynorm,
+ chart.wnorm, chart.hnorm)
+ patch = 255 * its.cv2image.gray_scale_img(patch)
+ patch = its.cv2image.scale_img(patch.astype(np.uint8), chart.scale)
+
+ # save full images if in debug
+ if debug:
+ its.image.write_image(template[:, :, np.newaxis]/255.0,
+ '%s_template.jpg' % NAME)
+
+ # save patch
+ its.image.write_image(patch[:, :, np.newaxis]/255.0,
+ '%s_scene_patch.jpg' % NAME)
+
+ # crop center areas and strip off any extra rows/columns
+ template = its.image.get_image_patch(template, (1-X_CROP)/2, (1-Y_CROP)/2,
+ X_CROP, Y_CROP)
+ patch = its.image.get_image_patch(patch, (1-X_CROP)/2,
+ (1-Y_CROP)/2, X_CROP, Y_CROP)
+ patch = patch[0:min(patch.shape[0], template.shape[0]),
+ 0:min(patch.shape[1], template.shape[1])]
+ comp_chart = patch
+
+ # determine optimum orientation
+ opts = []
+ for orientation in CHART_ORIENTATIONS:
+ if orientation == 'flip':
+ comp_chart = np.flipud(patch)
+ elif orientation == 'mirror':
+ comp_chart = np.fliplr(patch)
+ elif orientation == 'rotate':
+ comp_chart = np.flipud(np.fliplr(patch))
+ correlation = cv2.matchTemplate(comp_chart, template, cv2.TM_CCOEFF)
+ _, opt_val, _, _ = cv2.minMaxLoc(correlation)
+ if debug:
+ cv2.imwrite('%s_%s.jpg' % (NAME, orientation), comp_chart)
+ print ' %s correlation value: %d' % (orientation, opt_val)
+ opts.append(opt_val)
+
+ # determine if 'nominal' or 'rotated' is best orientation
+ assert_flag = (opts[0] == max(opts) or opts[3] == max(opts))
+ assert assert_flag, ('Optimum orientation is %s' %
+ CHART_ORIENTATIONS[np.argmax(opts)])
+ # print warning if rotated
+ if opts[3] == max(opts):
+ print 'Image is rotated 180 degrees. Try "rotate" flag.'
+
+
+def main():
+ """Test if image is properly oriented."""
+
+ print '\nStarting test_flip_mirror.py'
+
+ # initialize chart class and locate chart in scene
+ 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()
+ fmt = {'format': 'yuv', 'width': VGA_WIDTH, 'height': VGA_HEIGHT}
+
+ # test that image is not flipped, mirrored, or rotated
+ test_flip_mirror(cam, props, fmt, chart)
+
+
+if __name__ == '__main__':
+ main()
diff --git a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
index 68575ee..24c6841 100644
--- a/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
+++ b/apps/CameraITS/tests/scene3/test_lens_movement_reporting.py
@@ -84,7 +84,7 @@
print ' current lens location (diopters): %.3f' % data['loc']
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)
+ y = its.image.rotate_img_per_argv(y)
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))
diff --git a/apps/CameraITS/tests/scene3/test_lens_position.py b/apps/CameraITS/tests/scene3/test_lens_position.py
index 6fb09e6..2194b8c 100644
--- a/apps/CameraITS/tests/scene3/test_lens_position.py
+++ b/apps/CameraITS/tests/scene3/test_lens_position.py
@@ -105,7 +105,7 @@
print ' focus distance (diopters): %.3f' % data['fd']
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)
+ y = its.image.rotate_img_per_argv(y)
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))
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 20ff1cd..15dd837 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -109,6 +109,7 @@
],
"scene3": [
"test_3a_consistency",
+ "test_flip_mirror",
"test_lens_movement_reporting",
"test_lens_position"
],
@@ -311,8 +312,9 @@
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)
+ chart_loc_arg = 'chart_loc=%.2f,%.2f,%.2f,%.2f,%.3f' % (
+ chart.xnorm, chart.ynorm, chart.wnorm, chart.hnorm,
+ chart.scale)
# Run each test, capturing stdout and stderr.
for (testname, testpath) in tests:
if auto_scene_switch: