Create normalize/blur/sharpen operations

Reduces pipeline by ~100 LOC
This commit is contained in:
Lovell Fuller 2015-07-11 21:44:39 +01:00
parent c40cd1aa50
commit 2ec845b083
3 changed files with 173 additions and 113 deletions

View File

@ -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

View File

@ -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_

View File

@ -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)) {
if (Blur(hook, image, &blurred, baton->blurSigma)) {
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();
}
}
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)) {
if (Sharpen(hook, image, &sharpened, baton->sharpenRadius, baton->sharpenFlat, baton->sharpenJagged)) {
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();
}
}
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)) {
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)) {
if (Normalize(hook, image, &normalized)) {
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;
}
}
}
#endif
// Composite with overlay, if present
if (hasOverlay) {