Release notes v1
We're excited to announce the release of a new major version of ImageJS. This version brings TypeScript support and a more intuitive API while maintaining the powerful image processing capabilities you love.
๐ท TypeScript Supportโ
All APIs now have strict TypeScript definitions:
// Before: loose typing
const pixel = img.getPixel(x, y); // any[]
// After: strict typing
const pixel = img.getPixel(x, y); // number[] with proper channel count
This eliminates runtime type errors and provides better IntelliSense, autocomplete, and refactoring support in your IDE. Developers can now catch bugs at compile time rather than discovering them in production.
โ ๏ธ Breaking changesโ
Imagesโ
Loading and savingโ
load()
and save()
have been replaced with dedicated functions read()
and write()
.
//Before
import { Image } from 'image-js';
const img = await Image.load('cat.jpg');
img.save('newCat.jpg');
//After
import { read, write } from 'image-js';
const img2 = await read('cat.jpg');
await write('newCat.jpg', img);
There are also synchronous versions of these functions.
import { readSync, writeSync } from 'image-js';
const img = readSync('cat.jpg');
writeSync('newCat.jpg', img);
Those changes separates I/O operations from image manipulation for a clearer API design.
Creatingโ
When creating a new image, unlike before, image's width and height must be specified.
import { Image } from 'image-js';
// Would work before, will throw an error in the new version.
const image = new Image();
// Works fine.
const image2 = new Image(10, 10);
This change makes the Image constructor more explicit by requiring you to specify the dimensions upfront, preventing potential errors from working with uninitialized or undefined-sized images.
Coordinate System Changesโ
Coordinates are now represented using Point
objects instead of arrays. This change affects methods that require coordinate input like cropping, drawing, pixel manipulation etc.
// Before
const croppedImage = img.crop({
x:10,
y:10
width: 10,
height: 10,
});
// After
const croppedImage = img.crop({
origin: { column: 10, row: 10 },
width: 10,
height: 10,
});
It is a more explicit and self-documenting code. It also eliminates confusion about array order (column vs row).
Masksโ
Binary images are now handled by a dedicated Mask
class instead of Image with kind: 'BINARY'
.
// Before
const mask = new Image(10, 10, { kind: 'BINARY' });
// After
const mask = new Mask(10, 10);
Mask
provides better type safety and clearer API.
The new Mask
class uses 1 byte per pixel (vs 8 pixels per byte), trading ~8x memory usage for significantly faster bit operations and simpler data manipulation.
Regions of Interestโ
API for handling of regions of interest has also been changed.
ROI creation methods like fromMask()
and fromWatershed()
are now standalone functions fromMask()
and watershed()
.
//Before
import { Image } from 'image-js';
const roiManager = mask.getRoiManager();
roiManager.fromMask(mask);
const rois = roiManager.getRois();
//After
import { Image, fromMask } from 'image-js';
const roiManager = fromMask(mask);
const rois = roiManager.getRois();
This simplifies the process of creating a map of regions of interest and eliminates the need for a separate initialization step, providing a more direct and functional approach to ROI creation.
For more information, please, visit these tutorials:
Sobel and Scharr filtersโ
Sobel, Scharr filters are now combined into a single derivative()
method.
// Before
const sobelX = img.sobelX();
const sobelY = img.sobelY();
// After
const sobelX = img.derivative({ filter: 'sobel' });
const sobelY = img.derivative({ filter: 'scharr' });
This filter now also accepts only grayscale images, since filters, like Sobel or Scharr, are used mainly on grayscale images to detect edges.
Method Renamingโ
Several methods have been renamed for consistency:
Drawing methods:
img.paintPolyline()
โก๏ธ img.drawPolyline()
img.paintPolygon()
โก๏ธ img.drawPolygon()
img.paintCircle()
โก๏ธ img.drawCircle()
img.paintPoints()
โก๏ธ img.drawMarkers()
Stack methods
stack.getMinImage()
โก๏ธ stack.minImage()
stack.getMinImage()
โก๏ธ stack.maxImage()
stack.getAverageImage()
โก๏ธ stack.meanImage()
Pixel and value getters/setters:
img.getBitXY()
โก๏ธ mask.getBit()
img.getBit()
โก๏ธ mask.getBitByIndex()
img.getPixelXY()
โก๏ธ img.getPixel()
img.getPixel()
โก๏ธ img.getPixelByIndex()
img.getValueXY()
โก๏ธ img.getValue()
img.getValue()
โก๏ธ img.getValueByIndex()
img.setBitXY()
โก๏ธ mask.setBit()
img.setBit()
โก๏ธ mask.setBitByIndex()
img.setPixelXY()
โก๏ธ img.setPixel()
img.setPixel()
โก๏ธ img.setPixelByIndex()
img.setValueXY()
โก๏ธ img.setValue()
img.setValue()
โก๏ธ img.setValueByIndex()
Other methods:
img.getLocalMaxima()
โก๏ธ img.getExtrema()
img.getChannel()
โก๏ธ img.extractChannel()
img.rotateLeft()
& img.rotateRight()
โก๏ธ img.rotate()
img.flipX()
& img.flipY()
โก๏ธ img.flip()
img.colorDepth()
โก๏ธ img.convertBitDepth()
img.mask()
โก๏ธ img.threshold()
img.cannyEdge()
โก๏ธ img.cannyEdgeDetector()
img.blurFilter()
โก๏ธ img.blur()
img.insert()
โก๏ธ img.copyTo()
img.convolution()
โก๏ธ img.directConvolution()
img.getBestMatch()
โก๏ธ alignMinDifference()
img.pad()
โก๏ธ img.extendBorder()
img.minimalBoundingRectangle()
โก๏ธ mask.getMbr()
img.monotoneChainConvexHull()
โก๏ธ mask.getConvexHull()
Compatibility requirementsโ
- Node.js: 18+ (previously 14+)
Removed Featuresโ
The following deprecated features have been removed:
Imagesโ
countAlphaPixel()
- Use custom pixel counting withgetPixel()
.paintLabels()
androi.paint()
- Features have been removed due to dependency issues. We plan to add it back in future updates.warpingFourPoints()
- UsegetPerspectiveWarpMatrix()
+transform()
instead.- 32-bit color depth support and
abs()
have been removed. CMYK
andHSL
color models have been removed.paintMasks()
has been removed. UsepaintMask()
+ afor
loop.clearBit()
andtoggleBit()
have been removed, due to changes inMask
data representation (see "Masks"). UsesetBit()
orsetValue()
instead.combineChannels()
has been removed.rgba8()
andrgba()
have been removed. Use a combination ofconvertColor()
andconvertBitDepth()
instead.histograms()
andcolorHistogram()
have been removed.getPixelGrid()
has been removed.getClosestCommonParent()
andgetRelativePosition()
have been removed.getSimilarity()
andgetIntersection()
have been removed.paintPolygons()
andpaintPolylines()
have been removed. UsedrawPolygon()
/drawPolyline()
+ afor
loop.
ROIs and its managementโ
colsInfo()
androwsInfo()
have been removed.fromPoints()
has been removed.fromMaxima()
has been removed.fromMaskConnectedComponentLabelingAlgorithm()
andgetAnalysisMasks()
have been removed.findCorrespondingRoi()
has been removed.resetPainted()
has been removed.mergeRoi()
andmergeRois()
have been removed.minX
,minY
,meanX
,meanY
,maxX
,maxY
have been removed. Use ROI'sposition
, combined with itswidth
andheight
.
๐ New Featuresโ
transform()
โ
transform()
allows applying a transformation matrix on the image. Which means that the image can now be translated, sheared, or warped based on the matrix that the user entered. transform()
accepts both 2x3 and 3x3 matrices, depending on whether you want an affine transformation or a perspective one.
const matrix = getPerspectiveWarp(sourcePoints);
const warped = img.transform(matrix);
For more details, visit our tutorial on how image transformations work how they can be used.
Bicubic Interpolationโ
High-quality image scaling is now available with bicubic interpolation:
const resized = img.resize(800, 600, { interpolation: 'bicubic' });
Use cases: In many cases it gives a better quality when scaling images, especially for photographs.
Prewitt filterโ
Prewitt filter has been added to the derivative()
filter.
const prewitt = img.derivative({ filter: 'prewitt' });
Use cases: Object detection, image segmentation, feature extraction. You can learn more about it here.
Migration from deprecated methodsโ
warpingFourPoints()
has been removed. Now you have getPerspectiveWarp()
that returns a matrix that can be applied on the image of interest in a new transform()
.
// Before
const warped = img.warpingFourPoints(corners);
// After
const matrix = getPerspectiveWarp(corners);
const warped = img.transform(matrix);
Use cases: Rectification of a perspective angle of an image. You can learn more about it here.
merge()
โ
merge()
is the opposite of split()
. It allows combining several one-channel images into one multi-channel image:
// Creates 3 grayscale images;
const img1 = new Image(2, 2, { colorModel: 'GREY', bitDepth: 8 }).fill(0);
const img2 = new Image(2, 2, { colorModel: 'GREY', bitDepth: 8 }).fill(0);
const img3 = new Image(2, 2, { colorModel: 'GREY', bitDepth: 8 }).fill(255);
// Creates RGB image. In this case, it creates blue 2x2 image.
const img4 = merge([img1, img2, img3]);
Use cases: Combination of multiple channels into one image after they were modified.
correctColor()
โ
This function performs color correction matches image's colors to reference standards.
const measured = [
{ r: 180, g: 120, b: 90 }, // Colors sampled from the image
{ r: 200, g: 200, b: 180 },
];
const reference = [
{ r: 255, g: 128, b: 64 }, // What those colors should actually be
{ r: 255, g: 255, b: 255 },
];
const corrected = image.correctColor(measured, reference);
Use cases: Camera calibration, white balance correction, matching images from different devices, scientific imaging standardization.
increaseContrast()
โ
This function increases image contrast by stretching the pixel value range to use the full available dynamic range.
const highContrast = image.increaseContrast();
The function automatically handles different color models, processing only relevant channels (excludes alpha in RGBA, processes only luminance in GREYA). Works with 8-bit and 16-bit images.
Use cases: Enhancing low-contrast images, improving visibility of subtle details, preparing images for analysis, correcting underexposed photographs.
pixelate()
โ
pixelate()
creates a pixelated effect by dividing the image into square cells and filling each cell with a representative value.
const pixelatedImage = image.pixelate({
cellSize: 8,
});
Use cases: Creating retro 8-bit effects, preparing images for low-resolution displays or anonymization.
cropRectangle()
โ
While crop()
and cropRectangle()
might appear similar. However, they provide provide different approaches to extracting image regions.
crop()
- Standard rectangular cropping that maintains the original image orientation:
const cropped = image.crop({
column: 10,
row: 10,
width: 50,
height: 50,
});
// Returns a piece of image with the same
// orientation as the parent image.
cropRectangle()
- Advanced cropping that extracts rotated rectangular regions defined by four corner points:
const points = [
{ row: 30, column: 30 },
{ row: 60, column: 60 },
{ row: 90, column: 30 },
{ row: 60, column: 0 },
];
const rotatedCrop = image.cropRectangle(points);
// Returns a cropped oriented rectangle.
Use crop()
for simple rectangular selections aligned with image axes, and cropRectangle()
when you need to extract tilted or rotated rectangular regions.
drawPoints()
โ
This function draws individual 1x1 pixel points at the specified coordinates. Unlike drawMarker() which creates shaped markers (circles, crosses, etc.), drawPoints() renders simple single-pixel points, making it ideal for dense point visualization or precise coordinate marking.
const points = [
{ row: 50, column: 100 },
{ row: 150, column: 200 },
];
const annotated = image.drawPoints(points, {
color: [255, 0, 0], // Red dots
});
Each point is drawn centered on the specified point coordinates. The function creates a new image without modifying the original, making it ideal for creating annotated versions while preserving source data.
Use cases: Plotting data points, marking precise coordinates, creating scatter plot overlays, visualizing pixel-level analysis results, dense feature visualization.
Mask
featuresโ
The Mask
has been enhanced with new methods for geometric analysis and border processing.
getFeret()
โ
Computes the Feret diameters of the mask region, which are fundamental measurements in particle analysis and shape characterization. The Feret diameter represents the distance between two parallel lines that are tangent to the object's boundary.
const feretDiameters = mask.getFeret();
/*An object containing:
minDiameter: The minimum Feret diameter (narrowest width)
maxDiameter: The maximum Feret diameter (longest distance between any two boundary points)
aspectRatio: Ratio of minimum to maximum diameter (minDiameter.length / maxDiameter.length)*/
Use cases: Particle size analysis, shape characterization, elongation measurement, quality control in manufacturing, biological specimen analysis.
getBorderPoints()
โ
Extracts all points that lie on the border/perimeter of the mask regions. This method identifies pixels that are part of the mask but have at least one neighboring pixel that is not part of the mask.
const borderPoints = mask.getBorderPoints();
// Returns: Array of {row: number, column: number} objects
Use cases: Contour extraction, perimeter analysis, shape boundary detection.
clearBorders()
โ
Removes mask regions that are connected to the image borders. This operation uses flood fill to eliminate any connected components that have pixels touching the edge of the image, useful for removing incomplete objects that extend beyond the image boundaries.
const clearedMask = mask.clearBorder();
// Returns a mask with removed borders.
Use cases: Object segmentation cleanup, removing partial objects, preparing masks for complete object analysis, eliminating edge artifacts, preprocessing for particle analysis.
Stack
featuresโ
The Stack class has been significantly expanded with new methods for batch processing and statistical analysis of image collections.
Filtering and Transformationsโ
A user can now filter images based on custom criteria using filter()
:
// Create sample images
const img1 = new Image(2, 2, { colorModel: 'GREY', bitDepth: 8 }).fill(0);
const img2 = new Image(2, 2, { colorModel: 'GREY', bitDepth: 8 }).fill(0);
const img3 = new Image(2, 2, { colorModel: 'GREY', bitDepth: 8 }).fill(255);
const stack = new Stack([img1, img2, img3]);
// Filter images where top-left pixel is white (255)
const brightImages = stack.filter((img) => img.getValue(0, 0, 0) === 255);
// Result: [img3]
One can also apply the same operation to every image in the stack with map()
:
// Set top-left corner to gray (125) on all images
const modifiedStack = stack.map((img) => {
img.setValue(0, 0, 0, 125);
return img;
});
Stack Analysis Operationsโ
It is now possible to generate a median image from the entire stack - useful for noise reduction and background subtraction:
const medianImage = stack.medianImage();
or create a cumulative sum of all images in the stack:
const summedImage = stack.sum();
Use Cases: Time-lapse analysis, scientific imaging.
Stack Pixel Value Accessโ
Access specific pixel values from any image in the stack using two convenient methods:
By Coordinates
const stackIndex = 1; // Second image in stack.
const row = 0;
const column = 0;
const channel = 0;
// Get pixel value at specific coordinates.
const pixelValue = stack.getValue(stackIndex, row, column, channel);
By Linear Index
const stackIndex = 1;
const pixelIndex = row * image.width + column; // Convert 2D to 1D index.
const channel = 0;
// Get the same pixel value using linear indexing.
const pixelValue = stack.getValueByIndex(stackIndex, pixelIndex, channel);
๐ Bug fixesโ
Bugs in multiple functions have been fixed:
cannyEdgeDetector()
resize()
drawCircle()
๐ Resourcesโ
๐ค Contributingโ
We welcome contributions! The new TypeScript codebase makes it easier than ever to contribute.
๐ Acknowledgmentsโ
Special thanks to all contributors who made this release possible and to the community for their feedback and support during the development process.