add shrink on load

though openjpeg seems very unreliable if you actually use this
This commit is contained in:
John Cupitt 2021-03-18 12:46:30 +00:00
parent 52e32dec19
commit 197239727d
1 changed files with 86 additions and 22 deletions

View File

@ -58,6 +58,15 @@
#include "pforeign.h"
/* TODO:
*
* - we only support 8 and 16-bit images, is this OK?
*
* - we only support images where all components have the same numeric
* type, is this OK?
*
*/
/* Surely enough ... does anyone do multispectral imaging with jp2k?
*/
#define MAX_BANDS (100)
@ -69,6 +78,12 @@ typedef struct _VipsForeignLoadJp2k {
*/
VipsSource *source;
/* Shrink on load factor.
*/
int shrink;
/* Decompress state.
*/
opj_stream_t *stream; /* Source as an opj stream */
OPJ_CODEC_FORMAT format; /* libopenjp2 format */
opj_codec_t *codec; /* Decompress codec */
@ -109,7 +124,8 @@ vips_foreign_load_jp2k_dispose( GObject *gobject )
*
*/
opj_destroy_cstr_info( &jp2k->info );
if( jp2k->info )
opj_destroy_cstr_info( &jp2k->info );
VIPS_FREEF( opj_destroy_codec, jp2k->codec );
VIPS_FREEF( opj_stream_destroy, jp2k->stream );
VIPS_FREEF( opj_image_destroy, jp2k->image );
@ -356,6 +372,8 @@ vips_foreign_load_jp2k_set_header( VipsForeignLoadJp2k *jp2k, VipsImage *out )
opj_image_comp_t *first;
int i;
int width;
int height;
VipsBandFormat format;
VipsInterpretation interpretation;
@ -392,8 +410,13 @@ vips_foreign_load_jp2k_set_header( VipsForeignLoadJp2k *jp2k, VipsImage *out )
}
}
if( first->w != jp2k->image->x1 ||
first->h != jp2k->image->y1 ) {
/* Image size shrunk down should match components in size. jp2k uses
* round to nearest.
*/
width = VIPS_ROUND_UINT( (double) jp2k->image->x1 / jp2k->shrink );
height = VIPS_ROUND_UINT( (double) jp2k->image->y1 / jp2k->shrink );
if( width != first->w ||
height != first->h ) {
vips_error( class->nickname,
"%s", _( "components not same size as image" ) );
return( -1 );
@ -432,6 +455,8 @@ vips_foreign_load_jp2k_set_header( VipsForeignLoadJp2k *jp2k, VipsImage *out )
break;
case OPJ_CLRSPC_UNSPECIFIED:
/* Try to guess something sensible.
*/
if( jp2k->image->numcomps < 3 )
interpretation = format == VIPS_FORMAT_UCHAR ?
VIPS_INTERPRETATION_B_W :
@ -449,17 +474,23 @@ vips_foreign_load_jp2k_set_header( VipsForeignLoadJp2k *jp2k, VipsImage *out )
return( -1 );
}
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
/* Even though this is a tiled reader, we hint thinstrip since with
* the cache we are quite happy serving that if anything downstream
* would like it.
*/
vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
vips_image_init_fields( out,
jp2k->image->x1, jp2k->image->y1, jp2k->image->numcomps,
format,
width, height, jp2k->image->numcomps, format,
VIPS_CODING_NONE, interpretation, 1.0, 1.0 );
/* openjpeg allows left and top of the coordinate grid to be
* non-zero.
* non-zero. These are always in unshrunk coordinates.
*/
out->Xoffset = -jp2k->image->x0;
out->Yoffset = -jp2k->image->y0;
out->Xoffset =
-VIPS_ROUND_INT( (double) jp2k->image->x0 / jp2k->shrink );
out->Yoffset =
-VIPS_ROUND_INT( (double) jp2k->image->y0 / jp2k->shrink );
if( jp2k->image->icc_profile_buf &&
jp2k->image->icc_profile_len > 0 )
@ -480,6 +511,8 @@ vips_foreign_load_jp2k_header( VipsForeignLoad *load )
printf( "vips_foreign_load_jp2k_header:\n" );
#endif /*DEBUG*/
int bits;
jp2k->format = vips_foreign_load_jp2k_get_format( jp2k->source );
vips_source_rewind( jp2k->source );
if( !(jp2k->codec = opj_create_decompress( jp2k->format )) ) {
@ -490,9 +523,13 @@ vips_foreign_load_jp2k_header( VipsForeignLoad *load )
vips_foreign_load_jp2k_attach_handlers( jp2k, jp2k->codec );
/* Useful:
* jp2k->parameters.cp_reduce ... shrink on load factor
*/
bits = vips_ispoweroftwo( jp2k->shrink );
if( !bits ) {
vips_error( class->nickname,
"%s", _( "shrink must be a power of two" ) );
return( -1 );
}
jp2k->parameters.cp_reduce = bits - 1;
if( !opj_setup_decoder( jp2k->codec, &jp2k->parameters ) )
return( -1 );
@ -572,11 +609,19 @@ static int
vips_foreign_load_jp2k_generate( VipsRegion *out,
void *seq, void *a, void *b, gboolean *stop )
{
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) a;
int tile_width = jp2k->info->tdx;
int tile_height = jp2k->info->tdy;
VipsForeignLoad *load = (VipsForeignLoad *) a;
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) load;
VipsRect *r = &out->valid;
/* jp2k get smaller with the layer size.
*/
int tile_width = jp2k->info->tdx / jp2k->shrink;
int tile_height = jp2k->info->tdy / jp2k->shrink;
/* ... so tiles_across is always the same.
*/
int tiles_across = jp2k->info->tw;
int x, y, z;
#ifdef DEBUG_VERBOSE
@ -606,12 +651,12 @@ vips_foreign_load_jp2k_generate( VipsRegion *out,
int xs = tx * tile_width;
int ys = ty * tile_height;
int tile_index = ty * jp2k->info->tw + tx;
int tile_index = ty * tiles_across + tx;
/* Fetch the tile.
*/
#ifdef DEBUG_VERBOSE
printf( " fetch file %d\n", tile_index );
printf( " fetch tile %d\n", tile_index );
#endif /*DEBUG_VERBOSE*/
if( !opj_get_decoded_tile( jp2k->codec,
jp2k->stream, jp2k->image, tile_index ) )
@ -648,6 +693,14 @@ vips_foreign_load_jp2k_generate( VipsRegion *out,
y += hit.height;
}
if( load->fail &&
jp2k->n_errors > 0 ) {
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( jp2k );
vips_error( class->nickname, "%s", _( "load error" ) );
return( -1 );
}
return( 0 );
}
@ -655,8 +708,13 @@ static int
vips_foreign_load_jp2k_load( VipsForeignLoad *load )
{
VipsForeignLoadJp2k *jp2k = (VipsForeignLoadJp2k *) load;
/* jp2k tiles get smaller with the layer size, but we don't want tiny
* tiles for the libvips tile cache, so leave them at the base size.
*/
int tile_width = jp2k->info->tdx;
int tile_height = jp2k->info->tdy;
int tiles_across = jp2k->info->tw;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( load ), 3 );
@ -665,8 +723,6 @@ vips_foreign_load_jp2k_load( VipsForeignLoad *load )
printf( "vips_foreign_load_jp2k_load:\n" );
#endif /*DEBUG*/
/* Tile cache: keep enough for two complete rows of tiles.
*/
t[0] = vips_image_new();
if( vips_foreign_load_jp2k_set_header( jp2k, t[0] ) )
return( -1 );
@ -675,13 +731,13 @@ vips_foreign_load_jp2k_load( VipsForeignLoad *load )
NULL, vips_foreign_load_jp2k_generate, NULL, jp2k, NULL ) )
return( -1 );
/* Copy to out, adding a cache. Enough tiles for a complete
* row, plus 50%.
/* Copy to out, adding a cache. Enough tiles for two complete
* rows, plus 50%.
*/
if( vips_tilecache( t[0], &t[1],
"tile_width", tile_width,
"tile_height", tile_height,
"max_tiles", (int) (1.5 * (1 + t[0]->Xsize / tile_width)),
"max_tiles", (int) (2.5 * tiles_across),
NULL ) )
return( -1 );
if( vips_image_write( t[1], load->real ) )
@ -708,11 +764,19 @@ vips_foreign_load_jp2k_class_init( VipsForeignLoadJp2kClass *class )
load_class->get_flags = vips_foreign_load_jp2k_get_flags;
load_class->header = vips_foreign_load_jp2k_header;
load_class->load = vips_foreign_load_jp2k_load;
VIPS_ARG_INT( class, "shrink", 20,
_( "Shrink" ),
_( "Shrink factor on load" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadJp2k, shrink ),
1, 1000, 1 );
}
static void
vips_foreign_load_jp2k_init( VipsForeignLoadJp2k *jp2k )
{
jp2k->shrink = 1;
}
typedef struct _VipsForeignLoadJp2kFile {