CameraITS: Added tool for converting YUV420 NV21 and planar to JPEG

Change-Id: Ib0f9622164e635e08dca28b6d7e64154bf87b5a1
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 4769bb7..89e579f 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -71,7 +71,7 @@
         y = cap["data"][0:w*h]
         u = cap["data"][w*h:w*h*5/4]
         v = cap["data"][w*h*5/4:w*h*6/4]
-        return convert_yuv420_to_rgb_image(y, u, v, w, h)
+        return convert_yuv420_planar_to_rgb_image(y, u, v, w, h)
     elif cap["format"] == "jpeg":
         return decompress_jpeg_to_rgb_image(cap["data"])
     elif cap["format"] == "raw":
@@ -371,10 +371,10 @@
     img = numpy.dot(img.reshape(w*h,3), ccm.T).reshape(h,w,3).clip(0.0,1.0)
     return img
 
-def convert_yuv420_to_rgb_image(y_plane, u_plane, v_plane,
-                                w, h,
-                                ccm_yuv_to_rgb=DEFAULT_YUV_TO_RGB_CCM,
-                                yuv_off=DEFAULT_YUV_OFFSETS):
+def convert_yuv420_planar_to_rgb_image(y_plane, u_plane, v_plane,
+                                       w, h,
+                                       ccm_yuv_to_rgb=DEFAULT_YUV_TO_RGB_CCM,
+                                       yuv_off=DEFAULT_YUV_OFFSETS):
     """Convert a YUV420 8-bit planar image to an RGB image.
 
     Args:
@@ -426,14 +426,20 @@
 
 def load_yuv420_to_rgb_image(yuv_fname,
                              w, h,
+                             layout="planar",
                              ccm_yuv_to_rgb=DEFAULT_YUV_TO_RGB_CCM,
                              yuv_off=DEFAULT_YUV_OFFSETS):
     """Load a YUV420 image file, and return as an RGB image.
 
+    Supported layouts include "planar" and "nv21". The "yuv" formatted captures
+    returned from the device via do_capture are in the "planar" layout; other
+    layouts may only be needed for loading files from other sources.
+
     Args:
         yuv_fname: The path of the YUV420 file.
         w: The width of the image.
         h: The height of the image.
+        layout: (Optional) the layout of the YUV data (as a string).
         ccm_yuv_to_rgb: (Optional) the 3x3 CCM to convert from YUV to RGB.
         yuv_off: (Optional) offsets to subtract from each of Y,U,V values.
 
@@ -441,13 +447,24 @@
         RGB float-3 image array, with pixel values in [0.0, 1.0].
     """
     with open(yuv_fname, "rb") as f:
-        y = numpy.fromfile(f, numpy.uint8, w*h, "")
-        v = numpy.fromfile(f, numpy.uint8, w*h/4, "")
-        u = numpy.fromfile(f, numpy.uint8, w*h/4, "")
-        return convert_yuv420_to_rgb_image(y,u,v,w,h,ccm_yuv_to_rgb,yuv_off)
+        if layout == "planar":
+            # Plane of Y, plane of V, plane of U.
+            y = numpy.fromfile(f, numpy.uint8, w*h, "")
+            v = numpy.fromfile(f, numpy.uint8, w*h/4, "")
+            u = numpy.fromfile(f, numpy.uint8, w*h/4, "")
+        elif layout == "nv21":
+            # Plane of Y, plane of interleaved VUVUVU...
+            y = numpy.fromfile(f, numpy.uint8, w*h, "")
+            vu = numpy.fromfile(f, numpy.uint8, w*h/2, "")
+            v = vu[0::2]
+            u = vu[1::2]
+        else:
+            raise its.error.Error('Unsupported image layout')
+        return convert_yuv420_planar_to_rgb_image(
+                y,u,v,w,h,ccm_yuv_to_rgb,yuv_off)
 
-def load_yuv420_to_yuv_planes(yuv_fname, w, h):
-    """Load a YUV420 image file, and return separate Y, U, and V plane images.
+def load_yuv420_planar_to_yuv_planes(yuv_fname, w, h):
+    """Load a YUV420 planar image file, and return Y, U, and V plane images.
 
     Args:
         yuv_fname: The path of the YUV420 file.
diff --git a/apps/CameraITS/tools/convert_yuv_to_jpg.py b/apps/CameraITS/tools/convert_yuv_to_jpg.py
new file mode 100644
index 0000000..4498c2a
--- /dev/null
+++ b/apps/CameraITS/tools/convert_yuv_to_jpg.py
@@ -0,0 +1,37 @@
+# Copyright 2015 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 its.image
+import sys
+
+def main():
+    """Open a YUV420 file and save it as a JPEG.
+
+    Command line args:
+        filename.yuv: The YUV420 file to open.
+        w: The width of the image.
+        h: The height of the image.
+        layout: The layout of the data, in ["planar", "nv21"].
+    """
+    if len(sys.argv) != 5:
+        print "Usage: python %s <filename.yuv> <w> <h> <layout>"%(sys.argv[0])
+    else:
+        fname, w,h = sys.argv[1], int(sys.argv[2]), int(sys.argv[3])
+        layout = sys.argv[4]
+        img = its.image.load_yuv420_to_rgb_image(fname, w,h, layout=layout)
+        its.image.write_image(img, fname.replace(".yuv",".jpg"), False)
+
+if __name__ == '__main__':
+    main()
+