image-js
Version:
Image processing and manipulation in JavaScript
95 lines (83 loc) • 2.67 kB
text/typescript
import { ssim as bufferSsim } from 'ssim.js';
import type { Image } from '../../Image.ts';
import checkProcessable from '../../utils/validators/checkProcessable.ts';
import { validateForComparison } from '../../utils/validators/validators.ts';
export interface SsimOptions {
/**
* Window size for SSIM map.
* @default `Math.min(11, image.width, image.height)`
*/
windowSize?: number;
/**
* Algorithm to use to compute the SSIM.
* @default `'original'`
*/
algorithm?: 'fast' | 'original' | 'bezkrovny' | 'weber';
}
export interface Ssim {
/**
* Mean SSIM of the whole image. It is the mean value of the SSIM map.
* It is a similarity score between two images.
*/
mssim: number;
/**
* Similarity map of the two images. The dimensions of the map depend the windowSize option.
* Create a GREY image based on this map to visualize the similarity of the different regions of the image.
*/
ssimMap: { data: number[]; width: number; height: number };
}
/**
* Compute the Structural Similarity (SSIM) of two RGBA or two GREY images.
* "The resultant SSIM index is a decimal value between -1 and 1,
* where 1 indicates perfect similarity, 0 indicates no similarity,
* and -1 indicates perfect anti-correlation."
* @see {@link https://en.wikipedia.org/wiki/Structural_similarity}
* @param image - First image.
* @param otherImage - Second image.
* @param options - SSIM options.
* @returns SSIM of the two images.
*/
export function computeSsim(
image: Image,
otherImage: Image,
options: SsimOptions = {},
): Ssim {
let { windowSize } = options;
const { algorithm = 'original' } = options;
if (windowSize) {
if (windowSize > image.width || windowSize > image.height) {
throw new RangeError('windowSize cannot exceed image dimensions');
}
} else {
windowSize = Math.min(11, image.height, image.width);
}
checkProcessable(image, {
bitDepth: [8],
channels: [1, 3, 4],
});
validateForComparison(image, otherImage);
if (image.colorModel !== 'RGBA') {
image = image.convertColor('RGBA');
otherImage = otherImage.convertColor('RGBA');
}
const imageData = new Uint8ClampedArray(image.getRawImage().data);
const imageBuffer = {
height: image.height,
width: image.width,
data: imageData,
};
const otherData = new Uint8ClampedArray(otherImage.getRawImage().data);
const otherBuffer = {
height: otherImage.height,
width: otherImage.width,
data: otherData,
};
const ssim = bufferSsim(imageBuffer, otherBuffer, {
windowSize,
ssim: algorithm,
});
return {
mssim: ssim.mssim,
ssimMap: ssim.ssim_map,
};
}