mirror of
https://github.com/webmproject/libwebp.git
synced 2024-12-25 21:28:22 +01:00
Enc: add a qmin / qmax range for quality factor
This is particularly useful for multi-pass search (but not only), to prevent the search from going over or below a reasonable threshold. E.g.: 'cwebp -qrange 50 80 ...' will prevent any unreasonable degradation. new cwebp option: -qrange min max Change-Id: I59f394533535fc20b6996bc0895f4301476d5eff
This commit is contained in:
parent
0fa56f307f
commit
6a0ff35872
3
README
3
README
@ -254,6 +254,8 @@ Options:
|
||||
-partition_limit <int> . limit quality to fit the 512k limit on
|
||||
the first partition (0=no degradation ... 100=full)
|
||||
-pass <int> ............ analysis pass number (1..10)
|
||||
-qrange <min> <max> .... specifies the permissible quality range
|
||||
(default: 0 100)
|
||||
-crop <x> <y> <w> <h> .. crop picture with the given rectangle
|
||||
-resize <w> <h> ........ resize picture (after any cropping)
|
||||
-mt .................... use multi-threading if available
|
||||
@ -294,6 +296,7 @@ Experimental Options:
|
||||
-af .................... auto-adjust filter strength
|
||||
-pre <int> ............. pre-processing filter
|
||||
|
||||
|
||||
The main options you might want to try in order to further tune the
|
||||
visual quality are:
|
||||
-preset
|
||||
|
@ -565,6 +565,8 @@ static void HelpLong(void) {
|
||||
printf(" "
|
||||
"the first partition (0=no degradation ... 100=full)\n");
|
||||
printf(" -pass <int> ............ analysis pass number (1..10)\n");
|
||||
printf(" -qrange <min> <max> .... specifies the permissible quality range\n"
|
||||
" (default: 0 100)\n");
|
||||
printf(" -crop <x> <y> <w> <h> .. crop picture with the given rectangle\n");
|
||||
printf(" -resize <w> <h> ........ resize picture (after any cropping)\n");
|
||||
printf(" -mt .................... use multi-threading if available\n");
|
||||
@ -691,9 +693,9 @@ int main(int argc, const char* argv[]) {
|
||||
} else if (!strcmp(argv[c], "-H") || !strcmp(argv[c], "-longhelp")) {
|
||||
HelpLong();
|
||||
FREE_WARGV_AND_RETURN(0);
|
||||
} else if (!strcmp(argv[c], "-o") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-o") && c + 1 < argc) {
|
||||
out_file = (const char*)GET_WARGV(argv, ++c);
|
||||
} else if (!strcmp(argv[c], "-d") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-d") && c + 1 < argc) {
|
||||
dump_file = (const char*)GET_WARGV(argv, ++c);
|
||||
config.show_compressed = 1;
|
||||
} else if (!strcmp(argv[c], "-print_psnr")) {
|
||||
@ -707,7 +709,7 @@ int main(int argc, const char* argv[]) {
|
||||
print_distortion = 2;
|
||||
} else if (!strcmp(argv[c], "-short")) {
|
||||
++short_output;
|
||||
} else if (!strcmp(argv[c], "-s") && c < argc - 2) {
|
||||
} else if (!strcmp(argv[c], "-s") && c + 2 < argc) {
|
||||
picture.width = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
picture.height = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
if (picture.width > WEBP_MAX_DIMENSION || picture.width < 0 ||
|
||||
@ -717,30 +719,30 @@ int main(int argc, const char* argv[]) {
|
||||
picture.width, picture.height);
|
||||
goto Error;
|
||||
}
|
||||
} else if (!strcmp(argv[c], "-m") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-m") && c + 1 < argc) {
|
||||
config.method = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
} else if (!strcmp(argv[c], "-q") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-q") && c + 1 < argc) {
|
||||
config.quality = ExUtilGetFloat(argv[++c], &parse_error);
|
||||
use_lossless_preset = 0; // disable -z option
|
||||
} else if (!strcmp(argv[c], "-z") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-z") && c + 1 < argc) {
|
||||
lossless_preset = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
if (use_lossless_preset != 0) use_lossless_preset = 1;
|
||||
} else if (!strcmp(argv[c], "-alpha_q") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-alpha_q") && c + 1 < argc) {
|
||||
config.alpha_quality = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-alpha_method") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-alpha_method") && c + 1 < argc) {
|
||||
config.alpha_compression = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-alpha_cleanup")) {
|
||||
// This flag is obsolete, does opposite of -exact.
|
||||
config.exact = 0;
|
||||
} else if (!strcmp(argv[c], "-exact")) {
|
||||
config.exact = 1;
|
||||
} else if (!strcmp(argv[c], "-blend_alpha") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-blend_alpha") && c + 1 < argc) {
|
||||
blend_alpha = 1;
|
||||
// background color is given in hex with an optional '0x' prefix
|
||||
background_color = ExUtilGetInt(argv[++c], 16, &parse_error);
|
||||
background_color = background_color & 0x00ffffffu;
|
||||
} else if (!strcmp(argv[c], "-alpha_filter") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-alpha_filter") && c + 1 < argc) {
|
||||
++c;
|
||||
if (!strcmp(argv[c], "none")) {
|
||||
config.alpha_filtering = 0;
|
||||
@ -756,10 +758,10 @@ int main(int argc, const char* argv[]) {
|
||||
keep_alpha = 0;
|
||||
} else if (!strcmp(argv[c], "-lossless")) {
|
||||
config.lossless = 1;
|
||||
} else if (!strcmp(argv[c], "-near_lossless") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-near_lossless") && c + 1 < argc) {
|
||||
config.near_lossless = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.lossless = 1; // use near-lossless only with lossless
|
||||
} else if (!strcmp(argv[c], "-hint") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-hint") && c + 1 < argc) {
|
||||
++c;
|
||||
if (!strcmp(argv[c], "photo")) {
|
||||
config.image_hint = WEBP_HINT_PHOTO;
|
||||
@ -771,13 +773,13 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "Error! Unrecognized image hint: %s\n", argv[c]);
|
||||
goto Error;
|
||||
}
|
||||
} else if (!strcmp(argv[c], "-size") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-size") && c + 1 < argc) {
|
||||
config.target_size = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-psnr") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-psnr") && c + 1 < argc) {
|
||||
config.target_PSNR = ExUtilGetFloat(argv[++c], &parse_error);
|
||||
} else if (!strcmp(argv[c], "-sns") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-sns") && c + 1 < argc) {
|
||||
config.sns_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-f") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-f") && c + 1 < argc) {
|
||||
config.filter_strength = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-af")) {
|
||||
config.autofilter = 1;
|
||||
@ -791,27 +793,32 @@ int main(int argc, const char* argv[]) {
|
||||
config.filter_type = 1;
|
||||
} else if (!strcmp(argv[c], "-nostrong")) {
|
||||
config.filter_type = 0;
|
||||
} else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-sharpness") && c + 1 < argc) {
|
||||
config.filter_sharpness = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-sharp_yuv")) {
|
||||
config.use_sharp_yuv = 1;
|
||||
} else if (!strcmp(argv[c], "-pass") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-pass") && c + 1 < argc) {
|
||||
config.pass = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-pre") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-qrange") && c + 2 < argc) {
|
||||
config.qmin = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
config.qmax = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
if (config.qmin < 0) config.qmin = 0;
|
||||
if (config.qmax > 100) config.qmax = 100;
|
||||
} else if (!strcmp(argv[c], "-pre") && c + 1 < argc) {
|
||||
config.preprocessing = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-segments") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-segments") && c + 1 < argc) {
|
||||
config.segments = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-partition_limit") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-partition_limit") && c + 1 < argc) {
|
||||
config.partition_limit = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-map") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-map") && c + 1 < argc) {
|
||||
picture.extra_info_type = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-crop") && c < argc - 4) {
|
||||
} else if (!strcmp(argv[c], "-crop") && c + 4 < argc) {
|
||||
crop = 1;
|
||||
crop_x = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
crop_y = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
crop_w = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
crop_h = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
} else if (!strcmp(argv[c], "-resize") && c < argc - 2) {
|
||||
} else if (!strcmp(argv[c], "-resize") && c + 2 < argc) {
|
||||
resize_w = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
resize_h = ExUtilGetInt(argv[++c], 0, &parse_error);
|
||||
#ifndef WEBP_DLL
|
||||
@ -827,7 +834,7 @@ int main(int argc, const char* argv[]) {
|
||||
show_progress = 1;
|
||||
} else if (!strcmp(argv[c], "-quiet")) {
|
||||
quiet = 1;
|
||||
} else if (!strcmp(argv[c], "-preset") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-preset") && c + 1 < argc) {
|
||||
WebPPreset preset;
|
||||
++c;
|
||||
if (!strcmp(argv[c], "default")) {
|
||||
@ -850,7 +857,7 @@ int main(int argc, const char* argv[]) {
|
||||
fprintf(stderr, "Error! Could initialize configuration with preset.\n");
|
||||
goto Error;
|
||||
}
|
||||
} else if (!strcmp(argv[c], "-metadata") && c < argc - 1) {
|
||||
} else if (!strcmp(argv[c], "-metadata") && c + 1 < argc) {
|
||||
static const struct {
|
||||
const char* option;
|
||||
int flag;
|
||||
@ -898,7 +905,7 @@ int main(int argc, const char* argv[]) {
|
||||
} else if (!strcmp(argv[c], "-v")) {
|
||||
verbose = 1;
|
||||
} else if (!strcmp(argv[c], "--")) {
|
||||
if (c < argc - 1) in_file = (const char*)GET_WARGV(argv, ++c);
|
||||
if (c + 1 < argc) in_file = (const char*)GET_WARGV(argv, ++c);
|
||||
break;
|
||||
} else if (argv[c][0] == '-') {
|
||||
fprintf(stderr, "Error! Unknown option '%s'\n", argv[c]);
|
||||
|
@ -134,6 +134,13 @@ options \fB\-size\fP or \fB\-psnr\fP. Maximum value is 10, default is 1.
|
||||
If options \fB\-size\fP or \fB\-psnr\fP were used, but \fB\-pass\fP wasn't
|
||||
specified, a default value of '6' passes will be used.
|
||||
.TP
|
||||
.BI \-qrange " int int
|
||||
Specifies the permissible interval for the quality factor. This is particularly
|
||||
useful when using multi-pass (\fB\-size\fP or \fB\-psnr\fP options).
|
||||
Default is 0 100.
|
||||
If the quality factor is outside this range, it will be clamped.
|
||||
If the minimum value must be less or equal to the maximum one.
|
||||
.TP
|
||||
.B \-af
|
||||
Turns auto\-filter on. This algorithm will spend additional time optimizing
|
||||
the filtering strength to reach a well\-balanced quality.
|
||||
|
@ -39,6 +39,8 @@ int WebPConfigInitInternal(WebPConfig* config,
|
||||
config->partitions = 0;
|
||||
config->segments = 4;
|
||||
config->pass = 1;
|
||||
config->qmin = 0;
|
||||
config->qmax = 100;
|
||||
config->show_compressed = 0;
|
||||
config->preprocessing = 0;
|
||||
config->autofilter = 0;
|
||||
@ -106,6 +108,9 @@ int WebPValidateConfig(const WebPConfig* config) {
|
||||
if (config->filter_type < 0 || config->filter_type > 1) return 0;
|
||||
if (config->autofilter < 0 || config->autofilter > 1) return 0;
|
||||
if (config->pass < 1 || config->pass > 10) return 0;
|
||||
if (config->qmin < 0 || config->qmax > 100 || config->qmin > config->qmax) {
|
||||
return 0;
|
||||
}
|
||||
if (config->show_compressed < 0 || config->show_compressed > 1) return 0;
|
||||
if (config->preprocessing < 0 || config->preprocessing > 7) return 0;
|
||||
if (config->partitions < 0 || config->partitions > 3) return 0;
|
||||
|
@ -31,10 +31,15 @@
|
||||
// we allow 2k of extra head-room in PARTITION0 limit.
|
||||
#define PARTITION0_SIZE_LIMIT ((VP8_MAX_PARTITION0_SIZE - 2048ULL) << 11)
|
||||
|
||||
static float Clamp(float v, float min, float max) {
|
||||
return (v < min) ? min : (v > max) ? max : v;
|
||||
}
|
||||
|
||||
typedef struct { // struct for organizing convergence in either size or PSNR
|
||||
int is_first;
|
||||
float dq;
|
||||
float q, last_q;
|
||||
float qmin, qmax;
|
||||
double value, last_value; // PSNR or size
|
||||
double target;
|
||||
int do_size_search;
|
||||
@ -47,7 +52,9 @@ static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
|
||||
|
||||
s->is_first = 1;
|
||||
s->dq = 10.f;
|
||||
s->q = s->last_q = enc->config_->quality;
|
||||
s->qmin = enc->config_->qmin;
|
||||
s->qmax = enc->config_->qmax;
|
||||
s->q = s->last_q = Clamp(enc->config_->quality, s->qmin, s->qmax);
|
||||
s->target = do_size_search ? (double)target_size
|
||||
: (target_PSNR > 0.) ? target_PSNR
|
||||
: 40.; // default, just in case
|
||||
@ -56,10 +63,6 @@ static int InitPassStats(const VP8Encoder* const enc, PassStats* const s) {
|
||||
return do_size_search;
|
||||
}
|
||||
|
||||
static float Clamp(float v, float min, float max) {
|
||||
return (v < min) ? min : (v > max) ? max : v;
|
||||
}
|
||||
|
||||
static float ComputeNextQ(PassStats* const s) {
|
||||
float dq;
|
||||
if (s->is_first) {
|
||||
@ -75,7 +78,7 @@ static float ComputeNextQ(PassStats* const s) {
|
||||
s->dq = Clamp(dq, -30.f, 30.f);
|
||||
s->last_q = s->q;
|
||||
s->last_value = s->value;
|
||||
s->q = Clamp(s->q + s->dq, 0.f, 100.f);
|
||||
s->q = Clamp(s->q + s->dq, s->qmin, s->qmax);
|
||||
return s->q;
|
||||
}
|
||||
|
||||
@ -848,9 +851,10 @@ int VP8EncTokenLoop(VP8Encoder* const enc) {
|
||||
}
|
||||
|
||||
#if (DEBUG_SEARCH > 0)
|
||||
printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf\n",
|
||||
printf("#%2d metric:%.1lf -> %.1lf last_q=%.2lf q=%.2lf dq=%.2lf "
|
||||
" range:[%.1f, %.1f]\n",
|
||||
num_pass_left, stats.last_value, stats.value,
|
||||
stats.last_q, stats.q, stats.dq);
|
||||
stats.last_q, stats.q, stats.dq, stats.qmin, stats.qmax);
|
||||
#endif
|
||||
if (enc->max_i4_header_bits_ > 0 && size_p0 > PARTITION0_SIZE_LIMIT) {
|
||||
++num_pass_left;
|
||||
|
@ -20,7 +20,7 @@
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#define WEBP_ENCODER_ABI_VERSION 0x020f // MAJOR(8b) + MINOR(8b)
|
||||
#define WEBP_ENCODER_ABI_VERSION 0x0210 // MAJOR(8b) + MINOR(8b)
|
||||
|
||||
// Note: forward declaring enumerations is not allowed in (strict) C and C++,
|
||||
// the types are left here for reference.
|
||||
@ -121,6 +121,8 @@ struct WebPConfig {
|
||||
int alpha_quality; // Between 0 (smallest size) and 100 (lossless).
|
||||
// Default is 100.
|
||||
int pass; // number of entropy-analysis passes (in [1..10]).
|
||||
int qmin; // minimum permissible quality factor
|
||||
int qmax; // maximum permissible quality factor
|
||||
|
||||
int show_compressed; // if true, export the compressed picture back.
|
||||
// In-loop filtering is not applied.
|
||||
|
Loading…
Reference in New Issue
Block a user