Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement decodeStack #422

Merged
merged 4 commits into from
Dec 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/load/decodeTiff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,15 +14,15 @@ type TiffIfd = ReturnType<typeof decode>[number];
export function decodeTiff(buffer: Uint8Array): Image {
const result = decode(buffer);
return getImageFromIFD(result[0]);
// TODO: handle stacks (many IFDs)
// TODO: optimize not to decode whole file
}

/**
* Create image from a single IFD.
* @param ifd - The IFD.
* @returns The decoded image.
*/
function getImageFromIFD(ifd: TiffIfd): Image {
export function getImageFromIFD(ifd: TiffIfd): Image {
if (ifd.type === 3) {
// Palette
const data = new Uint16Array(3 * ifd.width * ifd.height);
Expand Down
42 changes: 42 additions & 0 deletions src/stack/load/__tests__/decodeStack.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { TestImagePath } from '../../../../test/TestImagePath';
import { decodeStack } from '../decodeStack';

test.each([
{
name: 'formats/tif/grey8-multi.tif',
colorModel: 'GREY',
bitDepth: 8,
pages: 2,
},
{
name: 'formats/tif/grey16-multi.tif',
colorModel: 'GREY',
bitDepth: 16,
pages: 2,
},
{
name: 'formats/tif/color8-multi.tif',
colorModel: 'RGB',
bitDepth: 8,
pages: 2,
},
{
name: 'formats/tif/color16-multi.tif',
colorModel: 'RGB',
bitDepth: 16,
pages: 2,
},
])('stacks with 2 images ($colorModel, bitDepth = $bitDepth)', (data) => {
const buffer = testUtils.loadBuffer(data.name as TestImagePath);
const stack = decodeStack(buffer);
expect(stack.size).toBe(data.pages);
for (const image of stack) {
expect(image.colorModel).toBe(data.colorModel);
expect(image.bitDepth).toBe(data.bitDepth);
}
});

test('invalid data format', () => {
const buffer = testUtils.loadBuffer('formats/grey8.png');
expect(() => decodeStack(buffer)).toThrow('invalid data format: image/png');
});
26 changes: 26 additions & 0 deletions src/stack/load/decodeStack.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import imageType from 'image-type';

import { Stack } from '../../Stack';

import { decodeStackFromTiff } from './decodeTiff';

/**
* Decode input data and create stack. Data format is automatically detected.
* Possible formats: tiff.
* @param data - Data to decode.
* @returns The decoded image.
*/
export function decodeStack(data: ArrayBufferView): Stack {
const typedArray = new Uint8Array(
data.buffer,
data.byteOffset,
data.byteLength,
);
const type = imageType(typedArray);
switch (type?.mime) {
case 'image/tiff':
return decodeStackFromTiff(typedArray);
default:
throw new RangeError(`invalid data format: ${type?.mime}`);
}
}
19 changes: 19 additions & 0 deletions src/stack/load/decodeTiff.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { decode } from 'tiff';

import { Stack } from '../../Stack';
import { getImageFromIFD } from '../../load/decodeTiff';

/**
* Decode a TIFF and create a stack of images.
* @param buffer - The data to decode.
* @returns The stack of images.
*/
export function decodeStackFromTiff(buffer: Uint8Array): Stack {
const decoded = decode(buffer);
const images = [];
for (const IFD of decoded) {
images.push(getImageFromIFD(IFD));
}

return new Stack(images);
}
4 changes: 4 additions & 0 deletions test/TestImagePath.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ export type TestImagePath =
| 'formats/tif/greya32.tif'
| 'formats/tif/rgb16.tif'
| 'formats/tif/rgba8.tif'
| 'formats/tif/grey8-multi.tif'
| 'formats/tif/grey16-multi.tif'
| 'formats/tif/rgb16-multi.tif'
| 'formats/tif/rgba8-multi.tif'
| 'formats/tif/palette.tif'
| 'opencv/test.png'
| 'opencv/testBlur.png'
Expand Down
Binary file added test/img/formats/tif/color16-multi.tif
Binary file not shown.
Binary file added test/img/formats/tif/color8-multi.tif
Binary file not shown.
Binary file added test/img/formats/tif/grey16-multi.tif
Binary file not shown.
Binary file added test/img/formats/tif/grey8-multi.tif
Binary file not shown.
Loading