Skip to content

Commit e94b15c

Browse files
Latent viewer now handles multiple latent images + fp16 latents.
1 parent 714409f commit e94b15c

File tree

1 file changed

+63
-20
lines changed

1 file changed

+63
-20
lines changed

latent_preview/index.html

+63-20
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,25 @@ <h3>Drag a .latent file on this page or select it with the input below to previe
1414
<br>
1515
<input type="file" accept=".latent" onchange="previewLatent(this.files[0]);"></input>
1616
<br>
17-
<img id="display_image" style="width: 100%;image-rendering: pixelated;"></img>
17+
<div id="display_image"></div>
1818
This page decodes the file entirely in the browser in only a few lines of javascript and calculates a low quality preview from the latent image data using a simple matrix multiplication.
1919
<br>
2020

2121
<script>
2222
// This code is released under the 0BSD license which is a public-domain-equivalent license so feel free to do whatever you want with it.
23+
function getFloat16(uint16) {
24+
var sign = 1 - (2 * (uint16 >> 15));
25+
var exponent = ((uint16 >> 10) & 0x1f) - 15;
26+
var fraction = uint16 & 0x3ff;
27+
28+
//Doesn't handle inf and nan but I don't think we care about those values
29+
if (exponent === -15) {
30+
return sign * fraction * Math.pow(2, -24);
31+
}
32+
33+
return sign * (1 + fraction / 1024.0) * Math.pow(2, exponent);
34+
}
35+
2336
function getLatentImage(file) {
2437
return new Promise((r) => {
2538
const reader = new FileReader();
@@ -33,37 +46,67 @@ <h3>Drag a .latent file on this page or select it with the input below to previe
3346
let start_offset = header.latent_tensor.data_offsets[0] + header_size + 8;
3447
let end_offset = header.latent_tensor.data_offsets[1] + header_size + 8;
3548
let dtype = header.latent_tensor.dtype; //F32 or F16?
49+
let batch = header.latent_tensor.shape[0];
3650
let height = header.latent_tensor.shape[2];
3751
let width = header.latent_tensor.shape[3];
3852

39-
var canvas = document.createElement('canvas');
40-
canvas.height = height;
41-
canvas.width = width;
42-
var ctxEdited = canvas.getContext('2d');
43-
const arr = new Uint8ClampedArray(height * width * 4);
44-
45-
for (let i = 0; i < height * width; ++i) {
46-
let v1 = dataView.getFloat32(start_offset + 4*i, true);
47-
let v2 = dataView.getFloat32(start_offset + 4*i + 4*height*width, true);
48-
let v3 = dataView.getFloat32(start_offset + 4*i + 4*height*width*2, true);
49-
let v4 = dataView.getFloat32(start_offset + 4*i + 4*height*width*3, true);
50-
arr[i* 4 ] = (0.298 * v1 + 0.187 * v2 - 0.158 * v3 - 0.184 *v4 + 1.0) * 127.5;
51-
arr[i* 4 + 1] = (0.207 * v1 + 0.286 * v2 + 0.189 * v3 - 0.271 *v4 + 1.0) * 127.5;
52-
arr[i* 4 + 2] = (0.208 * v1 + 0.173 * v2 + 0.264 * v3 - 0.473 *v4 + 1.0) * 127.5;
53-
arr[i* 4 + 3] = 255;
53+
54+
let data_func, data_size;
55+
if (dtype === "F32") {
56+
data_func = function(dataView, offset) {return dataView.getFloat32(offset, true);}
57+
data_size = 4;
58+
} else if (dtype === "F16") {
59+
data_func = function(dataView, offset) {return getFloat16(dataView.getUint16(offset, true));}
60+
data_size = 2;
5461
}
55-
let imageData = new ImageData(arr, width);
56-
ctxEdited.putImageData(imageData, 0, 0);
5762

58-
r(canvas.toDataURL());
63+
// Convert batch of latent images into list of DataURL images
64+
let output = [];
65+
for (let b = 0; b < batch; ++b) {
66+
const arr = new Uint8ClampedArray(height * width * 4);
67+
let batch_offset = start_offset + data_size*height*width*4*b;
68+
69+
for (let i = 0; i < height * width; ++i) {
70+
let v1 = data_func(dataView, batch_offset + data_size*i, true);
71+
let v2 = data_func(dataView, batch_offset + data_size*i + data_size*height*width, true);
72+
let v3 = data_func(dataView, batch_offset + data_size*i + data_size*height*width*2, true);
73+
let v4 = data_func(dataView, batch_offset + data_size*i + data_size*height*width*3, true);
74+
arr[i* 4 ] = (0.298 * v1 + 0.187 * v2 - 0.158 * v3 - 0.184 *v4 + 1.0) * 127.5;
75+
arr[i* 4 + 1] = (0.207 * v1 + 0.286 * v2 + 0.189 * v3 - 0.271 *v4 + 1.0) * 127.5;
76+
arr[i* 4 + 2] = (0.208 * v1 + 0.173 * v2 + 0.264 * v3 - 0.473 *v4 + 1.0) * 127.5;
77+
arr[i* 4 + 3] = 255;
78+
}
79+
80+
// Convert to DataURL image
81+
let imageData = new ImageData(arr, width);
82+
let canvas = document.createElement('canvas');
83+
canvas.height = height;
84+
canvas.width = width;
85+
let ctxEdited = canvas.getContext('2d');
86+
ctxEdited.putImageData(imageData, 0, 0);
87+
output.push({data: canvas.toDataURL(), width:width, height:height});
88+
}
89+
r(output);
5990
};
6091

6192
reader.readAsArrayBuffer(file);
6293
});
6394
}
6495

6596
function previewLatent(file) {
66-
getLatentImage(file).then((image) => document.getElementById('display_image').src = image);
97+
getLatentImage(file).then((images) => {
98+
let displayer = document.getElementById('display_image');
99+
displayer.innerHTML = '';
100+
for (let i = 0; i < images.length; ++i) {
101+
let image_element = document.createElement('img');
102+
image_element.style.width = "100%";
103+
image_element.style.maxWidth = images[i].width * 8 + "px";
104+
image_element.style.imageRendering = "pixelated";
105+
image_element.style.margin = "8px";
106+
image_element.src = images[i].data;
107+
displayer.appendChild(image_element);
108+
}
109+
});
67110
}
68111

69112
document.addEventListener("drop", async (event) => {

0 commit comments

Comments
 (0)