diff --git a/src/enums/complex-operator.ts b/src/enums/complex-operator.ts new file mode 100644 index 00000000..1a7060ca --- /dev/null +++ b/src/enums/complex-operator.ts @@ -0,0 +1,49 @@ +/* + Copyright Dirk Lemstra https://github.com/dlemstra/magick-wasm. + Licensed under the Apache License, Version 2.0. +*/ + +/** + * Specifies a kind of complex operator. + */ +export enum ComplexOperator { + /** + * Undefined. + */ + Undefined, + + /** + * Add. + */ + Add, + + /** + * Conjugate. + */ + Conjugate, + + /** + * Divide. + */ + Divide, + + /** + * Magnitude phase. + */ + MagnitudePhase, + + /** + * Multiply. + */ + Multiply, + + /** + * Real imaginary. + */ + RealImaginary, + + /** + * Subtract. + */ + Subtract, +} diff --git a/src/helpers/temporary-defines.ts b/src/helpers/temporary-defines.ts index 13cf17cb..1a4d74d5 100644 --- a/src/helpers/temporary-defines.ts +++ b/src/helpers/temporary-defines.ts @@ -15,10 +15,11 @@ export class TemporaryDefines { this._image = image; } - setArtifact(name: string, value: string): void; setArtifact(name: string, value: boolean): void; setArtifact(name: string, value: IMagickColor): void; - setArtifact(name: string, value: string | boolean | IMagickColor): void { + setArtifact(name: string, value: number): void; + setArtifact(name: string, value: string): void; + setArtifact(name: string, value: boolean | IMagickColor | number | string): void { this._names.push(name); this._image.setArtifact(name, value); } diff --git a/src/index.ts b/src/index.ts index 93e5ee9b..71ddb06b 100644 --- a/src/index.ts +++ b/src/index.ts @@ -40,6 +40,7 @@ export * from './enums/channels'; export * from './enums/class-type'; export * from './enums/color-space'; export * from './enums/color-type'; +export * from './enums/complex-operator'; export * from './enums/composite-operator'; export * from './enums/compression-method'; export * from './enums/density-unit'; @@ -86,6 +87,7 @@ export * from './pixels/pixel-collection'; export * from './profiles/image-profile'; export * from './quantum'; export * from './settings/compare-settings'; +export * from './settings/complex-settings'; export * from './settings/connected-components-settings'; export * from './settings/distort-settings'; export * from './settings/magick-read-settings'; diff --git a/src/magick-image-collection.ts b/src/magick-image-collection.ts index d3e5eb6a..1b2b3931 100644 --- a/src/magick-image-collection.ts +++ b/src/magick-image-collection.ts @@ -5,6 +5,7 @@ import { ByteArray } from './byte-array'; import { ColorSpace } from './enums/color-space'; +import { ComplexSettings } from './settings/complex-settings'; import { Disposable } from './internal/disposable'; import { DisposableArray } from './internal/disposable-array'; import { EvaluateOperator } from './enums/evaluate-operator'; @@ -20,6 +21,7 @@ import { MagickImage } from './magick-image'; import { MagickReadSettings } from './settings/magick-read-settings'; import { MagickSettings } from './settings/magick-settings'; import { MontageSettings } from './settings/montage-settings'; +import { TemporaryDefines } from './helpers/temporary-defines'; export interface IMagickImageCollection extends Array, IDisposable { /** @internal */ @@ -95,6 +97,19 @@ export interface IMagickImageCollection extends Array, IDisposable */ combine(colorSpace: ColorSpace, func: (image: IMagickImage) => Promise): Promise; + /** + * Perform complex mathematics on an image sequence. + * @param func - The function to execute with the image. + */ + complex(settings: ComplexSettings, func: (image: IMagickImage) => TReturnType): TReturnType; + + /** + * Perform complex mathematics on an image sequence. + * @param func - The function to execute with the image. + */ + complex(settings: ComplexSettings, func: (image: IMagickImage) => Promise): Promise; + + /** * Evaluate image pixels into a single image. All the images in the collection must be the * same size in pixels. @@ -219,6 +234,7 @@ export class MagickImageCollection extends Array implements IMagick * Creates a new {@link IMagickImageCollection} instance. */ static create(): IMagickImageCollection; + /** * Creates a new {@link IMagickImageCollection} instance from the specified byte array. */ @@ -296,6 +312,17 @@ export class MagickImageCollection extends Array implements IMagick }, callback!); } + complex(settings: ComplexSettings, func: (image: IMagickImage) => TReturnType): TReturnType; + complex(settings: ComplexSettings, func: (image: IMagickImage) => Promise): Promise + complex(settings: ComplexSettings, func: (image: IMagickImage) => TReturnType | Promise): TReturnType | Promise { + return TemporaryDefines.use(this[0], temporaryDefines => { + settings._setArtifacts(temporaryDefines); + return this.createImage((instance, exception) => { + return ImageMagick._api._MagickImageCollection_Complex(instance, settings.complexOperator, exception.ptr); + }, func); + }); + } + evaluate(evaluateOperator: EvaluateOperator, func: (image: IMagickImage) => TReturnType): TReturnType; evaluate(evaluateOperator: EvaluateOperator, func: (image: IMagickImage) => Promise): Promise; evaluate(evaluateOperator: EvaluateOperator, func: (image: IMagickImage) => TReturnType | Promise): TReturnType | Promise { diff --git a/src/magick-image.ts b/src/magick-image.ts index eb20b5ec..969b111a 100644 --- a/src/magick-image.ts +++ b/src/magick-image.ts @@ -1469,7 +1469,7 @@ export interface IMagickImage extends IDisposable { * @param name - The name of the artifact. * @param value - The value of the artifact. */ - setArtifact(name: string, value: string | boolean | IMagickColor): void; + setArtifact(name: string, value: boolean | IMagickColor | number | string): void; /** * Inserts the attribute with the specified name and value into the artifact tree of the image. @@ -2938,7 +2938,7 @@ export class MagickImage extends NativeInstance implements IMagickImage { }); } - setArtifact(name: string, value: string | boolean | IMagickColor): void { + setArtifact(name: string, value: boolean | IMagickColor | number | string): void { let strValue: string; if (typeof value === 'string') strValue = value; diff --git a/src/settings/complex-settings.ts b/src/settings/complex-settings.ts new file mode 100644 index 00000000..cd42044b --- /dev/null +++ b/src/settings/complex-settings.ts @@ -0,0 +1,37 @@ +/* + Copyright Dirk Lemstra https://github.com/dlemstra/magick-wasm. + Licensed under the Apache License, Version 2.0. +*/ + +import { ComplexOperator } from '../enums/complex-operator'; +import { TemporaryDefines } from '../helpers/temporary-defines'; + +/** + * Settings for the complex operation. + */ +export class ComplexSettings { + /** + * Initializes a new instance of the {@link ComplexSettings} class. + * @param complexOperator - The complex operator. + */ + constructor(complexOperator: ComplexOperator) { + this.complexOperator = complexOperator; + } + + /**F + * Gets or sets the complex operator. + **/ + readonly complexOperator: ComplexOperator; + + /** + * Gets or sets the signal to noise ratio. + **/ + signalToNoiseRatio?: number; + + + /** @internal */ + _setArtifacts(temporaryDefines: TemporaryDefines): void { + if (this.signalToNoiseRatio !== undefined) + temporaryDefines.setArtifact('complex:snr', this.signalToNoiseRatio); + } +} diff --git a/tests/magick-image-collection/complex.spec.ts b/tests/magick-image-collection/complex.spec.ts new file mode 100644 index 00000000..cb59d8e8 --- /dev/null +++ b/tests/magick-image-collection/complex.spec.ts @@ -0,0 +1,32 @@ +/* + Copyright Dirk Lemstra https://github.com/dlemstra/magick-wasm. + Licensed under the Apache License, Version 2.0. +*/ + +import { ComplexOperator } from '@src/enums/complex-operator'; +import { ErrorMetric } from '@src/enums/error-metric'; +import { ComplexSettings } from '@src/settings/complex-settings'; +import { TestImages } from '@test/test-images'; + +describe('MagickImageCollection#coalesce', () => { + it('should throw exception when collection is empty', () => { + const settings = new ComplexSettings(ComplexOperator.Add); + + TestImages.emptyCollection.use((images) => { + expect(() => { + images.complex(settings, () => { /* never reached */ }); + }).toThrowError('operation requires at least one image'); + }); + }); + + it('should apply the complex operation', () => { + const settings = new ComplexSettings(ComplexOperator.Divide); + settings.signalToNoiseRatio = 5; + + TestImages.roseSparkleGif.use(images => { + images.complex(settings, (image) => { + expect(image.compare(images[0], ErrorMetric.RootMeanSquared)).toBeCloseTo(0.56843); + }); + }); + }); +}); diff --git a/tests/settings/complex-settings.spec.ts b/tests/settings/complex-settings.spec.ts new file mode 100644 index 00000000..08e78d5d --- /dev/null +++ b/tests/settings/complex-settings.spec.ts @@ -0,0 +1,28 @@ +/* + Copyright Dirk Lemstra https://github.com/dlemstra/magick-wasm. + Licensed under the Apache License, Version 2.0. +*/ + +import { ComplexOperator } from '@src/enums/complex-operator'; +import { TemporaryDefines } from '@src/helpers/temporary-defines'; +import { ComplexSettings } from '@src/settings/complex-settings'; +import { TestImages } from '@test/test-images'; + +describe('ConnectedComponentsSettings', () => { + const settings = new ComplexSettings(ComplexOperator.Multiply); + settings.signalToNoiseRatio = 42; + + describe('#_setArtifacts', () => { + it('should add all defined artifacts to the provided image', () => { + TestImages.Builtin.logo.use((image) => { + TemporaryDefines.use(image, (temporaryDefines) => { + settings._setArtifacts(temporaryDefines); + + expect(settings.complexOperator).toBe(ComplexOperator.Multiply); + expect(image.artifactNames.length).toBe(1); + expect(image.getArtifact('complex:snr')).toBe('42'); + }); + }); + }); + }); +});