From 8b8de3e17d5cc1fe757a45aa1ba9ee24e027f8b5 Mon Sep 17 00:00:00 2001 From: Matt McCormick Date: Tue, 10 Oct 2023 16:04:29 -0400 Subject: [PATCH] feat(image-io): add writeImage function --- .../compare-double-images-controller.ts | 5 +- .../demo-app/vector-magnitude-controller.ts | 5 +- .../demo-app/compress-stringify-controller.ts | 5 +- .../parse-string-decompress-controller.ts | 5 +- ...-presentation-state-to-image-controller.ts | 5 +- .../demo-app/read-dicom-tags-controller.ts | 5 +- .../structured-report-to-text-controller.ts | 5 +- .../typescript/cypress/e2e/read-image.cy.ts | 8 +- .../typescript/cypress/e2e/write-image.cy.ts | 96 +++++++++++ .../src/bio-rad-write-image-options.ts | 2 +- .../typescript/src/bmp-write-image-options.ts | 2 +- .../typescript/src/fdf-write-image-options.ts | 2 +- .../src/gdcm-write-image-options.ts | 2 +- .../src/ge-adw-write-image-options.ts | 2 +- .../typescript/src/ge4-write-image-options.ts | 2 +- .../typescript/src/ge5-write-image-options.ts | 2 +- .../src/gipl-write-image-options.ts | 2 +- .../src/hdf5-write-image-options.ts | 2 +- packages/image-io/typescript/src/index.ts | 9 + .../src/jpeg-write-image-options.ts | 2 +- .../typescript/src/lsm-write-image-options.ts | 2 +- .../src/meta-write-image-options.ts | 2 +- .../typescript/src/mgh-write-image-options.ts | 2 +- .../src/minc-write-image-options.ts | 2 +- .../typescript/src/mrc-write-image-options.ts | 2 +- .../src/nifti-write-image-options.ts | 2 +- .../src/nrrd-write-image-options.ts | 2 +- .../typescript/src/png-write-image-options.ts | 2 +- .../image-io/typescript/src/read-image.ts | 4 +- .../src/scanco-write-image-options.ts | 2 +- .../src/tiff-write-image-options.ts | 2 +- .../typescript/src/vtk-write-image-options.ts | 2 +- .../src/wasm-write-image-options.ts | 2 +- .../src/wasm-zstd-write-image-options.ts | 2 +- .../typescript/src/write-image-node.ts | 6 +- .../typescript/src/write-image-options.ts | 5 +- .../typescript/src/write-image-result.ts | 11 ++ .../image-io/typescript/src/write-image.ts | 86 ++++++++++ .../test/browser/demo-app/index.html | 82 ++++++--- .../typescript/test/browser/demo-app/index.ts | 1 + .../demo-app/write-image-controller.ts | 158 ++++++++++++++++++ .../write-image-load-sample-inputs.ts | 28 ++++ packages/image-io/write-image.cxx | 4 +- .../interface-functions-demo-typescript.js | 5 +- 44 files changed, 499 insertions(+), 85 deletions(-) create mode 100644 packages/image-io/typescript/cypress/e2e/write-image.cy.ts create mode 100644 packages/image-io/typescript/src/write-image-result.ts create mode 100644 packages/image-io/typescript/src/write-image.ts create mode 100644 packages/image-io/typescript/test/browser/demo-app/write-image-controller.ts create mode 100644 packages/image-io/typescript/test/browser/demo-app/write-image-load-sample-inputs.ts diff --git a/packages/compare-images/typescript/test/browser/demo-app/compare-double-images-controller.ts b/packages/compare-images/typescript/test/browser/demo-app/compare-double-images-controller.ts index 4031a90dc..1cb976bc4 100644 --- a/packages/compare-images/typescript/test/browser/demo-app/compare-double-images-controller.ts +++ b/packages/compare-images/typescript/test/browser/demo-app/compare-double-images-controller.ts @@ -7,7 +7,6 @@ import * as compareImages from '../../../dist/bundles/compare-images.js' import compareDoubleImagesLoadSampleInputs, { usePreRun } from "./compare-double-images-load-sample-inputs.js" class CompareDoubleImagesModel { - inputs: Map options: Map outputs: Map @@ -17,10 +16,10 @@ class CompareDoubleImagesModel { this.options = new Map() this.outputs = new Map() } - } +} -class CompareDoubleImagesController { +class CompareDoubleImagesController { constructor(loadSampleInputs) { this.loadSampleInputs = loadSampleInputs diff --git a/packages/compare-images/typescript/test/browser/demo-app/vector-magnitude-controller.ts b/packages/compare-images/typescript/test/browser/demo-app/vector-magnitude-controller.ts index d2405d7c9..71ca09ffa 100644 --- a/packages/compare-images/typescript/test/browser/demo-app/vector-magnitude-controller.ts +++ b/packages/compare-images/typescript/test/browser/demo-app/vector-magnitude-controller.ts @@ -7,7 +7,6 @@ import * as compareImages from '../../../dist/bundles/compare-images.js' import vectorMagnitudeLoadSampleInputs, { usePreRun } from "./vector-magnitude-load-sample-inputs.js" class VectorMagnitudeModel { - inputs: Map options: Map outputs: Map @@ -17,10 +16,10 @@ class VectorMagnitudeModel { this.options = new Map() this.outputs = new Map() } - } +} -class VectorMagnitudeController { +class VectorMagnitudeController { constructor(loadSampleInputs) { this.loadSampleInputs = loadSampleInputs diff --git a/packages/compress-stringify/typescript/test/browser/demo-app/compress-stringify-controller.ts b/packages/compress-stringify/typescript/test/browser/demo-app/compress-stringify-controller.ts index f8afafe90..fea29513b 100644 --- a/packages/compress-stringify/typescript/test/browser/demo-app/compress-stringify-controller.ts +++ b/packages/compress-stringify/typescript/test/browser/demo-app/compress-stringify-controller.ts @@ -4,7 +4,6 @@ import * as compressStringify from '../../../dist/bundles/compress-stringify.js' import compressStringifyLoadSampleInputs, { usePreRun } from "./compress-stringify-load-sample-inputs.js" class CompressStringifyModel { - inputs: Map options: Map outputs: Map @@ -14,10 +13,10 @@ class CompressStringifyModel { this.options = new Map() this.outputs = new Map() } - } +} -class CompressStringifyController { +class CompressStringifyController { constructor(loadSampleInputs) { this.loadSampleInputs = loadSampleInputs diff --git a/packages/compress-stringify/typescript/test/browser/demo-app/parse-string-decompress-controller.ts b/packages/compress-stringify/typescript/test/browser/demo-app/parse-string-decompress-controller.ts index 1e68f0107..0fd0f9a24 100644 --- a/packages/compress-stringify/typescript/test/browser/demo-app/parse-string-decompress-controller.ts +++ b/packages/compress-stringify/typescript/test/browser/demo-app/parse-string-decompress-controller.ts @@ -4,7 +4,6 @@ import * as compressStringify from '../../../dist/bundles/compress-stringify.js' import parseStringDecompressLoadSampleInputs, { usePreRun } from "./parse-string-decompress-load-sample-inputs.js" class ParseStringDecompressModel { - inputs: Map options: Map outputs: Map @@ -14,10 +13,10 @@ class ParseStringDecompressModel { this.options = new Map() this.outputs = new Map() } - } +} -class ParseStringDecompressController { +class ParseStringDecompressController { constructor(loadSampleInputs) { this.loadSampleInputs = loadSampleInputs diff --git a/packages/dicom/typescript/test/browser/demo-app/apply-presentation-state-to-image-controller.ts b/packages/dicom/typescript/test/browser/demo-app/apply-presentation-state-to-image-controller.ts index e263397c6..afa10fcb1 100644 --- a/packages/dicom/typescript/test/browser/demo-app/apply-presentation-state-to-image-controller.ts +++ b/packages/dicom/typescript/test/browser/demo-app/apply-presentation-state-to-image-controller.ts @@ -6,7 +6,6 @@ import * as dicom from '../../../dist/bundles/dicom.js' import applyPresentationStateToImageLoadSampleInputs, { usePreRun } from "./apply-presentation-state-to-image-load-sample-inputs.js" class ApplyPresentationStateToImageModel { - inputs: Map options: Map outputs: Map @@ -16,10 +15,10 @@ class ApplyPresentationStateToImageModel { this.options = new Map() this.outputs = new Map() } - } +} -class ApplyPresentationStateToImageController { +class ApplyPresentationStateToImageController { constructor(loadSampleInputs) { this.loadSampleInputs = loadSampleInputs diff --git a/packages/dicom/typescript/test/browser/demo-app/read-dicom-tags-controller.ts b/packages/dicom/typescript/test/browser/demo-app/read-dicom-tags-controller.ts index 0958d96af..fe175a0a2 100644 --- a/packages/dicom/typescript/test/browser/demo-app/read-dicom-tags-controller.ts +++ b/packages/dicom/typescript/test/browser/demo-app/read-dicom-tags-controller.ts @@ -4,7 +4,6 @@ import * as dicom from '../../../dist/bundles/dicom.js' import readDicomTagsLoadSampleInputs, { usePreRun } from "./read-dicom-tags-load-sample-inputs.js" class ReadDicomTagsModel { - inputs: Map options: Map outputs: Map @@ -14,10 +13,10 @@ class ReadDicomTagsModel { this.options = new Map() this.outputs = new Map() } - } +} -class ReadDicomTagsController { +class ReadDicomTagsController { constructor(loadSampleInputs) { this.loadSampleInputs = loadSampleInputs diff --git a/packages/dicom/typescript/test/browser/demo-app/structured-report-to-text-controller.ts b/packages/dicom/typescript/test/browser/demo-app/structured-report-to-text-controller.ts index c783c2e9d..e166c8c1b 100644 --- a/packages/dicom/typescript/test/browser/demo-app/structured-report-to-text-controller.ts +++ b/packages/dicom/typescript/test/browser/demo-app/structured-report-to-text-controller.ts @@ -4,7 +4,6 @@ import * as dicom from '../../../dist/bundles/dicom.js' import structuredReportToTextLoadSampleInputs, { usePreRun } from "./structured-report-to-text-load-sample-inputs.js" class StructuredReportToTextModel { - inputs: Map options: Map outputs: Map @@ -14,10 +13,10 @@ class StructuredReportToTextModel { this.options = new Map() this.outputs = new Map() } - } +} -class StructuredReportToTextController { +class StructuredReportToTextController { constructor(loadSampleInputs) { this.loadSampleInputs = loadSampleInputs diff --git a/packages/image-io/typescript/cypress/e2e/read-image.cy.ts b/packages/image-io/typescript/cypress/e2e/read-image.cy.ts index 91e735f2d..ac33ac73a 100644 --- a/packages/image-io/typescript/cypress/e2e/read-image.cy.ts +++ b/packages/image-io/typescript/cypress/e2e/read-image.cy.ts @@ -40,7 +40,7 @@ describe('read-image', () => { }) }) - it('Reads an image File in the dem', function () { + it('Reads an image File in the demo', function () { cy.get('sl-tab[panel="readImage-panel"]').click() const testFile = { contents: new Uint8Array(this['cthead1.png']), fileName: 'cthead1.png' } @@ -97,11 +97,11 @@ describe('read-image', () => { }) }) - it('Throws a catchable error for an invalid file', function () { + it('Throws a catchable error for an invalid file', { defaultCommandTimeout: 120000 }, function () { cy.window().then(async (win) => { const invalidArray = new Uint8Array([21, 4, 4, 4, 4, 9, 5, 0, 82, 42]) - const invalidBlob = new window.Blob([invalidArray]) - const invalidFile = new window.File([invalidBlob], 'invalid.file') + const invalidBlob = new win.Blob([invalidArray]) + const invalidFile = new win.File([invalidBlob], 'invalid.file') try { const { webWorker, image } = await win.imageIo.readImage(null, invalidFile) webWorker.terminate() diff --git a/packages/image-io/typescript/cypress/e2e/write-image.cy.ts b/packages/image-io/typescript/cypress/e2e/write-image.cy.ts new file mode 100644 index 000000000..c4844c0f5 --- /dev/null +++ b/packages/image-io/typescript/cypress/e2e/write-image.cy.ts @@ -0,0 +1,96 @@ +import { demoServer } from './common.ts' + +import { IntTypes, PixelTypes } from 'itk-wasm' + +const cthead1SmallBase64DataURI = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAAAAABWESUoAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAAAAmJLR0QA/4ePzL8AAAAHdElNRQfhBQYVKw8AZTNIAAADdklEQVQ4y2WTa2wUVRiGp6W7O3POnLmc2VrstokJlrBIUBJigjfSICVCCAo/QKM/FFNRIESJQKAws3M7M2f20t3GthRKQQq0kkoXMIq9oFwCXkg0UpMakGLgR9EmJF4TNOvZhRBb31+TvM955/vO+T6Ou69pAgSwKCCAEPc/lYUhFEUkMgH2ESmbYocEEUmKLIQqBKmEgUlERQhAPhyJiDMXPFZZDmRGoP8Q5TwC4ciMpatfXE9zmT2NVRVIQiLi76cDUVRDT/m72zLUc/Srv+gNCi8jhCrupvMAQIWf1zJx58pRj7g7h/sduunhiIIkUAJ4AUBZ0LZev3TondmeS42TuaYms6kOapJUalYQAAKxt+j4qD3yxvMZ0z47NLi/ydhWA7GMinWyAH6G1Wwe/OdUz6dz33T35dPdIxdIYrPGK0qxTnYrobVtjm+3pNvPxGu9/dTRgw8/e89et0AKF1uFItS2u7ZP7fr4K3H19VbP94me/T6fXRifM6+a/QKC6N5+PWGYZhVeNn9pzvUoTVnt3/QEz81dUTONgwjis4UzvS2Z5JbY9JlPdxmEuFZzX9va0yu5WlXmRAlWd3Tmjg980vXBprJZbYPtza0dXw40ZleeP1ZbrWKOXXpsu7Grb3gnsY/27B46+e3ElVuF3w+sm7Pki2VAUxkAo1t0a7TL8YnVPZxy6KG9fX/+2qu/+9DARoAVBiDYaHjnfc/3nHOdicA1Em6WpnOdG/I6zwCA5PCzrn6uw6VO99gBnRBKGUyIMfz3BgmrHHta8cEdu04dN6wjPwy6FinaTNT8emKNzGrgBEmJLLf7T6Tf/60wpFP2oKToB/bNr+pVTWHjghQxZuTzW51C4aIZENdj8gMv+1f3I7iYwPEqrFu+z1/zzI3vHN/ziEd9P0haV39aXxXFRaBMRrCu9Vjj5o/S5C4QBCnjws+pJ9SoqpZmRlqyeNWlPa922El22PMCl5if38q9FGV+CeAaFuK4OZY5nLRoksnsPX19nL5do2GsREoAlCtr68lo4VoXNROWdXD8j7GUNV96AMPye5MtYgU/ujF/887tHy+PXLt9o9/asUipvDfWpc1QNFWKPfla8PHI5Ysnsua2l2dH1Un7WS6rKlamxx9f/MKKhkX1syoxmLqcUMVRDTNMlZGkilPsUrOsJ6wxRSel/wuAkzbenLRf4gAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAxNy0wNS0wNlQxNzoyNjozNC0wNDowMORO/MMAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMTctMDUtMDZUMTc6MjY6MzQtMDQ6MDCVE0R/AAAAAElFTkSuQmCC' +const byteString = window.atob(cthead1SmallBase64DataURI.split(',')[1]) +const mimeString = cthead1SmallBase64DataURI.split(',')[0].split(':')[1].split(';')[0] +const intArray = new Uint8Array(byteString.length) +for (let ii = 0; ii < byteString.length; ++ii) { + intArray[ii] = byteString.charCodeAt(ii) +} +const cthead1SmallBlob = new window.Blob([intArray], { type: mimeString }) + +function verifyImage (image, componentType, pixelType) { + cy.expect(image.imageType.dimension).to.equal(2) + cy.expect(image.imageType.componentType).to.equal(componentType) + cy.expect(image.imageType.pixelType).to.equal(pixelType) + cy.expect(image.imageType.components).to.equal(1) + cy.expect(image.origin[0]).to.equal(0.0) + cy.expect(image.origin[1]).to.equal(0.0) + cy.expect(image.spacing[0]).to.equal(1.0) + cy.expect(image.spacing[1]).to.equal(1.0) + cy.expect(image.size[0]).to.equal(32) + cy.expect(image.size[1]).to.equal(32) + cy.expect(image.data.length).to.equal(1024) + cy.expect(image.data[512]).to.equal(12) +} + +describe('write-image', () => { + beforeEach(function() { + cy.visit(demoServer) + + const testPathPrefix = '../test/data/input/' + + const testImageFiles = [ + 'cthead1.iwi.cbor' + ] + testImageFiles.forEach((fileName) => { + cy.readFile(`${testPathPrefix}${fileName}`, null).as(fileName) + }) + }) + + it('Writes an image in the demo', function () { + cy.get('sl-tab[panel="writeImage-panel"]').click() + + const testFile = { contents: new Uint8Array(this['cthead1.iwi.cbor']), fileName: 'cthead1.iwi.cbor' } + cy.get('#writeImageInputs input[name="image-file"]').selectFile([testFile,], { force: true }) + cy.get('#writeImage-image-details').should('contain', 'imageType') + cy.get('#writeImageInputs sl-input[name="serialized-image"]').find('input', { includeShadowDom: true }).type('cthead1.png', { force: true }) + + cy.get('#writeImageInputs sl-button[name="run"]').click() + + cy.get('#writeImage-serialized-image-details').should('contain', '0,3') + }) + + it('Writes an image to an ArrayBuffer', function () { + cy.window().then(async (win) => { + const arrayBuffer = await cthead1SmallBlob.arrayBuffer() + const { image, webWorker } = await win.imageIo.readImage(null, { data: new Uint8Array(arrayBuffer), path: 'cthead1Small.png' }) + const { serializedImage } = await win.imageIo.writeImage(webWorker, image, 'cthead1.mha') + const { image: imageBack } = await win.imageIo.readImage(webWorker, serializedImage) + webWorker.terminate() + const componentType = IntTypes.UInt8 + const pixelType = PixelTypes.Scalar + verifyImage(imageBack, componentType, pixelType) + }) + }) + + it('Writes an image to an ArrayBuffer, given componentType, pixelType', function () { + cy.window().then(async (win) => { + const componentType = IntTypes.UInt16 + const pixelType = PixelTypes.Vector + const arrayBuffer = await cthead1SmallBlob.arrayBuffer() + const { image, webWorker } = await win.imageIo.readImage(null, { data: new Uint8Array(arrayBuffer), path: 'cthead1Small.png' }) + const { serializedImage } = await win.imageIo.writeImage(webWorker, image, 'cthead1.mha', { pixelType, componentType}) + // Reading back, the pixelType is always Scalar (reader behavior). Is this a bug? + const { image: imageBack } = await win.imageIo.readImage(webWorker, serializedImage, { pixelType }) + webWorker.terminate() + verifyImage(imageBack, componentType, pixelType) + }) + }) + + it('Writes an image to an ArrayBuffer, uses compression', function () { + cy.window().then(async (win) => { + const arrayBuffer = await cthead1SmallBlob.arrayBuffer() + const { image, webWorker } = await win.imageIo.readImage(null, { data: new Uint8Array(arrayBuffer), path: 'cthead1Small.png' }) + const options = { useCompression: false } + const { serializedImage } = await win.imageIo.writeImage(webWorker, image, 'cthead1.mha', options) + const { image: imageBack } = await win.imageIo.readImage(webWorker, serializedImage) + webWorker.terminate() + const componentType = IntTypes.UInt8 + const pixelType = PixelTypes.Scalar + verifyImage(imageBack, componentType, pixelType) + }) + }) +}) diff --git a/packages/image-io/typescript/src/bio-rad-write-image-options.ts b/packages/image-io/typescript/src/bio-rad-write-image-options.ts index bbde5a8ae..fa61cfcf6 100644 --- a/packages/image-io/typescript/src/bio-rad-write-image-options.ts +++ b/packages/image-io/typescript/src/bio-rad-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface BioRadWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/bmp-write-image-options.ts b/packages/image-io/typescript/src/bmp-write-image-options.ts index b3391db44..5a86540fb 100644 --- a/packages/image-io/typescript/src/bmp-write-image-options.ts +++ b/packages/image-io/typescript/src/bmp-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface BmpWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/fdf-write-image-options.ts b/packages/image-io/typescript/src/fdf-write-image-options.ts index 2e8c05494..4286dc21b 100644 --- a/packages/image-io/typescript/src/fdf-write-image-options.ts +++ b/packages/image-io/typescript/src/fdf-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface FdfWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/gdcm-write-image-options.ts b/packages/image-io/typescript/src/gdcm-write-image-options.ts index b86fb750d..4e9bdf31f 100644 --- a/packages/image-io/typescript/src/gdcm-write-image-options.ts +++ b/packages/image-io/typescript/src/gdcm-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface GdcmWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/ge-adw-write-image-options.ts b/packages/image-io/typescript/src/ge-adw-write-image-options.ts index 8908e20ce..2c463263e 100644 --- a/packages/image-io/typescript/src/ge-adw-write-image-options.ts +++ b/packages/image-io/typescript/src/ge-adw-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface GeAdwWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/ge4-write-image-options.ts b/packages/image-io/typescript/src/ge4-write-image-options.ts index 399a6afe4..8995466ab 100644 --- a/packages/image-io/typescript/src/ge4-write-image-options.ts +++ b/packages/image-io/typescript/src/ge4-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface Ge4WriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/ge5-write-image-options.ts b/packages/image-io/typescript/src/ge5-write-image-options.ts index 68aaa3542..ef0cb23f5 100644 --- a/packages/image-io/typescript/src/ge5-write-image-options.ts +++ b/packages/image-io/typescript/src/ge5-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface Ge5WriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/gipl-write-image-options.ts b/packages/image-io/typescript/src/gipl-write-image-options.ts index dee02209b..3e1fe6da0 100644 --- a/packages/image-io/typescript/src/gipl-write-image-options.ts +++ b/packages/image-io/typescript/src/gipl-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface GiplWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/hdf5-write-image-options.ts b/packages/image-io/typescript/src/hdf5-write-image-options.ts index 09eee03c4..775a49b42 100644 --- a/packages/image-io/typescript/src/hdf5-write-image-options.ts +++ b/packages/image-io/typescript/src/hdf5-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface Hdf5WriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/index.ts b/packages/image-io/typescript/src/index.ts index cae480510..b01846eb2 100644 --- a/packages/image-io/typescript/src/index.ts +++ b/packages/image-io/typescript/src/index.ts @@ -11,6 +11,15 @@ export type { ReadImageResult } import readImage from './read-image.js' export { readImage } +import WriteImageOptions from './write-image-options.js' +export type { WriteImageOptions } + +import WriteImageResult from './write-image-result.js' +export type { WriteImageResult } + +import writeImage from './write-image.js' +export { writeImage } + import BioRadReadImageResult from './bio-rad-read-image-result.js' export type { BioRadReadImageResult } diff --git a/packages/image-io/typescript/src/jpeg-write-image-options.ts b/packages/image-io/typescript/src/jpeg-write-image-options.ts index c72fc9af3..5c8f8aa8a 100644 --- a/packages/image-io/typescript/src/jpeg-write-image-options.ts +++ b/packages/image-io/typescript/src/jpeg-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface JpegWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/lsm-write-image-options.ts b/packages/image-io/typescript/src/lsm-write-image-options.ts index 35066d8bd..98e7eff46 100644 --- a/packages/image-io/typescript/src/lsm-write-image-options.ts +++ b/packages/image-io/typescript/src/lsm-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface LsmWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/meta-write-image-options.ts b/packages/image-io/typescript/src/meta-write-image-options.ts index 4d0a420af..7b9b3d079 100644 --- a/packages/image-io/typescript/src/meta-write-image-options.ts +++ b/packages/image-io/typescript/src/meta-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface MetaWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/mgh-write-image-options.ts b/packages/image-io/typescript/src/mgh-write-image-options.ts index 7dc635d10..bdce77654 100644 --- a/packages/image-io/typescript/src/mgh-write-image-options.ts +++ b/packages/image-io/typescript/src/mgh-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface MghWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/minc-write-image-options.ts b/packages/image-io/typescript/src/minc-write-image-options.ts index 004b859f4..f757d6d02 100644 --- a/packages/image-io/typescript/src/minc-write-image-options.ts +++ b/packages/image-io/typescript/src/minc-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface MincWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/mrc-write-image-options.ts b/packages/image-io/typescript/src/mrc-write-image-options.ts index 8c1676103..f01c1dde6 100644 --- a/packages/image-io/typescript/src/mrc-write-image-options.ts +++ b/packages/image-io/typescript/src/mrc-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface MrcWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/nifti-write-image-options.ts b/packages/image-io/typescript/src/nifti-write-image-options.ts index 26e0091db..8fbb793c0 100644 --- a/packages/image-io/typescript/src/nifti-write-image-options.ts +++ b/packages/image-io/typescript/src/nifti-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface NiftiWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/nrrd-write-image-options.ts b/packages/image-io/typescript/src/nrrd-write-image-options.ts index 7319344cd..b76d8b6bf 100644 --- a/packages/image-io/typescript/src/nrrd-write-image-options.ts +++ b/packages/image-io/typescript/src/nrrd-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface NrrdWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/png-write-image-options.ts b/packages/image-io/typescript/src/png-write-image-options.ts index 927d93954..c651d3fd7 100644 --- a/packages/image-io/typescript/src/png-write-image-options.ts +++ b/packages/image-io/typescript/src/png-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface PngWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/read-image.ts b/packages/image-io/typescript/src/read-image.ts index 2e8ff4855..329cff09c 100644 --- a/packages/image-io/typescript/src/read-image.ts +++ b/packages/image-io/typescript/src/read-image.ts @@ -40,7 +40,7 @@ async function readImage( const mimeType = (serializedImage as File).type ?? '' const fileName = (serializedImage as File).name ?? (serializedImage as BinaryFile).path ?? 'fileName' - const extension = getFileExtension(fileName) + const extension = getFileExtension(fileName).toLowerCase() let usedWebWorker = webWorker let serializedImageFile = serializedImage as BinaryFile @@ -52,7 +52,7 @@ async function readImage( let io = null if (mimeType && mimeToImageIo.has(mimeType)) { io = mimeToImageIo.get(mimeType) - } else if (!extensionToImageIo.has(extension)) { + } else if (extensionToImageIo.has(extension)) { io = extensionToImageIo.get(extension) } else { for (const readerWriter of imageIoIndex.values()) { diff --git a/packages/image-io/typescript/src/scanco-write-image-options.ts b/packages/image-io/typescript/src/scanco-write-image-options.ts index 6744d4052..c7bcce1ac 100644 --- a/packages/image-io/typescript/src/scanco-write-image-options.ts +++ b/packages/image-io/typescript/src/scanco-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface ScancoWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/tiff-write-image-options.ts b/packages/image-io/typescript/src/tiff-write-image-options.ts index 9a273702f..834049c6f 100644 --- a/packages/image-io/typescript/src/tiff-write-image-options.ts +++ b/packages/image-io/typescript/src/tiff-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface TiffWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/vtk-write-image-options.ts b/packages/image-io/typescript/src/vtk-write-image-options.ts index db387d6aa..6b22864c0 100644 --- a/packages/image-io/typescript/src/vtk-write-image-options.ts +++ b/packages/image-io/typescript/src/vtk-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface VtkWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/wasm-write-image-options.ts b/packages/image-io/typescript/src/wasm-write-image-options.ts index d5aedcf8c..60a016e18 100644 --- a/packages/image-io/typescript/src/wasm-write-image-options.ts +++ b/packages/image-io/typescript/src/wasm-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface WasmWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/wasm-zstd-write-image-options.ts b/packages/image-io/typescript/src/wasm-zstd-write-image-options.ts index 2d633a9f6..8d87dccb9 100644 --- a/packages/image-io/typescript/src/wasm-zstd-write-image-options.ts +++ b/packages/image-io/typescript/src/wasm-zstd-write-image-options.ts @@ -1,7 +1,7 @@ // Generated file. To retain edits, remove this comment. interface WasmZstdWriteImageOptions { - /** Only read image metadata -- do not read pixel data. */ + /** Only write image metadata -- do not write pixel data. */ informationOnly?: boolean /** Use compression in the written file */ diff --git a/packages/image-io/typescript/src/write-image-node.ts b/packages/image-io/typescript/src/write-image-node.ts index eab1bb7f7..94931bc85 100644 --- a/packages/image-io/typescript/src/write-image-node.ts +++ b/packages/image-io/typescript/src/write-image-node.ts @@ -1,5 +1,4 @@ import path from 'path' -import mime from 'mime-types' import { Image, @@ -15,7 +14,6 @@ import WriteImageOptions from './write-image-options.js' interface WriterOptions { useCompression?: boolean - mimeType?: boolean } interface WriterResult { couldWrite: boolean @@ -38,7 +36,7 @@ async function writeImageNode( options: WriteImageOptions = {} ) : Promise { const absoluteFilePath = path.resolve(serializedImage) - const mimeType = mime.lookup(absoluteFilePath) + const mimeType = options.mimeType const extension = getFileExtension(absoluteFilePath) let inputImage = image @@ -47,7 +45,7 @@ async function writeImageNode( } let io = null - if (mimeType !== false && mimeToImageIo.has(mimeType)) { + if (typeof mimeType !== 'undefined' && mimeToImageIo.has(mimeType)) { io = mimeToImageIo.get(mimeType) } else if (extensionToImageIo.has(extension)) { io = extensionToImageIo.get(extension) diff --git a/packages/image-io/typescript/src/write-image-options.ts b/packages/image-io/typescript/src/write-image-options.ts index dea9164da..e7b86ff46 100644 --- a/packages/image-io/typescript/src/write-image-options.ts +++ b/packages/image-io/typescript/src/write-image-options.ts @@ -11,7 +11,10 @@ interface WriteImageOptions { useCompression?: boolean /** Mime type of the output image file. */ - mimeType?: boolean + mimeType?: string + + /** Only write the image information, not the pixel data. */ + informationOnly?: boolean } export default WriteImageOptions diff --git a/packages/image-io/typescript/src/write-image-result.ts b/packages/image-io/typescript/src/write-image-result.ts new file mode 100644 index 000000000..df2e0bc15 --- /dev/null +++ b/packages/image-io/typescript/src/write-image-result.ts @@ -0,0 +1,11 @@ +import { BinaryFile } from 'itk-wasm' + +interface WriteImageResult { + /** WebWorker used for computation */ + webWorker: Worker | null + + /** Output image serialized in the file format. */ + serializedImage: BinaryFile +} + +export default WriteImageResult diff --git a/packages/image-io/typescript/src/write-image.ts b/packages/image-io/typescript/src/write-image.ts new file mode 100644 index 000000000..f6ab4aca8 --- /dev/null +++ b/packages/image-io/typescript/src/write-image.ts @@ -0,0 +1,86 @@ +import { + Image, + BinaryFile, + castImage, + copyImage, + getFileExtension, +} from 'itk-wasm' + +import mimeToImageIo from './mime-to-image-io.js' +import extensionToImageIo from './extension-to-image-io.js' +import imageIoIndex from './image-io-index.js' +import WriteImageOptions from './write-image-options.js' +import WriteImageResult from './write-image-result.js' + +interface WriterOptions { + informationOnly?: boolean + useCompression?: boolean +} +interface WriterResult { + webWorker: Worker + couldWrite: boolean + serializedImage: BinaryFile +} +type Writer = (webWorker: Worker | null, image: Image, serializedImage: string, options: WriterOptions) => Promise + +/** + * Write an itk-wasm Image converted to an serialized image file format + * + * @param {Image} image - Input image + * @param {string} serializedImage - Output image serialized in the file format. + * @param {WriteImageOptions} options - options object + * + * @returns {Promise} - result object + */ +async function writeImage( + webWorker: null | Worker, + image: Image, + serializedImage: string, + options: WriteImageOptions = {} +) : Promise { + + let inputImage = image + if (typeof options.componentType !== 'undefined' || typeof options.pixelType !== 'undefined') { + inputImage = castImage(image, options) + } + + const mimeType = options.mimeType + const extension = getFileExtension(serializedImage).toLowerCase() + let usedWebWorker = webWorker + + let io = null + if (typeof mimeType !== 'undefined' && mimeToImageIo.has(mimeType)) { + io = mimeToImageIo.get(mimeType) + } else if (extensionToImageIo.has(extension)) { + io = extensionToImageIo.get(extension) + } else { + for (const readerWriter of imageIoIndex.values()) { + if (readerWriter[1] !== null) { + let { webWorker: testWebWorker, couldWrite, serializedImage: serializedImageBuffer } = await (readerWriter[1] as unknown as Writer)(usedWebWorker, copyImage(inputImage), serializedImage, options) + usedWebWorker = testWebWorker + if (couldWrite) { + return { webWorker: usedWebWorker as Worker, serializedImage: serializedImageBuffer } + } + } + } + } + if (!io) { + throw Error('Could not find IO for: ' + serializedImage) + } + const readerWriter = imageIoIndex.get(io as string) + + const writer = (readerWriter as Array)[1] + let { webWorker: testWebWorker, couldWrite, serializedImage: serializedImageBuffer } = await writer(usedWebWorker, inputImage, serializedImage, options) + usedWebWorker = testWebWorker + if (!couldWrite) { + throw Error('Could not write: ' + serializedImage) + } + + const result = { + webWorker: usedWebWorker as Worker, + serializedImage: serializedImageBuffer + } + return result +} + +export default writeImage diff --git a/packages/image-io/typescript/test/browser/demo-app/index.html b/packages/image-io/typescript/test/browser/demo-app/index.html index 1750d461e..4c3ea15bc 100644 --- a/packages/image-io/typescript/test/browser/demo-app/index.html +++ b/packages/image-io/typescript/test/browser/demo-app/index.html @@ -58,6 +58,7 @@

👨‍💻 Live API Demo ✨

readImage + writeImage bioRadReadImage @@ -156,6 +157,37 @@

👨‍💻 Live API Demo ✨

+ + + + + Write an itk-wasm Image to an image file format

+ +
+ + +

+ informationOnly - Only write image metadata -- do not write pixel data. +

+ useCompression - Use compression in the written file +

+ + +
Load sample inputs + Run

+ +
+ + +
+ + Download +

+
+ +
+ + @@ -219,7 +251,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -303,7 +335,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -335,7 +367,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.


Load sample inputs @@ -387,7 +419,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -471,7 +503,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -555,7 +587,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -639,7 +671,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -723,7 +755,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -807,7 +839,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -975,7 +1007,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1059,7 +1091,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1143,7 +1175,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1227,7 +1259,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1311,7 +1343,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1343,7 +1375,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.


Load sample inputs @@ -1395,7 +1427,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1479,7 +1511,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1563,7 +1595,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1647,7 +1679,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1731,7 +1763,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1815,7 +1847,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1899,7 +1931,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -1931,7 +1963,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only wire image metadata -- do not read pixel data.


Load sample inputs @@ -1983,7 +2015,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

@@ -2067,7 +2099,7 @@

👨‍💻 Live API Demo ✨



- informationOnly - Only read image metadata -- do not read pixel data. + informationOnly - Only write image metadata -- do not write pixel data.

useCompression - Use compression in the written file

diff --git a/packages/image-io/typescript/test/browser/demo-app/index.ts b/packages/image-io/typescript/test/browser/demo-app/index.ts index 58f394554..77eac8ee6 100644 --- a/packages/image-io/typescript/test/browser/demo-app/index.ts +++ b/packages/image-io/typescript/test/browser/demo-app/index.ts @@ -19,6 +19,7 @@ if (!params.has('functionName')) { } import './read-image-controller.js' +import './write-image-controller.js' // End added content import './bio-rad-read-image-controller.js' import './bio-rad-write-image-controller.js' diff --git a/packages/image-io/typescript/test/browser/demo-app/write-image-controller.ts b/packages/image-io/typescript/test/browser/demo-app/write-image-controller.ts new file mode 100644 index 000000000..c66cfb92f --- /dev/null +++ b/packages/image-io/typescript/test/browser/demo-app/write-image-controller.ts @@ -0,0 +1,158 @@ +import { readImageFile } from 'itk-wasm' +import { copyImage } from 'itk-wasm' +import * as imageIo from '../../../dist/bundles/image-io.js' +import writeImageLoadSampleInputs, { usePreRun } from "./write-image-load-sample-inputs.js" + +class WriteImageModel { + inputs: Map + options: Map + outputs: Map + + constructor() { + this.inputs = new Map() + this.options = new Map() + this.outputs = new Map() + } +} + + +class WriteImageController { + + constructor(loadSampleInputs) { + this.loadSampleInputs = loadSampleInputs + + this.model = new WriteImageModel() + const model = this.model + + this.webWorker = null + + if (loadSampleInputs) { + const loadSampleInputsButton = document.querySelector("#writeImageInputs [name=loadSampleInputs]") + loadSampleInputsButton.setAttribute('style', 'display: block-inline;') + loadSampleInputsButton.addEventListener('click', async (event) => { + loadSampleInputsButton.loading = true + await loadSampleInputs(model) + loadSampleInputsButton.loading = false + }) + } + + // ---------------------------------------------- + // Inputs + const imageElement = document.querySelector('#writeImageInputs input[name=image-file]') + imageElement.addEventListener('change', async (event) => { + const dataTransfer = event.dataTransfer + const files = event.target.files || dataTransfer.files + + const { image, webWorker } = await readImageFile(null, files[0]) + webWorker.terminate() + model.inputs.set("image", image) + const details = document.getElementById("writeImage-image-details") + details.innerHTML = `
${globalThis.escapeHtml(JSON.stringify(image, globalThis.interfaceTypeJsonReplacer, 2))}
` + details.disabled = false + }) + + const serializedImageElement = document.querySelector('#writeImageInputs sl-input[name=serialized-image]') + serializedImageElement.addEventListener('sl-change', (event) => { + model.inputs.set("serializedImage", serializedImageElement.value) + }) + + // ---------------------------------------------- + // Options + const informationOnlyElement = document.querySelector('#writeImageInputs sl-checkbox[name=information-only]') + informationOnlyElement.addEventListener('sl-change', (event) => { + model.options.set("informationOnly", informationOnlyElement.checked) + }) + + const useCompressionElement = document.querySelector('#writeImageInputs sl-checkbox[name=use-compression]') + useCompressionElement.addEventListener('sl-change', (event) => { + model.options.set("useCompression", useCompressionElement.checked) + }) + + // ---------------------------------------------- + // Outputs + const serializedImageOutputDownload = document.querySelector('#writeImageOutputs sl-button[name=serialized-image-download]') + serializedImageOutputDownload.addEventListener('click', (event) => { + event.preventDefault() + event.stopPropagation() + if (model.outputs.has("serializedImage")) { + globalThis.downloadFile(model.outputs.get("serializedImage").data, model.outputs.get("serializedImage").path) + } + }) + + const preRun = async () => { + if (!this.webWorker && loadSampleInputs && usePreRun) { + await loadSampleInputs(model, true) + await this.run() + } + } + + const onSelectTab = async (event) => { + if (event.detail.name === 'writeImage-panel') { + const params = new URLSearchParams(window.location.search) + if (!params.has('functionName') || params.get('functionName') !== 'writeImage') { + params.set('functionName', 'writeImage') + const url = new URL(document.location) + url.search = params + window.history.replaceState({ functionName: 'writeImage' }, '', url) + await preRun() + } + } + } + + const tabGroup = document.querySelector('sl-tab-group') + tabGroup.addEventListener('sl-tab-show', onSelectTab) + function onInit() { + const params = new URLSearchParams(window.location.search) + if (params.has('functionName') && params.get('functionName') === 'writeImage') { + tabGroup.show('writeImage-panel') + preRun() + } + } + onInit() + + const runButton = document.querySelector('#writeImageInputs sl-button[name="run"]') + runButton.addEventListener('click', async (event) => { + event.preventDefault() + + if(!model.inputs.has('image')) { + globalThis.notify("Required input not provided", "image", "danger", "exclamation-octagon") + return + } + + + try { + runButton.loading = true + + const t0 = performance.now() + const { serializedImage, } = await this.run() + const t1 = performance.now() + globalThis.notify("writeImage successfully completed", `in ${t1 - t0} milliseconds.`, "success", "rocket-fill") + + model.outputs.set("serializedImage", serializedImage) + serializedImageOutputDownload.variant = "success" + serializedImageOutputDownload.disabled = false + const serializedImageOutput = document.getElementById("writeImage-serialized-image-details") + serializedImageOutput.innerHTML = `
${globalThis.escapeHtml(serializedImage.data.subarray(0, 1024).toString() + ' ...')}
` + serializedImageOutput.disabled = false + } catch (error) { + globalThis.notify("Error while running pipeline", error.toString(), "danger", "exclamation-octagon") + throw error + } finally { + runButton.loading = false + } + }) + } + + async run() { + const { webWorker, serializedImage, } = await imageIo.writeImage(this.webWorker, + copyImage(this.model.inputs.get('image')), + this.model.inputs.get('serializedImage'), + Object.fromEntries(this.model.options.entries()) + ) + this.webWorker = webWorker + + return { serializedImage, } + } +} + +const writeImageController = new WriteImageController(writeImageLoadSampleInputs) diff --git a/packages/image-io/typescript/test/browser/demo-app/write-image-load-sample-inputs.ts b/packages/image-io/typescript/test/browser/demo-app/write-image-load-sample-inputs.ts new file mode 100644 index 000000000..d6ba20cfc --- /dev/null +++ b/packages/image-io/typescript/test/browser/demo-app/write-image-load-sample-inputs.ts @@ -0,0 +1,28 @@ +// Generated file. To retain edits, remove this comment. + +export default null +// export default async function bioRadWriteImageLoadSampleInputs (model, preRun=false) { + + // Load sample inputs for the bioRadWriteImage function. + // + // This function should load sample inputs: + // + // 1) In the provided model map. + // 2) Into the corresponding HTML input elements if preRun is not true. + // + // Example for an input named `exampleInput`: + + // const exampleInput = 5 + // model.inputs.set("exampleInput", exampleInput) + // if (!preRun) { + // const exampleElement = document.querySelector("#bioRadWriteImageInputs [name=example-input]") + // exampleElement.value = 5 + // } + + // return model +// } + +// Use this function to run the pipeline when this tab group is select. +// This will load the web worker if it is not already loaded, download the wasm module, and allocate memory in the wasm model. +// Set this to `false` if sample inputs are very large or sample pipeline computation is long. +export const usePreRun = true diff --git a/packages/image-io/write-image.cxx b/packages/image-io/write-image.cxx index 050ab3392..2de92abe7 100644 --- a/packages/image-io/write-image.cxx +++ b/packages/image-io/write-image.cxx @@ -156,10 +156,10 @@ int main (int argc, char * argv[]) pipeline.add_option("serialized-image", outputFileName, "Output image serialized in the file format.")->required()->type_name("OUTPUT_BINARY_FILE"); bool informationOnly = false; - pipeline.add_flag("-i,--information-only", informationOnly, "Only read image metadata -- do not read pixel data."); + pipeline.add_flag("-i,--information-only", informationOnly, "Only write image metadata -- do not write pixel data."); bool useCompression = false; - pipeline.add_flag("-c,--use-compression", informationOnly, "Use compression in the written file"); + pipeline.add_flag("-c,--use-compression", useCompression, "Use compression in the written file"); ITK_WASM_PARSE(pipeline); diff --git a/src/bindgen/typescript/demo/interface-functions-demo-typescript.js b/src/bindgen/typescript/demo/interface-functions-demo-typescript.js index bfb2f797e..bf4d4624e 100644 --- a/src/bindgen/typescript/demo/interface-functions-demo-typescript.js +++ b/src/bindgen/typescript/demo/interface-functions-demo-typescript.js @@ -76,7 +76,6 @@ export const usePreRun = true result += ` class ${functionNamePascalCase}Model { - inputs: Map options: Map outputs: Map @@ -86,13 +85,13 @@ class ${functionNamePascalCase}Model { this.options = new Map() this.outputs = new Map() } - } +} ` const controllerClassName = camelCase(`${functionNamePascalCase}Controller`) - result += `class ${controllerClassName} { + result += `class ${controllerClassName} { constructor(loadSampleInputs) { this.loadSampleInputs = loadSampleInputs