anim_diff: Add an experimental option for max inter-frame diff.

After the introduction of lossy frame rectangles
we need equivalent option in anim_diff for merging similar frames.

Change-Id: I1d03acace396ec4cb0212586c6e8b8ec5b0b0bfc
This commit is contained in:
Urvang Joshi 2016-04-04 18:43:58 +00:00 committed by James Zern
parent 31f2b8d8e1
commit 47dd07080f
2 changed files with 76 additions and 7 deletions

View File

@ -21,7 +21,7 @@ if BUILD_ANIMDIFF
endif endif
anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h anim_diff_SOURCES = anim_diff.c anim_util.c anim_util.h
anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(GIF_INCLUDES) anim_diff_CPPFLAGS = $(AM_CPPFLAGS) $(USE_EXPERIMENTAL_CODE) $(GIF_INCLUDES)
anim_diff_LDADD = ../src/demux/libwebpdemux.la anim_diff_LDADD = ../src/demux/libwebpdemux.la
anim_diff_LDADD += libexampleutil.la anim_diff_LDADD += libexampleutil.la
anim_diff_LDADD += $(GIF_LIBS) -lm anim_diff_LDADD += $(GIF_LIBS) -lm

View File

@ -13,6 +13,7 @@
// //
// example: anim_diff foo.gif bar.webp // example: anim_diff foo.gif bar.webp
#include <assert.h>
#include <limits.h> #include <limits.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> // for 'strtod'. #include <stdlib.h> // for 'strtod'.
@ -29,20 +30,67 @@ static int AdditionWillOverflow(int a, int b) {
return (b > 0) && (a > INT_MAX - b); return (b > 0) && (a > INT_MAX - b);
} }
// Minimize number of frames by combining successive frames that have exact same static int FramesAreEqual(const uint8_t* const rgba1,
// ARGB data into a single longer duration frame. const uint8_t* const rgba2, int width, int height) {
static void MinimizeAnimationFrames(AnimatedImage* const img) { const int stride = width * 4; // Always true for 'DecodedFrame.rgba'.
return !memcmp(rgba1, rgba2, stride * height);
}
static WEBP_INLINE int PixelsAreSimilar(uint32_t src, uint32_t dst,
int max_allowed_diff) {
const int src_a = (src >> 24) & 0xff;
const int src_r = (src >> 16) & 0xff;
const int src_g = (src >> 8) & 0xff;
const int src_b = (src >> 0) & 0xff;
const int dst_a = (dst >> 24) & 0xff;
const int dst_r = (dst >> 16) & 0xff;
const int dst_g = (dst >> 8) & 0xff;
const int dst_b = (dst >> 0) & 0xff;
return (abs(src_r * src_a - dst_r * dst_a) <= (max_allowed_diff * 255)) &&
(abs(src_g * src_a - dst_g * dst_a) <= (max_allowed_diff * 255)) &&
(abs(src_b * src_a - dst_b * dst_a) <= (max_allowed_diff * 255)) &&
(abs(src_a - dst_a) <= max_allowed_diff);
}
static int FramesAreSimilar(const uint8_t* const rgba1,
const uint8_t* const rgba2,
int width, int height, int max_allowed_diff) {
int i, j;
assert(max_allowed_diff > 0);
for (j = 0; j < height; ++j) {
for (i = 0; i < width; ++i) {
const int stride = width * 4;
const size_t offset = j * stride + i;
if (!PixelsAreSimilar(rgba1[offset], rgba2[offset], max_allowed_diff)) {
return 0;
}
}
}
return 1;
}
// Minimize number of frames by combining successive frames that have at max
// 'max_diff' difference per channel between corresponding pixels.
static void MinimizeAnimationFrames(AnimatedImage* const img, int max_diff) {
uint32_t i; uint32_t i;
for (i = 1; i < img->num_frames; ++i) { for (i = 1; i < img->num_frames; ++i) {
DecodedFrame* const frame1 = &img->frames[i - 1]; DecodedFrame* const frame1 = &img->frames[i - 1];
DecodedFrame* const frame2 = &img->frames[i]; DecodedFrame* const frame2 = &img->frames[i];
const uint8_t* const rgba1 = frame1->rgba; const uint8_t* const rgba1 = frame1->rgba;
const uint8_t* const rgba2 = frame2->rgba; const uint8_t* const rgba2 = frame2->rgba;
int should_merge_frames = 0;
// If merging frames will result in integer overflow for 'duration', // If merging frames will result in integer overflow for 'duration',
// skip merging. // skip merging.
if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue; if (AdditionWillOverflow(frame1->duration, frame2->duration)) continue;
if (!memcmp(rgba1, rgba2, img->canvas_width * 4 * img->canvas_height)) { if (max_diff > 0) {
// Merge 'i+1'th frame into 'i'th frame. should_merge_frames = FramesAreSimilar(rgba1, rgba2, img->canvas_width,
img->canvas_height, max_diff);
} else {
should_merge_frames =
FramesAreEqual(rgba1, rgba2, img->canvas_width, img->canvas_height);
}
if (should_merge_frames) { // Merge 'i+1'th frame into 'i'th frame.
frame1->duration += frame2->duration; frame1->duration += frame2->duration;
if (i + 1 < img->num_frames) { if (i + 1 < img->num_frames) {
memmove(&img->frames[i], &img->frames[i + 1], memmove(&img->frames[i], &img->frames[i + 1],
@ -125,6 +173,11 @@ static void Help(void) {
printf(" -min_psnr <float> ... minimum per-frame PSNR\n"); printf(" -min_psnr <float> ... minimum per-frame PSNR\n");
printf(" -raw_comparison ..... if this flag is not used, RGB is\n"); printf(" -raw_comparison ..... if this flag is not used, RGB is\n");
printf(" premultiplied before comparison\n"); printf(" premultiplied before comparison\n");
#ifdef WEBP_EXPERIMENTAL_FEATURES
printf(" -max_diff <int> ..... maximum allowed difference per channel "
" between corresponding pixels in subsequent"
" frames\n");
#endif
} }
int main(int argc, const char* argv[]) { int main(int argc, const char* argv[]) {
@ -135,6 +188,7 @@ int main(int argc, const char* argv[]) {
int got_input1 = 0; int got_input1 = 0;
int got_input2 = 0; int got_input2 = 0;
int premultiply = 1; int premultiply = 1;
int max_diff = 0;
int i, c; int i, c;
const char* files[2] = { NULL, NULL }; const char* files[2] = { NULL, NULL };
AnimatedImage images[2]; AnimatedImage images[2];
@ -168,6 +222,21 @@ int main(int argc, const char* argv[]) {
} }
} else if (!strcmp(argv[c], "-raw_comparison")) { } else if (!strcmp(argv[c], "-raw_comparison")) {
premultiply = 0; premultiply = 0;
#ifdef WEBP_EXPERIMENTAL_FEATURES
} else if (!strcmp(argv[c], "-max_diff")) {
if (c < argc - 1) {
const char* const v = argv[++c];
char* end = NULL;
const int n = (int)strtol(v, &end, 10);
if (end == v) {
parse_error = 1;
fprintf(stderr, "Error! '%s' is not an integer.\n", v);
}
max_diff = n;
} else {
parse_error = 1;
}
#endif
} else { } else {
if (!got_input1) { if (!got_input1) {
files[0] = argv[c]; files[0] = argv[c];
@ -201,7 +270,7 @@ int main(int argc, const char* argv[]) {
return_code = -2; return_code = -2;
goto End; goto End;
} else { } else {
MinimizeAnimationFrames(&images[i]); MinimizeAnimationFrames(&images[i], max_diff);
} }
} }