opencomic-ai-bin 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +260 -0
- package/calculate-latency.mts +72 -0
- package/dist/calculate-latency.mjs +761 -0
- package/dist/index.cjs +714 -0
- package/dist/index.d.ts +57 -0
- package/dist/index.mjs +712 -0
- package/index.mts +922 -0
- package/linux/arm64/realcugan/realcugan-ncnn-vulkan +0 -0
- package/linux/arm64/waifu2x/waifu2x-ncnn-vulkan +0 -0
- package/linux/x64/realcugan/realcugan-ncnn-vulkan +0 -0
- package/linux/x64/upscayl/upscayl-bin +0 -0
- package/linux/x64/waifu2x/waifu2x-ncnn-vulkan +0 -0
- package/mac/arm64/realcugan/realcugan-ncnn-vulkan.app +0 -0
- package/mac/arm64/upscayl/upscayl-bin.app +0 -0
- package/mac/arm64/waifu2x/waifu2x-ncnn-vulkan.app +0 -0
- package/mac/x64/realcugan/realcugan-ncnn-vulkan.app +0 -0
- package/mac/x64/upscayl/upscayl-bin.app +0 -0
- package/mac/x64/waifu2x/waifu2x-ncnn-vulkan.app +0 -0
- package/package.json +42 -0
- package/rollup.config.js +47 -0
- package/tsconfig.json +13 -0
- package/win/x64/realcugan/realcugan-ncnn-vulkan.exe +0 -0
- package/win/x64/realcugan/vcomp140.dll +0 -0
- package/win/x64/upscayl/upscayl-bin.exe +0 -0
- package/win/x64/waifu2x/vcomp140.dll +0 -0
- package/win/x64/waifu2x/waifu2x-ncnn-vulkan.exe +0 -0
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,712 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import fsp from 'fs/promises';
|
|
3
|
+
import p from 'path';
|
|
4
|
+
import crypto from 'crypto';
|
|
5
|
+
import { spawn } from 'child_process';
|
|
6
|
+
|
|
7
|
+
const ___dirname = typeof __dirname !== 'undefined' ? __dirname : import.meta.dirname;
|
|
8
|
+
const upscalers = {
|
|
9
|
+
realcugan: {
|
|
10
|
+
name: 'RealCUGAN NCNN Vulkan',
|
|
11
|
+
binary: 'realcugan-ncnn-vulkan',
|
|
12
|
+
platforms: {
|
|
13
|
+
darwin: {
|
|
14
|
+
x64: 'mac/x64/realcugan/realcugan-ncnn-vulkan.app',
|
|
15
|
+
arm64: 'mac/arm64/realcugan/realcugan-ncnn-vulkan.app',
|
|
16
|
+
},
|
|
17
|
+
win32: {
|
|
18
|
+
x64: 'win/x64/realcugan/realcugan-ncnn-vulkan.exe',
|
|
19
|
+
},
|
|
20
|
+
linux: {
|
|
21
|
+
x64: 'linux/x64/realcugan/realcugan-ncnn-vulkan',
|
|
22
|
+
arm64: 'linux/arm64/realcugan/realcugan-ncnn-vulkan',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
waifu2x: {
|
|
27
|
+
name: 'Waifu2x NCNN Vulkan',
|
|
28
|
+
binary: 'waifu2x-ncnn-vulkan',
|
|
29
|
+
platforms: {
|
|
30
|
+
darwin: {
|
|
31
|
+
x64: 'mac/x64/waifu2x/waifu2x-ncnn-vulkan.app',
|
|
32
|
+
arm64: 'mac/arm64/waifu2x/waifu2x-ncnn-vulkan.app',
|
|
33
|
+
},
|
|
34
|
+
win32: {
|
|
35
|
+
x64: 'win/x64/waifu2x/waifu2x-ncnn-vulkan.exe',
|
|
36
|
+
},
|
|
37
|
+
linux: {
|
|
38
|
+
x64: 'linux/x64/waifu2x/waifu2x-ncnn-vulkan',
|
|
39
|
+
arm64: 'linux/arm64/waifu2x/waifu2x-ncnn-vulkan',
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
upscayl: {
|
|
44
|
+
name: 'Upscayl',
|
|
45
|
+
binary: 'upscayl-bin',
|
|
46
|
+
platforms: {
|
|
47
|
+
darwin: {
|
|
48
|
+
x64: 'mac/x64/upscayl/upscayl-bin.app',
|
|
49
|
+
arm64: 'mac/arm64/upscayl/upscayl-bin.app',
|
|
50
|
+
},
|
|
51
|
+
win32: {
|
|
52
|
+
x64: 'win/x64/upscayl/upscayl-bin.exe',
|
|
53
|
+
},
|
|
54
|
+
linux: {
|
|
55
|
+
x64: 'linux/x64/upscayl/upscayl-bin',
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
let models = {
|
|
61
|
+
upscale: {
|
|
62
|
+
/*'realcugan-nose': {
|
|
63
|
+
name: 'RealCUGAN NoSE',
|
|
64
|
+
upscaper: 'realcugan',
|
|
65
|
+
scales: [2],
|
|
66
|
+
noise: [0, 3],
|
|
67
|
+
latency: 0,
|
|
68
|
+
folder: './realcugan/models-nose',
|
|
69
|
+
files: [],
|
|
70
|
+
},
|
|
71
|
+
'realcugan-pro': {
|
|
72
|
+
name: 'RealCUGAN Pro',
|
|
73
|
+
upscaper: 'realcugan',
|
|
74
|
+
scales: [1, 2, 3],
|
|
75
|
+
noise: [0, 3],
|
|
76
|
+
latency: 0,
|
|
77
|
+
folder: './realcugan/models-pro',
|
|
78
|
+
files: [],
|
|
79
|
+
},*/
|
|
80
|
+
'realcugan': {
|
|
81
|
+
name: 'RealCUGAN',
|
|
82
|
+
upscaler: 'realcugan',
|
|
83
|
+
scales: [/*1, */ 2, 3, 4],
|
|
84
|
+
noise: [0, 3],
|
|
85
|
+
latency: 1.32,
|
|
86
|
+
folder: './realcugan/models-se',
|
|
87
|
+
files: [
|
|
88
|
+
'up2x-conservative.bin',
|
|
89
|
+
'up2x-conservative.param',
|
|
90
|
+
'up2x-denoise1x.bin',
|
|
91
|
+
'up2x-denoise1x.param',
|
|
92
|
+
'up2x-denoise2x.bin',
|
|
93
|
+
'up2x-denoise2x.param',
|
|
94
|
+
'up2x-denoise3x.bin',
|
|
95
|
+
'up2x-denoise3x.param',
|
|
96
|
+
'up2x-no-denoise.bin',
|
|
97
|
+
'up2x-no-denoise.param',
|
|
98
|
+
'up3x-conservative.bin',
|
|
99
|
+
'up3x-conservative.param',
|
|
100
|
+
'up3x-denoise3x.bin',
|
|
101
|
+
'up3x-denoise3x.param',
|
|
102
|
+
'up3x-no-denoise.bin',
|
|
103
|
+
'up3x-no-denoise.param',
|
|
104
|
+
'up4x-conservative.bin',
|
|
105
|
+
'up4x-conservative.param',
|
|
106
|
+
'up4x-denoise3x.bin',
|
|
107
|
+
'up4x-denoise3x.param',
|
|
108
|
+
'up4x-no-denoise.bin',
|
|
109
|
+
'up4x-no-denoise.param',
|
|
110
|
+
],
|
|
111
|
+
},
|
|
112
|
+
'realesr-animevideov3': {
|
|
113
|
+
name: 'RealESR AnimeVideo v3',
|
|
114
|
+
upscaler: 'upscayl',
|
|
115
|
+
scales: [2, 3, 4],
|
|
116
|
+
noise: undefined,
|
|
117
|
+
latency: 1.53,
|
|
118
|
+
folder: './models',
|
|
119
|
+
files: [
|
|
120
|
+
'realesr-animevideov3-x2.bin',
|
|
121
|
+
'realesr-animevideov3-x2.param',
|
|
122
|
+
'realesr-animevideov3-x3.bin',
|
|
123
|
+
'realesr-animevideov3-x3.param',
|
|
124
|
+
'realesr-animevideov3-x4.bin',
|
|
125
|
+
'realesr-animevideov3-x4.param',
|
|
126
|
+
],
|
|
127
|
+
},
|
|
128
|
+
'realesrgan-x4plus': {
|
|
129
|
+
name: 'RealESRGAN x4 Plus',
|
|
130
|
+
upscaler: 'upscayl',
|
|
131
|
+
scales: [2, 3, 4],
|
|
132
|
+
noise: undefined,
|
|
133
|
+
latency: 7.5,
|
|
134
|
+
folder: './models',
|
|
135
|
+
files: [
|
|
136
|
+
'realesrgan-x4plus.bin',
|
|
137
|
+
'realesrgan-x4plus.param',
|
|
138
|
+
],
|
|
139
|
+
},
|
|
140
|
+
'realesrgan-x4plus-anime': {
|
|
141
|
+
name: 'RealESRGAN x4 Plus Anime',
|
|
142
|
+
upscaler: 'upscayl',
|
|
143
|
+
scales: [2, 3, 4],
|
|
144
|
+
noise: undefined,
|
|
145
|
+
latency: 3.26,
|
|
146
|
+
folder: './models',
|
|
147
|
+
files: [
|
|
148
|
+
'realesrgan-x4plus-anime.bin',
|
|
149
|
+
'realesrgan-x4plus-anime.param',
|
|
150
|
+
],
|
|
151
|
+
},
|
|
152
|
+
'realesrnet-x4plus': {
|
|
153
|
+
name: 'RealESRNet x4 Plus',
|
|
154
|
+
upscaler: 'upscayl',
|
|
155
|
+
scales: [2, 3, 4],
|
|
156
|
+
noise: undefined,
|
|
157
|
+
latency: 7.17,
|
|
158
|
+
folder: './models',
|
|
159
|
+
files: [
|
|
160
|
+
'realesrnet-x4plus.bin',
|
|
161
|
+
'realesrnet-x4plus.param',
|
|
162
|
+
],
|
|
163
|
+
},
|
|
164
|
+
'waifu2x-models-cunet': {
|
|
165
|
+
name: 'Waifu2x CUnet',
|
|
166
|
+
upscaler: 'waifu2x',
|
|
167
|
+
scales: [1, 2, 4, 8, 16, 32],
|
|
168
|
+
noise: [0, 1, 2, 3],
|
|
169
|
+
latency: 2.92,
|
|
170
|
+
folder: './waifu2x/models-cunet',
|
|
171
|
+
files: [
|
|
172
|
+
'noise0_model.bin',
|
|
173
|
+
'noise0_model.param',
|
|
174
|
+
'noise0_scale2.0x_model.bin',
|
|
175
|
+
'noise0_scale2.0x_model.param',
|
|
176
|
+
'noise1_model.bin',
|
|
177
|
+
'noise1_model.param',
|
|
178
|
+
'noise1_scale2.0x_model.bin',
|
|
179
|
+
'noise1_scale2.0x_model.param',
|
|
180
|
+
'noise2_model.bin',
|
|
181
|
+
'noise2_model.param',
|
|
182
|
+
'noise2_scale2.0x_model.bin',
|
|
183
|
+
'noise2_scale2.0x_model.param',
|
|
184
|
+
'noise3_model.bin',
|
|
185
|
+
'noise3_model.param',
|
|
186
|
+
'noise3_scale2.0x_model.bin',
|
|
187
|
+
'noise3_scale2.0x_model.param',
|
|
188
|
+
'scale2.0x_model.bin',
|
|
189
|
+
'scale2.0x_model.param',
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
'waifu2x-models-upconv': {
|
|
193
|
+
name: 'Waifu2x UpConv',
|
|
194
|
+
upscaler: 'waifu2x',
|
|
195
|
+
scales: [1, 2, 4, 8, 16, 32],
|
|
196
|
+
noise: [0, 1, 2, 3],
|
|
197
|
+
latency: 0.8,
|
|
198
|
+
folder: './waifu2x/models-upconv_7_anime_style_art_rgb',
|
|
199
|
+
files: [
|
|
200
|
+
'noise0_scale2.0x_model.bin',
|
|
201
|
+
'noise0_scale2.0x_model.param',
|
|
202
|
+
'noise1_scale2.0x_model.bin',
|
|
203
|
+
'noise1_scale2.0x_model.param',
|
|
204
|
+
'noise2_scale2.0x_model.bin',
|
|
205
|
+
'noise2_scale2.0x_model.param',
|
|
206
|
+
'noise3_scale2.0x_model.bin',
|
|
207
|
+
'noise3_scale2.0x_model.param',
|
|
208
|
+
'scale2.0x_model.bin',
|
|
209
|
+
'scale2.0x_model.param',
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
'4x-WTP-ColorDS': {
|
|
213
|
+
name: 'WTP ColorDS',
|
|
214
|
+
upscaler: 'upscayl',
|
|
215
|
+
scales: [2, 3, 4],
|
|
216
|
+
noise: undefined,
|
|
217
|
+
latency: 7.62,
|
|
218
|
+
folder: './models',
|
|
219
|
+
files: [
|
|
220
|
+
'4x-WTP-ColorDS.bin',
|
|
221
|
+
'4x-WTP-ColorDS.param',
|
|
222
|
+
],
|
|
223
|
+
},
|
|
224
|
+
'remacri-4x': {
|
|
225
|
+
name: 'Remacri',
|
|
226
|
+
upscaler: 'upscayl',
|
|
227
|
+
scales: [2, 3, 4],
|
|
228
|
+
noise: undefined,
|
|
229
|
+
latency: 7.82,
|
|
230
|
+
folder: './models',
|
|
231
|
+
files: [
|
|
232
|
+
'remacri-4x.bin',
|
|
233
|
+
'remacri-4x.param',
|
|
234
|
+
],
|
|
235
|
+
},
|
|
236
|
+
'ultramix-balanced-4x': {
|
|
237
|
+
name: 'Ultramix Balanced',
|
|
238
|
+
upscaler: 'upscayl',
|
|
239
|
+
scales: [2, 3, 4],
|
|
240
|
+
noise: undefined,
|
|
241
|
+
latency: 10,
|
|
242
|
+
folder: './models',
|
|
243
|
+
files: [
|
|
244
|
+
'ultramix-balanced-4x.bin',
|
|
245
|
+
'ultramix-balanced-4x.param',
|
|
246
|
+
],
|
|
247
|
+
},
|
|
248
|
+
'ultrasharp-4x': {
|
|
249
|
+
name: 'Ultrasharp',
|
|
250
|
+
upscaler: 'upscayl',
|
|
251
|
+
scales: [2, 3, 4],
|
|
252
|
+
noise: undefined,
|
|
253
|
+
latency: 7.46,
|
|
254
|
+
folder: './models',
|
|
255
|
+
files: [
|
|
256
|
+
'ultrasharp-4x.bin',
|
|
257
|
+
'ultrasharp-4x.param',
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
/*'2x-AnimeSharpV4_RCAN_fp16_op17': {
|
|
261
|
+
name: 'AnimeSharpV4 RCAN',
|
|
262
|
+
upscaler: 'upscayl',
|
|
263
|
+
scales: [2],
|
|
264
|
+
noise: undefined,
|
|
265
|
+
latency: 0,
|
|
266
|
+
folder: './models',
|
|
267
|
+
files: [
|
|
268
|
+
'2x-AnimeSharpV4_RCAN_fp16_op17.bin',
|
|
269
|
+
'2x-AnimeSharpV4_RCAN_fp16_op17.param',
|
|
270
|
+
],
|
|
271
|
+
},*/
|
|
272
|
+
'4xHFA2k': {
|
|
273
|
+
name: 'HFA2k',
|
|
274
|
+
upscaler: 'upscayl',
|
|
275
|
+
scales: [2, 3, 4],
|
|
276
|
+
noise: undefined,
|
|
277
|
+
latency: 7.39,
|
|
278
|
+
folder: './models',
|
|
279
|
+
files: [
|
|
280
|
+
'4xHFA2k.bin',
|
|
281
|
+
'4xHFA2k.param',
|
|
282
|
+
],
|
|
283
|
+
},
|
|
284
|
+
'4xLSDIRCompactC3': {
|
|
285
|
+
name: 'LSDIR Compact C3',
|
|
286
|
+
upscaler: 'upscayl',
|
|
287
|
+
scales: [2, 3, 4],
|
|
288
|
+
noise: undefined,
|
|
289
|
+
latency: 1.37,
|
|
290
|
+
folder: './models',
|
|
291
|
+
files: [
|
|
292
|
+
'4xLSDIRCompactC3.bin',
|
|
293
|
+
'4xLSDIRCompactC3.param',
|
|
294
|
+
],
|
|
295
|
+
},
|
|
296
|
+
'4xLSDIRplusC': {
|
|
297
|
+
name: 'LSDIR Plus C',
|
|
298
|
+
upscaler: 'upscayl',
|
|
299
|
+
scales: [2, 3, 4],
|
|
300
|
+
noise: undefined,
|
|
301
|
+
latency: 8.3,
|
|
302
|
+
folder: './models',
|
|
303
|
+
files: [
|
|
304
|
+
'4xLSDIRplusC.bin',
|
|
305
|
+
'4xLSDIRplusC.param',
|
|
306
|
+
],
|
|
307
|
+
},
|
|
308
|
+
'4x_NMKD-Siax_200k': {
|
|
309
|
+
name: 'NMKD Siax',
|
|
310
|
+
upscaler: 'upscayl',
|
|
311
|
+
scales: [2, 3, 4],
|
|
312
|
+
noise: undefined,
|
|
313
|
+
latency: 7.24,
|
|
314
|
+
folder: './models',
|
|
315
|
+
files: [
|
|
316
|
+
'4x_NMKD-Siax_200k.bin',
|
|
317
|
+
'4x_NMKD-Siax_200k.param',
|
|
318
|
+
],
|
|
319
|
+
},
|
|
320
|
+
'4xNomos8kSC': {
|
|
321
|
+
name: 'Nomos 8k SC',
|
|
322
|
+
upscaler: 'upscayl',
|
|
323
|
+
scales: [2, 3, 4],
|
|
324
|
+
noise: undefined,
|
|
325
|
+
latency: 7.11,
|
|
326
|
+
folder: './models',
|
|
327
|
+
files: [
|
|
328
|
+
'4xNomos8kSC.bin',
|
|
329
|
+
'4xNomos8kSC.param',
|
|
330
|
+
],
|
|
331
|
+
},
|
|
332
|
+
'RealESRGAN_General_WDN_x4_v3': {
|
|
333
|
+
name: 'RealESRGAN General WDN v3',
|
|
334
|
+
upscaler: 'upscayl',
|
|
335
|
+
scales: [2, 3, 4],
|
|
336
|
+
noise: undefined,
|
|
337
|
+
latency: 1.47,
|
|
338
|
+
folder: './models',
|
|
339
|
+
files: [
|
|
340
|
+
'RealESRGAN_General_WDN_x4_v3.bin',
|
|
341
|
+
'RealESRGAN_General_WDN_x4_v3.param',
|
|
342
|
+
],
|
|
343
|
+
},
|
|
344
|
+
'RealESRGAN_General_x4_v3': {
|
|
345
|
+
name: 'RealESRGAN General v3',
|
|
346
|
+
upscaler: 'upscayl',
|
|
347
|
+
scales: [2, 3, 4],
|
|
348
|
+
noise: undefined,
|
|
349
|
+
latency: 1.45,
|
|
350
|
+
folder: './models',
|
|
351
|
+
files: [
|
|
352
|
+
'RealESRGAN_General_x4_v3.bin',
|
|
353
|
+
'RealESRGAN_General_x4_v3.param',
|
|
354
|
+
],
|
|
355
|
+
},
|
|
356
|
+
'uniscale_restore_x4': {
|
|
357
|
+
name: 'Uniscale Restore x4',
|
|
358
|
+
upscaler: 'upscayl',
|
|
359
|
+
scales: [2, 3, 4],
|
|
360
|
+
noise: undefined,
|
|
361
|
+
latency: 7.01,
|
|
362
|
+
folder: './models',
|
|
363
|
+
files: [
|
|
364
|
+
'uniscale_restore_x4.bin',
|
|
365
|
+
'uniscale_restore_x4.param',
|
|
366
|
+
],
|
|
367
|
+
},
|
|
368
|
+
'unknown-2.0.1': {
|
|
369
|
+
name: 'Unknown 2.0.1',
|
|
370
|
+
upscaler: 'upscayl',
|
|
371
|
+
scales: [2, 3, 4],
|
|
372
|
+
noise: undefined,
|
|
373
|
+
latency: 7.33,
|
|
374
|
+
folder: './models',
|
|
375
|
+
files: [
|
|
376
|
+
'unknown-2.0.1.bin',
|
|
377
|
+
'unknown-2.0.1.param',
|
|
378
|
+
],
|
|
379
|
+
},
|
|
380
|
+
},
|
|
381
|
+
descreen: {
|
|
382
|
+
'1x_halftone_patch_060000_G': {
|
|
383
|
+
name: 'Halftone Patch 060000 G',
|
|
384
|
+
upscaler: 'upscayl',
|
|
385
|
+
scales: [1],
|
|
386
|
+
noise: undefined,
|
|
387
|
+
latency: 6.71,
|
|
388
|
+
folder: './models',
|
|
389
|
+
files: [
|
|
390
|
+
'1x_halftone_patch_060000_G.bin',
|
|
391
|
+
'1x_halftone_patch_060000_G.param',
|
|
392
|
+
],
|
|
393
|
+
},
|
|
394
|
+
'1x_wtp_descreenton_compact': {
|
|
395
|
+
name: 'WTP DescreenTon Compact',
|
|
396
|
+
upscaler: 'upscayl',
|
|
397
|
+
scales: [1],
|
|
398
|
+
noise: undefined,
|
|
399
|
+
latency: 0.5,
|
|
400
|
+
folder: './models',
|
|
401
|
+
files: [
|
|
402
|
+
'1x_wtp_descreenton_compact.bin',
|
|
403
|
+
'1x_wtp_descreenton_compact.param',
|
|
404
|
+
],
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
'artifact-removal': {
|
|
408
|
+
'1x_NMKD-Jaywreck3-Lite_320k': {
|
|
409
|
+
name: 'NMKD Jaywreck3 Lite',
|
|
410
|
+
upscaler: 'upscayl',
|
|
411
|
+
scales: [1],
|
|
412
|
+
noise: undefined,
|
|
413
|
+
latency: 3.66,
|
|
414
|
+
folder: './models',
|
|
415
|
+
files: [
|
|
416
|
+
'1x_NMKD-Jaywreck3-Lite_320k.bin',
|
|
417
|
+
'1x_NMKD-Jaywreck3-Lite_320k.param',
|
|
418
|
+
],
|
|
419
|
+
},
|
|
420
|
+
'1x_NMKD-Jaywreck3-Soft-Lite_320k': {
|
|
421
|
+
name: 'NMKD Jaywreck3 Soft Lite',
|
|
422
|
+
upscaler: 'upscayl',
|
|
423
|
+
scales: [1],
|
|
424
|
+
noise: undefined,
|
|
425
|
+
latency: 3.66,
|
|
426
|
+
folder: './models',
|
|
427
|
+
files: [
|
|
428
|
+
'1x_NMKD-Jaywreck3-Soft-Lite_320k.bin',
|
|
429
|
+
'1x_NMKD-Jaywreck3-Soft-Lite_320k.param',
|
|
430
|
+
],
|
|
431
|
+
},
|
|
432
|
+
'1x-SaiyaJin-DeJpeg': {
|
|
433
|
+
name: 'SaiyaJin DeJpeg',
|
|
434
|
+
upscaler: 'upscayl',
|
|
435
|
+
scales: [1],
|
|
436
|
+
noise: undefined,
|
|
437
|
+
latency: 8.55,
|
|
438
|
+
folder: './models',
|
|
439
|
+
files: [
|
|
440
|
+
'1x-SaiyaJin-DeJpeg.bin',
|
|
441
|
+
'1x-SaiyaJin-DeJpeg.param',
|
|
442
|
+
],
|
|
443
|
+
},
|
|
444
|
+
},
|
|
445
|
+
};
|
|
446
|
+
const modelSpeed = (latency) => {
|
|
447
|
+
if (latency <= 1)
|
|
448
|
+
return 'Very Fast';
|
|
449
|
+
else if (latency <= 4)
|
|
450
|
+
return 'Fast';
|
|
451
|
+
else if (latency <= 7)
|
|
452
|
+
return 'Moderate';
|
|
453
|
+
else if (latency <= 10)
|
|
454
|
+
return 'Slow';
|
|
455
|
+
else
|
|
456
|
+
return 'Very Slow'; // Not used yet
|
|
457
|
+
};
|
|
458
|
+
const parseModels = (models, type) => {
|
|
459
|
+
const parsedModels = {};
|
|
460
|
+
for (const [key, model] of Object.entries(models)) {
|
|
461
|
+
parsedModels[key] = {
|
|
462
|
+
key,
|
|
463
|
+
type,
|
|
464
|
+
...model,
|
|
465
|
+
speed: modelSpeed(model.latency),
|
|
466
|
+
supportCurrentPlatform: upscalers[model.upscaler].platforms[process.platform]?.[process.arch] ? true : false,
|
|
467
|
+
};
|
|
468
|
+
}
|
|
469
|
+
return parsedModels;
|
|
470
|
+
};
|
|
471
|
+
models = {
|
|
472
|
+
upscale: parseModels(models.upscale, 'upscale'),
|
|
473
|
+
descreen: parseModels(models.descreen, 'descreen'),
|
|
474
|
+
'artifact-removal': parseModels(models['artifact-removal'], 'artifact-removal'),
|
|
475
|
+
};
|
|
476
|
+
const DEFAULT_MODEL = 'realcugan';
|
|
477
|
+
const DOWNLOADING_URL = 'https://raw.githubusercontent.com/ollm/opencomic-ai-models/db60a923bfab0afccee4e478b2ca6666ec75fdb4/models/';
|
|
478
|
+
const modelsList = [...Object.keys(models.upscale), ...Object.keys(models.descreen), ...Object.keys(models['artifact-removal'])];
|
|
479
|
+
const modelsTypeList = {
|
|
480
|
+
upscale: Object.keys(models.upscale),
|
|
481
|
+
descreen: Object.keys(models.descreen),
|
|
482
|
+
'artifact-removal': Object.keys(models['artifact-removal']),
|
|
483
|
+
};
|
|
484
|
+
class OpenComicAI {
|
|
485
|
+
static models = models;
|
|
486
|
+
static modelsList = modelsList;
|
|
487
|
+
static modelsTypeList = modelsTypeList;
|
|
488
|
+
static modelsPath = undefined;
|
|
489
|
+
static resolve = (path) => {
|
|
490
|
+
if (!p.isAbsolute(path)) {
|
|
491
|
+
if (typeof module !== 'undefined')
|
|
492
|
+
path = p.resolve(module?.parent?.path ?? '', path);
|
|
493
|
+
else
|
|
494
|
+
path = p.resolve(import.meta?.dirname ?? '', path);
|
|
495
|
+
}
|
|
496
|
+
return p.normalize(path);
|
|
497
|
+
};
|
|
498
|
+
static setModelsPath = (path) => {
|
|
499
|
+
path = OpenComicAI.resolve(path);
|
|
500
|
+
if (!fs.existsSync(path))
|
|
501
|
+
throw new Error(`Models path does not exist: ${path}`);
|
|
502
|
+
OpenComicAI.modelsPath = path;
|
|
503
|
+
};
|
|
504
|
+
static model = (model = DEFAULT_MODEL) => {
|
|
505
|
+
if (!modelsList.includes(model))
|
|
506
|
+
throw new Error(`Model not found: ${model}`);
|
|
507
|
+
const _model = model;
|
|
508
|
+
const modelInfo = models.upscale[_model] || models.descreen[_model] || models['artifact-removal'][_model];
|
|
509
|
+
const modelType = modelInfo.type;
|
|
510
|
+
return {
|
|
511
|
+
...modelInfo,
|
|
512
|
+
path: OpenComicAI.modelsPath ? p.join(OpenComicAI.modelsPath, modelType, modelInfo.folder) : p.join(modelType, modelInfo.folder),
|
|
513
|
+
};
|
|
514
|
+
};
|
|
515
|
+
static binary = (model) => {
|
|
516
|
+
if (!modelsList.includes(model))
|
|
517
|
+
throw new Error(`Model not found: ${model}`);
|
|
518
|
+
const base = p.join(___dirname, '..');
|
|
519
|
+
const upscaler = OpenComicAI.model(model).upscaler;
|
|
520
|
+
const result = upscalers[upscaler].platforms[process.platform]?.[process.arch] ?? upscalers[upscaler].platforms[process.platform]?.x64 ?? upscalers[upscaler].platforms.linux?.x64 ?? '';
|
|
521
|
+
return p.join(base, result);
|
|
522
|
+
};
|
|
523
|
+
static download = async (fileUrl, destPath, downloading) => {
|
|
524
|
+
const response = await fetch(fileUrl);
|
|
525
|
+
if (response.ok) {
|
|
526
|
+
const contentLength = response.headers.get('content-length');
|
|
527
|
+
const len = contentLength ? parseInt(contentLength, 10) : 0;
|
|
528
|
+
if (!response.body) {
|
|
529
|
+
console.error('Response body is null', fileUrl);
|
|
530
|
+
return;
|
|
531
|
+
}
|
|
532
|
+
const reader = response.body.getReader();
|
|
533
|
+
const fileStream = fs.createWriteStream(destPath);
|
|
534
|
+
let downloaded = 0;
|
|
535
|
+
while (true) {
|
|
536
|
+
const { done, value } = await reader.read();
|
|
537
|
+
if (done)
|
|
538
|
+
break;
|
|
539
|
+
fileStream.write(value);
|
|
540
|
+
downloaded += value.byteLength;
|
|
541
|
+
if (downloading && downloading?.progress)
|
|
542
|
+
downloading?.progress(downloaded / len);
|
|
543
|
+
}
|
|
544
|
+
if (downloading && downloading?.progress)
|
|
545
|
+
downloading?.progress(1);
|
|
546
|
+
let resolve;
|
|
547
|
+
const promise = new Promise((_resolve) => {
|
|
548
|
+
resolve = _resolve;
|
|
549
|
+
});
|
|
550
|
+
fileStream.end(resolve);
|
|
551
|
+
await promise;
|
|
552
|
+
}
|
|
553
|
+
else {
|
|
554
|
+
throw new Error(`Failed to download file: ${fileUrl}, status: ${response.status}`);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
557
|
+
static getModels = async (steps, downloading) => {
|
|
558
|
+
const toGetModels = new Map();
|
|
559
|
+
for (const step of steps) {
|
|
560
|
+
const modelInfo = OpenComicAI.model(step.model || DEFAULT_MODEL);
|
|
561
|
+
for (const file of modelInfo.files) {
|
|
562
|
+
const filePath = p.join(modelInfo.path, file);
|
|
563
|
+
if (!fs.existsSync(filePath)) {
|
|
564
|
+
const base = new URL(`${modelInfo.type}/`, DOWNLOADING_URL);
|
|
565
|
+
const folder = new URL(`${modelInfo.folder}/`, base);
|
|
566
|
+
const fileUrl = new URL(file, folder).href;
|
|
567
|
+
toGetModels.set(fileUrl, filePath);
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
if (toGetModels.size > 0) {
|
|
572
|
+
if (downloading && downloading.start)
|
|
573
|
+
downloading.start();
|
|
574
|
+
const entries = toGetModels.entries();
|
|
575
|
+
const binNum = [...toGetModels.values()].reduce((accumulator, destPath) => {
|
|
576
|
+
return accumulator + (destPath.endsWith('.bin') ? 1 : 0);
|
|
577
|
+
}, 0);
|
|
578
|
+
let index = -1;
|
|
579
|
+
for (const [fileUrl, destPath] of entries) {
|
|
580
|
+
const folder = p.dirname(destPath);
|
|
581
|
+
if (!fs.existsSync(folder))
|
|
582
|
+
await fsp.mkdir(folder, { recursive: true });
|
|
583
|
+
let _downloading = {};
|
|
584
|
+
if (destPath.endsWith('.bin')) {
|
|
585
|
+
index++;
|
|
586
|
+
_downloading = {
|
|
587
|
+
progress: (progress) => {
|
|
588
|
+
if (downloading && downloading.progress)
|
|
589
|
+
downloading.progress(((index + progress) / binNum));
|
|
590
|
+
}
|
|
591
|
+
};
|
|
592
|
+
}
|
|
593
|
+
await OpenComicAI.download(fileUrl, destPath, _downloading);
|
|
594
|
+
}
|
|
595
|
+
if (downloading && downloading.end)
|
|
596
|
+
downloading.end();
|
|
597
|
+
}
|
|
598
|
+
};
|
|
599
|
+
static pipeline = async (source, dest, steps, progress, downloading) => {
|
|
600
|
+
if (!OpenComicAI.modelsPath)
|
|
601
|
+
throw new Error('Models path is not set, use OpenComicAI.setModelsPath to set it before calling pipe.');
|
|
602
|
+
await OpenComicAI.getModels(steps, downloading);
|
|
603
|
+
const parsed = p.parse(dest);
|
|
604
|
+
let prevIntermediateDest = '';
|
|
605
|
+
for (let i = 0, len = steps.length; i < len; i++) {
|
|
606
|
+
const step = steps[i];
|
|
607
|
+
const intermediateDest = i < len - 1 ? p.join(p.dirname(dest), `${crypto.randomUUID()}${parsed.ext}`) : dest;
|
|
608
|
+
const _progress = (p) => {
|
|
609
|
+
if (!progress)
|
|
610
|
+
return;
|
|
611
|
+
const overallProgress = (i + (p ?? 0)) / len;
|
|
612
|
+
progress(overallProgress);
|
|
613
|
+
};
|
|
614
|
+
if (progress)
|
|
615
|
+
progress(i / len);
|
|
616
|
+
await OpenComicAI.image(source, intermediateDest, step, _progress);
|
|
617
|
+
if (prevIntermediateDest && fs.existsSync(prevIntermediateDest))
|
|
618
|
+
await fsp.unlink(prevIntermediateDest);
|
|
619
|
+
source = intermediateDest;
|
|
620
|
+
prevIntermediateDest = OpenComicAI.resolve(intermediateDest);
|
|
621
|
+
}
|
|
622
|
+
return source;
|
|
623
|
+
};
|
|
624
|
+
static closest = (array, target) => {
|
|
625
|
+
return array.reduce((prev, curr) => {
|
|
626
|
+
return Math.abs(curr - target) < Math.abs(prev - target) ? curr : prev;
|
|
627
|
+
});
|
|
628
|
+
};
|
|
629
|
+
static image = async (source, dest, options, progress) => {
|
|
630
|
+
options = { ...options };
|
|
631
|
+
source = OpenComicAI.resolve(source);
|
|
632
|
+
dest = OpenComicAI.resolve(dest);
|
|
633
|
+
const { dir, name } = p.parse(dest);
|
|
634
|
+
if (!options.model)
|
|
635
|
+
options.model = DEFAULT_MODEL;
|
|
636
|
+
if (!modelsList.includes(options.model))
|
|
637
|
+
throw new Error(`Model not found: ${options.model}`);
|
|
638
|
+
const folder = p.dirname(dest);
|
|
639
|
+
if (!fs.existsSync(folder))
|
|
640
|
+
await fsp.mkdir(folder, { recursive: true });
|
|
641
|
+
const binary = OpenComicAI.binary(options.model);
|
|
642
|
+
const modelInfo = OpenComicAI.model(options.model);
|
|
643
|
+
const model = options.model;
|
|
644
|
+
// const format = options.format ?? p.extname(source).slice(1);
|
|
645
|
+
const threads = options.threads ? +options.threads : false;
|
|
646
|
+
let noise = options.noise ? +options.noise : false;
|
|
647
|
+
let scale = options.scale ? +options.scale : false;
|
|
648
|
+
const tileSize = options.tileSize?.toString() ?? false;
|
|
649
|
+
const gpuId = options.gpuId ?? false;
|
|
650
|
+
const tta = !!options.tta;
|
|
651
|
+
if (noise !== false && !modelInfo?.noise?.includes(noise))
|
|
652
|
+
noise = modelInfo?.noise ? OpenComicAI.closest(modelInfo.noise, noise) : false;
|
|
653
|
+
if (scale && !modelInfo.scales.includes(scale))
|
|
654
|
+
scale = OpenComicAI.closest(modelInfo.scales, scale);
|
|
655
|
+
const args = [
|
|
656
|
+
'-i', source,
|
|
657
|
+
'-o', dest,
|
|
658
|
+
'-m', modelInfo?.path,
|
|
659
|
+
// ...(format ? ['-f', format] : []),
|
|
660
|
+
...(threads ? ['-j', `${threads}:${threads}:${threads}`] : []),
|
|
661
|
+
...(noise !== false ? ['-n', noise.toString()] : []),
|
|
662
|
+
...(scale ? ['-s', scale.toString()] : []),
|
|
663
|
+
...(tileSize ? ['-t', tileSize] : []),
|
|
664
|
+
...(gpuId ? ['-g', gpuId] : []),
|
|
665
|
+
...(tta ? ['-x'] : []),
|
|
666
|
+
];
|
|
667
|
+
switch (modelInfo.upscaler) {
|
|
668
|
+
case 'waifu2x':
|
|
669
|
+
// No additional args for waifu2x
|
|
670
|
+
break;
|
|
671
|
+
case 'realcugan':
|
|
672
|
+
// No additional args for realcugan
|
|
673
|
+
break;
|
|
674
|
+
case 'upscayl':
|
|
675
|
+
args.push('-n', model);
|
|
676
|
+
break;
|
|
677
|
+
}
|
|
678
|
+
let result = '';
|
|
679
|
+
// console.log(`Executing: ${binary} ${args.join(' ')}`);
|
|
680
|
+
return new Promise((resolve, reject) => {
|
|
681
|
+
const proc = spawn(binary, args);
|
|
682
|
+
proc.stderr.on('data', (data) => {
|
|
683
|
+
data = data.toString();
|
|
684
|
+
result += data;
|
|
685
|
+
if (!progress)
|
|
686
|
+
return;
|
|
687
|
+
const match = data.match(/([\d\.\,]+)%/);
|
|
688
|
+
if (match) {
|
|
689
|
+
const percent = +(match[1].replace(',', '.'));
|
|
690
|
+
const _progress = Math.min(Math.max(percent / 100, 0), 1);
|
|
691
|
+
progress(_progress);
|
|
692
|
+
}
|
|
693
|
+
});
|
|
694
|
+
proc.on('error', (error) => {
|
|
695
|
+
reject(error);
|
|
696
|
+
});
|
|
697
|
+
proc.on('close', (code) => {
|
|
698
|
+
if (code === 0) {
|
|
699
|
+
resolve(dest);
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const lines = result.split('\n').filter(line => line.trim() !== '');
|
|
703
|
+
const lastLine = lines[lines.length - 1] || '';
|
|
704
|
+
const lastLines = lines.slice(-20).join('\n');
|
|
705
|
+
console.error(lastLines);
|
|
706
|
+
reject(new Error(`Process exited with code ${code}: ${lastLine}`));
|
|
707
|
+
});
|
|
708
|
+
});
|
|
709
|
+
};
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
export { OpenComicAI as default };
|