@@ -14,12 +14,25 @@ <h3>Drag a .latent file on this page or select it with the input below to previe
14
14
< br >
15
15
< input type ="file " accept =".latent " onchange ="previewLatent(this.files[0]); "> </ input >
16
16
< br >
17
- < img id ="display_image " style =" width: 100%;image-rendering: pixelated; " > </ img >
17
+ < div id ="display_image "> </ div >
18
18
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.
19
19
< br >
20
20
21
21
< script >
22
22
// 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
+
23
36
function getLatentImage ( file ) {
24
37
return new Promise ( ( r ) => {
25
38
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
33
46
let start_offset = header . latent_tensor . data_offsets [ 0 ] + header_size + 8 ;
34
47
let end_offset = header . latent_tensor . data_offsets [ 1 ] + header_size + 8 ;
35
48
let dtype = header . latent_tensor . dtype ; //F32 or F16?
49
+ let batch = header . latent_tensor . shape [ 0 ] ;
36
50
let height = header . latent_tensor . shape [ 2 ] ;
37
51
let width = header . latent_tensor . shape [ 3 ] ;
38
52
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 ;
54
61
}
55
- let imageData = new ImageData ( arr , width ) ;
56
- ctxEdited . putImageData ( imageData , 0 , 0 ) ;
57
62
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 ) ;
59
90
} ;
60
91
61
92
reader . readAsArrayBuffer ( file ) ;
62
93
} ) ;
63
94
}
64
95
65
96
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
+ } ) ;
67
110
}
68
111
69
112
document . addEventListener ( "drop" , async ( event ) => {
0 commit comments