From 2ec845b0831dcd00f986b7d187a8b378a1af203b Mon Sep 17 00:00:00 2001 From: Lovell Fuller Date: Sat, 11 Jul 2015 21:44:39 +0100 Subject: [PATCH] Create normalize/blur/sharpen operations Reduces pipeline by ~100 LOC --- src/operations.cc | 147 ++++++++++++++++++++++++++++++++++++++++++++++ src/operations.h | 15 +++++ src/pipeline.cc | 124 ++++---------------------------------- 3 files changed, 173 insertions(+), 113 deletions(-) diff --git a/src/operations.cc b/src/operations.cc index 9338ff9a..88d6979a 100755 --- a/src/operations.cc +++ b/src/operations.cc @@ -183,4 +183,151 @@ namespace sharp { #endif } + /* + * Stretch luminance to cover full dynamic range. + */ + int Normalize(VipsObject *context, VipsImage *image, VipsImage **out) { +#ifndef _WIN32 + // Get original colourspace + VipsInterpretation typeBeforeNormalize = image->Type; + if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) { + typeBeforeNormalize = VIPS_INTERPRETATION_sRGB; + } + // Convert to LAB colourspace + VipsImage *lab; + if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, NULL)) { + return -1; + } + vips_object_local(context, lab); + // Extract luminance + VipsImage *luminance; + if (vips_extract_band(lab, &luminance, 0, "n", 1, NULL)) { + return -1; + } + vips_object_local(context, luminance); + // Extract chroma + VipsImage *chroma; + if (vips_extract_band(lab, &chroma, 1, "n", 2, NULL)) { + return -1; + } + vips_object_local(context, chroma); + // Find luminance range + VipsImage *stats; + if (vips_stats(luminance, &stats, NULL)) { + return -1; + } + vips_object_local(context, stats); + double min = *VIPS_MATRIX(stats, 0, 0); + double max = *VIPS_MATRIX(stats, 1, 0); + if (min != max) { + double f = 100.0 / (max - min); + double a = -(min * f); + // Scale luminance + VipsImage *luminance100; + if (vips_linear1(luminance, &luminance100, f, a, NULL)) { + return -1; + } + vips_object_local(context, luminance100); + // Join scaled luminance to chroma + VipsImage *normalizedLab; + if (vips_bandjoin2(luminance100, chroma, &normalizedLab, NULL)) { + return -1; + } + vips_object_local(context, normalizedLab); + // Convert to original colourspace + VipsImage *normalized; + if (vips_colourspace(normalizedLab, &normalized, typeBeforeNormalize, NULL)) { + return -1; + } + vips_object_local(context, normalized); + // Attach original alpha channel, if any + if (HasAlpha(image)) { + // Extract original alpha channel + VipsImage *alpha; + if (vips_extract_band(image, &alpha, image->Bands - 1, "n", 1, NULL)) { + return -1; + } + vips_object_local(context, alpha); + // Join alpha channel to normalised image + VipsImage *normalizedAlpha; + if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, NULL)) { + return -1; + } + vips_object_local(context, normalizedAlpha); + *out = normalizedAlpha; + } else { + *out = normalized; + } + } else { + // Cannot normalise zero-range image + *out = image; + } +#else + // The normalize operation is currently unsupported on Windows + // See https://github.com/lovell/sharp/issues/152 + *out = image; +#endif + return 0; + } + + /* + * Gaussian blur (use sigma <0 for fast blur) + */ + int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma) { + VipsImage *blurred; + if (sigma < 0.0) { + // Fast, mild blur - averages neighbouring pixels + VipsImage *blur = vips_image_new_matrixv(3, 3, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0, + 1.0, 1.0, 1.0); + vips_image_set_double(blur, "scale", 9); + vips_object_local(context, blur); + if (vips_conv(image, &blurred, blur, NULL)) { + return -1; + } + } else { + // Slower, accurate Gaussian blur + // Create Gaussian function for standard deviation + VipsImage *gaussian; + if (vips_gaussmat(&gaussian, sigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) { + return -1; + } + vips_object_local(context, gaussian); + // Apply Gaussian function + if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) { + return -1; + } + } + vips_object_local(context, blurred); + *out = blurred; + return 0; + } + + /* + * Sharpen flat and jagged areas. Use radius of -1 for fast sharpen. + */ + int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged) { + VipsImage *sharpened; + if (radius == -1) { + // Fast, mild sharpen + VipsImage *sharpen = vips_image_new_matrixv(3, 3, + -1.0, -1.0, -1.0, + -1.0, 32.0, -1.0, + -1.0, -1.0, -1.0); + vips_image_set_double(sharpen, "scale", 24); + vips_object_local(context, sharpen); + if (vips_conv(image, &sharpened, sharpen, NULL)) { + return -1; + } + } else { + // Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas + if (vips_sharpen(image, &sharpened, "radius", radius, "m1", flat, "m2", jagged, NULL)) { + return -1; + } + } + vips_object_local(context, sharpened); + *out = sharpened; + return 0; + } } // namespace sharp diff --git a/src/operations.h b/src/operations.h index b82fbcc1..bcd98d69 100755 --- a/src/operations.h +++ b/src/operations.h @@ -19,6 +19,21 @@ namespace sharp { */ int Unpremultiply(VipsObject *context, VipsImage *image, VipsImage **out); + /* + * Stretch luminance to cover full dynamic range. + */ + int Normalize(VipsObject *context, VipsImage *image, VipsImage **out); + + /* + * Gaussian blur. Use sigma of -1 for fast blur. + */ + int Blur(VipsObject *context, VipsImage *image, VipsImage **out, double sigma); + + /* + * Sharpen flat and jagged areas. Use radius of -1 for fast sharpen. + */ + int Sharpen(VipsObject *context, VipsImage *image, VipsImage **out, int radius, double flat, double jagged); + } // namespace sharp #endif // SRC_OPERATIONS_H_ diff --git a/src/pipeline.cc b/src/pipeline.cc index f448e2e6..674879ba 100755 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -25,6 +25,9 @@ using v8::Exception; using sharp::Composite; using sharp::Premultiply; using sharp::Unpremultiply; +using sharp::Normalize; +using sharp::Blur; +using sharp::Sharpen; using sharp::ImageType; using sharp::DetermineImageType; @@ -672,55 +675,18 @@ class PipelineWorker : public NanAsyncWorker { // Blur if (shouldBlur) { VipsImage *blurred; - if (baton->blurSigma < 0.0) { - // Fast, mild blur - averages neighbouring pixels - VipsImage *blur = vips_image_new_matrixv(3, 3, - 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0, - 1.0, 1.0, 1.0); - vips_image_set_double(blur, "scale", 9); - vips_object_local(hook, blur); - if (vips_conv(image, &blurred, blur, NULL)) { - return Error(); - } - } else { - // Slower, accurate Gaussian blur - // Create Gaussian function for standard deviation - VipsImage *gaussian; - if (vips_gaussmat(&gaussian, baton->blurSigma, 0.2, "separable", TRUE, "integer", TRUE, NULL)) { - return Error(); - } - vips_object_local(hook, gaussian); - // Apply Gaussian function - if (vips_convsep(image, &blurred, gaussian, "precision", VIPS_PRECISION_INTEGER, NULL)) { - return Error(); - } + if (Blur(hook, image, &blurred, baton->blurSigma)) { + return Error(); } - vips_object_local(hook, blurred); image = blurred; } // Sharpen if (shouldSharpen) { VipsImage *sharpened; - if (baton->sharpenRadius == -1) { - // Fast, mild sharpen - VipsImage *sharpen = vips_image_new_matrixv(3, 3, - -1.0, -1.0, -1.0, - -1.0, 32.0, -1.0, - -1.0, -1.0, -1.0); - vips_image_set_double(sharpen, "scale", 24); - vips_object_local(hook, sharpen); - if (vips_conv(image, &sharpened, sharpen, NULL)) { - return Error(); - } - } else { - // Slow, accurate sharpen in LAB colour space, with control over flat vs jagged areas - if (vips_sharpen(image, &sharpened, "radius", baton->sharpenRadius, "m1", baton->sharpenFlat, "m2", baton->sharpenJagged, NULL)) { - return Error(); - } + if (Sharpen(hook, image, &sharpened, baton->sharpenRadius, baton->sharpenFlat, baton->sharpenJagged)) { + return Error(); } - vips_object_local(hook, sharpened); image = sharpened; } @@ -734,82 +700,14 @@ class PipelineWorker : public NanAsyncWorker { image = gammaDecoded; } -#ifndef _WIN32 - // Apply normalization + // Apply normalization - stretch luminance to cover full dynamic range if (baton->normalize) { - VipsInterpretation typeBeforeNormalize = image->Type; - if (typeBeforeNormalize == VIPS_INTERPRETATION_RGB) { - typeBeforeNormalize = VIPS_INTERPRETATION_sRGB; - } - - // normalize the luminance band in LAB space: - VipsImage *lab; - if (vips_colourspace(image, &lab, VIPS_INTERPRETATION_LAB, NULL)) { + VipsImage *normalized; + if (Normalize(hook, image, &normalized)) { return Error(); } - vips_object_local(hook, lab); - - VipsImage *luminance; - if (vips_extract_band(lab, &luminance, 0, "n", 1, NULL)) { - return Error(); - } - vips_object_local(hook, luminance); - - VipsImage *chroma; - if (vips_extract_band(lab, &chroma, 1, "n", 2, NULL)) { - return Error(); - } - vips_object_local(hook, chroma); - - VipsImage *stats; - if (vips_stats(luminance, &stats, NULL)) { - return Error(); - } - vips_object_local(hook, stats); - - double min = *VIPS_MATRIX(stats, 0, 0); - double max = *VIPS_MATRIX(stats, 1, 0); - if (min != max) { - double f = 100.0 / (max - min); - double a = -(min * f); - - VipsImage *luminance100; - if (vips_linear1(luminance, &luminance100, f, a, NULL)) { - return Error(); - } - vips_object_local(hook, luminance100); - - VipsImage *normalizedLab; - if (vips_bandjoin2(luminance100, chroma, &normalizedLab, NULL)) { - return Error(); - } - vips_object_local(hook, normalizedLab); - - VipsImage *normalized; - if (vips_colourspace(normalizedLab, &normalized, typeBeforeNormalize, NULL)) { - return Error(); - } - vips_object_local(hook, normalized); - - if (HasAlpha(image)) { - VipsImage *alpha; - if (vips_extract_band(image, &alpha, image->Bands - 1, "n", 1, NULL)) { - return Error(); - } - vips_object_local(hook, alpha); - - VipsImage *normalizedAlpha; - if (vips_bandjoin2(normalized, alpha, &normalizedAlpha, NULL)) { - return Error(); - } - vips_object_local(hook, normalizedAlpha); - image = normalizedAlpha; - } else { - image = normalized; - } - } + image = normalized; } -#endif // Composite with overlay, if present if (hasOverlay) {