From edb050ed219beeb86c88e865422e90979a481511 Mon Sep 17 00:00:00 2001 From: DarthSim Date: Tue, 17 Dec 2024 21:44:12 +0300 Subject: [PATCH] Fix handling ICC profiles with vips 8.15+ --- CHANGELOG.md | 1 + processing/export_color_profile.go | 4 +++ processing/import_color_profile.go | 12 +++++-- vips/vips.c | 52 ++++++++++++++++++++++++++++++ vips/vips.go | 20 ++++++++++++ vips/vips.h | 2 ++ 6 files changed, 88 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e0037977..ba14ea97 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -23,6 +23,7 @@ - Set `Error` status for errorred traces in OpenTelemetry. - Fix URL parsing error when a non-http(s) URL contains a `%` symbol outside of the percent-encoded sequence. - Fix importing ICC profiles for 16-bit images with an alpha channel. +- Fix handling ICC profiles with vips 8.15+. - (pro) Fix opject detection accuracy when using YOLOv8 or YOLOv10 models. - (pro) Fix usage of the `obj` and `objw` gravity types inside the `crop` processing option. - (pro) Fix detecting of width and height when orientation is specified in EXIF but EXIF info is not requested. diff --git a/processing/export_color_profile.go b/processing/export_color_profile.go index 30218536..df672bad 100644 --- a/processing/export_color_profile.go +++ b/processing/export_color_profile.go @@ -15,6 +15,10 @@ func exportColorProfile(pctx *pipelineContext, img *vips.Image, po *options.Proc } } + // vips 8.15+ tends to lose the colour profile during some color conversions. + // We probably have a backup of the colour profile, so we need to restore it. + img.RestoreColourProfile() + if img.ColourProfileImported() { if keepProfile { // We imported ICC profile and want to keep it, diff --git a/processing/import_color_profile.go b/processing/import_color_profile.go index b56ea7a5..77e7d999 100644 --- a/processing/import_color_profile.go +++ b/processing/import_color_profile.go @@ -22,9 +22,15 @@ func importColorProfile(pctx *pipelineContext, img *vips.Image, po *options.Proc // The image is linear. If we keep its ICC, we'll get wrong colors after // converting it to sRGB img.RemoveColourProfile() - } else if convertToLinear || !img.IsRGB() { - if err := img.ImportColourProfile(); err != nil { - return err + } else { + // vips 8.15+ tends to lose the colour profile during some color conversions. + // We need to backup the colour profile before the conversion and restore it later. + img.BackupColourProfile() + + if convertToLinear || !img.IsRGB() { + if err := img.ImportColourProfile(); err != nil { + return err + } } } diff --git a/vips/vips.c b/vips/vips.c index 00a253d1..b9fe92de 100644 --- a/vips/vips.c +++ b/vips/vips.c @@ -6,6 +6,8 @@ #define VIPS_META_PALETTE_BITS_DEPTH "palette-bit-depth" +#define IMGPROXY_META_ICC_NAME "imgproxy-icc-profile" + int vips_initialize() { @@ -430,6 +432,55 @@ vips_has_embedded_icc(VipsImage *in) return vips_image_get_typeof(in, VIPS_META_ICC_NAME) != 0; } +int +vips_icc_backup(VipsImage *in, VipsImage **out) +{ + if (vips_copy(in, out, NULL)) + return 1; + + if (!vips_image_get_typeof(in, VIPS_META_ICC_NAME)) + return 0; + + const void *data = NULL; + size_t data_len = 0; + + if (vips_image_get_blob(in, VIPS_META_ICC_NAME, &data, &data_len)) + return 0; + + if (!data || data_len < 128) + return 0; + + vips_image_remove(*out, IMGPROXY_META_ICC_NAME); + vips_image_set_blob_copy(*out, IMGPROXY_META_ICC_NAME, data, data_len); + + return 0; +} + +int +vips_icc_restore(VipsImage *in, VipsImage **out) +{ + if (vips_copy(in, out, NULL)) + return 1; + + if (vips_image_get_typeof(in, VIPS_META_ICC_NAME) || + !vips_image_get_typeof(in, IMGPROXY_META_ICC_NAME)) + return 0; + + const void *data = NULL; + size_t data_len = 0; + + if (vips_image_get_blob(in, IMGPROXY_META_ICC_NAME, &data, &data_len)) + return 0; + + if (!data || data_len < 128) + return 0; + + vips_image_remove(*out, VIPS_META_ICC_NAME); + vips_image_set_blob_copy(*out, VIPS_META_ICC_NAME, data, data_len); + + return 0; +} + int vips_icc_import_go(VipsImage *in, VipsImage **out) { @@ -506,6 +557,7 @@ vips_icc_remove(VipsImage *in, VipsImage **out) return 1; vips_image_remove(*out, VIPS_META_ICC_NAME); + vips_image_remove(*out, IMGPROXY_META_ICC_NAME); vips_image_remove(*out, "exif-ifd0-WhitePoint"); vips_image_remove(*out, "exif-ifd0-PrimaryChromaticities"); vips_image_remove(*out, "exif-ifd2-ColorSpace"); diff --git a/vips/vips.go b/vips/vips.go index b216a85b..22ade3ed 100644 --- a/vips/vips.go +++ b/vips/vips.go @@ -736,6 +736,26 @@ func (img *Image) IsLinear() bool { return C.vips_image_guess_interpretation(img.VipsImage) == C.VIPS_INTERPRETATION_scRGB } +func (img *Image) BackupColourProfile() { + var tmp *C.VipsImage + + if C.vips_icc_backup(img.VipsImage, &tmp) == 0 { + C.swap_and_clear(&img.VipsImage, tmp) + } else { + log.Warningf("Can't backup ICC profile: %s", Error()) + } +} + +func (img *Image) RestoreColourProfile() { + var tmp *C.VipsImage + + if C.vips_icc_restore(img.VipsImage, &tmp) == 0 { + C.swap_and_clear(&img.VipsImage, tmp) + } else { + log.Warningf("Can't restore ICC profile: %s", Error()) + } +} + func (img *Image) ImportColourProfile() error { var tmp *C.VipsImage diff --git a/vips/vips.h b/vips/vips.h index 9a4b2314..84c9352e 100644 --- a/vips/vips.h +++ b/vips/vips.h @@ -48,6 +48,8 @@ int vips_resize_go(VipsImage *in, VipsImage **out, double wscale, double hscale) int vips_icc_is_srgb_iec61966(VipsImage *in); int vips_has_embedded_icc(VipsImage *in); +int vips_icc_backup(VipsImage *in, VipsImage **out); +int vips_icc_restore(VipsImage *in, VipsImage **out); int vips_icc_import_go(VipsImage *in, VipsImage **out); int vips_icc_export_go(VipsImage *in, VipsImage **out); int vips_icc_export_srgb(VipsImage *in, VipsImage **out);