mirror of
https://github.com/webmproject/libwebp.git
synced 2024-11-19 20:08:28 +01:00
add a -jpeg_like option
This option remaps internal parameters to better match the expected compression curve of JPEG and produce output files of similar size, but with better quality. Change-Id: I96a1cbb480b1f6a0c6845a23c33dfd63f197b689
This commit is contained in:
parent
1f803f645d
commit
e895059a05
1
README
1
README
@ -183,6 +183,7 @@ options:
|
||||
-progress .............. report encoding progress
|
||||
|
||||
Experimental Options:
|
||||
-jpeg_like ............. Roughly match expected JPEG size.
|
||||
-af .................... auto-adjust filter strength.
|
||||
-pre <int> ............. pre-processing filter
|
||||
|
||||
|
@ -580,6 +580,7 @@ static void HelpLong(void) {
|
||||
printf(" -progress .............. report encoding progress\n");
|
||||
printf("\n");
|
||||
printf("Experimental Options:\n");
|
||||
printf(" -jpeg_like ............. Roughly match expected JPEG size.\n");
|
||||
printf(" -af .................... auto-adjust filter strength.\n");
|
||||
printf(" -pre <int> ............. pre-processing filter\n");
|
||||
printf("\n");
|
||||
@ -719,6 +720,8 @@ int main(int argc, const char *argv[]) {
|
||||
config.filter_strength = strtol(argv[++c], NULL, 0);
|
||||
} else if (!strcmp(argv[c], "-af")) {
|
||||
config.autofilter = 1;
|
||||
} else if (!strcmp(argv[c], "-jpeg_like")) {
|
||||
config.emulate_jpeg_size = 1;
|
||||
} else if (!strcmp(argv[c], "-strong")) {
|
||||
config.filter_type = 1;
|
||||
} else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
|
||||
|
@ -1,5 +1,5 @@
|
||||
.\" Hey, EMACS: -*- nroff -*-
|
||||
.TH CWEBP 1 "February 01, 2013"
|
||||
.TH CWEBP 1 "February 6, 2013"
|
||||
.SH NAME
|
||||
cwebp \- compress an image file to a WebP file
|
||||
.SH SYNOPSIS
|
||||
@ -76,6 +76,12 @@ additional encoding possibilities and decide on the quality gain.
|
||||
Lower value can result is faster processing time at the expense of
|
||||
larger file size and lower compression quality.
|
||||
.TP
|
||||
.B \-jpeg_like
|
||||
Change the internal parameter mapping to better match the expected size
|
||||
of JPEG compression. This flag will generally produce an output file of
|
||||
similar size to its JPEG equivalent (for the same \fB\-q\fP setting), but
|
||||
with less visual distortion.
|
||||
.TP
|
||||
.B \-af
|
||||
Turns auto-filter on. This algorithm will spend additional time optimizing
|
||||
the filtering strength to reach a well-balanced quality.
|
||||
|
@ -318,7 +318,8 @@ static int MBAnalyzeBestUVMode(VP8EncIterator* const it) {
|
||||
}
|
||||
|
||||
static void MBAnalyze(VP8EncIterator* const it,
|
||||
int alphas[MAX_ALPHA + 1], int* const uv_alpha) {
|
||||
int alphas[MAX_ALPHA + 1],
|
||||
int* const alpha, int* const uv_alpha) {
|
||||
const VP8Encoder* const enc = it->enc_;
|
||||
int best_alpha, best_uv_alpha;
|
||||
|
||||
@ -340,8 +341,11 @@ static void MBAnalyze(VP8EncIterator* const it,
|
||||
best_alpha = (3 * best_alpha + best_uv_alpha + 2) >> 2;
|
||||
best_alpha = FinalAlphaValue(best_alpha);
|
||||
alphas[best_alpha]++;
|
||||
*uv_alpha += best_uv_alpha;
|
||||
it->mb_->alpha_ = best_alpha; // for later remapping.
|
||||
|
||||
// Accumulate for later complexity analysis.
|
||||
*alpha += best_alpha; // mixed susceptibility (not just luma)
|
||||
*uv_alpha += best_uv_alpha;
|
||||
}
|
||||
|
||||
static void DefaultMBInfo(VP8MBInfo* const mb) {
|
||||
@ -362,35 +366,42 @@ static void DefaultMBInfo(VP8MBInfo* const mb) {
|
||||
// and decide intra4/intra16, but that's usually almost always a bad choice at
|
||||
// this stage.
|
||||
|
||||
static void ResetAllMBInfo(VP8Encoder* const enc) {
|
||||
int n;
|
||||
for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
|
||||
DefaultMBInfo(&enc->mb_info_[n]);
|
||||
}
|
||||
// Default susceptibilities.
|
||||
enc->dqm_[0].alpha_ = 0;
|
||||
enc->dqm_[0].beta_ = 0;
|
||||
// Note: we can't compute this alpha_ / uv_alpha_.
|
||||
WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
||||
}
|
||||
|
||||
int VP8EncAnalyze(VP8Encoder* const enc) {
|
||||
int ok = 1;
|
||||
const int do_segments =
|
||||
enc->config_->emulate_jpeg_size || // We need the complexity evaluation.
|
||||
(enc->segment_hdr_.num_segments_ > 1) ||
|
||||
(enc->method_ <= 2); // for methods 0,1,2, we need preds_[] to be filled.
|
||||
enc->alpha_ = 0;
|
||||
enc->uv_alpha_ = 0;
|
||||
if (do_segments) {
|
||||
int alphas[MAX_ALPHA + 1] = { 0 };
|
||||
VP8EncIterator it;
|
||||
|
||||
VP8IteratorInit(enc, &it);
|
||||
enc->uv_alpha_ = 0;
|
||||
do {
|
||||
VP8IteratorImport(&it);
|
||||
MBAnalyze(&it, alphas, &enc->uv_alpha_);
|
||||
MBAnalyze(&it, alphas, &enc->alpha_, &enc->uv_alpha_);
|
||||
ok = VP8IteratorProgress(&it, 20);
|
||||
// Let's pretend we have perfect lossless reconstruction.
|
||||
} while (ok && VP8IteratorNext(&it, it.yuv_in_));
|
||||
enc->alpha_ /= enc->mb_w_ * enc->mb_h_;
|
||||
enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
|
||||
if (ok) AssignSegments(enc, alphas);
|
||||
} else { // Use only one default segment.
|
||||
int n;
|
||||
for (n = 0; n < enc->mb_w_ * enc->mb_h_; ++n) {
|
||||
DefaultMBInfo(&enc->mb_info_[n]);
|
||||
}
|
||||
// Default susceptibilities.
|
||||
enc->dqm_[0].alpha_ = 0;
|
||||
enc->dqm_[0].beta_ = 0;
|
||||
enc->uv_alpha_ = 0; // we can't compute this one.
|
||||
WebPReportProgress(enc->pic_, enc->percent_ + 20, &enc->percent_);
|
||||
ResetAllMBInfo(enc);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ int WebPConfigInitInternal(WebPConfig* config,
|
||||
config->alpha_quality = 100;
|
||||
config->lossless = 0;
|
||||
config->image_hint = WEBP_HINT_DEFAULT;
|
||||
config->emulate_jpeg_size = 0;
|
||||
|
||||
// TODO(skal): tune.
|
||||
switch (preset) {
|
||||
@ -122,6 +123,8 @@ int WebPValidateConfig(const WebPConfig* config) {
|
||||
return 0;
|
||||
if (config->image_hint >= WEBP_HINT_LAST)
|
||||
return 0;
|
||||
if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -224,9 +224,35 @@ static void SetupFilterStrength(VP8Encoder* const enc) {
|
||||
// We want to emulate jpeg-like behaviour where the expected "good" quality
|
||||
// is around q=75. Internally, our "good" middle is around c=50. So we
|
||||
// map accordingly using linear piece-wise function
|
||||
static double QualityToCompression(double q) {
|
||||
const double c = q / 100.;
|
||||
return (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.;
|
||||
static double QualityToCompression(double c) {
|
||||
const double linear_c = (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.;
|
||||
// The file size roughly scales as pow(quantizer, 3.). Actually, the
|
||||
// exponent is somewhere between 2.8 and 3.2, but we're mostly interested
|
||||
// in the mid-quant range. So we scale the compressibility inversely to
|
||||
// this power-law: quant ~= compression ^ 1/3. This law holds well for
|
||||
// low quant. Finer modelling for high-quant would make use of kAcTable[]
|
||||
// more explicitly.
|
||||
const double v = pow(linear_c, 1 / 3.);
|
||||
return v;
|
||||
}
|
||||
|
||||
static double QualityToJPEGCompression(double c, double alpha) {
|
||||
// We map the complexity 'alpha' and quality setting 'c' to a compression
|
||||
// exponent empirically matched to the compression curve of libjpeg6b.
|
||||
// On average, the WebP output size will be roughly similar to that of a
|
||||
// JPEG file compressed with same quality factor.
|
||||
const double amin = 0.30;
|
||||
const double amax = 0.85;
|
||||
const double exp_min = 0.4;
|
||||
const double exp_max = 0.9;
|
||||
const double slope = (exp_min - exp_max) / (amax - amin);
|
||||
// Linearly interpolate 'expn' from exp_min to exp_max
|
||||
// in the [amin, amax] range.
|
||||
const double expn = (alpha > amax) ? exp_min
|
||||
: (alpha < amin) ? exp_max
|
||||
: exp_max + slope * (alpha - amin);
|
||||
const double v = pow(c, expn);
|
||||
return v;
|
||||
}
|
||||
|
||||
static int SegmentsAreEquivalent(const VP8SegmentInfo* const S1,
|
||||
@ -274,18 +300,14 @@ void VP8SetSegmentParams(VP8Encoder* const enc, float quality) {
|
||||
int dq_uv_ac, dq_uv_dc;
|
||||
const int num_segments = enc->segment_hdr_.num_segments_;
|
||||
const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.;
|
||||
const double c_base = QualityToCompression(quality);
|
||||
const double Q = quality / 100.;
|
||||
const double c_base = enc->config_->emulate_jpeg_size ?
|
||||
QualityToJPEGCompression(Q, enc->alpha_ / 255.) :
|
||||
QualityToCompression(Q);
|
||||
for (i = 0; i < num_segments; ++i) {
|
||||
// The file size roughly scales as pow(quantizer, 3.). Actually, the
|
||||
// exponent is somewhere between 2.8 and 3.2, but we're mostly interested
|
||||
// in the mid-quant range. So we scale the compressibility inversely to
|
||||
// this power-law: quant ~= compression ^ 1/3. This law holds well for
|
||||
// low quant. Finer modelling for high-quant would make use of kAcTable[]
|
||||
// more explicitely.
|
||||
// Additionally, we modulate the base exponent 1/3 to accommodate for the
|
||||
// quantization susceptibility and allow denser segments to be quantized
|
||||
// more.
|
||||
const double expn = (1. - amp * enc->dqm_[i].alpha_) / 3.;
|
||||
// We modulate the base coefficient to accommodate for the quantization
|
||||
// susceptibility and allow denser segments to be quantized more.
|
||||
const double expn = 1. - amp * enc->dqm_[i].alpha_;
|
||||
const double c = pow(c_base, expn);
|
||||
const int q = (int)(127. * (1. - c));
|
||||
assert(expn > 0.);
|
||||
|
@ -390,6 +390,7 @@ struct VP8Encoder {
|
||||
VP8SegmentInfo dqm_[NUM_MB_SEGMENTS];
|
||||
int base_quant_; // nominal quantizer value. Only used
|
||||
// for relative coding of segments' quant.
|
||||
int alpha_; // global susceptibility (<=> complexity)
|
||||
int uv_alpha_; // U/V quantization susceptibility
|
||||
// global offset of quantizers, shared by all segments
|
||||
int dq_y1_dc_;
|
||||
|
@ -121,8 +121,12 @@ struct WebPConfig {
|
||||
int partition_limit; // quality degradation allowed to fit the 512k limit
|
||||
// on prediction modes coding (0: no degradation,
|
||||
// 100: maximum possible degradation).
|
||||
int emulate_jpeg_size; // If true, compression parameters will be remapped
|
||||
// to better match the expected output size from
|
||||
// JPEG compression. Generally, the output size will
|
||||
// be similar but the degradation will be lower.
|
||||
|
||||
uint32_t pad[8]; // padding for later use
|
||||
uint32_t pad[7]; // padding for later use
|
||||
};
|
||||
|
||||
// Enumerate some predefined settings for WebPConfig, depending on the type
|
||||
|
Loading…
Reference in New Issue
Block a user