Spaces:
Running on CPU Upgrade
Running on CPU Upgrade
File size: 8,710 Bytes
597a8a6 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 | // Matplotlib "viridis" colormap as 256 RGB triples (perceptually uniform).
// Generated from matplotlib.cm.get_cmap("viridis", 256). Shared by the WebGL
// LUT texture and the 2D-canvas fallback in ColormappedVideo, and used to
// recolor single-channel (grayscale) camera feeds.
// prettier-ignore
export const VIRIDIS_LUT = new Uint8Array([
68, 1, 84, 68, 2, 86, 69, 4, 87, 69, 5, 89, 70, 7, 90, 70, 8, 92, 70, 10, 93, 70, 11, 94,
71, 13, 96, 71, 14, 97, 71, 16, 99, 71, 17, 100, 71, 19, 101, 72, 20, 103, 72, 22, 104, 72, 23, 105,
72, 24, 106, 72, 26, 108, 72, 27, 109, 72, 28, 110, 72, 29, 111, 72, 31, 112, 72, 32, 113, 72, 33, 115,
72, 35, 116, 72, 36, 117, 72, 37, 118, 72, 38, 119, 72, 40, 120, 72, 41, 121, 71, 42, 122, 71, 44, 122,
71, 45, 123, 71, 46, 124, 71, 47, 125, 70, 48, 126, 70, 50, 126, 70, 51, 127, 70, 52, 128, 69, 53, 129,
69, 55, 129, 69, 56, 130, 68, 57, 131, 68, 58, 131, 68, 59, 132, 67, 61, 132, 67, 62, 133, 66, 63, 133,
66, 64, 134, 66, 65, 134, 65, 66, 135, 65, 68, 135, 64, 69, 136, 64, 70, 136, 63, 71, 136, 63, 72, 137,
62, 73, 137, 62, 74, 137, 62, 76, 138, 61, 77, 138, 61, 78, 138, 60, 79, 138, 60, 80, 139, 59, 81, 139,
59, 82, 139, 58, 83, 139, 58, 84, 140, 57, 85, 140, 57, 86, 140, 56, 88, 140, 56, 89, 140, 55, 90, 140,
55, 91, 141, 54, 92, 141, 54, 93, 141, 53, 94, 141, 53, 95, 141, 52, 96, 141, 52, 97, 141, 51, 98, 141,
51, 99, 141, 50, 100, 142, 50, 101, 142, 49, 102, 142, 49, 103, 142, 49, 104, 142, 48, 105, 142, 48, 106, 142,
47, 107, 142, 47, 108, 142, 46, 109, 142, 46, 110, 142, 46, 111, 142, 45, 112, 142, 45, 113, 142, 44, 113, 142,
44, 114, 142, 44, 115, 142, 43, 116, 142, 43, 117, 142, 42, 118, 142, 42, 119, 142, 42, 120, 142, 41, 121, 142,
41, 122, 142, 41, 123, 142, 40, 124, 142, 40, 125, 142, 39, 126, 142, 39, 127, 142, 39, 128, 142, 38, 129, 142,
38, 130, 142, 38, 130, 142, 37, 131, 142, 37, 132, 142, 37, 133, 142, 36, 134, 142, 36, 135, 142, 35, 136, 142,
35, 137, 142, 35, 138, 141, 34, 139, 141, 34, 140, 141, 34, 141, 141, 33, 142, 141, 33, 143, 141, 33, 144, 141,
33, 145, 140, 32, 146, 140, 32, 146, 140, 32, 147, 140, 31, 148, 140, 31, 149, 139, 31, 150, 139, 31, 151, 139,
31, 152, 139, 31, 153, 138, 31, 154, 138, 30, 155, 138, 30, 156, 137, 30, 157, 137, 31, 158, 137, 31, 159, 136,
31, 160, 136, 31, 161, 136, 31, 161, 135, 31, 162, 135, 32, 163, 134, 32, 164, 134, 33, 165, 133, 33, 166, 133,
34, 167, 133, 34, 168, 132, 35, 169, 131, 36, 170, 131, 37, 171, 130, 37, 172, 130, 38, 173, 129, 39, 173, 129,
40, 174, 128, 41, 175, 127, 42, 176, 127, 44, 177, 126, 45, 178, 125, 46, 179, 124, 47, 180, 124, 49, 181, 123,
50, 182, 122, 52, 182, 121, 53, 183, 121, 55, 184, 120, 56, 185, 119, 58, 186, 118, 59, 187, 117, 61, 188, 116,
63, 188, 115, 64, 189, 114, 66, 190, 113, 68, 191, 112, 70, 192, 111, 72, 193, 110, 74, 193, 109, 76, 194, 108,
78, 195, 107, 80, 196, 106, 82, 197, 105, 84, 197, 104, 86, 198, 103, 88, 199, 101, 90, 200, 100, 92, 200, 99,
94, 201, 98, 96, 202, 96, 99, 203, 95, 101, 203, 94, 103, 204, 92, 105, 205, 91, 108, 205, 90, 110, 206, 88,
112, 207, 87, 115, 208, 86, 117, 208, 84, 119, 209, 83, 122, 209, 81, 124, 210, 80, 127, 211, 78, 129, 211, 77,
132, 212, 75, 134, 213, 73, 137, 213, 72, 139, 214, 70, 142, 214, 69, 144, 215, 67, 147, 215, 65, 149, 216, 64,
152, 216, 62, 155, 217, 60, 157, 217, 59, 160, 218, 57, 162, 218, 55, 165, 219, 54, 168, 219, 52, 170, 220, 50,
173, 220, 48, 176, 221, 47, 178, 221, 45, 181, 222, 43, 184, 222, 41, 186, 222, 40, 189, 223, 38, 192, 223, 37,
194, 223, 35, 197, 224, 33, 200, 224, 32, 202, 225, 31, 205, 225, 29, 208, 225, 28, 210, 226, 27, 213, 226, 26,
216, 226, 25, 218, 227, 25, 221, 227, 24, 223, 227, 24, 226, 228, 24, 229, 228, 25, 231, 228, 25, 234, 229, 26,
236, 229, 27, 239, 229, 28, 241, 229, 29, 244, 230, 30, 246, 230, 32, 248, 230, 33, 251, 231, 35, 253, 231, 37,
]);
// Maps a normalized luminance t in [0, 1] to its viridis RGB triple. Inputs
// are clamped, so out-of-range values resolve to the colormap endpoints.
export function viridisColor(t: number): [number, number, number] {
const idx = Math.min(255, Math.max(0, Math.round(t * 255)));
const o = idx * 3;
return [VIRIDIS_LUT[o], VIRIDIS_LUT[o + 1], VIRIDIS_LUT[o + 2]];
}
// A video feature is grayscale when its shape's channel dimension
// ([height, width, channels]) is 1.
export function isGrayscaleShape(
shape: readonly number[] | undefined,
): boolean {
return shape?.[2] === 1;
}
// Unwraps lerobot's nested per-channel image-stat shape (e.g. [[[v]]] for a
// single-channel depth feature) down to its first scalar.
function firstScalar(value: unknown): number | undefined {
let v: unknown = value;
while (Array.isArray(v)) v = v[0];
return typeof v === "number" ? v : undefined;
}
// Log/linear depth-video encoding params from an info.json feature's `info`
// block (lerobot.datasets.depth_utils.quantize_depth). depthMin/Max/shift are
// in metres; unitToMetres scales the stored depth stats into metres.
export interface DepthEncoding {
depthMin: number;
depthMax: number;
shift: number;
useLog: boolean;
unitToMetres: number;
}
// Reads the depth-quantization params from an info.json feature. Returns
// undefined unless the feature is flagged `is_depth_map` and carries valid
// tuning params — callers then treat the feed as plain (already [0,1]) data.
export function depthEncodingFromFeature(
feature: unknown,
): DepthEncoding | undefined {
if (feature == null || typeof feature !== "object") return undefined;
const info = (feature as Record<string, unknown>).info;
if (info == null || typeof info !== "object") return undefined;
const i = info as Record<string, unknown>;
if (i.is_depth_map !== true) return undefined;
const depthMin = i["video.depth_min"];
const depthMax = i["video.depth_max"];
const shift = i["video.shift"];
const useLog = i["video.use_log"];
if (
typeof depthMin !== "number" ||
typeof depthMax !== "number" ||
typeof shift !== "number" ||
typeof useLog !== "boolean" ||
!(depthMax > depthMin) ||
(useLog && depthMin + shift <= 0)
) {
return undefined;
}
// depth_unit names the unit of the stored depth stats. Without it we don't
// rescale — the stats are used as-is. "mm" scales down to metres; "m" is a
// no-op.
if (i.depth_unit == null) {
console.warn(
'[depth] feature is missing `info["depth_unit"]` in info.json — using the stats as-is (no rescaling). Set depth_unit to "mm" or "m" to silence this.',
);
}
const unitToMetres = i.depth_unit === "mm" ? 1 / 1000 : 1;
return { depthMin, depthMax, shift, useLog, unitToMetres };
}
// Derives the [low, high] luminance band the colormap should span from a
// meta/stats.json feature entry's q01/q99 quantiles, stretching the feed so
// outliers don't wash out the colormap. Returns undefined when the quantiles
// are missing, non-finite, or don't form a valid increasing range — callers
// then fall back to 0..1.
//
// For a plain (already 0..1) grayscale feed the q01/q99 band is used directly.
// For a depth map, q01/q99 live in the feed's stored depth units, so they're
// mapped through the same forward quantization the video used: the browser's
// decoded luminance equals that normalized code, so the quantized q01/q99
// become the luminance window to stretch across the colormap. depth_min/max/
// shift are in metres, so the quantiles are first scaled to metres via the
// encoding's unitToMetres factor (derived from the feature's depth_unit).
export function depthColormapRange(
statsEntry: unknown,
encoding?: DepthEncoding,
): [number, number] | undefined {
if (statsEntry == null || typeof statsEntry !== "object") return undefined;
const entry = statsEntry as Record<string, unknown>;
const low = firstScalar(entry.q01) ?? firstScalar(entry.min);
const high = firstScalar(entry.q99) ?? firstScalar(entry.max);
if (low === undefined || high === undefined) return undefined;
if (!Number.isFinite(low) || !Number.isFinite(high)) return undefined;
if (high <= low) return undefined;
if (!encoding) return [low, high];
const { depthMin, depthMax, shift, useLog, unitToMetres } = encoding;
const normCode = (depth: number): number => {
const d = depth * unitToMetres;
const norm = useLog
? (Math.log(d + shift) - Math.log(depthMin + shift)) /
(Math.log(depthMax + shift) - Math.log(depthMin + shift))
: (d - depthMin) / (depthMax - depthMin);
return Math.min(1, Math.max(0, norm));
};
const normLow = normCode(low);
const normHigh = normCode(high);
if (normHigh <= normLow) return undefined;
return [normLow, normHigh];
}
|