A pure JavaScript / Node.js port of Stéphane Brunner's sbrunner/deskew library. It calculates the skew angle of a text-containing image and provides functionality to rotate and align it.
This implementation has zero external native/binary dependencies, meaning it will run anywhere Node.js runs without needing a compiler, Python, or external image utilities.
- Pure JavaScript: Built-in Canny edge detector, bilinear Non-Maximum Suppression, and Hough Line Transform.
- Accurate: Passes all 8 official deskew test cases with 100% precision.
- Self-Contained: The core library accepts raw pixel data, making it compatible with any image library (
pngjs,jimp,sharp, HTML5 Canvas, etc.). - CLI Tool Included: Rotate and save corrected images directly from your terminal.
npm install @flueschpluesch/deskewnpx @flueschpluesch/deskew input.pngnpx @flueschpluesch/deskew --output output.png input.png-o, --output <path>: Path to save the rotated output image.--sigma <val>: Blur strength (Gaussian sigma). Default is3.0.--num-peaks <val>: Number of peaks to detect in Hough space. Default is20.--num-angles <val>: The number of angles to evaluate. Default is180.--background <val>: Background fill color for empty corners. Single value for grayscale (e.g.255) or comma-separated values (e.g.255,255,255).
The library can be used with any package that provides raw pixel data. Here is an example using pngjs:
const fs = require('fs');
const { PNG } = require('pngjs');
const { determineSkew, rotate } = require('@flueschpluesch/deskew');
// 1. Load an image
fs.createReadStream('input.png')
.pipe(new PNG())
.on('parsed', function () {
// 2. Determine the skew angle in degrees
const angle = determineSkew({
width: this.width,
height: this.height,
data: this.data // Uint8Array of RGBA values
});
console.log(`Skew angle detected: ${angle} degrees`);
if (angle !== null) {
// 3. Rotate the image to correct the skew
const rotated = rotate(
{ width: this.width, height: this.height, data: this.data },
angle,
{ resize: true, background: [255, 255, 255, 255] } // White background fill
);
// 4. Save the corrected image
const outPng = new PNG({ width: rotated.width, height: rotated.height });
outPng.data = Buffer.from(rotated.data);
outPng.pack().pipe(fs.createWriteStream('output.png'));
}
});Calculates the skew angle in degrees of an image.
image: An object containing{ width: number, height: number, data: Uint8Array | Buffer }.- Supports RGBA (width * height * 4 bytes), RGB (width * height * 3 bytes), or Grayscale (width * height bytes) data.
options:sigma(number, default:3.0): Standard deviation of the Gaussian filter.numPeaks(number, default:20): Number of peaks to detect in Hough space.anglePm90(boolean, default:false): If true, returns angles in range[-90, 90]degrees; otherwise returns angles in range[-45, 45]degrees.minAngle(number): Minimum angle in degrees to consider.maxAngle(number): Maximum angle in degrees to consider.minDeviation(number, default:1.0): Minimum deviation between angles in degrees.numAngles(number): Optional backward-compatible way to specify resolution (forcesminDeviation = 180 / numAngles).
Returns: The skew angle in degrees (float), or null if no skew could be determined.
Rotates the image by a given angle around its center.
image:{ width, height, data }object.angleDegrees: Angle to rotate by in degrees. Positive rotates counter-clockwise.options:resize(boolean, default:true): Adjusts the output dimensions to fit the rotated image.background(number | number[], default:[0, 0, 0, 0]): Color used to fill the empty corners created by rotation.
Returns: A new { width, height, data } object.
This project is licensed under the MIT License.