mirror of
https://github.com/webmproject/libwebp.git
synced 2025-02-13 07:22:52 +01:00
Merge "add a -jpeg_like option"
This commit is contained in:
commit
5c3e381b2f
1
README
1
README
@ -183,6 +183,7 @@ options:
|
|||||||
-progress .............. report encoding progress
|
-progress .............. report encoding progress
|
||||||
|
|
||||||
Experimental Options:
|
Experimental Options:
|
||||||
|
-jpeg_like ............. Roughly match expected JPEG size.
|
||||||
-af .................... auto-adjust filter strength.
|
-af .................... auto-adjust filter strength.
|
||||||
-pre <int> ............. pre-processing filter
|
-pre <int> ............. pre-processing filter
|
||||||
|
|
||||||
|
@ -580,6 +580,7 @@ static void HelpLong(void) {
|
|||||||
printf(" -progress .............. report encoding progress\n");
|
printf(" -progress .............. report encoding progress\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
printf("Experimental Options:\n");
|
printf("Experimental Options:\n");
|
||||||
|
printf(" -jpeg_like ............. Roughly match expected JPEG size.\n");
|
||||||
printf(" -af .................... auto-adjust filter strength.\n");
|
printf(" -af .................... auto-adjust filter strength.\n");
|
||||||
printf(" -pre <int> ............. pre-processing filter\n");
|
printf(" -pre <int> ............. pre-processing filter\n");
|
||||||
printf("\n");
|
printf("\n");
|
||||||
@ -719,6 +720,8 @@ int main(int argc, const char *argv[]) {
|
|||||||
config.filter_strength = strtol(argv[++c], NULL, 0);
|
config.filter_strength = strtol(argv[++c], NULL, 0);
|
||||||
} else if (!strcmp(argv[c], "-af")) {
|
} else if (!strcmp(argv[c], "-af")) {
|
||||||
config.autofilter = 1;
|
config.autofilter = 1;
|
||||||
|
} else if (!strcmp(argv[c], "-jpeg_like")) {
|
||||||
|
config.emulate_jpeg_size = 1;
|
||||||
} else if (!strcmp(argv[c], "-strong")) {
|
} else if (!strcmp(argv[c], "-strong")) {
|
||||||
config.filter_type = 1;
|
config.filter_type = 1;
|
||||||
} else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
|
} else if (!strcmp(argv[c], "-sharpness") && c < argc - 1) {
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
.\" Hey, EMACS: -*- nroff -*-
|
.\" Hey, EMACS: -*- nroff -*-
|
||||||
.TH CWEBP 1 "February 01, 2013"
|
.TH CWEBP 1 "February 6, 2013"
|
||||||
.SH NAME
|
.SH NAME
|
||||||
cwebp \- compress an image file to a WebP file
|
cwebp \- compress an image file to a WebP file
|
||||||
.SH SYNOPSIS
|
.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
|
Lower value can result is faster processing time at the expense of
|
||||||
larger file size and lower compression quality.
|
larger file size and lower compression quality.
|
||||||
.TP
|
.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
|
.B \-af
|
||||||
Turns auto-filter on. This algorithm will spend additional time optimizing
|
Turns auto-filter on. This algorithm will spend additional time optimizing
|
||||||
the filtering strength to reach a well-balanced quality.
|
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,
|
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_;
|
const VP8Encoder* const enc = it->enc_;
|
||||||
int best_alpha, best_uv_alpha;
|
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 = (3 * best_alpha + best_uv_alpha + 2) >> 2;
|
||||||
best_alpha = FinalAlphaValue(best_alpha);
|
best_alpha = FinalAlphaValue(best_alpha);
|
||||||
alphas[best_alpha]++;
|
alphas[best_alpha]++;
|
||||||
*uv_alpha += best_uv_alpha;
|
|
||||||
it->mb_->alpha_ = best_alpha; // for later remapping.
|
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) {
|
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
|
// and decide intra4/intra16, but that's usually almost always a bad choice at
|
||||||
// this stage.
|
// 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 VP8EncAnalyze(VP8Encoder* const enc) {
|
||||||
int ok = 1;
|
int ok = 1;
|
||||||
const int do_segments =
|
const int do_segments =
|
||||||
|
enc->config_->emulate_jpeg_size || // We need the complexity evaluation.
|
||||||
(enc->segment_hdr_.num_segments_ > 1) ||
|
(enc->segment_hdr_.num_segments_ > 1) ||
|
||||||
(enc->method_ <= 2); // for methods 0,1,2, we need preds_[] to be filled.
|
(enc->method_ <= 2); // for methods 0,1,2, we need preds_[] to be filled.
|
||||||
|
enc->alpha_ = 0;
|
||||||
|
enc->uv_alpha_ = 0;
|
||||||
if (do_segments) {
|
if (do_segments) {
|
||||||
int alphas[MAX_ALPHA + 1] = { 0 };
|
int alphas[MAX_ALPHA + 1] = { 0 };
|
||||||
VP8EncIterator it;
|
VP8EncIterator it;
|
||||||
|
|
||||||
VP8IteratorInit(enc, &it);
|
VP8IteratorInit(enc, &it);
|
||||||
enc->uv_alpha_ = 0;
|
|
||||||
do {
|
do {
|
||||||
VP8IteratorImport(&it);
|
VP8IteratorImport(&it);
|
||||||
MBAnalyze(&it, alphas, &enc->uv_alpha_);
|
MBAnalyze(&it, alphas, &enc->alpha_, &enc->uv_alpha_);
|
||||||
ok = VP8IteratorProgress(&it, 20);
|
ok = VP8IteratorProgress(&it, 20);
|
||||||
// Let's pretend we have perfect lossless reconstruction.
|
// Let's pretend we have perfect lossless reconstruction.
|
||||||
} while (ok && VP8IteratorNext(&it, it.yuv_in_));
|
} while (ok && VP8IteratorNext(&it, it.yuv_in_));
|
||||||
|
enc->alpha_ /= enc->mb_w_ * enc->mb_h_;
|
||||||
enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
|
enc->uv_alpha_ /= enc->mb_w_ * enc->mb_h_;
|
||||||
if (ok) AssignSegments(enc, alphas);
|
if (ok) AssignSegments(enc, alphas);
|
||||||
} else { // Use only one default segment.
|
} else { // Use only one default segment.
|
||||||
int n;
|
ResetAllMBInfo(enc);
|
||||||
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_);
|
|
||||||
}
|
}
|
||||||
return ok;
|
return ok;
|
||||||
}
|
}
|
||||||
|
@ -46,6 +46,7 @@ int WebPConfigInitInternal(WebPConfig* config,
|
|||||||
config->alpha_quality = 100;
|
config->alpha_quality = 100;
|
||||||
config->lossless = 0;
|
config->lossless = 0;
|
||||||
config->image_hint = WEBP_HINT_DEFAULT;
|
config->image_hint = WEBP_HINT_DEFAULT;
|
||||||
|
config->emulate_jpeg_size = 0;
|
||||||
|
|
||||||
// TODO(skal): tune.
|
// TODO(skal): tune.
|
||||||
switch (preset) {
|
switch (preset) {
|
||||||
@ -122,6 +123,8 @@ int WebPValidateConfig(const WebPConfig* config) {
|
|||||||
return 0;
|
return 0;
|
||||||
if (config->image_hint >= WEBP_HINT_LAST)
|
if (config->image_hint >= WEBP_HINT_LAST)
|
||||||
return 0;
|
return 0;
|
||||||
|
if (config->emulate_jpeg_size < 0 || config->emulate_jpeg_size > 1)
|
||||||
|
return 0;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -224,9 +224,35 @@ static void SetupFilterStrength(VP8Encoder* const enc) {
|
|||||||
// We want to emulate jpeg-like behaviour where the expected "good" quality
|
// 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
|
// is around q=75. Internally, our "good" middle is around c=50. So we
|
||||||
// map accordingly using linear piece-wise function
|
// map accordingly using linear piece-wise function
|
||||||
static double QualityToCompression(double q) {
|
static double QualityToCompression(double c) {
|
||||||
const double c = q / 100.;
|
const double linear_c = (c < 0.75) ? c * (2. / 3.) : 2. * c - 1.;
|
||||||
return (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,
|
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;
|
int dq_uv_ac, dq_uv_dc;
|
||||||
const int num_segments = enc->segment_hdr_.num_segments_;
|
const int num_segments = enc->segment_hdr_.num_segments_;
|
||||||
const double amp = SNS_TO_DQ * enc->config_->sns_strength / 100. / 128.;
|
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) {
|
for (i = 0; i < num_segments; ++i) {
|
||||||
// The file size roughly scales as pow(quantizer, 3.). Actually, the
|
// We modulate the base coefficient to accommodate for the quantization
|
||||||
// exponent is somewhere between 2.8 and 3.2, but we're mostly interested
|
// susceptibility and allow denser segments to be quantized more.
|
||||||
// in the mid-quant range. So we scale the compressibility inversely to
|
const double expn = 1. - amp * enc->dqm_[i].alpha_;
|
||||||
// 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.;
|
|
||||||
const double c = pow(c_base, expn);
|
const double c = pow(c_base, expn);
|
||||||
const int q = (int)(127. * (1. - c));
|
const int q = (int)(127. * (1. - c));
|
||||||
assert(expn > 0.);
|
assert(expn > 0.);
|
||||||
|
@ -389,6 +389,7 @@ struct VP8Encoder {
|
|||||||
VP8SegmentInfo dqm_[NUM_MB_SEGMENTS];
|
VP8SegmentInfo dqm_[NUM_MB_SEGMENTS];
|
||||||
int base_quant_; // nominal quantizer value. Only used
|
int base_quant_; // nominal quantizer value. Only used
|
||||||
// for relative coding of segments' quant.
|
// for relative coding of segments' quant.
|
||||||
|
int alpha_; // global susceptibility (<=> complexity)
|
||||||
int uv_alpha_; // U/V quantization susceptibility
|
int uv_alpha_; // U/V quantization susceptibility
|
||||||
// global offset of quantizers, shared by all segments
|
// global offset of quantizers, shared by all segments
|
||||||
int dq_y1_dc_;
|
int dq_y1_dc_;
|
||||||
|
@ -121,8 +121,12 @@ struct WebPConfig {
|
|||||||
int partition_limit; // quality degradation allowed to fit the 512k limit
|
int partition_limit; // quality degradation allowed to fit the 512k limit
|
||||||
// on prediction modes coding (0: no degradation,
|
// on prediction modes coding (0: no degradation,
|
||||||
// 100: maximum possible 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
|
// Enumerate some predefined settings for WebPConfig, depending on the type
|
||||||
|
Loading…
x
Reference in New Issue
Block a user