Skip to content

Commit

Permalink
feat(image-io): initial image-io pkg sources
Browse files Browse the repository at this point in the history
  • Loading branch information
floryst authored and thewtex committed Oct 3, 2023
1 parent 3b5a496 commit 376e70f
Show file tree
Hide file tree
Showing 16 changed files with 544 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import { CastImageOptions } from 'itk-wasm'

interface ReadImageArrayBufferOptions extends CastImageOptions {
mimeType?: string
}

export default ReadImageArrayBufferOptions
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { CastImageOptions } from 'itk-wasm'

interface ReadImageFileSeriesOptions extends CastImageOptions {
zSpacing?: number
zOrigin?: number
sortedSeries?: boolean
}

export default ReadImageFileSeriesOptions
8 changes: 8 additions & 0 deletions packages/image-io/typescript/src/ReadImageFileSeriesResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Image, WorkerPool } from 'itk-wasm'

interface ReadImageFileSeriesResult {
image: Image
webWorkerPool: WorkerPool
}

export default ReadImageFileSeriesResult
8 changes: 8 additions & 0 deletions packages/image-io/typescript/src/ReadImageResult.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { Image } from 'itk-wasm'

interface ReadImageResult {
image: Image
webWorker: Worker
}

export default ReadImageResult
8 changes: 8 additions & 0 deletions packages/image-io/typescript/src/WriteImageOptions.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { CastImageOptions } from 'itk-wasm'

interface WriteImageOptions extends CastImageOptions {
useCompression?: boolean
mimeType?: boolean
}

export default WriteImageOptions
71 changes: 71 additions & 0 deletions packages/image-io/typescript/src/extensionToImageIO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const extensionToIO = new Map([
['bmp', 'BMPImageIO'],
['BMP', 'BMPImageIO'],

['dcm', 'GDCMImageIO'],
['DCM', 'GDCMImageIO'],

['gipl', 'GiplImageIO'],
['gipl.gz', 'GiplImageIO'],

['hdf5', 'HDF5ImageIO'],

['jpg', 'JPEGImageIO'],
['JPG', 'JPEGImageIO'],
['jpeg', 'JPEGImageIO'],
['JPEG', 'JPEGImageIO'],

['iwi', 'WasmImageIO'],
['iwi.cbor', 'WasmImageIO'],
['iwi.cbor.zstd', 'WasmZstdImageIO'],

['lsm', 'LSMImageIO'],

['mnc', 'MINCImageIO'],
['MNC', 'MINCImageIO'],
['mnc.gz', 'MINCImageIO'],
['MNC.GZ', 'MINCImageIO'],
['mnc2', 'MINCImageIO'],
['MNC2', 'MINCImageIO'],

['mgh', 'MGHImageIO'],
['mgz', 'MGHImageIO'],
['mgh.gz', 'MGHImageIO'],

['mha', 'MetaImageIO'],
['mhd', 'MetaImageIO'],

['mrc', 'MRCImageIO'],

['nia', 'NiftiImageIO'],
['nii', 'NiftiImageIO'],
['nii.gz', 'NiftiImageIO'],
['hdr', 'NiftiImageIO'],

['nrrd', 'NrrdImageIO'],
['NRRD', 'NrrdImageIO'],
['nhdr', 'NrrdImageIO'],
['NHDR', 'NrrdImageIO'],

['png', 'PNGImageIO'],
['PNG', 'PNGImageIO'],

['pic', 'BioRadImageIO'],
['PIC', 'BioRadImageIO'],

['tif', 'TIFFImageIO'],
['TIF', 'TIFFImageIO'],
['tiff', 'TIFFImageIO'],
['TIFF', 'TIFFImageIO'],

['vtk', 'VTKImageIO'],
['VTK', 'VTKImageIO'],

['isq', 'ScancoImageIO'],
['ISQ', 'ScancoImageIO'],

['fdf', 'FDFImageIO'],
['FDF', 'FDFImageIO']
])

export default extensionToIO
11 changes: 11 additions & 0 deletions packages/image-io/typescript/src/internal/MimeToImageIO.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
const mimeToIO = new Map([
['image/jpeg', 'JPEGImageIO'],
['image/png', 'PNGImageIO'],
['image/tiff', 'TIFFImageIO'],
['image/x-ms-bmp', 'BMPImageIO'],
['image/x-bmp', 'BMPImageIO'],
['image/bmp', 'BMPImageIO'],
['application/dicom', 'GDCMImageIO']
])

export default mimeToIO
18 changes: 18 additions & 0 deletions packages/image-io/typescript/src/internal/findLocalImageIOPath.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import fs from 'fs'

import { localPathRelativeToModule } from 'itk-wasm/internal'

// TODO?????
function findLocalImageIOPath (): string {
const buildPath = localPathRelativeToModule(import.meta.url, '../../image-io')
if (fs.existsSync(buildPath)) {
return buildPath
}
const packagePath = localPathRelativeToModule(import.meta.url, '../../../../itk-image-io')
if (fs.existsSync(packagePath)) {
return packagePath
}
throw Error("Cannot find path to itk image IO's. Try: npm install --save itk-image-io")
}

export default findLocalImageIOPath
55 changes: 55 additions & 0 deletions packages/image-io/typescript/src/readImageArrayBuffer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import { createWebWorkerPromise, Image, InterfaceTypes, PipelineInput, castImage } from 'itk-wasm'

import ReadImageResult from './ReadImageResult.js'
import ReadImageArrayBufferOptions from './ReadImageArrayBufferOptions.js'

async function readImageArrayBuffer (webWorker: Worker | null, arrayBuffer: ArrayBuffer, fileName: string, options?: ReadImageArrayBufferOptions | string): Promise<ReadImageResult> {
let worker = webWorker
const { webworkerPromise, worker: usedWorker } = await createWebWorkerPromise(worker)
worker = usedWorker

const filePath = `./${fileName}`
const args = [filePath, '0', '--memory-io', '--quiet']
const outputs = [
{ type: InterfaceTypes.Image }
]
const inputs = [
{ type: InterfaceTypes.BinaryFile, data: { path: filePath, data: new Uint8Array(arrayBuffer) } }
] as PipelineInput[]

const transferables: ArrayBuffer[] = [arrayBuffer]
interface RunReadImagePipelineResult {
stdout: string
stderr: string
outputs: any[]
}
let mimeType
if (typeof options === 'string') {
// backwards compatibility
mimeType = options
} else if (typeof options === 'object') {
if (typeof options.mimeType === 'string') {
mimeType = options.mimeType
}
}
const result: RunReadImagePipelineResult = await webworkerPromise.postMessage(
{
operation: 'readImage',
mimeType,
fileName,
pipelinePath: 'read-image', // placeholder
args,
outputs,
inputs
},
transferables
)
let image = result.outputs[0].data as Image
if (typeof options === 'object' && (typeof options.componentType !== 'undefined' || typeof options.pixelType !== 'undefined')) {
image = castImage(image, options)
}

return { image, webWorker: worker }
}

export default readImageArrayBuffer
12 changes: 12 additions & 0 deletions packages/image-io/typescript/src/readImageBlob.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { readAsArrayBuffer } from 'promise-file-reader'

import readImageArrayBuffer from './readImageArrayBuffer.js'
import ReadImageResult from './ReadImageResult.js'
import ReadImageArrayBufferOptions from './ReadImageArrayBufferOptions.js'

async function readImageBlob (webWorker: Worker | null, blob: Blob, fileName: string, options?: ReadImageArrayBufferOptions | string): Promise<ReadImageResult> {
const arrayBuffer = await readAsArrayBuffer(blob)
return await readImageArrayBuffer(webWorker, arrayBuffer, fileName, options)
}

export default readImageBlob
15 changes: 15 additions & 0 deletions packages/image-io/typescript/src/readImageFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { readAsArrayBuffer } from 'promise-file-reader'

import { CastImageOptions } from 'itk-wasm'
import readImageArrayBuffer from './readImageArrayBuffer.js'
import ReadImageResult from './ReadImageResult.js'
import ReadImageArrayBufferOptions from './ReadImageArrayBufferOptions.js'

async function readImageFile (webWorker: Worker | null, file: File, options?: CastImageOptions): Promise<ReadImageResult> {
const arrayBuffer = await readAsArrayBuffer(file)
const optionsToPass: ReadImageArrayBufferOptions = typeof options === 'undefined' ? {} : { ...options }
optionsToPass.mimeType = file.type
return await readImageArrayBuffer(webWorker, arrayBuffer, file.name, optionsToPass)
}

export default readImageFile
93 changes: 93 additions & 0 deletions packages/image-io/typescript/src/readImageFileSeries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { stackImages, WorkerPool, castImage } from 'itk-wasm'

import { readAsArrayBuffer } from 'promise-file-reader'

import readImageArrayBuffer from './readImageArrayBuffer.js'

import ReadImageFileSeriesResult from './ReadImageFileSeriesResult.js'
import ReadImageFileSeriesOptions from './ReadImageFileSeriesOptions.js'

const numberOfWorkers = typeof globalThis.navigator?.hardwareConcurrency === 'number' ? globalThis.navigator.hardwareConcurrency : 6
const workerPool = new WorkerPool(numberOfWorkers, readImageArrayBuffer)

async function readImageFileSeries (
fileList: File[] | FileList,
options?: ReadImageFileSeriesOptions | number,
zOriginBackwardsCompatibility?: number,
sortedSeriesBackwardsCompatibility?: boolean
): Promise<ReadImageFileSeriesResult> {
let zSpacing = 1.0
let zOrigin = 0.0
let sortedSeries = false
if (typeof options === 'number') {
// Backwards compatibility
zSpacing = options
}
if (typeof zOriginBackwardsCompatibility !== 'undefined') {
zOrigin = zOriginBackwardsCompatibility
}
if (typeof sortedSeriesBackwardsCompatibility !== 'undefined') {
sortedSeries = sortedSeriesBackwardsCompatibility
}
if (typeof options === 'object') {
if (typeof options.zSpacing !== 'undefined') {
zSpacing = options.zSpacing
}
if (typeof options.zOrigin !== 'undefined') {
zOrigin = options.zOrigin
}
if (typeof options.sortedSeries !== 'undefined') {
sortedSeries = options.sortedSeries
}
}
const fetchFileDescriptions = Array.from(fileList, async function (file) {
return await readAsArrayBuffer(file).then(function (
arrayBuffer
) {
const fileDescription = {
name: file.name,
type: file.type,
data: arrayBuffer
}
return fileDescription
})
})

const fileDescriptions = await Promise.all(fetchFileDescriptions)
if (!sortedSeries) {
fileDescriptions.sort((a, b) => {
if (a.name < b.name) {
return -1
}
if (a.name > b.name) {
return 1
}
return 0
})
}
const taskArgsArray = []
for (let index = 0; index < fileDescriptions.length; index++) {
taskArgsArray.push([fileDescriptions[index].data, fileDescriptions[index].name])
}
const results = await workerPool.runTasks(taskArgsArray).promise
const images = results.map((result) => {
const image = result.image
image.imageType.dimension = 3
image.size.push(1)
image.spacing.push(zSpacing)
image.origin.push(zOrigin)
image.direction = new Float64Array(9)
image.direction.fill(0.0)
image.direction[0] = 1.0
image.direction[4] = 1.0
image.direction[8] = 1.0
return image
})
let stacked = stackImages(images)
if (typeof options === 'object' && (typeof options.componentType !== 'undefined' || typeof options.pixelType !== 'undefined')) {
stacked = castImage(stacked, options)
}
return { image: stacked, webWorkerPool: workerPool }
}

export default readImageFileSeries
15 changes: 15 additions & 0 deletions packages/image-io/typescript/src/readImageHTTP.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import axios from 'axios'

import { bufferToTypedArray, Image, FloatTypes, TypedArray } from 'itk-wasm'

async function readImageHTTP (url: string): Promise<Image> {
const imageResponse = await axios.get(`${url}/index.json`, { responseType: 'json' })
const image = imageResponse.data as Image
const dataResponse = await axios.get(`${url}/data/data.raw`, { responseType: 'arraybuffer' })
image.data = bufferToTypedArray(image.imageType.componentType, dataResponse.data as ArrayBuffer) as TypedArray
const directionResponse = await axios.get(`${url}/data/direction.raw`, { responseType: 'arraybuffer' })
image.direction = bufferToTypedArray(FloatTypes.Float64, directionResponse.data as ArrayBuffer) as TypedArray
return image
}

export default readImageHTTP
Loading

0 comments on commit 376e70f

Please sign in to comment.