Skip to content

Commit

Permalink
feat(image-io): add writeImageNode to the package
Browse files Browse the repository at this point in the history
  • Loading branch information
thewtex committed Oct 8, 2023
1 parent d52c46f commit cfbfd13
Show file tree
Hide file tree
Showing 5 changed files with 124 additions and 19 deletions.
6 changes: 6 additions & 0 deletions packages/image-io/typescript/src/index-node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ export type { ReadImageOptions }
import readImageNode from './read-image-node.js'
export { readImageNode }

import WriteImageOptions from './write-image-options.js'
export type { WriteImageOptions }

import writeImageNode from './write-image-node.js'
export { writeImageNode }

import BioRadReadImageNodeResult from './bio-rad-read-image-node-result.js'
export type { BioRadReadImageNodeResult }

Expand Down
76 changes: 76 additions & 0 deletions packages/image-io/typescript/src/write-image-node.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import path from 'path'
import mime from 'mime-types'

import {
Image,
getFileExtension,
castImage,
} from 'itk-wasm'

import mimeToImageIo from './mime-to-image-io.js'
import extensionToImageIo from './extension-to-image-io.js'
import imageIoIndexNode from './image-io-index-node.js'

import WriteImageOptions from './write-image-options.js'

interface WriterOptions {
useCompression?: boolean
mimeType?: boolean
}
interface WriterResult {
couldWrite: boolean
}
type Writer = (image: Image, serializedImage: string, options: WriterOptions) => Promise<WriterResult>


/**
* Write an image to a serialized file format and from an the itk-wasm Image
*
* @param {Image} image - Input image
* @param {string} serializedImage - Output image serialized in the file format.
* @param {WriteImageOptions} options - options object
*
* @returns {void} - result object
*/
async function writeImageNode(
image: Image,
serializedImage: string,
options: WriteImageOptions = {}
) : Promise<void> {
const absoluteFilePath = path.resolve(serializedImage)
const mimeType = mime.lookup(absoluteFilePath)
const extension = getFileExtension(absoluteFilePath)

let inputImage = image
if (typeof options.componentType !== 'undefined' || typeof options.pixelType !== 'undefined') {
inputImage = castImage(image, { componentType: options.componentType, pixelType: options.pixelType })
}

let io = null
if (mimeType !== false && mimeToImageIo.has(mimeType)) {
io = mimeToImageIo.get(mimeType)
} else if (extensionToImageIo.has(extension)) {
io = extensionToImageIo.get(extension)
} else {
for (const readerWriter of imageIoIndexNode.values()) {
if (readerWriter[1] !== null) {
let { couldWrite } = await (readerWriter[1] as Writer)(inputImage, absoluteFilePath, { useCompression: options.useCompression })
if (couldWrite) {
return
}
}
}
}
if (io === null ) {
throw Error('Could not find IO for: ' + absoluteFilePath)
}
const readerWriter = imageIoIndexNode.get(io as string)

const writer = (readerWriter as Array<Writer>)[1]
let { couldWrite } = await writer(inputImage, absoluteFilePath, { useCompression: options.useCompression })
if (!couldWrite) {
throw Error('Could not write: ' + absoluteFilePath)
}
}

export default writeImageNode
17 changes: 17 additions & 0 deletions packages/image-io/typescript/src/write-image-options.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { IntTypes, FloatTypes, PixelTypes } from 'itk-wasm'

interface WriteImageOptions {
/** 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]

/** Use compression when writing the image if the IO formt supports it. */
useCompression?: boolean

/** Mime type of the output image file. */
mimeType?: boolean
}

export default WriteImageOptions
10 changes: 5 additions & 5 deletions packages/image-io/typescript/test/node/read-image-node-test.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import path from 'path'
import { readImageNode } from '../../dist/bundles/image-io-node.js'
import { IntTypes, PixelTypes, getMatrixElement } from 'itk-wasm'

import { testInputPath, testOutputPath } from './common.js'
import { testInputPath } from './common.js'

const testInputFilePath = path.join(testInputPath, 'cthead1.png')

Expand All @@ -26,23 +26,23 @@ function verifyImage (t, image, componentType, pixelType) {
t.is(image.data.length, 196608)
}

test('Test reading a PNG with readImageNode file', async t => {
test('Test reading a PNG with readImageNode', async t => {
const image = await readImageNode(testInputFilePath)
const componentType = IntTypes.UInt8
const pixelType = PixelTypes.RGB
verifyImage(t, image, componentType, pixelType)
})

test('Test reading a PNG with readImageNode file cast to the specified componentType', async t => {
test('Test reading a PNG with readImageNode, cast to the specified componentType', async t => {
const componentType = IntTypes.UInt16
const image = await readImageNode(testInputFilePath, { componentType })
const pixelType = PixelTypes.RGB
verifyImage(t, image, componentType, pixelType)
})

test('Test reading a PNG with readImageNode file cast to the specified pixelType', async t => {
test('Test reading a PNG with readImageNode, cast to the specified pixelType', async t => {
const pixelType = PixelTypes.Vector
const image = await readImageNode(testInputFilePath, { pixelType })
const componentType = IntTypes.UInt8
verifyImage(t, image, componentType, pixelType)
})
})
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import test from 'ava'
import path from 'path'

import { IntTypes, PixelTypes, getMatrixElement, readImageLocalFile, writeImageLocalFile } from '../../../../dist/index.js'
import { readImageNode, writeImageNode } from '../../dist/bundles/image-io-node.js'
import { IntTypes, PixelTypes, getMatrixElement } from 'itk-wasm'

const testInputFilePath = path.resolve('build-emscripten', 'ExternalData', 'test', 'Input', 'cthead1.png')
const testOutputFilePath = path.resolve('build-emscripten', 'Testing', 'Temporary', 'writeImageLocalFileTest-cthead1.iwi.cbor')
import { testInputPath, testOutputPath } from './common.js'

const testInputFilePath = path.join(testInputPath, 'cthead1.png')
const testOutputFilePath = path.join(testOutputPath, 'write-image-node-test-cthead1.png')
const testOutputCastFilePath = path.join(testOutputPath, 'write-image-node-test-cast-cthead1.iwi.cbor')

const verifyImage = (t, image, expectedComponentType, expectedPixelType) => {
t.is(image.imageType.dimension, 2, 'dimension')
Expand Down Expand Up @@ -32,19 +36,21 @@ const verifyImage = (t, image, expectedComponentType, expectedPixelType) => {
t.is(image.data.length, 196608, 'data.length')
}

test('writeImageLocalFile writes a file path on the local filesystem', async t => {
const image = await readImageLocalFile(testInputFilePath)
await writeImageLocalFile(image, testOutputFilePath)
const imageSecondPass = await readImageLocalFile(testOutputFilePath)
verifyImage(t, imageSecondPass)

test('Test writing a PNG with readImageNode', async t => {
const image = await readImageNode(testInputFilePath)
await writeImageNode(image, testOutputFilePath)
const imageBack = await readImageNode(testOutputFilePath)
const componentType = IntTypes.UInt8
const pixelType = PixelTypes.RGB
verifyImage(t, imageBack, componentType, pixelType)
})

test('writeImageLocalFile writes a file path on the local filesystem given componentType, pixelType', async t => {
const image = await readImageLocalFile(testInputFilePath)
test('Test writing a PNG with writeImageNode, cast to the specified componentType and pixelType', async t => {
const image = await readImageNode(testInputFilePath)
const componentType = IntTypes.UInt16
const pixelType = PixelTypes.Vector
const outputFilePath = testOutputFilePath + 'componentTypePixelType.iwi.cbor'
await writeImageLocalFile(image, outputFilePath, { componentType, pixelType })
const imageSecondPass = await readImageLocalFile(outputFilePath)
verifyImage(t, imageSecondPass, componentType, pixelType)
await writeImageNode(image, testOutputCastFilePath, { componentType, pixelType})
const imageBack = await readImageNode(testOutputCastFilePath)
verifyImage(t, imageBack, componentType, pixelType)
})

0 comments on commit cfbfd13

Please sign in to comment.