Skip to content

Commit

Permalink
feat: add option to ignore color palette in TIFF images (#589)
Browse files Browse the repository at this point in the history
Add option flag to ignore the color table in type 3 TIF images that have
indexed colors, returning index values as single channel, greyscale
image instead of pseudo-colored, 3 channel RGB.
  • Loading branch information
gnodar01 committed Apr 11, 2022
1 parent 0060679 commit 07aa7d5
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 25 deletions.
13 changes: 8 additions & 5 deletions index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export declare class Image {
static createFrom(other: Image, options: ImageConstructorOptions): Image;
static load(
image: string | ArrayBuffer | Uint8Array,
options?: RequestInit,
options?: RequestInit & { ignorePalette: boolean },
): Promise<Image>;

getRoiManager(): RoiManager;
Expand Down Expand Up @@ -149,10 +149,13 @@ export declare class Image {
// paintPoints
// paintPolyline
// paintPolylines
paintPolygon(points: Array<Array<number>>, options?: {
color?: Array<number>;
filled?: boolean;
}): Image;
paintPolygon(
points: Array<Array<number>>,
options?: {
color?: Array<number>;
filled?: boolean;
},
): Image;

// paintPolygons

Expand Down
18 changes: 11 additions & 7 deletions src/image/core/__tests__/loadTIFF.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,19 @@ import { load } from 'test/common';

describe('Load TIFF', () => {
const tests = [
// ['name', components, alpha, bitDepth]
['grey8', 1, 0, 8],
['grey16', 1, 0, 16],
['palette', 3, 0, 16],
['grey32', 1, 0, 32],
// ['name', components, alpha, bitDepth, options]
['grey8', 1, 0, 8, undefined],
['grey16', 1, 0, 16, undefined],
['palette', 3, 0, 16, undefined],
['palette', 1, 0, 8, { ignorePalette: true }],
['grey32', 1, 0, 32, undefined],
];

it.each(tests)('%s', async (name, components, alpha, bitDepth) => {
const img = await load(`format/tif/${name}.tif`);
it.each(tests)('%s', async (name, components, alpha, bitDepth, options) => {
const img =
options && options.ignorePalette
? await load(`format/tif/${name}.tif`, options)
: await load(`format/tif/${name}.tif`);
expect(img.components).toBe(components);
expect(img.alpha).toBe(alpha);
expect(img.bitDepth).toBe(bitDepth);
Expand Down
41 changes: 30 additions & 11 deletions src/image/core/load.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ const isDataURL = /^data:[a-z]+\/(?:[a-z]+);base64,/;
* @static
* @param {string|ArrayBuffer|Buffer|Uint8Array} image - URL of the image (browser, can be a dataURL) or path (Node.js)
* or buffer containing the binary data
* @param {object} [options] - In the browser, the options object is passed to the underlying `fetch` call.
* @param {object} [options] - In the browser, the options object is passed to the underlying `fetch` call, along with
* the data URL. For binary data, the option specify decoding options.
* @param {boolean} [options.ignorePalette] - When set to true and loading a tiff from binary data, if the tiff is of
* type 3 (palette), load as single channel greyscale rather than as a psuedo-colored RGB.
* @return {Promise<Image>}
* @example
* const image = await Image.load('https://example.com/image.png');
Expand All @@ -28,15 +31,23 @@ export default function load(image, options) {
if (typeof image === 'string') {
return loadURL(image, options);
} else if (image instanceof ArrayBuffer) {
return Promise.resolve(loadBinary(new Uint8Array(image)));
return Promise.resolve(
loadBinary(
new Uint8Array(image),
undefined,
options && options.ignorePalette,
),
);
} else if (image.buffer) {
return Promise.resolve(loadBinary(image));
return Promise.resolve(
loadBinary(image, undefined, options && options.ignorePalette),
);
} else {
throw new Error('argument to "load" must be a string or buffer.');
}
}

function loadBinary(image, base64Url) {
function loadBinary(image, base64Url, ignorePalette) {
const type = imageType(image);
if (type) {
switch (type.mime) {
Expand All @@ -45,7 +56,7 @@ function loadBinary(image, base64Url) {
case 'image/jpeg':
return loadJPEG(image);
case 'image/tiff':
return loadTIFF(image);
return loadTIFF(image, ignorePalette);
default:
return loadGeneric(getBase64(type.mime));
}
Expand All @@ -70,7 +81,11 @@ function loadURL(url, options) {
}
return binaryDataP.then((binaryData) => {
const uint8 = new Uint8Array(binaryData);
return loadBinary(uint8, dataURL ? url : undefined);
return loadBinary(
uint8,
dataURL ? url : undefined,
options && options.ignorePalette,
);
});
}

Expand Down Expand Up @@ -159,12 +174,16 @@ function loadJPEG(data) {
return image;
}

function loadTIFF(data) {
function loadTIFF(data, ignorePalette) {
let result = decodeTiff(data);
if (result.length === 1) {
return getImageFromIFD(result[0]);
return getImageFromIFD(result[0], ignorePalette);
} else {
return new Stack(result.map(getImageFromIFD));
return new Stack(
result.map(function (image) {
return getImageFromIFD(image, ignorePalette);
}),
);
}
}

Expand All @@ -184,8 +203,8 @@ function getMetadata(image) {
return metadata;
}

function getImageFromIFD(image) {
if (image.type === 3) {
function getImageFromIFD(image, ignorePalette) {
if (!ignorePalette && image.type === 3) {
// Palette
const data = new Uint16Array(3 * image.width * image.height);
const palette = image.palette;
Expand Down
4 changes: 2 additions & 2 deletions test/common.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ export { getImage, getHash };

export { Image, Stack, IJS };

export function load(name) {
return Image.load(getImage(name));
export function load(name, options) {
return Image.load(getImage(name), options);
}

export function getSquare() {
Expand Down

0 comments on commit 07aa7d5

Please sign in to comment.