diff options
author | 2018-02-07 16:48:21 -0800 | |
---|---|---|
committer | 2018-02-07 16:48:21 -0800 | |
commit | f7f7036d1cdc5716aff976fae0ea4d1b9a931b56 (patch) | |
tree | 44c6fa35efa3edeaa9976a7edab21460f871d5f5 | |
parent | 6b1208c647079e822f19a4c869af52bf3a06d532 (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.cc | 42 | ||||
-rw-r--r-- | tensorflow/python/ops/image_ops_test.py | 26 |
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: |