ITS: add checks for android.control.availableEffects

bug: 117979055
bug: 112369094

Change-Id: I150b419dfe61ba39a55eee1240dcb1c7c5be13c1
diff --git a/apps/CameraITS/tests/scene2/test_effects.py b/apps/CameraITS/tests/scene2/test_effects.py
new file mode 100644
index 0000000..7d034f2
--- /dev/null
+++ b/apps/CameraITS/tests/scene2/test_effects.py
@@ -0,0 +1,104 @@
+# Copyright 2018 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.path
+import its.caps
+import its.device
+import its.image
+import its.objects
+import numpy as np
+
+# android.control.availableEffects
+EFFECTS = {0: 'OFF',
+           1: 'MONO',
+           2: 'NEGATIVE',
+           3: 'SOLARIZE',
+           4: 'SEPIA',
+           5: 'POSTERIZE',
+           6: 'WHITEBOARD',
+           7: 'BLACKBOARD',
+           8: 'AQUA'}
+MONO_UV_SPREAD_MAX = 2  # max spread for U & V channels [0:255] for mono image
+NAME = os.path.basename(__file__).split('.')[0]
+W, H = 640, 480
+YUV_MAX = 255.0  # normalization number for YUV images [0:1] --> [0:255]
+YUV_UV_SPREAD_MIN = 40  # min spread for U & V channels [0:255] for color image
+YUV_Y_SPREAD_MIN = 100  # min spread for Y channel [0:255] for color image
+
+
+def main():
+    """Test effects.
+
+    Test: capture frame for supported camera effects and check if generated
+    correctly. Note we only check effects OFF and MONO currently, but save
+    images for all supported effects.
+    """
+
+    print '\nStarting %s' % NAME
+    with its.device.ItsSession() as cam:
+        props = cam.get_camera_properties()
+        mono_camera = its.caps.mono_camera(props)
+        effects = props['android.control.availableEffects']
+        its.caps.skip_unless(effects)
+        print 'Supported effects:', effects
+        failed = []
+        for effect in effects:
+            req = its.objects.auto_capture_request()
+            req['android.control.effectMode'] = effect
+            fmt = {'format': 'yuv', 'width': W, 'height': H}
+            cap = cam.do_capture(req, fmt)
+
+            # Save image
+            img = its.image.convert_capture_to_rgb_image(cap, props=props)
+            its.image.write_image(img, '%s_%s.jpg' % (NAME, EFFECTS[effect]))
+
+            # Simple checks
+            if effect is 0:
+                print 'Checking effects OFF...'
+                y, u, v = its.image.convert_capture_to_planes(cap, props)
+                y_min, y_max = np.amin(y)*YUV_MAX, np.amax(y)*YUV_MAX
+                msg = 'Y_range:%.f,%.f THRESH:%d, ' % (
+                        y_min, y_max, YUV_Y_SPREAD_MIN)
+                if (y_max-y_min) < YUV_Y_SPREAD_MIN:
+                    failed.append({'effect': EFFECTS[effect], 'error': msg})
+                if not mono_camera:
+                    u_min, u_max = np.amin(u)*YUV_MAX, np.amax(u)*YUV_MAX
+                    v_min, v_max = np.amin(v)*YUV_MAX, np.amax(v)*YUV_MAX
+                    msg += 'U_range:%.f,%.f THRESH:%d, ' % (
+                            u_min, u_max, YUV_UV_SPREAD_MIN)
+                    msg += 'V_range:%.f,%.f THRESH:%d' % (
+                            v_min, v_max, YUV_UV_SPREAD_MIN)
+                    if ((u_max-u_min) < YUV_UV_SPREAD_MIN or
+                                (v_max-v_min) < YUV_UV_SPREAD_MIN):
+                        failed.append({'effect': EFFECTS[effect], 'error': msg})
+            if effect is 1:
+                print 'Checking MONO effect...'
+                _, u, v = its.image.convert_capture_to_planes(cap, props)
+                u_min, u_max = np.amin(u)*YUV_MAX, np.amax(u)*YUV_MAX
+                v_min, v_max = np.amin(v)*YUV_MAX, np.amax(v)*YUV_MAX
+                msg = 'U_range:%.f,%.f, ' % (u_min, u_max)
+                msg += 'V_range:%.f,%.f, TOL:%d' % (
+                        v_min, v_max, MONO_UV_SPREAD_MAX)
+                if ((u_max-u_min) > MONO_UV_SPREAD_MAX or
+                            (v_max-v_min) > MONO_UV_SPREAD_MAX):
+                    failed.append({'effect': EFFECTS[effect], 'error': msg})
+        if failed:
+            print 'Failed effects:'
+            for fail in failed:
+                print ' %s: %s' % (fail['effect'], fail['error'])
+        assert not failed
+
+
+if __name__ == '__main__':
+    main()