diff --git a/docs/api-operation.md b/docs/api-operation.md index 037c95d8d..fb7a747e2 100644 --- a/docs/api-operation.md +++ b/docs/api-operation.md @@ -243,6 +243,30 @@ Alternative spelling of normalise. Returns **Sharp** +## clahe + +Perform contrast limiting adaptive histogram equalization (CLAHE) + +This will, in general, enhance the clarity of the image by bringing out +darker details. Please read more about CLAHE here: +[https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE][8] + +### Parameters + +* `options` **[Object][2]** + + * `options.width` **[number][1]** integer width of the region in pixels. + * `options.height` **[number][1]** integer height of the region in pixels. + * `options.maxSlope` **[number][1]** maximum value for the slope of the + cumulative histogram. A value of 0 disables contrast limiting. Valid values + are integers in the range 0-100 (inclusive) (optional, default `3`) + + + +* Throws **[Error][5]** Invalid parameters + +Returns **Sharp** + ## convolve Convolve the image with the specified kernel. @@ -252,7 +276,7 @@ Convolve the image with the specified kernel. * `kernel` **[Object][2]** * `kernel.width` **[number][1]** width of the kernel in pixels. - * `kernel.height` **[number][1]** width of the kernel in pixels. + * `kernel.height` **[number][1]** height of the kernel in pixels. * `kernel.kernel` **[Array][7]<[number][1]>** Array of length `width*height` containing the kernel values. * `kernel.scale` **[number][1]** the scale of the kernel in pixels. (optional, default `sum`) * `kernel.offset` **[number][1]** the offset of the kernel in pixels. (optional, default `0`) @@ -304,7 +328,7 @@ the selected bitwise boolean `operation` between the corresponding pixels of the ### Parameters -* `operand` **([Buffer][8] | [string][3])** Buffer containing image data or string containing the path to an image file. +* `operand` **([Buffer][9] | [string][3])** Buffer containing image data or string containing the path to an image file. * `operator` **[string][3]** one of `and`, `or` or `eor` to perform that bitwise operation, like the C logic operators `&`, `|` and `^` respectively. * `options` **[Object][2]?** @@ -421,4 +445,6 @@ Returns **Sharp** [7]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array -[8]: https://nodejs.org/api/buffer.html +[8]: https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE + +[9]: https://nodejs.org/api/buffer.html diff --git a/docs/search-index.json b/docs/search-index.json index 46bc23c66..27117513c 100644 --- a/docs/search-index.json +++ b/docs/search-index.json @@ -1 +1 @@ -[{"t":"Prerequisites","d":"Node.js v10","k":"prerequisites node","l":"/install#prerequisites"},{"t":"Prebuilt binaries","d":"Ready-compiled sharp and libvips binaries are provided for use with Node.js v10 on the most common platforms macOS x64 10.13 Linux x64 glibc 2.17, musl 1.1.24 Linux ARM64 glibc 2.2","k":"prebuilt binaries compiled sharp libvips node common platforms macos linux glibc musl arm","l":"/install#prebuilt-binaries"},{"t":"Common problems","d":"The architecture and platform of Node.js used for npm install must be the same as the architecture and platform of Node.js used at runtime. See the cross-platform","k":"common problems architecture platform node npm install runtime cross","l":"/install#common-problems"},{"t":"Apple M1","d":"Prebuilt libvips binaries are provided for macOS on ARM64 since sharp v0.28.0. During npm install sharp will be built locally, which requires Xcode and Python - see building from s","k":"apple prebuilt libvips binaries macos arm sharp during npm install built locally requires xcode python building","l":"/install#apple-m1"},{"t":"Custom libvips","d":"To use a custom, globally-installed version of libvips instead of the provided binaries, make sure it is at least the version listed under config.libvips in the package.json file a","k":"custom libvips globally installed version instead binaries make listed config package json file","l":"/install#custom-libvips"},{"t":"Building from source","d":"This module will be compiled from source at npm install time when a globally-installed libvips is detected set the SHARP_IGNORE_GLOBAL_LIBVIPS environment variable to skip this, pr","k":"building source module compiled npm install time globally installed libvips detected environment variable skip","l":"/install#building-from-source"},{"t":"Custom prebuilt binaries","d":"This is an advanced approach that most people will not require. To install the prebuilt sharp binaries from a custom URL, set the sharp_binary_host npm config option or the npm_con","k":"custom prebuilt binaries advanced approach people require install sharp url npm config option npmcon","l":"/install#custom-prebuilt-binaries"},{"t":"Chinese mirror","d":"A mirror site based in China, provided by Alibaba, contains binaries for both sharp and libvips. To use this either set the following configuration sh npm config set sharp_binary_h","k":"chinese mirror site china alibaba contains binaries sharp libvips following configuration npm config sharpbinaryh","l":"/install#chinese-mirror"},{"t":"FreeBSD","d":"The vips package must be installed before npm install is run. sh pkg install -y pkgconf vips sh cd /usr/ports/graphics/vips/ make install clean","k":"freebsd vips package installed npm install run pkg pkgconf usr ports graphics make clean","l":"/install#freebsd"},{"t":"Linux memory allocator","d":"The default memory allocator on most glibc-based Linux systems e.g. Debian, Red Hat is unsuitable for long-running, multi-threaded processes that involve lots of small memory alloc","k":"linux memory allocator glibc systems debian red hat unsuitable long running multi threaded processes small alloc","l":"/install#linux-memory-allocator"},{"t":"Heroku","d":"Add the jemalloc buildpack to reduce the effects of memory fragmentation. Set NODE_MODULES_CACHE","k":"heroku add jemalloc buildpack reduce effects memory fragmentation","l":"/install#heroku"},{"t":"AWS Lambda","d":"The binaries in the node_modules directory of the deployment package must be for the Linux x64 platform. When building your deployment package on machines other than Linux x64 glib","k":"aws lambda binaries nodemodules directory deployment package linux platform building your machines other than glib","l":"/install#aws-lambda"},{"t":"Webpack","d":"Ensure sharp is added to the externals configuration. js externals sharp commonjs sharp","k":"webpack sharp added externals configuration commonjs","l":"/install#webpack"},{"t":"Worker threads","d":"The main thread must call requiresharp before worker threads are created to ensure shared libraries remain loaded in memory until after all threads are complete.","k":"worker threads main thread requiresharp created shared libraries remain loaded memory complete","l":"/install#worker-threads"},{"t":"Canvas and Windows","d":"The prebuilt binaries provided by canvas for Windows depend on the unmaintained GTK 2, last updated in 2011. These conflict with the modern, up-to-date binaries provided by sharp.","k":"canvas windows prebuilt binaries depend unmaintained gtk last updated conflict modern date sharp","l":"/install#canvas-and-windows"},{"t":"Sharp","d":"Constructor factory to create an instance of sharp, to which further methods are chained.","k":"sharp constructor factory create instance further methods chained","l":"/api-constructor#sharp"},{"t":"clone","d":"Take a snapshot of the Sharp instance, returning a new instance. Cloned instances inherit the input of their parent instance. This allows multiple output Streams and therefore mult","k":"clone snapshot sharp instance returning new cloned instances inherit input parent multiple output streams mult","l":"/api-constructor#clone"},{"t":"metadata","d":"Fast access to uncached image metadata without decoding any compressed image data. A Promise is returned when callback is not provided.","k":"metadata fast access uncached decoding compressed data promise returned callback","l":"/api-input#metadata"},{"t":"stats","d":"Access to pixel-derived image statistics for every channel in the image. A Promise is returned when callback is not provided.","k":"stats access pixel derived statistics channel promise returned callback","l":"/api-input#stats"},{"t":"toFile","d":"Write output image data to a file.","k":"tofile write output data file","l":"/api-output#tofile"},{"t":"toBuffer","d":"Write output to a Buffer. JPEG, PNG, WebP, AVIF, TIFF and raw pixel data output are supported.","k":"tobuffer write output buffer jpeg png webp avif tiff raw pixel data","l":"/api-output#tobuffer"},{"t":"withMetadata","d":"Include all metadata EXIF, XMP, IPTC from the input image in the output image. This will also convert to and add a web-friendly sRGB ICC profile unless a custom output profile is p","k":"withmetadata include metadata exif xmp iptc input output convert add web friendly srgb icc profile custom","l":"/api-output#withmetadata"},{"t":"toFormat","d":"Force output to a given format.","k":"toformat force output format","l":"/api-output#toformat"},{"t":"jpeg","d":"Use these JPEG options for output image.","k":"jpeg output","l":"/api-output#jpeg"},{"t":"png","d":"Use these PNG options for output image.","k":"png output","l":"/api-output#png"},{"t":"webp","d":"Use these WebP options for output image.","k":"webp output","l":"/api-output#webp"},{"t":"gif","d":"Use these GIF options for output image.","k":"gif output","l":"/api-output#gif"},{"t":"tiff","d":"Use these TIFF options for output image.","k":"tiff output","l":"/api-output#tiff"},{"t":"avif","d":"Use these AVIF options for output image.","k":"avif output","l":"/api-output#avif"},{"t":"heif","d":"Use these HEIF options for output image.","k":"heif output","l":"/api-output#heif"},{"t":"raw","d":"Force output to be raw, uncompressed, 8-bit unsigned integer unit8 pixel data. Pixel ordering is left-to-right, top-to-bottom, without padding. Channel ordering will be RGB or RGBA","k":"raw force output uncompressed bit unsigned integer unit pixel data ordering left right top bottom padding channel rgb rgba","l":"/api-output#raw"},{"t":"tile","d":"Use tile-based deep zoom image pyramid output. Set the format and options for tile images via the toFormat, jpeg, png or webp functions. Use a .zip or .szi file extension with toFi","k":"tile deep zoom pyramid output format images via toformat jpeg png webp functions zip szi file extension tofi","l":"/api-output#tile"},{"t":"resize","d":"Resize image to width, height or width x height.","k":"resize width height","l":"/api-resize#resize"},{"t":"extend","d":"Extends/pads the edges of the image with the provided background colour. This operation will always occur after resizing and extraction, if any.","k":"extend extends pads edges background colour operation resizing extraction","l":"/api-resize#extend"},{"t":"extract","d":"Extract/crop a region of the image.","k":"extract crop region","l":"/api-resize#extract"},{"t":"trim","d":"Trim boring pixels from all edges that contain values similar to the top-left pixel. Images consisting entirely of a single colour will calculate boring using the alpha channel, if","k":"trim boring pixels edges contain similar top left pixel images consisting entirely single colour calculate alpha channel","l":"/api-resize#trim"},{"t":"composite","d":"Composite images over the processed resized, extracted etc. image.","k":"composite images processed resized extracted","l":"/api-composite#composite"},{"t":"rotate","d":"Rotate the output image by either an explicit angle or auto-orient based on the EXIF Orientation tag.","k":"rotate output explicit angle auto orient exif orientation tag","l":"/api-operation#rotate"},{"t":"flip","d":"Flip the image about the vertical Y axis. This always occurs after rotation, if any. The use of flip implies the removal of the EXIF Orientation tag, if any.","k":"flip vertical axis rotation implies removal exif orientation tag","l":"/api-operation#flip"},{"t":"flop","d":"Flop the image about the horizontal X axis. This always occurs after rotation, if any. The use of flop implies the removal of the EXIF Orientation tag, if any.","k":"flop horizontal axis rotation implies removal exif orientation tag","l":"/api-operation#flop"},{"t":"affine","d":"Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.","k":"affine transform operation resizing extraction rotation","l":"/api-operation#affine"},{"t":"sharpen","d":"Sharpen the image. When used without parameters, performs a fast, mild sharpen of the output image. When a sigma is provided, performs a slower, more accurate sharpen of the L chan","k":"sharpen parameters fast mild output sigma slower accurate chan","l":"/api-operation#sharpen"},{"t":"median","d":"Apply median filter. When used without parameters the default window is 3x3.","k":"median apply filter parameters window","l":"/api-operation#median"},{"t":"blur","d":"Blur the image. When used without parameters, performs a fast, mild blur of the output image. When a sigma is provided, performs a slower, more accurate Gaussian blur.","k":"blur parameters fast mild output sigma slower accurate gaussian","l":"/api-operation#blur"},{"t":"flatten","d":"Merge alpha transparency channel, if any, with a background, then remove the alpha channel.","k":"flatten merge alpha transparency channel background then remove","l":"/api-operation#flatten"},{"t":"gamma","d":"Apply a gamma correction by reducing the encoding darken pre-resize at a factor of 1/gamma then increasing the encoding brighten post-resize at a factor of gamma. This can improve","k":"gamma apply correction reducing encoding darken pre resize factor then increasing brighten post improve","l":"/api-operation#gamma"},{"t":"negate","d":"Produce the negative of the image.","k":"negate produce negative","l":"/api-operation#negate"},{"t":"normalise","d":"Enhance output image contrast by stretching its luminance to cover the full dynamic range.","k":"normalise enhance output contrast stretching luminance cover full dynamic range","l":"/api-operation#normalise"},{"t":"normalize","d":"Alternative spelling of normalise.","k":"normalize normalise","l":"/api-operation#normalize"},{"t":"convolve","d":"Convolve the image with the specified kernel.","k":"convolve specified kernel","l":"/api-operation#convolve"},{"t":"threshold","d":"Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.","k":"threshold pixel greater than equal otherwise","l":"/api-operation#threshold"},{"t":"boolean","d":"Perform a bitwise boolean operation with operand image.","k":"boolean bitwise operation operand","l":"/api-operation#boolean"},{"t":"linear","d":"Apply the linear formula a input b to the image levels adjustment","k":"linear apply formula input levels adjustment","l":"/api-operation#linear"},{"t":"recomb","d":"Recomb the image with the specified matrix.","k":"recomb specified matrix","l":"/api-operation#recomb"},{"t":"modulate","d":"Transforms the image using brightness, saturation and hue rotation.","k":"modulate transforms brightness saturation hue rotation","l":"/api-operation#modulate"},{"t":"removeAlpha","d":"Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.","k":"removealpha remove alpha channel","l":"/api-channel#removealpha"},{"t":"ensureAlpha","d":"Ensure the output image has an alpha transparency channel. If missing, the added alpha channel will have the specified transparency level, defaulting to fully-opaque 1. This is a n","k":"ensurealpha output alpha transparency channel missing added specified level defaulting fully opaque","l":"/api-channel#ensurealpha"},{"t":"extractChannel","d":"Extract a single channel from a multi-channel image.","k":"extractchannel extract single channel multi","l":"/api-channel#extractchannel"},{"t":"joinChannel","d":"Join one or more channels to the image. The meaning of the added channels depends on the output colourspace, set with toColourspace. By default the output image will be web-friendl","k":"joinchannel join one channels meaning added depends output colourspace tocolourspace web friendl","l":"/api-channel#joinchannel"},{"t":"bandbool","d":"Perform a bitwise boolean operation on all input image channels bands to produce a single channel output image.","k":"bandbool bitwise boolean operation input channels bands produce single channel output","l":"/api-channel#bandbool"},{"t":"tint","d":"Tint the image using the provided chroma while preserving the image luminance. An alpha channel may be present and will be unchanged by the operation.","k":"tint chroma preserving luminance alpha channel present unchanged operation","l":"/api-colour#tint"},{"t":"greyscale","d":"Convert to 8-bit greyscale 256 shades of grey. This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use gamma with greyscale for the best re","k":"greyscale convert bit shades grey linear operation input colour space srgb gamma best","l":"/api-colour#greyscale"},{"t":"grayscale","d":"Alternative spelling of greyscale.","k":"grayscale greyscale","l":"/api-colour#grayscale"},{"t":"toColourspace","d":"Set the output colourspace. By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.","k":"tocolourspace output colourspace web friendly srgb additional channels interpreted alpha","l":"/api-colour#tocolourspace"},{"t":"toColorspace","d":"Alternative spelling of toColourspace.","k":"tocolorspace tocolourspace","l":"/api-colour#tocolorspace"},{"t":"format","d":"An Object containing nested boolean values representing the available input and output formats/methods.","k":"format object nested boolean representing available input output formats methods","l":"/api-utility#format"},{"t":"interpolators","d":"An Object containing the available interpolators and their proper values","k":"interpolators object available proper","l":"/api-utility#interpolators"},{"t":"versions","d":"An Object containing the version numbers of libvips and its dependencies.","k":"versions object version numbers libvips dependencies","l":"/api-utility#versions"},{"t":"cache","d":"Gets or, when options are provided, sets the limits of libvips operation cache. Existing entries in the cache will be trimmed after any change in limits. This method always returns","k":"cache limits libvips operation existing entries trimmed change method returns","l":"/api-utility#cache"},{"t":"concurrency","d":"Gets or, when a concurrency is provided, sets the number of threads libvips should create to process each image.","k":"concurrency number threads libvips create process","l":"/api-utility#concurrency"},{"t":"queue","d":"An EventEmitter that emits a change event when a task is either","k":"queue eventemitter emits change event task","l":"/api-utility#queue"},{"t":"counters","d":"Provides access to internal task counters.","k":"counters provides access internal task","l":"/api-utility#counters"},{"t":"simd","d":"Get and set use of SIMD vector unit instructions. Requires libvips to have been compiled with liborc support.","k":"simd vector unit instructions requires libvips compiled liborc","l":"/api-utility#simd"}] \ No newline at end of file +[{"t":"Prerequisites","d":"Node.js v10","k":"prerequisites node","l":"/install#prerequisites"},{"t":"Prebuilt binaries","d":"Ready-compiled sharp and libvips binaries are provided for use with Node.js v10 on the most common platforms macOS x64 10.13 Linux x64 glibc 2.17, musl 1.1.24 Linux ARM64 glibc 2.2","k":"prebuilt binaries compiled sharp libvips node common platforms macos linux glibc musl arm","l":"/install#prebuilt-binaries"},{"t":"Common problems","d":"The architecture and platform of Node.js used for npm install must be the same as the architecture and platform of Node.js used at runtime. See the cross-platform","k":"common problems architecture platform node npm install runtime cross","l":"/install#common-problems"},{"t":"Apple M1","d":"Prebuilt libvips binaries are provided for macOS on ARM64 since sharp v0.28.0. During npm install sharp will be built locally, which requires Xcode and Python - see building from s","k":"apple prebuilt libvips binaries macos arm sharp during npm install built locally requires xcode python building","l":"/install#apple-m1"},{"t":"Custom libvips","d":"To use a custom, globally-installed version of libvips instead of the provided binaries, make sure it is at least the version listed under config.libvips in the package.json file a","k":"custom libvips globally installed version instead binaries make listed config package json file","l":"/install#custom-libvips"},{"t":"Building from source","d":"This module will be compiled from source at npm install time when a globally-installed libvips is detected set the SHARP_IGNORE_GLOBAL_LIBVIPS environment variable to skip this, pr","k":"building source module compiled npm install time globally installed libvips detected environment variable skip","l":"/install#building-from-source"},{"t":"Custom prebuilt binaries","d":"This is an advanced approach that most people will not require. To install the prebuilt sharp binaries from a custom URL, set the sharp_binary_host npm config option or the npm_con","k":"custom prebuilt binaries advanced approach people require install sharp url npm config option npmcon","l":"/install#custom-prebuilt-binaries"},{"t":"Chinese mirror","d":"A mirror site based in China, provided by Alibaba, contains binaries for both sharp and libvips. To use this either set the following configuration sh npm config set sharp_binary_h","k":"chinese mirror site china alibaba contains binaries sharp libvips following configuration npm config sharpbinaryh","l":"/install#chinese-mirror"},{"t":"FreeBSD","d":"The vips package must be installed before npm install is run. sh pkg install -y pkgconf vips sh cd /usr/ports/graphics/vips/ make install clean","k":"freebsd vips package installed npm install run pkg pkgconf usr ports graphics make clean","l":"/install#freebsd"},{"t":"Linux memory allocator","d":"The default memory allocator on most glibc-based Linux systems e.g. Debian, Red Hat is unsuitable for long-running, multi-threaded processes that involve lots of small memory alloc","k":"linux memory allocator glibc systems debian red hat unsuitable long running multi threaded processes small alloc","l":"/install#linux-memory-allocator"},{"t":"Heroku","d":"Add the jemalloc buildpack to reduce the effects of memory fragmentation. Set NODE_MODULES_CACHE","k":"heroku add jemalloc buildpack reduce effects memory fragmentation","l":"/install#heroku"},{"t":"AWS Lambda","d":"The binaries in the node_modules directory of the deployment package must be for the Linux x64 platform. When building your deployment package on machines other than Linux x64 glib","k":"aws lambda binaries nodemodules directory deployment package linux platform building your machines other than glib","l":"/install#aws-lambda"},{"t":"Webpack","d":"Ensure sharp is added to the externals configuration. js externals sharp commonjs sharp","k":"webpack sharp added externals configuration commonjs","l":"/install#webpack"},{"t":"Worker threads","d":"The main thread must call requiresharp before worker threads are created to ensure shared libraries remain loaded in memory until after all threads are complete.","k":"worker threads main thread requiresharp created shared libraries remain loaded memory complete","l":"/install#worker-threads"},{"t":"Canvas and Windows","d":"The prebuilt binaries provided by canvas for Windows depend on the unmaintained GTK 2, last updated in 2011. These conflict with the modern, up-to-date binaries provided by sharp.","k":"canvas windows prebuilt binaries depend unmaintained gtk last updated conflict modern date sharp","l":"/install#canvas-and-windows"},{"t":"Sharp","d":"Constructor factory to create an instance of sharp, to which further methods are chained.","k":"sharp constructor factory create instance further methods chained","l":"/api-constructor#sharp"},{"t":"clone","d":"Take a snapshot of the Sharp instance, returning a new instance. Cloned instances inherit the input of their parent instance. This allows multiple output Streams and therefore mult","k":"clone snapshot sharp instance returning new cloned instances inherit input parent multiple output streams mult","l":"/api-constructor#clone"},{"t":"metadata","d":"Fast access to uncached image metadata without decoding any compressed image data. A Promise is returned when callback is not provided.","k":"metadata fast access uncached decoding compressed data promise returned callback","l":"/api-input#metadata"},{"t":"stats","d":"Access to pixel-derived image statistics for every channel in the image. A Promise is returned when callback is not provided.","k":"stats access pixel derived statistics channel promise returned callback","l":"/api-input#stats"},{"t":"toFile","d":"Write output image data to a file.","k":"tofile write output data file","l":"/api-output#tofile"},{"t":"toBuffer","d":"Write output to a Buffer. JPEG, PNG, WebP, AVIF, TIFF and raw pixel data output are supported.","k":"tobuffer write output buffer jpeg png webp avif tiff raw pixel data","l":"/api-output#tobuffer"},{"t":"withMetadata","d":"Include all metadata EXIF, XMP, IPTC from the input image in the output image. This will also convert to and add a web-friendly sRGB ICC profile unless a custom output profile is p","k":"withmetadata include metadata exif xmp iptc input output convert add web friendly srgb icc profile custom","l":"/api-output#withmetadata"},{"t":"toFormat","d":"Force output to a given format.","k":"toformat force output format","l":"/api-output#toformat"},{"t":"jpeg","d":"Use these JPEG options for output image.","k":"jpeg output","l":"/api-output#jpeg"},{"t":"png","d":"Use these PNG options for output image.","k":"png output","l":"/api-output#png"},{"t":"webp","d":"Use these WebP options for output image.","k":"webp output","l":"/api-output#webp"},{"t":"gif","d":"Use these GIF options for output image.","k":"gif output","l":"/api-output#gif"},{"t":"tiff","d":"Use these TIFF options for output image.","k":"tiff output","l":"/api-output#tiff"},{"t":"avif","d":"Use these AVIF options for output image.","k":"avif output","l":"/api-output#avif"},{"t":"heif","d":"Use these HEIF options for output image.","k":"heif output","l":"/api-output#heif"},{"t":"raw","d":"Force output to be raw, uncompressed, 8-bit unsigned integer unit8 pixel data. Pixel ordering is left-to-right, top-to-bottom, without padding. Channel ordering will be RGB or RGBA","k":"raw force output uncompressed bit unsigned integer unit pixel data ordering left right top bottom padding channel rgb rgba","l":"/api-output#raw"},{"t":"tile","d":"Use tile-based deep zoom image pyramid output. Set the format and options for tile images via the toFormat, jpeg, png or webp functions. Use a .zip or .szi file extension with toFi","k":"tile deep zoom pyramid output format images via toformat jpeg png webp functions zip szi file extension tofi","l":"/api-output#tile"},{"t":"resize","d":"Resize image to width, height or width x height.","k":"resize width height","l":"/api-resize#resize"},{"t":"extend","d":"Extends/pads the edges of the image with the provided background colour. This operation will always occur after resizing and extraction, if any.","k":"extend extends pads edges background colour operation resizing extraction","l":"/api-resize#extend"},{"t":"extract","d":"Extract/crop a region of the image.","k":"extract crop region","l":"/api-resize#extract"},{"t":"trim","d":"Trim boring pixels from all edges that contain values similar to the top-left pixel. Images consisting entirely of a single colour will calculate boring using the alpha channel, if","k":"trim boring pixels edges contain similar top left pixel images consisting entirely single colour calculate alpha channel","l":"/api-resize#trim"},{"t":"composite","d":"Composite images over the processed resized, extracted etc. image.","k":"composite images processed resized extracted","l":"/api-composite#composite"},{"t":"rotate","d":"Rotate the output image by either an explicit angle or auto-orient based on the EXIF Orientation tag.","k":"rotate output explicit angle auto orient exif orientation tag","l":"/api-operation#rotate"},{"t":"flip","d":"Flip the image about the vertical Y axis. This always occurs after rotation, if any. The use of flip implies the removal of the EXIF Orientation tag, if any.","k":"flip vertical axis rotation implies removal exif orientation tag","l":"/api-operation#flip"},{"t":"flop","d":"Flop the image about the horizontal X axis. This always occurs after rotation, if any. The use of flop implies the removal of the EXIF Orientation tag, if any.","k":"flop horizontal axis rotation implies removal exif orientation tag","l":"/api-operation#flop"},{"t":"affine","d":"Perform an affine transform on an image. This operation will always occur after resizing, extraction and rotation, if any.","k":"affine transform operation resizing extraction rotation","l":"/api-operation#affine"},{"t":"sharpen","d":"Sharpen the image. When used without parameters, performs a fast, mild sharpen of the output image. When a sigma is provided, performs a slower, more accurate sharpen of the L chan","k":"sharpen parameters fast mild output sigma slower accurate chan","l":"/api-operation#sharpen"},{"t":"median","d":"Apply median filter. When used without parameters the default window is 3x3.","k":"median apply filter parameters window","l":"/api-operation#median"},{"t":"blur","d":"Blur the image. When used without parameters, performs a fast, mild blur of the output image. When a sigma is provided, performs a slower, more accurate Gaussian blur.","k":"blur parameters fast mild output sigma slower accurate gaussian","l":"/api-operation#blur"},{"t":"flatten","d":"Merge alpha transparency channel, if any, with a background, then remove the alpha channel.","k":"flatten merge alpha transparency channel background then remove","l":"/api-operation#flatten"},{"t":"gamma","d":"Apply a gamma correction by reducing the encoding darken pre-resize at a factor of 1/gamma then increasing the encoding brighten post-resize at a factor of gamma. This can improve","k":"gamma apply correction reducing encoding darken pre resize factor then increasing brighten post improve","l":"/api-operation#gamma"},{"t":"negate","d":"Produce the negative of the image.","k":"negate produce negative","l":"/api-operation#negate"},{"t":"normalise","d":"Enhance output image contrast by stretching its luminance to cover the full dynamic range.","k":"normalise enhance output contrast stretching luminance cover full dynamic range","l":"/api-operation#normalise"},{"t":"normalize","d":"Alternative spelling of normalise.","k":"normalize normalise","l":"/api-operation#normalize"},{"t":"clahe","d":"Perform contrast limiting adaptive histogram equalization CLAHE","k":"clahe contrast limiting adaptive histogram equalization","l":"/api-operation#clahe"},{"t":"convolve","d":"Convolve the image with the specified kernel.","k":"convolve specified kernel","l":"/api-operation#convolve"},{"t":"threshold","d":"Any pixel value greater than or equal to the threshold value will be set to 255, otherwise it will be set to 0.","k":"threshold pixel greater than equal otherwise","l":"/api-operation#threshold"},{"t":"boolean","d":"Perform a bitwise boolean operation with operand image.","k":"boolean bitwise operation operand","l":"/api-operation#boolean"},{"t":"linear","d":"Apply the linear formula a input b to the image levels adjustment","k":"linear apply formula input levels adjustment","l":"/api-operation#linear"},{"t":"recomb","d":"Recomb the image with the specified matrix.","k":"recomb specified matrix","l":"/api-operation#recomb"},{"t":"modulate","d":"Transforms the image using brightness, saturation and hue rotation.","k":"modulate transforms brightness saturation hue rotation","l":"/api-operation#modulate"},{"t":"removeAlpha","d":"Remove alpha channel, if any. This is a no-op if the image does not have an alpha channel.","k":"removealpha remove alpha channel","l":"/api-channel#removealpha"},{"t":"ensureAlpha","d":"Ensure the output image has an alpha transparency channel. If missing, the added alpha channel will have the specified transparency level, defaulting to fully-opaque 1. This is a n","k":"ensurealpha output alpha transparency channel missing added specified level defaulting fully opaque","l":"/api-channel#ensurealpha"},{"t":"extractChannel","d":"Extract a single channel from a multi-channel image.","k":"extractchannel extract single channel multi","l":"/api-channel#extractchannel"},{"t":"joinChannel","d":"Join one or more channels to the image. The meaning of the added channels depends on the output colourspace, set with toColourspace. By default the output image will be web-friendl","k":"joinchannel join one channels meaning added depends output colourspace tocolourspace web friendl","l":"/api-channel#joinchannel"},{"t":"bandbool","d":"Perform a bitwise boolean operation on all input image channels bands to produce a single channel output image.","k":"bandbool bitwise boolean operation input channels bands produce single channel output","l":"/api-channel#bandbool"},{"t":"tint","d":"Tint the image using the provided chroma while preserving the image luminance. An alpha channel may be present and will be unchanged by the operation.","k":"tint chroma preserving luminance alpha channel present unchanged operation","l":"/api-colour#tint"},{"t":"greyscale","d":"Convert to 8-bit greyscale 256 shades of grey. This is a linear operation. If the input image is in a non-linear colour space such as sRGB, use gamma with greyscale for the best re","k":"greyscale convert bit shades grey linear operation input colour space srgb gamma best","l":"/api-colour#greyscale"},{"t":"grayscale","d":"Alternative spelling of greyscale.","k":"grayscale greyscale","l":"/api-colour#grayscale"},{"t":"toColourspace","d":"Set the output colourspace. By default output image will be web-friendly sRGB, with additional channels interpreted as alpha channels.","k":"tocolourspace output colourspace web friendly srgb additional channels interpreted alpha","l":"/api-colour#tocolourspace"},{"t":"toColorspace","d":"Alternative spelling of toColourspace.","k":"tocolorspace tocolourspace","l":"/api-colour#tocolorspace"},{"t":"format","d":"An Object containing nested boolean values representing the available input and output formats/methods.","k":"format object nested boolean representing available input output formats methods","l":"/api-utility#format"},{"t":"interpolators","d":"An Object containing the available interpolators and their proper values","k":"interpolators object available proper","l":"/api-utility#interpolators"},{"t":"versions","d":"An Object containing the version numbers of libvips and its dependencies.","k":"versions object version numbers libvips dependencies","l":"/api-utility#versions"},{"t":"cache","d":"Gets or, when options are provided, sets the limits of libvips operation cache. Existing entries in the cache will be trimmed after any change in limits. This method always returns","k":"cache limits libvips operation existing entries trimmed change method returns","l":"/api-utility#cache"},{"t":"concurrency","d":"Gets or, when a concurrency is provided, sets the number of threads libvips should create to process each image.","k":"concurrency number threads libvips create process","l":"/api-utility#concurrency"},{"t":"queue","d":"An EventEmitter that emits a change event when a task is either","k":"queue eventemitter emits change event task","l":"/api-utility#queue"},{"t":"counters","d":"Provides access to internal task counters.","k":"counters provides access internal task","l":"/api-utility#counters"},{"t":"simd","d":"Get and set use of SIMD vector unit instructions. Requires libvips to have been compiled with liborc support.","k":"simd vector unit instructions requires libvips compiled liborc","l":"/api-utility#simd"}] \ No newline at end of file diff --git a/lib/constructor.js b/lib/constructor.js index 85f7284cb..d25091d24 100644 --- a/lib/constructor.js +++ b/lib/constructor.js @@ -216,6 +216,9 @@ const Sharp = function (input, options) { gammaOut: 0, greyscale: false, normalise: false, + claheWidth: 0, + claheHeight: 0, + claheMaxSlope: 3, brightness: 1, saturation: 1, hue: 0, diff --git a/lib/operation.js b/lib/operation.js index 7ab114732..0be23407d 100644 --- a/lib/operation.js +++ b/lib/operation.js @@ -350,6 +350,46 @@ function normalize (normalize) { return this.normalise(normalize); } +/** + * Perform contrast limiting adaptive histogram equalization (CLAHE) + * + * This will, in general, enhance the clarity of the image by bringing out + * darker details. Please read more about CLAHE here: + * https://en.wikipedia.org/wiki/Adaptive_histogram_equalization#Contrast_Limited_AHE + * + * @param {Object} options + * @param {number} options.width - integer width of the region in pixels. + * @param {number} options.height - integer height of the region in pixels. + * @param {number} [options.maxSlope=3] - maximum value for the slope of the + * cumulative histogram. A value of 0 disables contrast limiting. Valid values + * are integers in the range 0-100 (inclusive) + * @returns {Sharp} + * @throws {Error} Invalid parameters + */ +function clahe (options) { + if (!is.plainObject(options)) { + throw is.invalidParameterError('options', 'plain object', options); + } + if (!('width' in options) || !is.integer(options.width) || options.width <= 0) { + throw is.invalidParameterError('width', 'integer above zero', options.width); + } else { + this.options.claheWidth = options.width; + } + if (!('height' in options) || !is.integer(options.height) || options.height <= 0) { + throw is.invalidParameterError('height', 'integer above zero', options.height); + } else { + this.options.claheHeight = options.height; + } + if (!is.defined(options.maxSlope)) { + this.options.claheMaxSlope = 3; + } else if (!is.integer(options.maxSlope) || options.maxSlope < 0 || options.maxSlope > 100) { + throw is.invalidParameterError('maxSlope', 'integer 0-100', options.maxSlope); + } else { + this.options.claheMaxSlope = options.maxSlope; + } + return this; +} + /** * Convolve the image with the specified kernel. * @@ -368,7 +408,7 @@ function normalize (normalize) { * * @param {Object} kernel * @param {number} kernel.width - width of the kernel in pixels. - * @param {number} kernel.height - width of the kernel in pixels. + * @param {number} kernel.height - height of the kernel in pixels. * @param {Array} kernel.kernel - Array of length `width*height` containing the kernel values. * @param {number} [kernel.scale=sum] - the scale of the kernel in pixels. * @param {number} [kernel.offset=0] - the offset of the kernel in pixels. @@ -594,6 +634,7 @@ module.exports = function (Sharp) { negate, normalise, normalize, + clahe, convolve, threshold, boolean, diff --git a/package.json b/package.json index 363e95a97..81415fd9e 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,8 @@ "Leon Radley ", "alza54 ", "Jacob Smith ", - "Michael Nutt " + "Michael Nutt ", + "Brad Parham " ], "scripts": { "install": "(node install/libvips && node install/dll-copy && prebuild-install) || (node install/can-compile && node-gyp rebuild && node install/dll-copy)", diff --git a/src/operations.cc b/src/operations.cc index 4cf56d4a3..79fa5c792 100644 --- a/src/operations.cc +++ b/src/operations.cc @@ -92,6 +92,13 @@ namespace sharp { return image; } + /* + * Contrast limiting adapative histogram equalization (CLAHE) + */ + VImage Clahe(VImage image, int const width, int const height, int const maxSlope) { + return image.hist_local(width, height, VImage::option()->set("max_slope", maxSlope)); + } + /* * Gamma encoding/decoding */ diff --git a/src/operations.h b/src/operations.h index 6659ff8c8..7e538a50d 100644 --- a/src/operations.h +++ b/src/operations.h @@ -35,6 +35,11 @@ namespace sharp { */ VImage Normalise(VImage image); + /* + * Contrast limiting adapative histogram equalization (CLAHE) + */ + VImage Clahe(VImage image, int const width, int const height, int const maxSlope); + /* * Gamma encoding/decoding */ diff --git a/src/pipeline.cc b/src/pipeline.cc index c329afbfa..66d0c97cb 100644 --- a/src/pipeline.cc +++ b/src/pipeline.cc @@ -345,6 +345,7 @@ class PipelineWorker : public Napi::AsyncWorker { bool const shouldApplyMedian = baton->medianSize > 0; bool const shouldComposite = !baton->composite.empty(); bool const shouldModulate = baton->brightness != 1.0 || baton->saturation != 1.0 || baton->hue != 0.0; + bool const shouldApplyClahe = baton->claheWidth != 0 && baton->claheHeight != 0; if (shouldComposite && !sharp::HasAlpha(image)) { image = sharp::EnsureAlpha(image, 1); @@ -650,6 +651,11 @@ class PipelineWorker : public Napi::AsyncWorker { image = sharp::Normalise(image); } + // Apply contrast limiting adaptive histogram equalization (CLAHE) + if (shouldApplyClahe) { + image = sharp::Clahe(image, baton->claheWidth, baton->claheHeight, baton->claheMaxSlope); + } + // Apply bitwise boolean operation between images if (baton->boolean != nullptr) { VImage booleanImage; @@ -1330,6 +1336,9 @@ Napi::Value pipeline(const Napi::CallbackInfo& info) { baton->linearB = sharp::AttrAsDouble(options, "linearB"); baton->greyscale = sharp::AttrAsBool(options, "greyscale"); baton->normalise = sharp::AttrAsBool(options, "normalise"); + baton->claheWidth = sharp::AttrAsUint32(options, "claheWidth"); + baton->claheHeight = sharp::AttrAsUint32(options, "claheHeight"); + baton->claheMaxSlope = sharp::AttrAsUint32(options, "claheMaxSlope"); baton->useExifOrientation = sharp::AttrAsBool(options, "useExifOrientation"); baton->angle = sharp::AttrAsInt32(options, "angle"); baton->rotationAngle = sharp::AttrAsDouble(options, "rotationAngle"); diff --git a/src/pipeline.h b/src/pipeline.h index 70d7ba9bf..980389250 100644 --- a/src/pipeline.h +++ b/src/pipeline.h @@ -109,6 +109,9 @@ struct PipelineBaton { double gammaOut; bool greyscale; bool normalise; + int claheWidth; + int claheHeight; + int claheMaxSlope; bool useExifOrientation; int angle; double rotationAngle; @@ -234,6 +237,9 @@ struct PipelineBaton { gamma(0.0), greyscale(false), normalise(false), + claheWidth(0), + claheHeight(0), + claheMaxSlope(3), useExifOrientation(false), angle(0), rotationAngle(0.0), diff --git a/test/fixtures/concert.jpg b/test/fixtures/concert.jpg new file mode 100644 index 000000000..7591c9199 Binary files /dev/null and b/test/fixtures/concert.jpg differ diff --git a/test/fixtures/expected/clahe-100-100-0.jpg b/test/fixtures/expected/clahe-100-100-0.jpg new file mode 100644 index 000000000..119bdd9a1 Binary files /dev/null and b/test/fixtures/expected/clahe-100-100-0.jpg differ diff --git a/test/fixtures/expected/clahe-100-50-3.jpg b/test/fixtures/expected/clahe-100-50-3.jpg new file mode 100644 index 000000000..b4c248169 Binary files /dev/null and b/test/fixtures/expected/clahe-100-50-3.jpg differ diff --git a/test/fixtures/expected/clahe-11-25-14.jpg b/test/fixtures/expected/clahe-11-25-14.jpg new file mode 100644 index 000000000..3ed0f51ab Binary files /dev/null and b/test/fixtures/expected/clahe-11-25-14.jpg differ diff --git a/test/fixtures/expected/clahe-5-5-0.jpg b/test/fixtures/expected/clahe-5-5-0.jpg new file mode 100644 index 000000000..83bd016d5 Binary files /dev/null and b/test/fixtures/expected/clahe-5-5-0.jpg differ diff --git a/test/fixtures/expected/clahe-5-5-5.jpg b/test/fixtures/expected/clahe-5-5-5.jpg new file mode 100644 index 000000000..5cea17a15 Binary files /dev/null and b/test/fixtures/expected/clahe-5-5-5.jpg differ diff --git a/test/fixtures/expected/clahe-50-50-0.jpg b/test/fixtures/expected/clahe-50-50-0.jpg new file mode 100644 index 000000000..f56f4b88e Binary files /dev/null and b/test/fixtures/expected/clahe-50-50-0.jpg differ diff --git a/test/fixtures/expected/clahe-50-50-14.jpg b/test/fixtures/expected/clahe-50-50-14.jpg new file mode 100644 index 000000000..b9d4244b7 Binary files /dev/null and b/test/fixtures/expected/clahe-50-50-14.jpg differ diff --git a/test/fixtures/index.js b/test/fixtures/index.js index 1aa2dfaea..fac1701c7 100644 --- a/test/fixtures/index.js +++ b/test/fixtures/index.js @@ -121,6 +121,8 @@ module.exports = { inputV: getPath('vfile.v'), + inputJpgClahe: getPath('concert.jpg'), // public domain - https://www.flickr.com/photos/mars_/14389236779/ + testPattern: getPath('test-pattern.png'), // Path for tests requiring human inspection diff --git a/test/unit/clahe.js b/test/unit/clahe.js new file mode 100644 index 000000000..e4247e521 --- /dev/null +++ b/test/unit/clahe.js @@ -0,0 +1,139 @@ +'use strict'; + +const assert = require('assert'); + +const sharp = require('../../lib'); +const fixtures = require('../fixtures'); + +describe('Clahe', function () { + it('width 5 width 5 maxSlope 0', function (done) { + sharp(fixtures.inputJpgClahe) + .clahe({ width: 5, height: 5, maxSlope: 0 }) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + fixtures.assertSimilar(fixtures.expected('clahe-5-5-0.jpg'), data, { threshold: 10 }, done); + }); + }); + + it('width 5 width 5 maxSlope 5', function (done) { + sharp(fixtures.inputJpgClahe) + .clahe({ width: 5, height: 5, maxSlope: 5 }) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + fixtures.assertSimilar(fixtures.expected('clahe-5-5-5.jpg'), data, done); + }); + }); + + it('width 11 width 25 maxSlope 14', function (done) { + sharp(fixtures.inputJpgClahe) + .clahe({ width: 11, height: 25, maxSlope: 14 }) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + fixtures.assertSimilar(fixtures.expected('clahe-11-25-14.jpg'), data, done); + }); + }); + + it('width 50 width 50 maxSlope 0', function (done) { + sharp(fixtures.inputJpgClahe) + .clahe({ width: 50, height: 50, maxSlope: 0 }) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + fixtures.assertSimilar(fixtures.expected('clahe-50-50-0.jpg'), data, done); + }); + }); + + it('width 50 width 50 maxSlope 14', function (done) { + sharp(fixtures.inputJpgClahe) + .clahe({ width: 50, height: 50, maxSlope: 14 }) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + fixtures.assertSimilar(fixtures.expected('clahe-50-50-14.jpg'), data, done); + }); + }); + + it('width 100 width 50 maxSlope 3', function (done) { + sharp(fixtures.inputJpgClahe) + .clahe({ width: 100, height: 50, maxSlope: 3 }) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done); + }); + }); + + it('width 100 width 100 maxSlope 0', function (done) { + sharp(fixtures.inputJpgClahe) + .clahe({ width: 100, height: 100, maxSlope: 0 }) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + fixtures.assertSimilar(fixtures.expected('clahe-100-100-0.jpg'), data, done); + }); + }); + + it('invalid maxSlope', function () { + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: -5 }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 110 }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 5.5 }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100, maxSlope: 'a string' }); + }); + }); + + it('invalid width', function () { + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100.5, height: 100 }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: -5, height: 100 }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: true, height: 100 }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 'string test', height: 100 }); + }); + }); + + it('invalid height', function () { + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 100.5 }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: -5 }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: true }); + }); + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe({ width: 100, height: 'string test' }); + }); + }); + + it('invalid options object', function () { + assert.throws(function () { + sharp(fixtures.inputJpgClahe).clahe(100, 100, 5); + }); + }); + + it('uses default maxSlope of 3', function (done) { + sharp(fixtures.inputJpgClahe) + .clahe({ width: 100, height: 50 }) + .toBuffer(function (err, data, info) { + if (err) throw err; + assert.strictEqual('jpeg', info.format); + fixtures.assertSimilar(fixtures.expected('clahe-100-50-3.jpg'), data, done); + }); + }); +});