mirror of
https://github.com/lovell/sharp.git
synced 2025-07-10 11:00:14 +02:00
Create normalize/blur/sharpen operations
Reduces pipeline by ~100 LOC
This commit is contained in:
parent
c40cd1aa50
commit
2ec845b083
@ -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
|
||||
|
@ -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_
|
||||
|
124
src/pipeline.cc
124
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) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user