Skip to content

Commit

Permalink
feat(castImage): Function to cast to pixelType, componentType
Browse files Browse the repository at this point in the history
  • Loading branch information
thewtex committed Oct 28, 2022
1 parent 045abe8 commit 52500fe
Show file tree
Hide file tree
Showing 6 changed files with 322 additions and 0 deletions.
148 changes: 148 additions & 0 deletions cypress/e2e/core/castImage.cy.ts
@@ -0,0 +1,148 @@
import { PixelTypes } from "../../../dist"
import compareImageToBaseline from "../../support/compareImageToBaseline"

describe('castImage', () => {
beforeEach(() => {
cy.visit('/')
})


it('copies the input when no options are passed', () => {
cy.window().then(async (win) => {
const itk = win.itk

const inputImageType = new itk.ImageType()
const inputImage = new itk.Image(inputImageType)
inputImage.size = [256, 256]
inputImage.data = new Uint8Array(256*256)
inputImage.data.fill(7)
inputImage.origin = [3.0, 4.0]
inputImage.spacing = [9.0, 4.0]
inputImage.direction[0] = -1.0

const outputImage = itk.castImage(inputImage, {})

compareImageToBaseline(itk, outputImage, inputImage)
})
})


it('casts to the specified pixel type', () => {
cy.window().then(async (win) => {
const itk = win.itk

const inputImageType = new itk.ImageType()
const inputImage = new itk.Image(inputImageType)
inputImage.size = [256, 256]
inputImage.data = new Uint8Array(256*256)
inputImage.data.fill(7)

const outputImage = itk.castImage(inputImage, { pixelType: itk.PixelTypes.CovariantVector })

const baseline = inputImage
baseline.imageType.pixelType = itk.PixelTypes.CovariantVector

compareImageToBaseline(itk, outputImage, baseline)
})
})


it('throws an error when casting a multi-component image to a scalar image', () => {
cy.window().then(async (win) => {
const itk = win.itk

const inputImageType = new itk.ImageType(2, itk.IntTypes.UInt8, itk.PixelTypes.Complex, 2)
const inputImage = new itk.Image(inputImageType)
inputImage.size = [256, 256]
inputImage.data = new Uint8Array(256*256 * 2)
inputImage.data.fill(7)

expect(() => {
itk.castImage(inputImage, { pixelType: itk.PixelTypes.Scalar })
}).to.throw()
})
})

it('casts to another TypedArray component type', () => {
cy.window().then(async (win) => {
const itk = win.itk

const inputImageType = new itk.ImageType()
const inputImage = new itk.Image(inputImageType)
inputImage.size = [256, 256]
inputImage.data = new Uint8Array(256*256)
inputImage.data.fill(7)

const outputImage = itk.castImage(inputImage, { componentType: itk.FloatTypes.Float32 })

const baseline = inputImage
baseline.imageType.componentType = itk.FloatTypes.Float32
baseline.data = new Float32Array(baseline.data)

compareImageToBaseline(itk, outputImage, baseline)
})
})

it('casts to a 64-bit integer component type', () => {
cy.window().then(async (win) => {
const itk = win.itk

const inputImageType = new itk.ImageType()
const inputImage = new itk.Image(inputImageType)
inputImage.size = [256, 256]
inputImage.data = new Uint8Array(256*256)
inputImage.data.fill(7)

const outputImage = itk.castImage(inputImage, { componentType: itk.IntTypes.UInt64 })

const baseline = inputImage
baseline.imageType.componentType = itk.IntTypes.UInt64
baseline.data = new BigUint64Array(baseline.data.length)
baseline.data.fill(7n)

compareImageToBaseline(itk, outputImage, baseline)
})
})

it('casts from 64-bit to TypedArray component type', () => {
cy.window().then(async (win) => {
const itk = win.itk

const inputImageType = new itk.ImageType(2, itk.IntTypes.UInt64, itk.PixelTypes.Scalar, 1)
const inputImage = new itk.Image(inputImageType)
inputImage.size = [256, 256]
inputImage.data = new BigUint64Array(256*256)
inputImage.data.fill(7n)

const outputImage = itk.castImage(inputImage, { componentType: itk.FloatTypes.Float32 })

const baseline = inputImage
baseline.imageType.componentType = itk.FloatTypes.Float32
baseline.data = new Float32Array(baseline.data.length)
baseline.data.fill(7)

compareImageToBaseline(itk, outputImage, baseline)
})
})

it('casts from 64-bit to another 64-bit integer component type', () => {
cy.window().then(async (win) => {
const itk = win.itk

const inputImageType = new itk.ImageType()
const inputImage = new itk.Image(inputImageType)
inputImage.size = [256, 256]
inputImage.data = new BigInt64Array(256*256)
inputImage.data.fill(7n)

const outputImage = itk.castImage(inputImage, { componentType: itk.IntTypes.UInt64 })

const baseline = inputImage
baseline.imageType.componentType = itk.IntTypes.UInt64
baseline.data = new BigUint64Array(baseline.data.length)
baseline.data.fill(7n)

compareImageToBaseline(itk, outputImage, baseline)
})
})
})
File renamed without changes.
File renamed without changes.
13 changes: 13 additions & 0 deletions src/core/CastImageOptions.ts
@@ -0,0 +1,13 @@
import IntTypes from './IntTypes.js'
import FloatTypes from './FloatTypes.js'
import PixelTypes from './PixelTypes.js'

interface CastImageOptions {
/** Component type, from itk-wasm IntTypes, FloatTypes, for the output pixel components. Defaults to the input component type. */
componentType?: typeof IntTypes[keyof typeof IntTypes] | typeof FloatTypes[keyof typeof FloatTypes]

/** Pixel type, from itk-wasm PixelTypes, for the output pixels. Defaults to the input pixel type. */
pixelType?: typeof PixelTypes[keyof typeof PixelTypes]
}

export default CastImageOptions
159 changes: 159 additions & 0 deletions src/core/castImage.ts
@@ -0,0 +1,159 @@
import Image from './Image.js'
import CastImageOptions from './CastImageOptions.js'
import PixelTypes from './PixelTypes.js'
import IntTypes from './IntTypes.js'
import FloatTypes from './FloatTypes.js'

/**
* Cast an image to another PixelType or ComponentType
*
* @param {Image} image - The input image
* @param {CastImageOptions} options - specify the componentType and/or pixelType of the output
*/
function castImage (inputImage: Image, options: CastImageOptions): Image {
const outputImageType = { ...inputImage.imageType }

if (typeof options.pixelType !== 'undefined') {
outputImageType.pixelType = options.pixelType
if (options.pixelType === PixelTypes.Scalar && outputImageType.components !== 1) {
throw new Error('Cannot cast multi-component image to a scalar image')
}
}
if (typeof options.componentType !== 'undefined' && options.componentType !== inputImage.imageType.componentType) {
outputImageType.componentType = options.componentType
}

const outputImage = new Image(outputImageType)

outputImage.name = inputImage.name
outputImage.origin = Array.from(inputImage.origin)
outputImage.spacing = Array.from(inputImage.spacing)
outputImage.direction = inputImage.direction.slice()
outputImage.size = Array.from(inputImage.size)
outputImage.metadata = { ...inputImage.metadata }

if (inputImage.data !== null) {
if (typeof options.componentType !== 'undefined' && options.componentType !== inputImage.imageType.componentType) {
switch (inputImage.imageType.componentType) {
case IntTypes.UInt8:
case IntTypes.Int8:
case IntTypes.UInt16:
case IntTypes.Int16:
case IntTypes.UInt32:
case IntTypes.Int32:
case FloatTypes.Float32:
case FloatTypes.Float64:
switch (outputImage.imageType.componentType) {
case IntTypes.UInt8:
outputImage.data = new Uint8Array(inputImage.data)
break
case IntTypes.Int8:
outputImage.data = new Int8Array(inputImage.data)
break
case IntTypes.UInt16:
outputImage.data = new Uint16Array(inputImage.data)
break
case IntTypes.Int16:
outputImage.data = new Int16Array(inputImage.data)
break
case IntTypes.UInt32:
outputImage.data = new Uint32Array(inputImage.data)
break
case IntTypes.Int32:
outputImage.data = new Int32Array(inputImage.data)
break
case FloatTypes.Float32:
outputImage.data = new Float32Array(inputImage.data)
break
case FloatTypes.Float64:
outputImage.data = new Float64Array(inputImage.data)
break
case IntTypes.UInt64:
outputImage.data = new BigUint64Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = BigInt.asIntN(64, BigInt(inputImage.data[idx]))
}
break
case IntTypes.Int64:
outputImage.data = new BigInt64Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = BigInt.asUintN(64, BigInt(inputImage.data[idx]))
}
break
}
break
case IntTypes.UInt64:
case IntTypes.Int64:
switch (outputImage.imageType.componentType) {
case IntTypes.UInt8:
outputImage.data = new Uint8Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = Number(inputImage.data[idx])
}
break
case IntTypes.Int8:
outputImage.data = new Int8Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = Number(inputImage.data[idx])
}
break
case IntTypes.UInt16:
outputImage.data = new Uint16Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = Number(inputImage.data[idx])
}
break
case IntTypes.Int16:
outputImage.data = new Int16Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = Number(inputImage.data[idx])
}
break
case IntTypes.UInt32:
outputImage.data = new Uint32Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = Number(inputImage.data[idx])
}
break
case IntTypes.Int32:
outputImage.data = new Int32Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = Number(inputImage.data[idx])
}
break
case FloatTypes.Float32:
outputImage.data = new Float32Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = Number(inputImage.data[idx])
}
break
case FloatTypes.Float64:
outputImage.data = new Float64Array(inputImage.data.length)
for (let idx = 0; idx < outputImage.data.length; idx++) {
outputImage.data[idx] = Number(inputImage.data[idx])
}
break
case IntTypes.UInt64:
outputImage.data = new BigUint64Array(inputImage.data)
break
case IntTypes.Int64:
outputImage.data = new BigInt64Array(inputImage.data)
break
}
break
}
} else {
// copy
const CTor = inputImage.data.constructor as new(length: number) => typeof inputImage.data
outputImage.data = new CTor(inputImage.data.length)
if (outputImage.data != null) {
// @ts-expect-error: error TS2345: Argument of type 'TypedArray' is not assignable to parameter of type 'ArrayLike<number> & ArrayLike<bigint>'
outputImage.data.set(inputImage.data, 0)
}
}
}

return outputImage
}

export default castImage
2 changes: 2 additions & 0 deletions src/core/index.ts
Expand Up @@ -34,6 +34,8 @@ export { default as bufferToTypedArray } from './bufferToTypedArray.js'
export { default as copyImage } from './copyImage.js'
export { default as stackImages } from './stackImages.js'
export { default as imageSharedBufferOrCopy } from './imageSharedBufferOrCopy.js'
export { default as CastImageOptions } from './CastImageOptions.js'
export { default as castImage } from './castImage.js'

export { default as WorkerPool } from './WorkerPool.js'
export { default as WorkerPoolFunction } from './WorkerPoolFunction.js'
Expand Down

0 comments on commit 52500fe

Please sign in to comment.