aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorGravatar Yong Tang <yong.tang.github@outlook.com>2018-02-07 16:48:21 -0800
committerGravatar Jonathan Hseu <vomjom@vomjom.net>2018-02-07 16:48:21 -0800
commitf7f7036d1cdc5716aff976fae0ea4d1b9a931b56 (patch)
tree44c6fa35efa3edeaa9976a7edab21460f871d5f5
parent6b1208c647079e822f19a4c869af52bf3a06d532 (diff)
Add optimized gif support for decode_gif (#16804)
* Add optimized gif support for decode_gif While revisiting the issue of 15838, I noticed that currently optimized gif is not supported. However, optimized gif is actually possible to be processed as essentially the subsequent frame just adds the content on top of the previous frame on canvas. This fix adds the support for optimized gif with decode_gif. As is shown in the added test case, optimized gif (`optimized.gif`) could be handled the same way as original gif (`scan.gif`). This fix fixes 15838. Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Format gif_io.cc with clang-format -i --style=Google Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Add test case to cover optimized gif support for decode_gif. Signed-off-by: Yong Tang <yong.tang.github@outlook.com> * Add `#include <algorithm>` to fix Windows build errors This commit add `#include <algorithm>` to fix build errors on Windows platform. Signed-off-by: Yong Tang <yong.tang.github@outlook.com>
-rw-r--r--tensorflow/core/lib/gif/gif_io.cc42
-rw-r--r--tensorflow/python/ops/image_ops_test.py26
2 files changed, 40 insertions, 28 deletions
diff --git a/tensorflow/core/lib/gif/gif_io.cc b/tensorflow/core/lib/gif/gif_io.cc
index e5deb2b873..9a5215320f 100644
--- a/tensorflow/core/lib/gif/gif_io.cc
+++ b/tensorflow/core/lib/gif/gif_io.cc
@@ -16,6 +16,7 @@ limitations under the License.
// Functions to read images in GIF format.
#include "tensorflow/core/lib/gif/gif_io.h"
+#include <algorithm>
#include "tensorflow/core/lib/gtl/cleanup.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/gif.h"
@@ -89,23 +90,52 @@ uint8* Decode(const void* srcdata, int datasize,
uint8* const dstdata = allocate_output(num_frames, width, height, channel);
if (!dstdata) return nullptr;
for (int k = 0; k < num_frames; k++) {
+ uint8* this_dst = dstdata + k * width * channel * height;
+
SavedImage* this_image = &gif_file->SavedImages[k];
GifImageDesc* img_desc = &this_image->ImageDesc;
+
+ int imgLeft = img_desc->Left;
+ int imgTop = img_desc->Top;
+ int imgRight = img_desc->Left + img_desc->Width;
+ int imgBottom = img_desc->Top + img_desc->Height;
+
if (img_desc->Left != 0 || img_desc->Top != 0 || img_desc->Width != width ||
img_desc->Height != height) {
- *error_string = strings::StrCat("can't process optimized gif");
- return nullptr;
+ // If the first frame does not fill the entire canvas then return error.
+ if (k == 0) {
+ *error_string =
+ strings::StrCat("the first frame does not fill the canvas");
+ return nullptr;
+ }
+ // Otherwise previous frame will be reused to fill the unoccupied canvas.
+ imgLeft = std::max(imgLeft, 0);
+ imgTop = std::max(imgTop, 0);
+ imgRight = std::min(imgRight, width);
+ imgBottom = std::min(imgBottom, height);
+
+ uint8* last_dst = dstdata + (k - 1) * width * channel * height;
+ for (int i = 0; i < height; ++i) {
+ uint8* p_dst = this_dst + i * width * channel;
+ uint8* l_dst = last_dst + i * width * channel;
+ for (int j = 0; j < width; ++j) {
+ p_dst[j * channel + 0] = l_dst[j * channel + 0];
+ p_dst[j * channel + 1] = l_dst[j * channel + 1];
+ p_dst[j * channel + 2] = l_dst[j * channel + 2];
+ }
+ }
}
ColorMapObject* color_map = this_image->ImageDesc.ColorMap
? this_image->ImageDesc.ColorMap
: gif_file->SColorMap;
- uint8* this_dst = dstdata + k * width * channel * height;
- for (int i = 0; i < height; ++i) {
+ for (int i = imgTop; i < imgBottom; ++i) {
uint8* p_dst = this_dst + i * width * channel;
- for (int j = 0; j < width; ++j) {
- GifByteType color_index = this_image->RasterBits[i * width + j];
+ for (int j = imgLeft; j < imgRight; ++j) {
+ GifByteType color_index =
+ this_image->RasterBits[(i - img_desc->Top) * (img_desc->Width) +
+ (j - img_desc->Left)];
const GifColorType& gif_color = color_map->Colors[color_index];
p_dst[j * channel + 0] = gif_color.Red;
p_dst[j * channel + 1] = gif_color.Green;
diff --git a/tensorflow/python/ops/image_ops_test.py b/tensorflow/python/ops/image_ops_test.py
index 82b77ee8e3..0dc1c56e7d 100644
--- a/tensorflow/python/ops/image_ops_test.py
+++ b/tensorflow/python/ops/image_ops_test.py
@@ -2821,20 +2821,9 @@ class PngTest(test_util.TensorFlowTestCase):
class GifTest(test_util.TensorFlowTestCase):
- def testOptimizedGifErrorString(self):
- filename = "tensorflow/core/lib/gif/testdata/optimized.gif"
-
- with self.test_session(use_gpu=True) as sess:
- gif = io_ops.read_file(filename)
- image = image_ops.decode_gif(gif)
- with self.assertRaisesRegexp(errors.InvalidArgumentError,
- "can't process optimized gif"):
- gif, image = sess.run([gif, image])
-
- def testValid(self):
+ def _testValid(self, filename):
# Read some real GIFs
prefix = "tensorflow/core/lib/gif/testdata/"
- filename = "scan.gif"
WIDTH = 20
HEIGHT = 40
STRIDE = 5
@@ -2861,16 +2850,9 @@ class GifTest(test_util.TensorFlowTestCase):
self.assertAllClose(frame, gt)
- def testInValid(self):
- # Read some real GIFs
- prefix = "tensorflow/core/lib/gif/testdata/"
- filename = "optimized.gif"
-
- with self.test_session(use_gpu=True) as sess:
- gif0 = io_ops.read_file(prefix + filename)
- image0 = image_ops.decode_gif(gif0)
- with self.assertRaises(errors.InvalidArgumentError):
- gif0, image0 = sess.run([gif0, image0])
+ def testValid(self):
+ self._testValid("scan.gif")
+ self._testValid("optimized.gif")
def testShape(self):
with self.test_session(use_gpu=True) as sess: