Cropping Images with Sharp Library
Sharp is a high-performance Node.js image processing library for cropping and resizing.
Basic Crop
import sharp from 'sharp';
async function cropImageRegion(
imageBuffer: Buffer,
region: {
left: number;
top: number;
width: number;
height: number;
}
): Promise<Buffer> {
return await sharp(imageBuffer)
.extract(region)
.png()
.toBuffer();
}
Crop and Resize
For scaling up during crop (e.g., 4K composites):
async function cropAndResizeImageRegion(
imageBuffer: Buffer,
options: {
region: { left: number; top: number; width: number; height: number };
targetWidth: number;
targetHeight: number;
}
): Promise<Buffer> {
return await sharp(imageBuffer)
.extract(options.region)
.resize(options.targetWidth, options.targetHeight, {
fit: 'fill', // Exact dimensions
kernel: 'lanczos3', // High quality
})
.png()
.toBuffer();
}
Get Image Dimensions
async function getImageDimensions(imageBuffer: Buffer): Promise<{
width: number;
height: number;
}> {
const metadata = await sharp(imageBuffer).metadata();
return {
width: metadata.width || 0,
height: metadata.height || 0,
};
}
Validate Image Dimensions
async function validateImageDimensions(
imageBuffer: Buffer,
expectedWidth: number,
expectedHeight: number
): Promise<boolean> {
const dimensions = await getImageDimensions(imageBuffer);
return dimensions.width === expectedWidth &&
dimensions.height === expectedHeight;
}
Cropping Stacked Composite
Example for cropping a 3×3 grid:
async function cropCompositeIntoScenes(
compositeBuffer: Buffer,
sceneCount: number
): Promise<Buffer[]> {
const dimensions = await getImageDimensions(compositeBuffer);
const cols = Math.ceil(Math.sqrt(sceneCount));
const cellWidth = Math.floor(dimensions.width / cols);
const cellHeight = Math.floor(dimensions.height / Math.ceil(sceneCount / cols));
const results: Buffer[] = [];
for (let i = 0; i < sceneCount; i++) {
const col = i % cols;
const row = Math.floor(i / cols);
const region = {
left: col * cellWidth,
top: row * cellHeight,
width: cellWidth,
height: cellHeight,
};
const buffer = await cropAndResizeImageRegion(compositeBuffer, {
region,
targetWidth: 1080,
targetHeight: 1920,
});
results.push(buffer);
}
return results;
}
Performance Tips
- Use
extract()for pure cropping (faster) - Use
resize()withfit: 'fill'for exact dimensions - Output as PNG for quality:
.png().toBuffer() - Use
lanczos3kernel for high-quality downscaling - Process images sequentially to avoid memory issues
Error Handling
try {
const cropped = await cropImageRegion(buffer, region);
} catch (error) {
if (error.message.includes('extract area exceeds image dimensions')) {
throw new Error('Crop region is larger than the image');
}
throw error;
}