Csplk commited on
Commit
3732233
Β·
verified Β·
1 Parent(s): be51b70

Update index.html

Browse files
Files changed (1) hide show
  1. index.html +699 -19
index.html CHANGED
@@ -1,19 +1,699 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <html><head><base href="https://www.example.com/advanced-image-preprocessing-ascii-converter/">
2
+ <meta charset="UTF-8">
3
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4
+ <title>Advanced Image Preprocessing and ASCII Art Converter</title>
5
+ <style>
6
+ :root {
7
+ --fluid-5-20: clamp(0.3125rem, 0.2731rem + 1.2605vw, 1.25rem);
8
+ --fluid-8-20: clamp(0.5rem, 0.4685rem + 1.0084vw, 1.25rem);
9
+ --fluid-5-9: clamp(0.3125rem, 0.302rem + 0.3361vw, 0.5625rem);
10
+ --fluid-2-5: clamp(0.125rem, 0.1168rem + 0.2609vw, 0.3125rem);
11
+ }
12
+
13
+ @font-face {
14
+ font-family: 'jgs9';
15
+ src: url('https://websim.ai/fonts/jgs9.woff2') format('woff2');
16
+ font-weight: normal;
17
+ font-style: normal;
18
+ }
19
+
20
+ body {
21
+ font-family: 'Roboto', Arial, sans-serif;
22
+ margin: 0;
23
+ padding: 20px;
24
+ background-color: #040404;
25
+ color: #fdfdfd;
26
+ }
27
+ .container {
28
+ max-width: 1200px;
29
+ margin: 0 auto;
30
+ background-color: #111;
31
+ padding: 30px;
32
+ border-radius: 8px;
33
+ box-shadow: 0 4px 6px rgba(255,255,255,0.1);
34
+ }
35
+ h1, h2 {
36
+ text-align: center;
37
+ color: #fdfdfd;
38
+ font-family: "jgs9", sans-serif;
39
+ }
40
+ .upload-section {
41
+ text-align: center;
42
+ margin-bottom: 30px;
43
+ }
44
+ #imageUpload {
45
+ display: none;
46
+ }
47
+ .upload-btn {
48
+ background-color: #3498db;
49
+ color: white;
50
+ padding: 12px 24px;
51
+ border: none;
52
+ border-radius: 4px;
53
+ cursor: pointer;
54
+ font-size: 16px;
55
+ transition: background-color 0.3s ease;
56
+ }
57
+ .upload-btn:hover {
58
+ background-color: #2980b9;
59
+ }
60
+ .image-container {
61
+ display: flex;
62
+ justify-content: space-between;
63
+ flex-wrap: wrap;
64
+ margin-bottom: 30px;
65
+ }
66
+ .image-preview {
67
+ flex-basis: calc(33% - 20px);
68
+ margin-bottom: 20px;
69
+ background-color: #222;
70
+ border-radius: 8px;
71
+ overflow: hidden;
72
+ box-shadow: 0 2px 4px rgba(255,255,255,0.1);
73
+ }
74
+ .image-preview h3 {
75
+ background-color: #34495e;
76
+ color: #fff;
77
+ margin: 0;
78
+ padding: 10px;
79
+ font-size: 18px;
80
+ }
81
+ canvas {
82
+ max-width: 100%;
83
+ height: auto;
84
+ display: block;
85
+ }
86
+ .controls {
87
+ background-color: #222;
88
+ padding: 20px;
89
+ border-radius: 8px;
90
+ margin-bottom: 20px;
91
+ }
92
+ .slider-container {
93
+ margin-bottom: 15px;
94
+ }
95
+ .slider-container label {
96
+ display: block;
97
+ margin-bottom: 5px;
98
+ font-weight: bold;
99
+ color: #fdfdfd;
100
+ }
101
+ .slider {
102
+ -webkit-appearance: none;
103
+ width: 100%;
104
+ height: 10px;
105
+ border-radius: 5px;
106
+ background: #555;
107
+ outline: none;
108
+ opacity: 0.7;
109
+ transition: opacity .2s;
110
+ }
111
+ .slider:hover {
112
+ opacity: 1;
113
+ }
114
+ .slider::-webkit-slider-thumb {
115
+ -webkit-appearance: none;
116
+ appearance: none;
117
+ width: 20px;
118
+ height: 20px;
119
+ border-radius: 50%;
120
+ background: #3498db;
121
+ cursor: pointer;
122
+ }
123
+ .slider::-moz-range-thumb {
124
+ width: 20px;
125
+ height: 20px;
126
+ border-radius: 50%;
127
+ background: #3498db;
128
+ cursor: pointer;
129
+ }
130
+ .number-input {
131
+ width: 50px;
132
+ margin-left: 10px;
133
+ background-color: #333;
134
+ color: #fdfdfd;
135
+ border: 1px solid #555;
136
+ border-radius: 3px;
137
+ padding: 3px;
138
+ }
139
+ #processBtn {
140
+ display: block;
141
+ width: 100%;
142
+ padding: 12px;
143
+ background-color: #2ecc71;
144
+ color: white;
145
+ border: none;
146
+ border-radius: 4px;
147
+ font-size: 16px;
148
+ cursor: pointer;
149
+ transition: background-color 0.3s ease;
150
+ }
151
+ #processBtn:hover {
152
+ background-color: #27ae60;
153
+ }
154
+ .ascii-art {
155
+ font-family: "jgs9", monospace;
156
+ white-space: pre;
157
+ background-color: #222;
158
+ padding: 20px;
159
+ border-radius: 5px;
160
+ overflow: auto;
161
+ max-height: 600px;
162
+ color: #fdfdfd;
163
+ font-size: 2px;
164
+ line-height: 1;
165
+ text-align: center;
166
+ box-shadow: 0 0 10px rgba(0,0,0,0.5);
167
+ margin-top: 20px;
168
+ }
169
+ .download-buttons {
170
+ display: flex;
171
+ justify-content: center;
172
+ gap: 10px;
173
+ margin-top: 20px;
174
+ }
175
+ .download-btn {
176
+ background-color: #4CAF50;
177
+ border: none;
178
+ color: white;
179
+ padding: 10px 20px;
180
+ text-align: center;
181
+ text-decoration: none;
182
+ display: inline-block;
183
+ font-size: 16px;
184
+ margin: 4px 2px;
185
+ cursor: pointer;
186
+ border-radius: 5px;
187
+ }
188
+ </style>
189
+ </head>
190
+ <body>
191
+ <div id="loadingMessage" style="text-align: center; padding: 20px;">
192
+ Loading OpenCV.js, please wait...
193
+ </div>
194
+ <div class="container">
195
+ <h1>Advanced Image Preprocessing and ASCII Art Converter</h1>
196
+ <div class="upload-section">
197
+ <input type="file" id="imageUpload" accept="image/*">
198
+ <label for="imageUpload" class="upload-btn">Upload Image</label>
199
+ </div>
200
+ <div class="image-container">
201
+ <div class="image-preview">
202
+ <h3>Original Image</h3>
203
+ <canvas id="originalCanvas"></canvas>
204
+ </div>
205
+ <div class="image-preview">
206
+ <h3>Preprocessed Image</h3>
207
+ <canvas id="preprocessedCanvas"></canvas>
208
+ </div>
209
+ <div class="image-preview">
210
+ <h3>Structure Map</h3>
211
+ <canvas id="structureMapCanvas"></canvas>
212
+ </div>
213
+ </div>
214
+ <div class="controls">
215
+ <div class="slider-container">
216
+ <label for="edgeThresholdSlider">Edge Detection Threshold:</label>
217
+ <input type="range" id="edgeThresholdSlider" class="slider" min="0" max="255" value="100">
218
+ <span id="edgeThresholdValue">100</span>
219
+ </div>
220
+ <div class="slider-container">
221
+ <label for="gaussianBlurSlider">Gaussian Blur:</label>
222
+ <input type="range" id="gaussianBlurSlider" class="slider" min="0" max="10" value="1" step="0.1">
223
+ <span id="gaussianBlurValue">1</span>
224
+ </div>
225
+ <div class="slider-container">
226
+ <label for="thinningIterationsSlider">Thinning Iterations:</label>
227
+ <input type="range" id="thinningIterationsSlider" class="slider" min="1" max="10" value="3">
228
+ <span id="thinningIterationsValue">3</span>
229
+ </div>
230
+ <div class="slider-container">
231
+ <label for="scaleSlider">Scale (%):</label>
232
+ <input type="range" id="scaleSlider" class="slider" min="10" max="200" value="100">
233
+ <span id="scaleValue">100</span>
234
+ </div>
235
+ <div class="slider-container">
236
+ <label for="dogThresholdSlider">DoG Threshold:</label>
237
+ <input type="range" id="dogThresholdSlider" class="slider" min="0" max="255" value="100">
238
+ <span id="dogThresholdValue">100</span>
239
+ </div>
240
+ <div class="slider-container">
241
+ <label for="outputHeightSlider">Output Height (rows):</label>
242
+ <input type="range" id="outputHeightSlider" class="slider" min="10" max="200" value="50">
243
+ <input type="number" id="outputHeightValue" class="number-input" min="10" max="200" value="50">
244
+ </div>
245
+ <div class="slider-container">
246
+ <label for="scaleCountSlider">Number of Scales:</label>
247
+ <input type="range" id="scaleCountSlider" class="slider" min="1" max="8" value="4">
248
+ <input type="number" id="scaleCountValue" class="number-input" min="1" max="8" value="4">
249
+ </div>
250
+ <div class="slider-container">
251
+ <label for="ncrfRadiusSlider">Non-CRF Radius:</label>
252
+ <input type="range" id="ncrfRadiusSlider" class="slider" min="1" max="20" value="5">
253
+ <input type="number" id="ncrfRadiusValue" class="number-input" min="1" max="20" value="5">
254
+ </div>
255
+ <div class="slider-container">
256
+ <label for="modulationStrengthSlider">Modulation Strength:</label>
257
+ <input type="range" id="modulationStrengthSlider" class="slider" min="0" max="1" step="0.1" value="0.5">
258
+ <input type="number" id="modulationStrengthValue" class="number-input" min="0" max="1" step="0.1" value="0.5">
259
+ </div>
260
+ <div class="slider-container">
261
+ <label for="detailThresholdSlider">Detail Threshold:</label>
262
+ <input type="range" id="detailThresholdSlider" class="slider" min="0" max="1" step="0.05" value="0.1">
263
+ <input type="number" id="detailThresholdValue" class="number-input" min="0" max="1" step="0.05" value="0.1">
264
+ </div>
265
+ <div class="slider-container">
266
+ <label for="charSetSelect">Character Set:</label>
267
+ <select id="charSetSelect">
268
+ <option value="standard">Standard</option>
269
+ <option value="structure">Structure</option>
270
+ <option value="outline">Outline</option>
271
+ <option value="blocks">Blocks</option>
272
+ <option value="geometric">Geometric</option>
273
+ <option value="dots">Dots</option>
274
+ </select>
275
+ </div>
276
+ <div class="slider-container">
277
+ <label for="invertColorsCheckbox">
278
+ <input type="checkbox" id="invertColorsCheckbox"> Invert Colors
279
+ </label>
280
+ </div>
281
+ <button id="processBtn">Process Image</button>
282
+ </div>
283
+ <div class="ascii-output">
284
+ <h2>ASCII Output</h2>
285
+ <div id="asciiOutput" class="ascii-art"></div>
286
+ </div>
287
+ <div class="download-buttons">
288
+ <button id="downloadPreprocessed" class="download-btn">Download Preprocessed Image</button>
289
+ <button id="downloadStructureMap" class="download-btn">Download Structure Map</button>
290
+ <button id="downloadAscii" class="download-btn">Download ASCII Art</button>
291
+ </div>
292
+ </div>
293
+
294
+ <script>
295
+ let originalMat, preprocessedMat, structureMap;
296
+ let originalCanvas, preprocessedCanvas, structureMapCanvas;
297
+
298
+ function handleImageUpload(event) {
299
+ const file = event.target.files[0];
300
+ const reader = new FileReader();
301
+ reader.onload = function(e) {
302
+ const img = new Image();
303
+ img.onload = function() {
304
+ try {
305
+ originalMat = cv.imread(img);
306
+ cv.imshow('originalCanvas', originalMat);
307
+ processImage();
308
+ } catch (err) {
309
+ console.error('Error processing image:', err);
310
+ alert('Error processing image. Please try again.');
311
+ }
312
+ }
313
+ img.src = e.target.result;
314
+ }
315
+ reader.readAsDataURL(file);
316
+ }
317
+
318
+ function openCvReady() {
319
+ document.getElementById('loadingMessage').style.display = 'none';
320
+ document.getElementById('imageUpload').disabled = false;
321
+ document.getElementById('processBtn').disabled = false;
322
+
323
+ originalCanvas = document.getElementById('originalCanvas');
324
+ preprocessedCanvas = document.getElementById('preprocessedCanvas');
325
+ structureMapCanvas = document.getElementById('structureMapCanvas');
326
+
327
+ document.getElementById('imageUpload').addEventListener('change', handleImageUpload);
328
+ document.getElementById('processBtn').addEventListener('click', processImage);
329
+ document.getElementById('downloadPreprocessed').addEventListener('click', downloadPreprocessedImage);
330
+ document.getElementById('downloadStructureMap').addEventListener('click', downloadStructureMap);
331
+ document.getElementById('downloadAscii').addEventListener('click', downloadAsciiArt);
332
+ }
333
+ </script>
334
+ <script async src="https://docs.opencv.org/4.5.1/opencv.js" onload="openCvReady();" type="text/javascript"></script>
335
+ <script>
336
+ function structureLineExtraction(src) {
337
+ let dst = new cv.Mat();
338
+ cv.Canny(src, dst, 50, 150, 3, false);
339
+ return dst;
340
+ }
341
+
342
+ function edgeDetection(src) {
343
+ let dst = new cv.Mat();
344
+ let ksize = new cv.Size(3, 3);
345
+ let sobel_x = new cv.Mat();
346
+ let sobel_y = new cv.Mat();
347
+ cv.Sobel(src, sobel_x, cv.CV_64F, 1, 0, ksize);
348
+ cv.Sobel(src, sobel_y, cv.CV_64F, 0, 1, ksize);
349
+ cv.magnitude(sobel_x, sobel_y, dst);
350
+ cv.normalize(dst, dst, 0, 255, cv.NORM_MINMAX, cv.CV_8U);
351
+ return dst;
352
+ }
353
+
354
+ function preThinning(src) {
355
+ let dst = src.clone();
356
+ let rows = src.rows;
357
+ let cols = src.cols;
358
+
359
+ for (let i = 1; i < rows - 1; i++) {
360
+ for (let j = 1; j < cols - 1; j++) {
361
+ let p = src.ucharPtr(i, j)[0];
362
+ if (p === 0) continue;
363
+
364
+ let p1 = src.ucharPtr(i-1, j)[0];
365
+ let p3 = src.ucharPtr(i, j+1)[0];
366
+ let p5 = src.ucharPtr(i+1, j)[0];
367
+ let p7 = src.ucharPtr(i, j-1)[0];
368
+
369
+ let B_odd = p1 + p3 + p5 + p7;
370
+
371
+ if (B_odd < 2) {
372
+ dst.ucharPtr(i, j)[0] = 0;
373
+ } else if (B_odd > 2) {
374
+ dst.ucharPtr(i, j)[0] = 255;
375
+ }
376
+ }
377
+ }
378
+ return dst;
379
+ }
380
+
381
+ function improvedThinning(src, iterations) {
382
+ let dst = src.clone();
383
+ let changed;
384
+
385
+ for (let i = 0; i < iterations; i++) {
386
+ changed = false;
387
+ dst = preThinning(dst);
388
+ changed |= zhangSuenSubIteration(dst, 0);
389
+ changed |= zhangSuenSubIteration(dst, 1);
390
+ if (!changed) break;
391
+ }
392
+
393
+ return dst;
394
+ }
395
+
396
+ function processImage() {
397
+ if (!originalMat) {
398
+ console.error('No image loaded');
399
+ return;
400
+ }
401
+
402
+ try {
403
+ const edgeThreshold = parseInt(document.getElementById('edgeThresholdSlider').value);
404
+ const blurSize = parseFloat(document.getElementById('gaussianBlurSlider').value);
405
+ const iterations = parseInt(document.getElementById('thinningIterationsSlider').value);
406
+ const outputHeight = parseInt(document.getElementById('outputHeightSlider').value);
407
+ const scaleCount = parseInt(document.getElementById('scaleCountSlider').value);
408
+ const ncrfRadius = parseInt(document.getElementById('ncrfRadiusSlider').value);
409
+ const modulationStrength = parseFloat(document.getElementById('modulationStrengthSlider').value);
410
+ const detailThreshold = parseFloat(document.getElementById('detailThresholdSlider').value);
411
+
412
+ let blurred = new cv.Mat();
413
+ let gray = new cv.Mat();
414
+ let edges = new cv.Mat();
415
+
416
+ cv.GaussianBlur(originalMat, blurred, new cv.Size(0, 0), blurSize, blurSize, cv.BORDER_DEFAULT);
417
+ cv.cvtColor(blurred, gray, cv.COLOR_RGBA2GRAY);
418
+ cv.Canny(gray, edges, edgeThreshold, edgeThreshold * 2, 3, false);
419
+
420
+ preprocessedMat = improvedThinning(edges, iterations);
421
+ cv.imshow('preprocessedCanvas', preprocessedMat);
422
+
423
+ structureMap = extractStructure(gray, scaleCount, ncrfRadius, modulationStrength);
424
+ displayStructureMap(structureMap);
425
+
426
+ convertToASCII(structureMap, detailThreshold);
427
+
428
+ blurred.delete();
429
+ gray.delete();
430
+ edges.delete();
431
+ } catch (err) {
432
+ console.error('Error processing image:', err);
433
+ alert('Error processing image. Please try again.');
434
+ }
435
+ }
436
+
437
+ function zhangSuenThinning(src, iterations) {
438
+ let dst = src.clone();
439
+ let changed;
440
+
441
+ for (let i = 0; i < iterations; i++) {
442
+ changed = false;
443
+ changed |= zhangSuenSubIteration(dst, 0);
444
+ changed |= zhangSuenSubIteration(dst, 1);
445
+ if (!changed) break;
446
+ }
447
+
448
+ return dst;
449
+ }
450
+
451
+ function zhangSuenSubIteration(img, step) {
452
+ let changed = false;
453
+ let rows = img.rows;
454
+ let cols = img.cols;
455
+ let markers = new cv.Mat(rows, cols, cv.CV_8U, new cv.Scalar(0));
456
+
457
+ for (let i = 1; i < rows - 1; i++) {
458
+ for (let j = 1; j < cols - 1; j++) {
459
+ if (img.ucharPtr(i, j)[0] === 0) continue;
460
+
461
+ let p2 = img.ucharPtr(i-1, j)[0];
462
+ let p3 = img.ucharPtr(i-1, j+1)[0];
463
+ let p4 = img.ucharPtr(i, j+1)[0];
464
+ let p5 = img.ucharPtr(i+1, j+1)[0];
465
+ let p6 = img.ucharPtr(i+1, j)[0];
466
+ let p7 = img.ucharPtr(i+1, j-1)[0];
467
+ let p8 = img.ucharPtr(i, j-1)[0];
468
+ let p9 = img.ucharPtr(i-1, j-1)[0];
469
+
470
+ let A = ((p2 === 0 && p3 === 255) + (p3 === 0 && p4 === 255) +
471
+ (p4 === 0 && p5 === 255) + (p5 === 0 && p6 === 255) +
472
+ (p6 === 0 && p7 === 255) + (p7 === 0 && p8 === 255) +
473
+ (p8 === 0 && p9 === 255) + (p9 === 0 && p2 === 255));
474
+ let B = p2 + p3 + p4 + p5 + p6 + p7 + p8 + p9;
475
+
476
+ let m1 = step === 0 ? (p2 * p4 * p6) : (p2 * p4 * p8);
477
+ let m2 = step === 0 ? (p4 * p6 * p8) : (p2 * p6 * p8);
478
+
479
+ if (A === 1 && (B >= 2 * 255 && B <= 6 * 255) && m1 === 0 && m2 === 0) {
480
+ markers.ucharPtr(i, j)[0] = 255;
481
+ changed = true;
482
+ }
483
+ }
484
+ }
485
+
486
+ for (let i = 0; i < rows; i++) {
487
+ for (let j = 0; j < cols; j++) {
488
+ if (markers.ucharPtr(i, j)[0] === 255) {
489
+ img.ucharPtr(i, j)[0] = 0;
490
+ }
491
+ }
492
+ }
493
+
494
+ markers.delete();
495
+ return changed;
496
+ }
497
+
498
+ function extractStructure(grayMat, scaleCount, ncrfRadius, modulationStrength) {
499
+ const width = grayMat.cols;
500
+ const height = grayMat.rows;
501
+ const data = grayMat.data;
502
+
503
+ const scales = [];
504
+ for (let i = 0; i < scaleCount; i++) {
505
+ scales.push(Math.pow(2, i));
506
+ }
507
+ let structureMap = new Array(width * height).fill(0);
508
+
509
+ for (let scale of scales) {
510
+ const response = simulateCRFResponse(data, width, height, scale);
511
+ for (let i = 0; i < structureMap.length; i++) {
512
+ structureMap[i] = Math.max(structureMap[i], response[i]);
513
+ }
514
+ }
515
+
516
+ structureMap = applyNonCRFModulation(structureMap, width, height, ncrfRadius, modulationStrength);
517
+ return structureMap;
518
+ }
519
+
520
+ function simulateCRFResponse(data, width, height, scale) {
521
+ const response = new Array(width * height).fill(0);
522
+
523
+ for (let y = 0; y < height; y++) {
524
+ for (let x = 0; x < width; x++) {
525
+ const i = y * width + x;
526
+ const grayValue = data[i];
527
+
528
+ if (x > scale && x < width - scale && y > scale && y < height - scale) {
529
+ const diff = Math.abs(grayValue - data[y * width + x - scale]) +
530
+ Math.abs(grayValue - data[y * width + x + scale]) +
531
+ Math.abs(grayValue - data[(y - scale) * width + x]) +
532
+ Math.abs(grayValue - data[(y + scale) * width + x]);
533
+ response[i] = diff / (4 * 255);
534
+ }
535
+ }
536
+ }
537
+
538
+ return response;
539
+ }
540
+
541
+ function applyNonCRFModulation(structureMap, width, height, radius, strength) {
542
+ const modulated = new Array(width * height).fill(0);
543
+
544
+ for (let y = 0; y < height; y++) {
545
+ for (let x = 0; x < width; x++) {
546
+ const i = y * width + x;
547
+ let sum = 0;
548
+ let count = 0;
549
+
550
+ for (let dy = -radius; dy <= radius; dy++) {
551
+ for (let dx = -radius; dx <= radius; dx++) {
552
+ const nx = x + dx;
553
+ const ny = y + dy;
554
+ if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
555
+ sum += structureMap[ny * width + nx];
556
+ count++;
557
+ }
558
+ }
559
+ }
560
+
561
+ const avgResponse = sum / count;
562
+ modulated[i] = structureMap[i] * (1 - strength * avgResponse);
563
+ }
564
+ }
565
+
566
+ return modulated;
567
+ }
568
+
569
+ function displayStructureMap(structureMap) {
570
+ const width = preprocessedCanvas.width;
571
+ const height = preprocessedCanvas.height;
572
+ const ctx = structureMapCanvas.getContext('2d');
573
+ const imageData = ctx.createImageData(width, height);
574
+
575
+ for (let i = 0; i < structureMap.length; i++) {
576
+ const value = Math.floor(structureMap[i] * 255);
577
+ imageData.data[i * 4] = value;
578
+ imageData.data[i * 4 + 1] = value;
579
+ imageData.data[i * 4 + 2] = value;
580
+ imageData.data[i * 4 + 3] = 255;
581
+ }
582
+
583
+ structureMapCanvas.width = width;
584
+ structureMapCanvas.height = height;
585
+ ctx.putImageData(imageData, 0, 0);
586
+ }
587
+
588
+ function convertToASCII(structureMap, detailThreshold) {
589
+ const width = preprocessedCanvas.width;
590
+ const height = preprocessedCanvas.height;
591
+ const outputHeight = parseInt(document.getElementById('outputHeightSlider').value);
592
+ const outputWidth = Math.floor(outputHeight * width / height);
593
+ const charSetType = document.getElementById('charSetSelect').value;
594
+ const invert = document.getElementById('invertColorsCheckbox').checked;
595
+
596
+ const charSets = {
597
+ standard: '@%#*+=-:. ',
598
+ structure: '.,/\\_|~\'*+^][}{ ',
599
+ outline: 'β–“β–’β–‘ ',
600
+ blocks: 'β–ˆβ–“β–’β–‘ ',
601
+ geometric: 'β– β–‘β–’β–£β–€β–₯β–¦β–§β–¨β–© ',
602
+ dots: 'β€’Β·β—‹β—β—Œβ—β—Žβ—‰ '
603
+ };
604
+ const characters = charSets[charSetType];
605
+
606
+ let asciiArt = '';
607
+ for (let y = 0; y < outputHeight; y++) {
608
+ for (let x = 0; x < outputWidth; x++) {
609
+ const srcX = Math.floor(x * width / outputWidth);
610
+ const srcY = Math.floor(y * height / outputHeight);
611
+ const value = structureMap[srcY * width + srcX];
612
+
613
+ let charIndex = Math.floor(value * (characters.length - 1));
614
+ if (invert) {
615
+ charIndex = characters.length - 1 - charIndex;
616
+ }
617
+ asciiArt += value > detailThreshold ? characters[charIndex] : ' ';
618
+ }
619
+ asciiArt += '\n';
620
+ }
621
+
622
+ document.getElementById('asciiOutput').textContent = asciiArt;
623
+ }
624
+
625
+ function downloadPreprocessedImage() {
626
+ if (!preprocessedCanvas.toDataURL) {
627
+ alert('No preprocessed image available.');
628
+ return;
629
+ }
630
+
631
+ const link = document.createElement('a');
632
+ link.download = 'preprocessed_image.png';
633
+ link.href = preprocessedCanvas.toDataURL();
634
+ link.click();
635
+ }
636
+
637
+ function downloadStructureMap() {
638
+ if (!structureMapCanvas.toDataURL) {
639
+ alert('No structure map available.');
640
+ return;
641
+ }
642
+
643
+ const link = document.createElement('a');
644
+ link.download = 'structure_map.png';
645
+ link.href = structureMapCanvas.toDataURL();
646
+ link.click();
647
+ }
648
+
649
+ function downloadAsciiArt() {
650
+ const asciiArt = document.getElementById('asciiOutput').textContent;
651
+ if (!asciiArt.trim()) {
652
+ alert('No ASCII art available.');
653
+ return;
654
+ }
655
+
656
+ const blob = new Blob([asciiArt], {type: 'text/plain'});
657
+ const link = document.createElement('a');
658
+ link.download = 'ascii_art.txt';
659
+ link.href = URL.createObjectURL(blob);
660
+ link.click();
661
+ }
662
+
663
+ document.getElementById('edgeThresholdSlider').addEventListener('input', function(e) {
664
+ document.getElementById('edgeThresholdValue').textContent = e.target.value;
665
+ });
666
+ document.getElementById('gaussianBlurSlider').addEventListener('input', function(e) {
667
+ document.getElementById('gaussianBlurValue').textContent = e.target.value;
668
+ });
669
+ document.getElementById('thinningIterationsSlider').addEventListener('input', function(e) {
670
+ document.getElementById('thinningIterationsValue').textContent = e.target.value;
671
+ });
672
+ document.getElementById('scaleSlider').addEventListener('input', function(e) {
673
+ document.getElementById('scaleValue').textContent = e.target.value;
674
+ });
675
+ document.getElementById('dogThresholdSlider').addEventListener('input', function(e) {
676
+ document.getElementById('dogThresholdValue').textContent = e.target.value;
677
+ });
678
+
679
+ function syncInputs(slider, value) {
680
+ slider.addEventListener('input', () => value.value = slider.value);
681
+ value.addEventListener('input', () => slider.value = value.value);
682
+ }
683
+
684
+ syncInputs(document.getElementById('outputHeightSlider'), document.getElementById('outputHeightValue'));
685
+ syncInputs(document.getElementById('scaleCountSlider'), document.getElementById('scaleCountValue'));
686
+ syncInputs(document.getElementById('ncrfRadiusSlider'), document.getElementById('ncrfRadiusValue'));
687
+ syncInputs(document.getElementById('modulationStrengthSlider'), document.getElementById('modulationStrengthValue'));
688
+ syncInputs(document.getElementById('detailThresholdSlider'), document.getElementById('detailThresholdValue'));
689
+
690
+ const sliders = document.querySelectorAll('.slider');
691
+ sliders.forEach(slider => {
692
+ slider.addEventListener('input', processImage);
693
+ });
694
+
695
+ document.getElementById('imageUpload').disabled = true;
696
+ document.getElementById('processBtn').disabled = true;
697
+ </script>
698
+ </body>
699
+ </html>