Add mozjpeg quant_table option to jpegsave
This allows to choose from several predefined quantization tables when libvips is compiled against mozjpeg 3.0+. Tuning the quantization table helps to improve file size in the higher quality range and avoid artefacts in the lower range.
This commit is contained in:
parent
ca731e7967
commit
c5fa0e1feb
@ -2217,6 +2217,7 @@ vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||
* * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
*
|
||||
* Write a VIPS image to a file as JPEG.
|
||||
*
|
||||
@ -2268,6 +2269,30 @@ vips_jpegload_buffer( void *buf, size_t len, VipsImage **out, ... )
|
||||
* (e.g. mozjpeg >= 3.0), split the spectrum of DCT coefficients into
|
||||
* separate scans. Reduces file size but increases compression time.
|
||||
*
|
||||
* If @quant_table is set and the version of libjpeg supports it
|
||||
* (e.g. mozjpeg >= 3.0) it selects the quantization table to use:
|
||||
*
|
||||
* * 0 Tables from JPEG Annex K (vips and libjpeg default)
|
||||
* * 1 Flat table
|
||||
* * 2 Table tuned for MSSIM on Kodak image set
|
||||
* * 3 Table from ImageMagick by N. Robidoux (current mozjpeg default)
|
||||
* * 4 Table tuned for PSNR-HVS-M on Kodak image set
|
||||
* * 5 Table from Relevance of Human Vision to JPEG-DCT Compression (1992)
|
||||
* * 6 Table from DCTune Perceptual Optimization of Compressed Dental X-Rays (1997)
|
||||
* * 7 Table from A Visual Detection Model for DCT Coefficient Quantization (1993)
|
||||
* * 8 Table from An Improved Detection Model for DCT Coefficient Quantization (1993)
|
||||
*
|
||||
* Quantization table 0 is the default in vips and libjpeg(-turbo), but it
|
||||
* tends to favor detail over color accuracy, producting colored patches and
|
||||
* stripes as well as heavy banding in flat areas at high compression ratios.
|
||||
* Quantization table 2 is a good candidate to try if the default quantization
|
||||
* table produces banding or color shifts and is well suited for hires images.
|
||||
* Quantization table 3 is the default in mozjpeg and has been tuned to produce
|
||||
* good results at the default quality setting; banding at high compression.
|
||||
* Quantization table 4 is the most accurate at the cost of compression ratio.
|
||||
* Tables 5-7 are based on older research papers, but generally achieve worse
|
||||
* compression ratios and/or quality than 2 or 4.
|
||||
*
|
||||
* See also: vips_jpegsave_buffer(), vips_image_write_to_file().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error.
|
||||
@ -2303,6 +2328,7 @@ vips_jpegsave( VipsImage *in, const char *filename, ... )
|
||||
* * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
*
|
||||
* As vips_jpegsave(), but save to a memory buffer.
|
||||
*
|
||||
@ -2357,6 +2383,7 @@ vips_jpegsave_buffer( VipsImage *in, void **buf, size_t *len, ... )
|
||||
* * @trellis_quant: %gboolean, apply trellis quantisation to each 8x8 block
|
||||
* * @overshoot_deringing: %gboolean, overshoot samples with extreme values
|
||||
* * @optimize_scans: %gboolean, split DCT coefficients into separate scans
|
||||
* * @quant_table: %gint, quantization table index
|
||||
*
|
||||
* As vips_jpegsave(), but save as a mime jpeg on stdout.
|
||||
*
|
||||
|
@ -103,6 +103,10 @@ typedef struct _VipsForeignSaveJpeg {
|
||||
*/
|
||||
gboolean optimize_scans;
|
||||
|
||||
/* Use predefined quantization table with given index.
|
||||
*/
|
||||
int quant_table;
|
||||
|
||||
} VipsForeignSaveJpeg;
|
||||
|
||||
typedef VipsForeignSaveClass VipsForeignSaveJpegClass;
|
||||
@ -194,6 +198,13 @@ vips_foreign_save_jpeg_class_init( VipsForeignSaveJpegClass *class )
|
||||
G_STRUCT_OFFSET( VipsForeignSaveJpeg, optimize_scans ),
|
||||
FALSE );
|
||||
|
||||
VIPS_ARG_INT( class, "quant_table", 18,
|
||||
_( "Quantization table" ),
|
||||
_( "Use predefined quantization table with given index" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsForeignSaveJpeg, quant_table ),
|
||||
0, 8, 0 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
@ -230,7 +241,8 @@ vips_foreign_save_jpeg_file_build( VipsObject *object )
|
||||
if( vips__jpeg_write_file( save->ready, file->filename,
|
||||
jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->no_subsample,
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) )
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing,
|
||||
jpeg->optimize_scans, jpeg->quant_table ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
@ -294,7 +306,8 @@ vips_foreign_save_jpeg_buffer_build( VipsObject *object )
|
||||
if( vips__jpeg_write_buffer( save->ready,
|
||||
&obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->no_subsample,
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) )
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing,
|
||||
jpeg->optimize_scans, jpeg->quant_table ) )
|
||||
return( -1 );
|
||||
|
||||
blob = vips_blob_new( (VipsCallbackFn) vips_free, obuf, olen );
|
||||
@ -357,7 +370,8 @@ vips_foreign_save_jpeg_mime_build( VipsObject *object )
|
||||
if( vips__jpeg_write_buffer( save->ready,
|
||||
&obuf, &olen, jpeg->Q, jpeg->profile, jpeg->optimize_coding,
|
||||
jpeg->interlace, save->strip, jpeg->no_subsample,
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing, jpeg->optimize_scans) )
|
||||
jpeg->trellis_quant, jpeg->overshoot_deringing,
|
||||
jpeg->optimize_scans, jpeg->quant_table ) )
|
||||
return( -1 );
|
||||
|
||||
printf( "Content-length: %zu\r\n", olen );
|
||||
|
@ -966,7 +966,7 @@ static int
|
||||
write_vips( Write *write, int qfac, const char *profile,
|
||||
gboolean optimize_coding, gboolean progressive, gboolean strip,
|
||||
gboolean no_subsample, gboolean trellis_quant,
|
||||
gboolean overshoot_deringing, gboolean optimize_scans )
|
||||
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table )
|
||||
{
|
||||
VipsImage *in;
|
||||
J_COLOR_SPACE space;
|
||||
@ -1022,10 +1022,9 @@ write_vips( Write *write, int qfac, const char *profile,
|
||||
JINT_COMPRESS_PROFILE, JCP_FASTEST );
|
||||
#endif
|
||||
|
||||
/* Rest to default.
|
||||
/* Reset to default.
|
||||
*/
|
||||
jpeg_set_defaults( &write->cinfo );
|
||||
jpeg_set_quality( &write->cinfo, qfac, TRUE );
|
||||
|
||||
/* Compute optimal Huffman coding tables.
|
||||
*/
|
||||
@ -1077,6 +1076,20 @@ write_vips( Write *write, int qfac, const char *profile,
|
||||
vips_warn( "vips2jpeg", "%s",
|
||||
_( "Ignoring optimize_scans for baseline" ) );
|
||||
}
|
||||
|
||||
/* Use predefined quantization table.
|
||||
*/
|
||||
if( quant_table > 0 ) {
|
||||
if( jpeg_c_int_param_supported( &write->cinfo,
|
||||
JINT_BASE_QUANT_TBL_IDX ) )
|
||||
jpeg_c_set_int_param( &write->cinfo,
|
||||
JINT_BASE_QUANT_TBL_IDX, quant_table );
|
||||
else
|
||||
vips_warn( "vips2jpeg",
|
||||
"%s", _( "Setting quant_table unsupported" ) );
|
||||
}
|
||||
vips_warn( "vips2jpeg", "quant_table %d",
|
||||
jpeg_c_get_int_param( &write->cinfo, JINT_BASE_QUANT_TBL_IDX ) );
|
||||
#else
|
||||
/* Using jpeglib.h without extension parameters, warn of ignored
|
||||
* options.
|
||||
@ -1088,8 +1101,14 @@ write_vips( Write *write, int qfac, const char *profile,
|
||||
"%s", _( "Ignoring overshoot_deringing" ) );
|
||||
if( optimize_scans )
|
||||
vips_warn( "vips2jpeg", "%s", _( "Ignoring optimize_scans" ) );
|
||||
if( quant_table > 0 )
|
||||
vips_warn( "vips2jpeg", "%s", _( "Ignoring quant_table" ) );
|
||||
#endif
|
||||
|
||||
/* Set compression quality. Must be called after setting params above.
|
||||
*/
|
||||
jpeg_set_quality( &write->cinfo, qfac, TRUE );
|
||||
|
||||
/* Enable progressive write.
|
||||
*/
|
||||
if( progressive )
|
||||
@ -1160,7 +1179,7 @@ vips__jpeg_write_file( VipsImage *in,
|
||||
const char *filename, int Q, const char *profile,
|
||||
gboolean optimize_coding, gboolean progressive, gboolean strip,
|
||||
gboolean no_subsample, gboolean trellis_quant,
|
||||
gboolean overshoot_deringing, gboolean optimize_scans )
|
||||
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
@ -1192,7 +1211,7 @@ vips__jpeg_write_file( VipsImage *in,
|
||||
*/
|
||||
if( write_vips( write,
|
||||
Q, profile, optimize_coding, progressive, strip, no_subsample,
|
||||
trellis_quant, overshoot_deringing, optimize_scans ) ) {
|
||||
trellis_quant, overshoot_deringing, optimize_scans, quant_table ) ) {
|
||||
write_destroy( write );
|
||||
return( -1 );
|
||||
}
|
||||
@ -1446,7 +1465,7 @@ vips__jpeg_write_buffer( VipsImage *in,
|
||||
void **obuf, size_t *olen, int Q, const char *profile,
|
||||
gboolean optimize_coding, gboolean progressive,
|
||||
gboolean strip, gboolean no_subsample, gboolean trellis_quant,
|
||||
gboolean overshoot_deringing, gboolean optimize_scans )
|
||||
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table )
|
||||
{
|
||||
Write *write;
|
||||
|
||||
@ -1477,7 +1496,7 @@ vips__jpeg_write_buffer( VipsImage *in,
|
||||
*/
|
||||
if( write_vips( write,
|
||||
Q, profile, optimize_coding, progressive, strip, no_subsample,
|
||||
trellis_quant, overshoot_deringing, optimize_scans ) ) {
|
||||
trellis_quant, overshoot_deringing, optimize_scans, quant_table ) ) {
|
||||
write_destroy( write );
|
||||
|
||||
return( -1 );
|
||||
|
@ -41,12 +41,12 @@ int vips__jpeg_write_file( VipsImage *in,
|
||||
const char *filename, int Q, const char *profile,
|
||||
gboolean optimize_coding, gboolean progressive, gboolean strip,
|
||||
gboolean no_subsample, gboolean trellis_quant,
|
||||
gboolean overshoot_deringing, gboolean optimize_scans );
|
||||
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table );
|
||||
int vips__jpeg_write_buffer( VipsImage *in,
|
||||
void **obuf, size_t *olen, int Q, const char *profile,
|
||||
gboolean optimize_coding, gboolean progressive, gboolean strip,
|
||||
gboolean no_subsample, gboolean trellis_quant,
|
||||
gboolean overshoot_deringing, gboolean optimize_scans );
|
||||
gboolean overshoot_deringing, gboolean optimize_scans, int quant_table );
|
||||
|
||||
int vips__isjpeg_buffer( const void *buf, size_t len );
|
||||
int vips__isjpeg( const char *filename );
|
||||
|
Loading…
Reference in New Issue
Block a user