Skip to content

Commit

Permalink
add grayscale image
Browse files Browse the repository at this point in the history
  • Loading branch information
BrianWhitneyAI committed Jun 13, 2024
1 parent 0172366 commit 5d9de04
Showing 1 changed file with 59 additions and 80 deletions.
139 changes: 59 additions & 80 deletions packages/core/entity/FileDetail/RenderZarrThumbnailURL.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,6 @@ import * as zarr from "@zarrita/core";
import { FetchStore } from "@zarrita/storage";
import { get as zarrGet } from "@zarrita/indexing";

// Function to map grayscale value (0-255) to RGB color
function grayscaleToRGB(value: number): [number, number, number] {
const r = value;
const g = 0 + value;
const b = 255 - value;
return [r, g, b];
}

interface AxisData {
name: string;
type: "time" | "channel" | "space";
Expand All @@ -24,17 +16,10 @@ interface TransformedAxes {

// Function to transform the array
function transformAxes(originalArray: AxisData[]): TransformedAxes[] {
return originalArray.map((item) => {
let value: number | null;
if (item.type !== "space") {
// set non spatial to first occurence
value = 0;
} else {
value = null;
}

return { name: item.name, value: value };
});
return originalArray.map((item) => ({
name: item.name,
value: item.type !== "space" ? 0 : null,
}));
}

export async function renderZarrThumbnailURL(zarrUrl: string): Promise<string> {
Expand All @@ -44,76 +29,70 @@ export async function renderZarrThumbnailURL(zarrUrl: string): Promise<string> {
const group = await zarr.open(root, { kind: "group" });

if (
group.attrs &&
Array.isArray(group.attrs.multiscales) &&
group.attrs.multiscales.length > 0
!group.attrs ||
!Array.isArray(group.attrs.multiscales) ||
group.attrs.multiscales.length === 0
) {
// Resolve file data
const { multiscales } = group.attrs;
const datasets = multiscales[0].datasets;
const lowestResolutionDataset = datasets[datasets.length - 1];
const lowestResolutionLocation = root.resolve(lowestResolutionDataset.path);
const lowestResolution = await zarr.open(lowestResolutionLocation, { kind: "array" }); // TODO: check the filesize before slicing

// Determine Slice
const axes = transformAxes(multiscales[0].axes);
if (axes.some((item) => item.name === "z")) {
const zIndex = axes.findIndex((item) => item.name === "z");
const zSliceIndex = Math.ceil(lowestResolution.shape[zIndex] / 2);
axes[zIndex].value = zSliceIndex;
}

console.log(axes);
const lowestResolutionView = await zarrGet(
lowestResolution,
axes.map((item) => item.value)
);

console.log("SHAPE", lowestResolutionView.shape);
throw new Error("Invalid multiscales attribute structure");
}

// Normalize Data
const u16data = lowestResolutionView.data as Uint16Array;
const min = Math.min(...u16data);
const max = Math.max(...u16data);
const normalizedData = new Uint8Array(u16data.length);
for (let i = 0; i < u16data.length; i++) {
normalizedData[i] = Math.round((255 * (u16data[i] - min)) / (max - min));
}
const { multiscales } = group.attrs;
const datasets = multiscales[0].datasets;
const lowestResolutionDataset = datasets[datasets.length - 1];
const lowestResolutionLocation = root.resolve(lowestResolutionDataset.path);
const lowestResolution = await zarr.open(lowestResolutionLocation, { kind: "array" });

// Determine Slice
const axes = transformAxes(multiscales[0].axes);
const zIndex = axes.findIndex((item) => item.name === "z");
if (zIndex !== -1) {
const zSliceIndex = Math.ceil(lowestResolution.shape[zIndex] / 2);
axes[zIndex].value = zSliceIndex;
}

// Draw Data
const width = lowestResolution.shape[axes.findIndex((item) => item.name === "x")];
const height = lowestResolution.shape[axes.findIndex((item) => item.name === "y")];
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext("2d");
const lowestResolutionView = await zarrGet(
lowestResolution,
axes.map((item) => item.value)
);
const u16data = lowestResolutionView.data as Uint16Array;

// Normalize Data
const min = Math.min(...u16data);
const max = Math.max(...u16data);
const normalizedData = new Uint8Array(u16data.length);
for (let i = 0; i < u16data.length; i++) {
normalizedData[i] = Math.round((255 * (u16data[i] - min)) / (max - min));
}

if (!context) {
throw new Error("Failed to get canvas context");
}
// Build Canvas
const width = lowestResolution.shape[axes.findIndex((item) => item.name === "x")];
const height = lowestResolution.shape[axes.findIndex((item) => item.name === "y")];
const canvas = document.createElement("canvas");
canvas.width = width;
canvas.height = height;
const context = canvas.getContext("2d");

const imageData = context.createImageData(width, height);
if (!context) {
throw new Error("Failed to get canvas context");
}

for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = (y * width + x) * 4;
const value = normalizedData[y * width + x];
const [r, g, b] = grayscaleToRGB(value);
imageData.data[idx] = r; // Red
imageData.data[idx + 1] = g; // Green
imageData.data[idx + 2] = b; // Blue
imageData.data[idx + 3] = 255; // Alpha
}
// Draw data
const imageData = context.createImageData(width, height);
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const idx = (y * width + x) * 4;
const value = normalizedData[y * width + x];
imageData.data[idx] = value; // Red
imageData.data[idx + 1] = value; // Green
imageData.data[idx + 2] = value; // Blue
imageData.data[idx + 3] = 255; // Alpha
}
}

context.putImageData(imageData, 0, 0);
context.putImageData(imageData, 0, 0);

// Convert data to data URL
const dataUrl = canvas.toDataURL("image/png");
return dataUrl;
} else {
throw new Error("Invalid multiscales attribute structure");
}
// Convert data to data URL
return canvas.toDataURL("image/png");
} catch (error) {
console.error("Error reading Zarr image:", error);
throw error;
Expand Down

0 comments on commit 5d9de04

Please sign in to comment.