Skip to content

Commit 1beaf15

Browse files
committed
Clean up color types and switch to large default color palette
1 parent 4f11e6e commit 1beaf15

File tree

3 files changed

+25
-26
lines changed

3 files changed

+25
-26
lines changed

src/layers/label-layer.ts

+17-14
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,18 @@ import type { ZarrPixelSource } from "../ZarrPixelSource";
88

99
type Texture = ReturnType<BitmapLayer["context"]["device"]["createTexture"]>;
1010

11-
export type OmeColors = Readonly<Record<number, readonly [number, number, number, number]>>;
11+
export type OmeColor = Readonly<{
12+
labelValue: number;
13+
rgba: readonly [r: number, g: number, b: number, a: number];
14+
}>;
15+
1216
export interface LabelLayerProps {
1317
id: string;
1418
loader: Array<ZarrPixelSource>;
1519
selection: Array<number>;
1620
opacity: number;
1721
modelMatrix: Matrix4;
18-
colors?: OmeColors;
22+
colors?: ReadonlyArray<OmeColor>;
1923
}
2024

2125
/**
@@ -237,7 +241,7 @@ function getTileSizeForResolutions(resolutions: Array<ZarrPixelSource>): number
237241
return tileSize;
238242
}
239243

240-
const SEEN_LUTS = new WeakSet<OmeColors>();
244+
const SEEN_LUTS = new WeakSet<ReadonlyArray<OmeColor>>();
241245

242246
/**
243247
* Creates a color lookup table (LUT) as a 2D texture.
@@ -246,7 +250,7 @@ const SEEN_LUTS = new WeakSet<OmeColors>();
246250
* @param options.maxTextureDimension2D - The maximum texture dimension size.
247251
*/
248252
function createColorTexture(options: {
249-
source?: OmeColors;
253+
source?: ReadonlyArray<OmeColor>;
250254
maxTextureDimension2D: number;
251255
}) {
252256
const { source, maxTextureDimension2D } = options;
@@ -261,7 +265,7 @@ function createColorTexture(options: {
261265
}
262266

263267
// pack the colors into a 2D texture
264-
const size = Math.max(...Object.keys(source).map(Number)) + 1;
268+
const size = Math.max(...source.map((e) => e.labelValue)) + 1;
265269
const width = Math.min(size, maxTextureDimension2D);
266270
const height = Math.ceil(size / width);
267271

@@ -274,15 +278,14 @@ function createColorTexture(options: {
274278
}
275279

276280
const data = new Uint8Array(width * height * 4);
277-
for (const [key, [r, g, b, a]] of Object.entries(source)) {
278-
const index = +key;
279-
const x = index % width;
280-
const y = Math.floor(index / width);
281+
for (const { labelValue, rgba } of source) {
282+
const x = labelValue % width;
283+
const y = Math.floor(labelValue / width);
281284
const texIndex = (y * width + x) * 4;
282-
data[texIndex] = r;
283-
data[texIndex + 1] = g;
284-
data[texIndex + 2] = b;
285-
data[texIndex + 3] = a;
285+
data[texIndex] = rgba[0];
286+
data[texIndex + 1] = rgba[1];
287+
data[texIndex + 2] = rgba[2];
288+
data[texIndex + 3] = rgba[3];
286289
}
287290

288291
return { data, width, height };
@@ -340,7 +343,7 @@ const COLOR_PALETTES = {
340343
],
341344
} as const;
342345

343-
const DEFAULT_COLOR_TEXTURE = Uint8Array.from(COLOR_PALETTES.pathology.flatMap((color) => [...color, 255]));
346+
const DEFAULT_COLOR_TEXTURE = Uint8Array.from(COLOR_PALETTES.pathologyLarge.flatMap((color) => [...color, 255]));
344347

345348
function typedArrayConstructorName(arr: zarr.TypedArray<LabelDataType>) {
346349
const ArrayType = arr.constructor as zarr.TypedArrayConstructor<LabelDataType>;

src/ome.ts

+6-10
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import * as zarr from "zarrita";
33
import type { ImageLabels, ImageLayerConfig, OnClickData, SourceData } from "./state";
44

55
import { ZarrPixelSource } from "./ZarrPixelSource";
6-
import type { OmeColors } from "./layers/label-layer";
6+
import type { OmeColor } from "./layers/label-layer";
77
import * as utils from "./utils";
88

99
export async function loadWell(
@@ -278,14 +278,17 @@ async function loadOmeImageLabel(root: zarr.Location<zarr.Readable>, name: strin
278278
const attrs = utils.resolveAttrs(grp.attrs);
279279
utils.assert(utils.isOmeImageLabel(attrs), "No 'image-label' metadata.");
280280
const data = await utils.loadMultiscales(grp, attrs.multiscales);
281-
const tileSize = utils.guessTileSize(data[0]);
281+
const baseResolution = data.at(0);
282+
utils.assert(baseResolution, "No base resolution found for multiscale labels.");
283+
const tileSize = utils.guessTileSize(baseResolution);
282284
const axes = utils.getNgffAxes(attrs.multiscales);
283285
const labels = utils.getNgffAxisLabels(axes);
286+
const colors = (attrs["image-label"].colors ?? []).map((d) => ({ labelValue: d["label-value"], rgba: d.rgba }));
284287
return {
285288
name,
286289
modelMatrix: utils.coordinateTransformationsToMatrix(attrs.multiscales),
287290
loader: data.map((arr) => new ZarrPixelSource(arr, { labels, tileSize })),
288-
colors: resolveImageLabelsLut(attrs["image-label"]),
291+
colors: colors.length > 0 ? colors : undefined,
289292
};
290293
}
291294

@@ -359,10 +362,3 @@ function parseOmeroMeta({ rdefs, channels, name }: Ome.Omero, axes: Ome.Axis[]):
359362
defaultSelection,
360363
};
361364
}
362-
363-
function resolveImageLabelsLut(attrs: Ome.ImageLabel): OmeColors | undefined {
364-
if (!attrs.colors || attrs.colors.length === 0) {
365-
return undefined;
366-
}
367-
return Object.fromEntries(attrs.colors.map((d) => [d["label-value"], d.rgba]));
368-
}

src/state.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import type { ZarrPixelSource } from "./ZarrPixelSource";
1111
import { initLayerStateFromSource } from "./io";
1212

1313
import { GridLayer, type GridLayerProps, type GridLoader } from "./layers/grid-layer";
14-
import { LabelLayer, type LabelLayerProps, type OmeColors } from "./layers/label-layer";
14+
import { LabelLayer, type LabelLayerProps, type OmeColor } from "./layers/label-layer";
1515
import {
1616
ImageLayer,
1717
type ImageLayerProps,
@@ -59,7 +59,7 @@ export type ImageLabels = Array<{
5959
name: string;
6060
loader: ZarrPixelSource[];
6161
modelMatrix: Matrix4;
62-
colors?: OmeColors;
62+
colors?: ReadonlyArray<OmeColor>;
6363
}>;
6464

6565
export type SourceData = {

0 commit comments

Comments
 (0)