From 40c020ccd20d1118d07a1b4c43da3da38ae739ea Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Tue, 23 Mar 2021 17:15:02 +0000 Subject: [PATCH] more jp2k tests --- libvips/foreign/jp2kload.c | 20 ++----- libvips/foreign/jp2ksave.c | 93 ++++++++++++++++++++------------- test/test-suite/test_foreign.py | 24 ++++++++- 3 files changed, 86 insertions(+), 51 deletions(-) diff --git a/libvips/foreign/jp2kload.c b/libvips/foreign/jp2kload.c index 9d847986..842574c3 100644 --- a/libvips/foreign/jp2kload.c +++ b/libvips/foreign/jp2kload.c @@ -367,24 +367,14 @@ vips_foreign_load_jp2k_set_header( VipsForeignLoadJp2k *jp2k, VipsImage *out ) VipsBandFormat format; VipsInterpretation interpretation; - switch( first->prec ) { - case 8: + /* OpenJPEG only supports up to 31 bpp. Treat it as 32. + */ + if( first->prec <= 8 ) format = first->sgnd ? VIPS_FORMAT_CHAR : VIPS_FORMAT_UCHAR; - break; - - case 16: + else if( first->prec <= 16 ) format = first->sgnd ? VIPS_FORMAT_SHORT : VIPS_FORMAT_USHORT; - break; - - case 32: + else format = first->sgnd ? VIPS_FORMAT_INT : VIPS_FORMAT_UINT; - break; - - default: - vips_error( class->nickname, - _( "unsupported precision %d" ), first->prec ); - return( -1 ); - } switch( jp2k->image->color_space ) { case OPJ_CLRSPC_SYCC: diff --git a/libvips/foreign/jp2ksave.c b/libvips/foreign/jp2ksave.c index 63909a60..62b78c04 100644 --- a/libvips/foreign/jp2ksave.c +++ b/libvips/foreign/jp2ksave.c @@ -38,13 +38,9 @@ /* TODO * - * - we will have 0.5 pixels if the image width or hight is odd and chroma + * - we will have 0.5 pixels if the image width or height is odd and chroma * subsampling is on ... this will cause at least read out of bounds. * - * - add 16 and 32-bit images to tests - * - * - add chroma modes to tests - * * - could support tiff-like depth parameter * * - could support png-like bitdepth parameter @@ -451,6 +447,9 @@ vips_foreign_save_jp2k_unpack( VipsForeignSaveJp2k *jp2k, VipsRect *tile ) static size_t vips_foreign_save_jp2k_sizeof_tile( VipsForeignSaveJp2k *jp2k, VipsRect *tile ) { + VipsForeignSave *save = (VipsForeignSave *) jp2k; + size_t sizeof_element = VIPS_IMAGE_SIZEOF_ELEMENT( save->ready ); + size_t size; int i; @@ -466,7 +465,7 @@ vips_foreign_save_jp2k_sizeof_tile( VipsForeignSaveJp2k *jp2k, VipsRect *tile ) int output_height = VIPS_ROUND_UINT( (double) tile->height / comp->dy );; - size += output_width * output_height * (comp->bpp / 8); + size += output_width * output_height * sizeof_element; } return( size ); @@ -579,6 +578,7 @@ vips_foreign_save_jp2k_build( VipsObject *object ) OPJ_COLOR_SPACE color_space; int expected_bands; + int bits_per_pixel; int i; size_t sizeof_tile; size_t sizeof_line; @@ -632,6 +632,56 @@ vips_foreign_save_jp2k_build( VipsObject *object ) jp2k->parameters.tcp_mct = FALSE; } + /* CIELAB etc. do not seem to be well documented. + */ + switch( save->ready->Type ) { + case VIPS_INTERPRETATION_B_W: + case VIPS_INTERPRETATION_GREY16: + color_space = OPJ_CLRSPC_GRAY; + expected_bands = 1; + break; + + case VIPS_INTERPRETATION_sRGB: + case VIPS_INTERPRETATION_RGB16: + color_space = jp2k->save_as_ycc ? + OPJ_CLRSPC_SYCC : OPJ_CLRSPC_SRGB; + expected_bands = 3; + break; + + case VIPS_INTERPRETATION_CMYK: + color_space = OPJ_CLRSPC_CMYK; + expected_bands = 4; + break; + + default: + color_space = OPJ_CLRSPC_UNSPECIFIED; + expected_bands = save->ready->Bands; + break; + } + + switch( save->ready->BandFmt ) { + case VIPS_FORMAT_CHAR: + case VIPS_FORMAT_UCHAR: + bits_per_pixel = 8; + break; + + case VIPS_FORMAT_SHORT: + case VIPS_FORMAT_USHORT: + bits_per_pixel = 16; + break; + + case VIPS_FORMAT_INT: + case VIPS_FORMAT_UINT: + /* OpenJPEG only supports up to 31. + */ + bits_per_pixel = 31; + break; + + default: + g_assert_not_reached(); + break; + } + /* Set parameters for compressor. */ @@ -659,8 +709,8 @@ vips_foreign_save_jp2k_build( VipsObject *object ) jp2k->comps[i].h = save->ready->Ysize; jp2k->comps[i].x0 = 0; jp2k->comps[i].y0 = 0; - jp2k->comps[i].prec = jp2k->comps[i].bpp = - 8 * vips_format_sizeof( save->ready->BandFmt ); + jp2k->comps[i].prec = bits_per_pixel; + jp2k->comps[i].bpp = bits_per_pixel; jp2k->comps[i].sgnd = !vips_band_format_isuint( save->ready->BandFmt ); } @@ -685,33 +735,6 @@ vips_foreign_save_jp2k_build( VipsObject *object ) /* Create output image. */ - /* CIELAB etc. do not seem to be well documented. - */ - switch( save->ready->Type ) { - case VIPS_INTERPRETATION_B_W: - case VIPS_INTERPRETATION_GREY16: - color_space = OPJ_CLRSPC_GRAY; - expected_bands = 1; - break; - - case VIPS_INTERPRETATION_sRGB: - case VIPS_INTERPRETATION_RGB16: - color_space = jp2k->save_as_ycc ? - OPJ_CLRSPC_SYCC : OPJ_CLRSPC_SRGB; - expected_bands = 3; - break; - - case VIPS_INTERPRETATION_CMYK: - color_space = OPJ_CLRSPC_CMYK; - expected_bands = 4; - break; - - default: - color_space = OPJ_CLRSPC_UNSPECIFIED; - expected_bands = save->ready->Bands; - break; - } - jp2k->image = opj_image_create( save->ready->Bands, jp2k->comps, color_space ); jp2k->image->x1 = save->ready->Xsize; diff --git a/test/test-suite/test_foreign.py b/test/test-suite/test_foreign.py index 8e3a52bb..b42cde82 100644 --- a/test/test-suite/test_foreign.py +++ b/test/test-suite/test_foreign.py @@ -1155,13 +1155,35 @@ class TestForeign: buf = self.colour.jp2ksave_buffer(lossless=True) im2 = pyvips.Image.new_from_buffer(buf, "") - assert self.colour.avg() == im2.avg() + assert (self.colour == im2).min() == 255 # higher Q should mean a bigger buffer b1 = self.mono.jp2ksave_buffer(Q=10) b2 = self.mono.jp2ksave_buffer(Q=90) assert len(b2) > len(b1) + # disabling chroma subsample should mean a bigger buffer + b1 = self.colour.jp2ksave_buffer(subsample_mode="on") + b2 = self.colour.jp2ksave_buffer(subsample_mode="off") + assert len(b2) > len(b1) + + # enabling lossless should mean a bigger buffer + b1 = self.colour.jp2ksave_buffer(lossless=False) + b2 = self.colour.jp2ksave_buffer(lossless=True) + assert len(b2) > len(b1) + + # 16-bit colour load and save + im = self.colour.colourspace("rgb16") + buf = im.jp2ksave_buffer(lossless=True) + im2 = pyvips.Image.new_from_buffer(buf, "") + assert (im == im2).min() == 255 + + # openjpeg 32-bit load and save doesn't seem to work, comment out + # im = self.colour.colourspace("rgb16").cast("uint") << 14 + # buf = im.jp2ksave_buffer(lossless=True) + # im2 = pyvips.Image.new_from_buffer(buf, "") + # assert (im == im2).min() == 255 + if __name__ == '__main__': pytest.main()