Skip to content

Commit dab81de

Browse files
committed
feat(compare-images): add compare-images-node.ts
1 parent c3a17aa commit dab81de

File tree

4 files changed

+121
-2
lines changed

4 files changed

+121
-2
lines changed

packages/compare-images/python/itkwasm-compare-images-wasi/itkwasm_compare_images_wasi/compare_images.py

+3-2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ def compare_images(
3737
) -> Tuple[Dict, Image, Image]:
3838
"""Compare double pixel type images with a tolerance for regression testing.
3939
40+
For multi-component images, the intensity difference threshold
41+
is based on the pixel vector magnitude.
42+
4043
:param test_image: The input test image
4144
:type test_image: Image
4245
@@ -68,5 +71,3 @@ def compare_images(
6871
baseline_images_double = [_to_scalar_double(baseline_image) for baseline_image in baseline_images]
6972

7073
return compare_double_images(test_image_double, baseline_images=baseline_images_double, difference_threshold=difference_threshold, radius_tolerance=radius_tolerance, number_of_pixels_tolerance=number_of_pixels_tolerance, ignore_boundary_pixels=ignore_boundary_pixels)
71-
72-
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
import {
2+
Image,
3+
FloatTypes,
4+
PixelTypes,
5+
castImage,
6+
} from 'itk-wasm'
7+
8+
import CompareDoubleImagesOptions from './compare-double-images-options.js'
9+
import CompareDoubleImagesNodeResult from './compare-double-images-node-result.js'
10+
import compareDoubleImagesNode from './compare-double-images-node.js'
11+
import vectorMagnitudeNode from './vector-magnitude-node.js'
12+
13+
async function _toScalarDouble(image: Image): Promise<Image> {
14+
let scalarDouble = image
15+
16+
if (scalarDouble.imageType.componentType !== FloatTypes.Float64) {
17+
let pixelType = undefined
18+
if (image.imageType.pixelType !== PixelTypes.Scalar && image.imageType.pixelType !== PixelTypes.VariableLengthVector) {
19+
pixelType = PixelTypes.VariableLengthVector
20+
}
21+
scalarDouble = castImage(image, { componentType: FloatTypes.Float64, pixelType })
22+
} else {
23+
if (image.imageType.pixelType !== PixelTypes.Scalar && image.imageType.pixelType !== PixelTypes.VariableLengthVector) {
24+
const pixelType = PixelTypes.VariableLengthVector
25+
scalarDouble = castImage(image, { pixelType })
26+
}
27+
}
28+
29+
if (scalarDouble.imageType.pixelType === PixelTypes.VariableLengthVector) {
30+
const magnitude = await vectorMagnitudeNode(scalarDouble)
31+
scalarDouble = magnitude.magnitudeImage
32+
}
33+
34+
return scalarDouble
35+
}
36+
37+
/**
38+
* Compare images with a tolerance for regression testing.
39+
*
40+
* For multi-component images, the intensity difference threshold
41+
* is based on the pixel vector magnitude.
42+
*
43+
* @param {Image} testImage - The input test image
44+
* @param {CompareDoubleImagesOptions} options - options object
45+
*
46+
* @returns {Promise<CompareDoubleImagesNodeResult>} - result object
47+
*/
48+
async function compareImagesNode(
49+
testImage: Image,
50+
options: CompareDoubleImagesOptions = { baselineImages: [] as Image[], }
51+
) : Promise<CompareDoubleImagesNodeResult> {
52+
53+
const testImageDouble = await _toScalarDouble(testImage)
54+
const baselineImagesDouble = await Promise.all(options.baselineImages.map(async (image) => {
55+
return await _toScalarDouble(image)
56+
}))
57+
58+
const otherOptions = { ...options }
59+
otherOptions.baselineImages = baselineImagesDouble
60+
61+
return compareDoubleImagesNode(testImageDouble, otherOptions)
62+
}
63+
64+
export default compareImagesNode

packages/compare-images/typescript/src/index-node.ts

+3
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,6 @@ export type { CompareDoubleImagesOptions }
99

1010
import compareDoubleImagesNode from './compare-double-images-node.js'
1111
export { compareDoubleImagesNode }
12+
13+
import compareImagesNode from './compare-images-node.js'
14+
export { compareImagesNode }
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import test from 'ava'
2+
import path from 'path'
3+
4+
import { compareImagesNode } from '../../dist/bundles/compare-images-node.js'
5+
import { readImageLocalFile } from 'itk-wasm'
6+
7+
const inputPathPrefix = '../test/data/input/'
8+
9+
test('compareImagesNode produces the expected metrics and difference images for uint8 inputs', async t => {
10+
const testImageFile = 'cake_easy.png'
11+
const testImagePath = path.join(inputPathPrefix, testImageFile)
12+
const testImage = await readImageLocalFile(testImagePath)
13+
14+
const baselineImageFile = 'cake_hard.png'
15+
const baselineImagePath = path.join(inputPathPrefix, baselineImageFile)
16+
const baselineImage = await readImageLocalFile(baselineImagePath)
17+
18+
const { metrics, differenceImage, differenceUchar2dImage } = await compareImagesNode(testImage, { baselineImages: [baselineImage,] })
19+
20+
t.is(metrics.almostEqual, false)
21+
t.is(metrics.numberOfPixelsWithDifferences, 9915)
22+
t.is(metrics.minimumDifference, 1.0)
23+
t.is(metrics.maximumDifference, 107.0)
24+
t.is(metrics.totalDifference, 337334.0)
25+
t.is(metrics.meanDifference, 34.02259203227433)
26+
27+
t.is(differenceImage.imageType.componentType, 'float64')
28+
t.is(differenceUchar2dImage.imageType.componentType, 'uint8')
29+
})
30+
31+
test('compareImagesNode produces the expected metrics and difference images for rgb inputs', async t => {
32+
const testImageFile = 'apple.jpg'
33+
const testImagePath = path.join(inputPathPrefix, testImageFile)
34+
const testImage = await readImageLocalFile(testImagePath)
35+
36+
const baselineImageFile = 'orange.jpg'
37+
const baselineImagePath = path.join(inputPathPrefix, baselineImageFile)
38+
const baselineImage = await readImageLocalFile(baselineImagePath)
39+
40+
const { metrics, differenceImage, differenceUchar2dImage } = await compareImagesNode(testImage, { baselineImages: [baselineImage,] })
41+
42+
t.is(metrics.almostEqual, false)
43+
t.is(metrics.numberOfPixelsWithDifferences, 26477)
44+
t.is(metrics.minimumDifference, 0.002273026683894841)
45+
t.is(metrics.maximumDifference, 312.2511648746159)
46+
t.is(metrics.totalDifference, 3121703.1639738297)
47+
t.is(metrics.meanDifference, 117.90244982338746)
48+
49+
t.is(differenceImage.imageType.componentType, 'float64')
50+
t.is(differenceUchar2dImage.imageType.componentType, 'uint8')
51+
})

0 commit comments

Comments
 (0)