Skip to content

Commit

Permalink
cwebp: Fix -print_psnr for near_lossless
Browse files Browse the repository at this point in the history
The output was always 99dB because the lossless pipeline is not
modifying the 'picture'. Changing that is not that simple because
near_lossless impacts both VP8ApplyNearLossless() and
ApplyPredictFilter(); the latter cannot be applied as is to the input
and thus the final modified 'picture' cannot be easily retrieved
without decoding the encoded bitstream. Hence ReadWebP() is called in
cwebp.c on the encoded bitstream kept in memory to get the correct
distortion.

However -get_psnr returns a different distortion than get_disto for
lossy encoding configurations because cwebp loads the source as YUV
while get_disto directly reads it as RGB without conversion loss.

Change-Id: I5c32cf8f89eb137973dc7eebda747682d921b8e2
  • Loading branch information
y-guyon committed Aug 17, 2020
1 parent cf2f88b commit def64e9
Showing 1 changed file with 80 additions and 32 deletions.
112 changes: 80 additions & 32 deletions examples/cwebp.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
//
// Author: Skal ([email protected])

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Expand All @@ -23,6 +24,7 @@
#include "../examples/example_util.h"
#include "../imageio/image_dec.h"
#include "../imageio/imageio_util.h"
#include "../imageio/webpdec.h"
#include "./stopwatch.h"
#include "./unicode.h"
#include "webp/encode.h"
Expand Down Expand Up @@ -148,7 +150,7 @@ static void PrintPercents(const int counts[4]) {
int s;
const int total = counts[0] + counts[1] + counts[2] + counts[3];
for (s = 0; s < 4; ++s) {
fprintf(stderr, "| %2d%%", (int)(100. * counts[s] / total + .5));
fprintf(stderr, "| %3d%%", (int)(100. * counts[s] / total + .5));
}
fprintf(stderr, "| %7d\n", total);
}
Expand Down Expand Up @@ -666,6 +668,7 @@ int main(int argc, const char* argv[]) {
WebPConfig config;
WebPAuxStats stats;
WebPMemoryWriter memory_writer;
int use_memory_writer;
Metadata metadata;
Stopwatch stop_watch;

Expand Down Expand Up @@ -981,6 +984,14 @@ int main(int argc, const char* argv[]) {
const double read_time = StopwatchReadAndReset(&stop_watch);
fprintf(stderr, "Time to read input: %.3fs\n", read_time);
}
// The bitstream should be kept in memory when metadata must be appended
// before writing it to a file/stream, and/or when the near-losslessly encoded
// bitstream must be decoded for distortion computation (lossy will modify the
// 'picture' but not the lossless pipeline).
// Otherwise directly write the bitstream to a file.
use_memory_writer = (out_file != NULL && keep_metadata) ||
(!quiet && print_distortion >= 0 && config.lossless &&
config.near_lossless < 100);

// Open the output
if (out_file != NULL) {
Expand All @@ -995,15 +1006,19 @@ int main(int argc, const char* argv[]) {
WFPRINTF(stderr, "Saving file '%s'\n", (const W_CHAR*)out_file);
}
}
if (keep_metadata == 0) {
picture.writer = MyWriter;
picture.custom_ptr = (void*)out;
} else {
if (use_memory_writer) {
picture.writer = WebPMemoryWrite;
picture.custom_ptr = (void*)&memory_writer;
} else {
picture.writer = MyWriter;
picture.custom_ptr = (void*)out;
}
} else {
out = NULL;
if (use_memory_writer) {
picture.writer = WebPMemoryWrite;
picture.custom_ptr = (void*)&memory_writer;
}
if (!quiet && !short_output) {
fprintf(stderr, "No output file specified (no -o flag). Encoding will\n");
fprintf(stderr, "be performed, but its results discarded.\n\n");
Expand Down Expand Up @@ -1082,8 +1097,12 @@ int main(int argc, const char* argv[]) {
if (picture.extra_info_type > 0) {
AllocExtraInfo(&picture);
}
if (print_distortion >= 0) { // Save original picture for later comparison
WebPPictureCopy(&picture, &original_picture);
// Save original picture for later comparison. Only for lossy as lossless does
// not modify 'picture' (even near-lossless).
if (print_distortion >= 0 && !config.lossless &&
!WebPPictureCopy(&picture, &original_picture)) {
fprintf(stderr, "Error! Cannot copy temporary picture\n");
goto Error;
}

// Compress.
Expand All @@ -1101,7 +1120,38 @@ int main(int argc, const char* argv[]) {
fprintf(stderr, "Time to encode picture: %.3fs\n", encode_time);
}

// Write info
// Get the decompressed image for the lossless pipeline.
if (!quiet && print_distortion >= 0 && config.lossless) {
if (config.near_lossless == 100) {
// Pure lossless: image was not modified, make 'original_picture' a view
// of 'picture' by copying all members except the freeable pointers.
original_picture = picture;
original_picture.memory_ = original_picture.memory_argb_ = NULL;
} else {
// Decode the bitstream stored in 'memory_writer' to get the altered image
// to 'picture'; save the 'original_picture' beforehand.
assert(use_memory_writer);
original_picture = picture;
if (!WebPPictureInit(&picture)) { // Do not free 'picture'.
fprintf(stderr, "Error! Version mismatch!\n");
goto Error;
}

picture.use_argb = 1;
if (!ReadWebP(memory_writer.mem, memory_writer.size, &picture,
/*keep_alpha=*/WebPPictureHasTransparency(&picture),
/*metadata=*/NULL)) {
fprintf(stderr, "Error! Cannot decode encoded WebP bitstream\n");
fprintf(stderr, "Error code: %d (%s)\n", picture.error_code,
kErrorMessages[picture.error_code]);
goto Error;
}
picture.stats = original_picture.stats;
}
original_picture.stats = NULL;
}

// Write the YUV planes to a PGM file. Only available for lossy.
if (dump_file) {
if (picture.use_argb) {
fprintf(stderr, "Warning: can't dump file (-d option) "
Expand All @@ -1112,31 +1162,29 @@ int main(int argc, const char* argv[]) {
}
}

if (keep_metadata != 0) {
if (out != NULL) {
if (!WriteWebPWithMetadata(out, &picture, &memory_writer,
&metadata, keep_metadata, &metadata_written)) {
fprintf(stderr, "Error writing WebP file with metadata!\n");
goto Error;
}
} else { // output is disabled, just display the metadata stats.
const struct {
const MetadataPayload* const payload;
int flag;
} *iter, info[] = {
{ &metadata.exif, METADATA_EXIF },
{ &metadata.iccp, METADATA_ICC },
{ &metadata.xmp, METADATA_XMP },
{ NULL, 0 }
};
uint32_t unused1 = 0;
uint64_t unused2 = 0;
if (use_memory_writer && out != NULL &&
!WriteWebPWithMetadata(out, &picture, &memory_writer, &metadata,
keep_metadata, &metadata_written)) {
fprintf(stderr, "Error writing WebP file!\n");
goto Error;
}

for (iter = info; iter->payload != NULL; ++iter) {
if (UpdateFlagsAndSize(iter->payload, !!(keep_metadata & iter->flag),
0, &unused1, &unused2)) {
metadata_written |= iter->flag;
}
if (out == NULL && keep_metadata) {
// output is disabled, just display the metadata stats.
const struct {
const MetadataPayload* const payload;
int flag;
} *iter, info[] = {{&metadata.exif, METADATA_EXIF},
{&metadata.iccp, METADATA_ICC},
{&metadata.xmp, METADATA_XMP},
{NULL, 0}};
uint32_t unused1 = 0;
uint64_t unused2 = 0;

for (iter = info; iter->payload != NULL; ++iter) {
if (UpdateFlagsAndSize(iter->payload, !!(keep_metadata & iter->flag),
/*flag=*/0, &unused1, &unused2)) {
metadata_written |= iter->flag;
}
}
}
Expand Down

0 comments on commit def64e9

Please sign in to comment.