Last active
October 17, 2025 23:44
-
-
Save twobob/93f504e3120c8369da1aeb6298b0ac77 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| export interface DeSepAIOptions { | |
| strength?: number | |
| notchWhiten?: boolean | |
| shiftBase?: [number, number, number] | |
| preserveDark?: boolean | |
| } | |
| export interface ImageData { | |
| data: Uint8ClampedArray | |
| width: number | |
| height: number | |
| } | |
| export function detectShiftBase(imageData: ImageData): [number, number, number] { | |
| const { data } = imageData | |
| const lum: number[] = [] | |
| for (let i = 0; i < data.length; i += 4) | |
| lum.push(0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2]) | |
| const sorted = lum.slice().sort((a, b) => a - b) | |
| const threshold = sorted[Math.floor(sorted.length * 0.9)] | |
| let sum = [0, 0, 0] | |
| let count = 0 | |
| for (let i = 0; i < lum.length; i++) { | |
| if (lum[i] >= threshold) { | |
| const idx = i * 4 | |
| sum[0] += data[idx] | |
| sum[1] += data[idx + 1] | |
| sum[2] += data[idx + 2] | |
| count++ | |
| } | |
| } | |
| const avg: [number, number, number] = count | |
| ? [sum[0] / count, sum[1] / count, sum[2] / count] | |
| : [255, 255, 255] | |
| return [255 - avg[0], 255 - avg[1], 255 - avg[2]] | |
| } | |
| export function processPixelData( | |
| imageData: ImageData, | |
| options: DeSepAIOptions = {} | |
| ): ImageData { | |
| const { | |
| strength = 100, | |
| notchWhiten = false, | |
| shiftBase = [9, 15, 27], | |
| preserveDark = strength > 50 | |
| } = options | |
| // Create new pixel data array (don't modify original) | |
| const d = new Uint8ClampedArray(imageData.data) | |
| const scale = strength / 100 | |
| if (notchWhiten) { | |
| const refR = 255 - shiftBase[0] | |
| const refG = 255 - shiftBase[1] | |
| const refB = 255 - shiftBase[2] | |
| const notch = 8 | |
| for (let i = 0; i < d.length; i += 4) { | |
| const r = d[i], g = d[i + 1], b = d[i + 2] | |
| const bri = 0.2126 * r + 0.7152 * g + 0.0722 * b | |
| const att = preserveDark ? Math.min(1, bri / 128) : 1 | |
| if ( | |
| r >= refR - notch && r <= refR + notch && | |
| g >= refG - notch && g <= refG + notch && | |
| b >= refB - notch && b <= refB + notch | |
| ) { | |
| d[i] = Math.min(255, r + (255 - r) * scale * att) | |
| d[i + 1] = Math.min(255, g + (255 - g) * scale * att) | |
| d[i + 2] = Math.min(255, b + (255 - b) * scale * att) | |
| } else { | |
| d[i] = Math.min(255, r + shiftBase[0] * scale * att) | |
| d[i + 1] = Math.min(255, g + shiftBase[1] * scale * att) | |
| d[i + 2] = Math.min(255, b + shiftBase[2] * scale * att) | |
| } | |
| } | |
| } else { | |
| for (let i = 0; i < d.length; i += 4) { | |
| const r = d[i], g = d[i + 1], b = d[i + 2] | |
| const bri = 0.2126 * r + 0.7152 * g + 0.0722 * b | |
| const att = preserveDark ? Math.min(1, bri / 128) : 1 | |
| d[i] = Math.min(255, r + shiftBase[0] * scale * att) | |
| d[i + 1] = Math.min(255, g + shiftBase[1] * scale * att) | |
| d[i + 2] = Math.min(255, b + shiftBase[2] * scale * att) | |
| } | |
| } | |
| return { | |
| data: d, | |
| width: imageData.width, | |
| height: imageData.height | |
| } | |
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>DeSepAI Image Processor Test</title> | |
| <style> | |
| body { | |
| font-family: Arial, sans-serif; | |
| max-width: 1200px; | |
| margin: 0 auto; | |
| padding: 20px; | |
| background-color: #f5f5f5; | |
| } | |
| .container { | |
| background: white; | |
| padding: 30px; | |
| border-radius: 10px; | |
| box-shadow: 0 2px 10px rgba(0,0,0,0.1); | |
| } | |
| h1 { | |
| color: #333; | |
| text-align: center; | |
| margin-bottom: 30px; | |
| } | |
| .controls { | |
| background: #f8f9fa; | |
| padding: 20px; | |
| border-radius: 8px; | |
| margin-bottom: 30px; | |
| } | |
| .control-group { | |
| margin-bottom: 15px; | |
| display: flex; | |
| align-items: center; | |
| gap: 10px; | |
| } | |
| label { | |
| font-weight: bold; | |
| min-width: 120px; | |
| } | |
| input[type="file"] { | |
| padding: 8px; | |
| border: 2px dashed #ddd; | |
| border-radius: 5px; | |
| background: white; | |
| } | |
| input[type="range"] { | |
| flex: 1; | |
| margin: 0 10px; | |
| } | |
| input[type="number"] { | |
| width: 60px; | |
| padding: 5px; | |
| border: 1px solid #ddd; | |
| border-radius: 3px; | |
| } | |
| input[type="checkbox"] { | |
| transform: scale(1.2); | |
| } | |
| .range-value { | |
| font-weight: bold; | |
| color: #007bff; | |
| min-width: 40px; | |
| } | |
| .shift-base { | |
| display: flex; | |
| gap: 10px; | |
| align-items: center; | |
| } | |
| .shift-base input { | |
| width: 50px; | |
| } | |
| button { | |
| background: #007bff; | |
| color: white; | |
| border: none; | |
| padding: 10px 20px; | |
| border-radius: 5px; | |
| cursor: pointer; | |
| font-size: 16px; | |
| } | |
| button:hover { | |
| background: #0056b3; | |
| } | |
| button:disabled { | |
| background: #ccc; | |
| cursor: not-allowed; | |
| } | |
| .results { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 20px; | |
| margin-top: 30px; | |
| } | |
| .image-container { | |
| text-align: center; | |
| } | |
| .image-container h3 { | |
| margin-bottom: 10px; | |
| color: #333; | |
| } | |
| .image-container img { | |
| max-width: 100%; | |
| max-height: 400px; | |
| border: 1px solid #ddd; | |
| border-radius: 5px; | |
| box-shadow: 0 2px 5px rgba(0,0,0,0.1); | |
| } | |
| .error { | |
| background: #f8d7da; | |
| color: #721c24; | |
| padding: 10px; | |
| border-radius: 5px; | |
| margin: 10px 0; | |
| } | |
| .info { | |
| background: #d1ecf1; | |
| color: #0c5460; | |
| padding: 10px; | |
| border-radius: 5px; | |
| margin: 10px 0; | |
| } | |
| .stats { | |
| background: #e2e3e5; | |
| padding: 15px; | |
| border-radius: 5px; | |
| margin-top: 20px; | |
| } | |
| .preset-buttons { | |
| display: flex; | |
| gap: 10px; | |
| margin-top: 10px; | |
| } | |
| .preset-buttons button { | |
| background: #6c757d; | |
| font-size: 14px; | |
| padding: 5px 10px; | |
| } | |
| .preset-buttons button:hover { | |
| background: #5a6268; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="container"> | |
| <h1>DeSepAI Image Processor Test</h1> | |
| <div class="controls"> | |
| <div class="control-group"> | |
| <label for="fileInput">Select Image:</label> | |
| <input type="file" id="fileInput" accept="image/*"> | |
| </div> | |
| <div class="control-group"> | |
| <label for="strength">Strength:</label> | |
| <input type="range" id="strength" min="0" max="200" value="120"> | |
| <span class="range-value" id="strengthValue">120</span> | |
| </div> | |
| <div class="control-group"> | |
| <label for="notchWhiten">Notch Whiten:</label> | |
| <input type="checkbox" id="notchWhiten" checked> | |
| </div> | |
| <div class="control-group"> | |
| <label>Shift Base (RGB):</label> | |
| <div class="shift-base"> | |
| <span>R:</span><input type="number" id="shiftR" value="9" min="0" max="255"> | |
| <span>G:</span><input type="number" id="shiftG" value="15" min="0" max="255"> | |
| <span>B:</span><input type="number" id="shiftB" value="27" min="0" max="255"> | |
| </div> | |
| </div> | |
| <div class="preset-buttons"> | |
| <button onclick="setPreset('default')">Default</button> | |
| <button onclick="setPreset('subtle')">Subtle</button> | |
| <button onclick="setPreset('strong')">Strong</button> | |
| <button onclick="setPreset('auto')">Auto Detect</button> | |
| </div> | |
| <div class="control-group" style="margin-top: 20px;"> | |
| <button id="processBtn" onclick="processImage()" disabled>Process Image</button> | |
| <button onclick="downloadResult()" id="downloadBtn" disabled>Download Result</button> | |
| </div> | |
| </div> | |
| <div id="errorContainer"></div> | |
| <div id="infoContainer"></div> | |
| <div class="results" id="results" style="display: none;"> | |
| <div class="image-container"> | |
| <h3>Original Image</h3> | |
| <img id="originalImage" alt="Original"> | |
| </div> | |
| <div class="image-container"> | |
| <h3>Processed Image</h3> | |
| <img id="processedImage" alt="Processed"> | |
| </div> | |
| </div> | |
| <div id="stats" class="stats" style="display: none;"> | |
| <h4>Processing Stats</h4> | |
| <div id="statsContent"></div> | |
| </div> | |
| </div> | |
| <script> | |
| console.log('Starting DeSepAI Test Page Load...'); | |
| // Global variables | |
| let currentFile = null; | |
| let processedBase64 = null; | |
| // DeSepAI Core Functions (UI-agnostic, converted from TypeScript) | |
| function detectShiftBase(imageData) { | |
| console.log('Detecting shift base from image data...'); | |
| const { data } = imageData; | |
| const lum = []; | |
| for (let i = 0; i < data.length; i += 4) { | |
| lum.push(0.2126 * data[i] + 0.7152 * data[i + 1] + 0.0722 * data[i + 2]); | |
| } | |
| const sorted = lum.slice().sort((a, b) => a - b); | |
| const threshold = sorted[Math.floor(sorted.length * 0.9)]; | |
| let sum = [0, 0, 0]; | |
| let count = 0; | |
| for (let i = 0; i < lum.length; i++) { | |
| if (lum[i] >= threshold) { | |
| const idx = i * 4; | |
| sum[0] += data[idx]; | |
| sum[1] += data[idx + 1]; | |
| sum[2] += data[idx + 2]; | |
| count++; | |
| } | |
| } | |
| const avg = count | |
| ? [sum[0] / count, sum[1] / count, sum[2] / count] | |
| : [255, 255, 255]; | |
| const result = [255 - avg[0], 255 - avg[1], 255 - avg[2]]; | |
| console.log('Detected shift base:', result); | |
| return result; | |
| } | |
| function processPixelData(imageData, options = {}) { | |
| console.log('Processing pixel data with options:', options); | |
| const { | |
| strength = 100, | |
| notchWhiten = false, | |
| shiftBase = [9, 15, 27], | |
| preserveDark = strength > 50 | |
| } = options; | |
| // Create new pixel data array (don't modify original) | |
| const d = new Uint8ClampedArray(imageData.data); | |
| const scale = strength / 100; | |
| if (notchWhiten) { | |
| const refR = 255 - shiftBase[0]; | |
| const refG = 255 - shiftBase[1]; | |
| const refB = 255 - shiftBase[2]; | |
| const notch = 8; | |
| for (let i = 0; i < d.length; i += 4) { | |
| const r = d[i], g = d[i + 1], b = d[i + 2]; | |
| const bri = 0.2126 * r + 0.7152 * g + 0.0722 * b; | |
| const att = preserveDark ? Math.min(1, bri / 128) : 1; | |
| if ( | |
| r >= refR - notch && r <= refR + notch && | |
| g >= refG - notch && g <= refG + notch && | |
| b >= refB - notch && b <= refB + notch | |
| ) { | |
| d[i] = Math.min(255, r + (255 - r) * scale * att); | |
| d[i + 1] = Math.min(255, g + (255 - g) * scale * att); | |
| d[i + 2] = Math.min(255, b + (255 - b) * scale * att); | |
| } else { | |
| d[i] = Math.min(255, r + shiftBase[0] * scale * att); | |
| d[i + 1] = Math.min(255, g + shiftBase[1] * scale * att); | |
| d[i + 2] = Math.min(255, b + shiftBase[2] * scale * att); | |
| } | |
| } | |
| } else { | |
| for (let i = 0; i < d.length; i += 4) { | |
| const r = d[i], g = d[i + 1], b = d[i + 2]; | |
| const bri = 0.2126 * r + 0.7152 * g + 0.0722 * b; | |
| const att = preserveDark ? Math.min(1, bri / 128) : 1; | |
| d[i] = Math.min(255, r + shiftBase[0] * scale * att); | |
| d[i + 1] = Math.min(255, g + shiftBase[1] * scale * att); | |
| d[i + 2] = Math.min(255, b + shiftBase[2] * scale * att); | |
| } | |
| } | |
| console.log('Pixel processing completed'); | |
| return { | |
| data: d, | |
| width: imageData.width, | |
| height: imageData.height | |
| }; | |
| } | |
| // UI-specific image handling functions | |
| async function getImageDataFromBase64(base64) { | |
| return new Promise((resolve, reject) => { | |
| const img = new Image(); | |
| img.onload = () => { | |
| const canvas = document.createElement("canvas"); | |
| const ctx = canvas.getContext("2d"); | |
| canvas.width = img.naturalWidth; | |
| canvas.height = img.naturalHeight; | |
| ctx.drawImage(img, 0, 0); | |
| const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height); | |
| resolve({ | |
| data: imageData.data, | |
| width: canvas.width, | |
| height: canvas.height | |
| }); | |
| }; | |
| img.onerror = () => reject(new Error("Failed to load image")); | |
| img.src = base64; | |
| }); | |
| } | |
| function imageDataToBase64(imageData) { | |
| const canvas = document.createElement("canvas"); | |
| const ctx = canvas.getContext("2d"); | |
| canvas.width = imageData.width; | |
| canvas.height = imageData.height; | |
| const canvasImageData = ctx.createImageData(imageData.width, imageData.height); | |
| canvasImageData.data.set(imageData.data); | |
| ctx.putImageData(canvasImageData, 0, 0); | |
| return canvas.toDataURL("image/png"); | |
| } | |
| async function fileToBase64(file) { | |
| return new Promise((resolve, reject) => { | |
| const reader = new FileReader(); | |
| reader.onload = () => resolve(reader.result); | |
| reader.onerror = () => reject(new Error("Failed to read file")); | |
| reader.readAsDataURL(file); | |
| }); | |
| } | |
| // High-level processing functions combining UI and core logic | |
| async function processBase64ToBase64(inputBase64, options) { | |
| const imageData = await getImageDataFromBase64(inputBase64); | |
| const processedImageData = processPixelData(imageData, options); | |
| return imageDataToBase64(processedImageData); | |
| } | |
| async function processFileBase64(file, options) { | |
| console.log('Processing file to base64:', file.name); | |
| const inputBase64 = await fileToBase64(file); | |
| return processBase64ToBase64(inputBase64, options); | |
| } | |
| // UI Event Handlers | |
| function setupEventListeners() { | |
| console.log('Setting up event listeners...'); | |
| const fileInput = document.getElementById('fileInput'); | |
| const strengthSlider = document.getElementById('strength'); | |
| const strengthValue = document.getElementById('strengthValue'); | |
| if (!fileInput || !strengthSlider || !strengthValue) { | |
| console.error('Critical UI elements not found!'); | |
| return; | |
| } | |
| fileInput.addEventListener('change', function(e) { | |
| console.log('File input changed:', e.target.files); | |
| if (e.target.files && e.target.files[0]) { | |
| currentFile = e.target.files[0]; | |
| console.log('File selected:', currentFile.name, 'Size:', currentFile.size, 'Type:', currentFile.type); | |
| document.getElementById('processBtn').disabled = false; | |
| // Show original image | |
| const reader = new FileReader(); | |
| reader.onload = function(e) { | |
| console.log('Image loaded for preview'); | |
| document.getElementById('originalImage').src = e.target.result; | |
| document.getElementById('results').style.display = 'grid'; | |
| }; | |
| reader.readAsDataURL(currentFile); | |
| clearMessages(); | |
| } | |
| }); | |
| strengthSlider.addEventListener('input', function(e) { | |
| strengthValue.textContent = e.target.value; | |
| }); | |
| console.log('Event listeners set up successfully'); | |
| } | |
| function setPreset(preset) { | |
| console.log('Setting preset:', preset); | |
| const strengthSlider = document.getElementById('strength'); | |
| const strengthValue = document.getElementById('strengthValue'); | |
| const notchWhiten = document.getElementById('notchWhiten'); | |
| const shiftR = document.getElementById('shiftR'); | |
| const shiftG = document.getElementById('shiftG'); | |
| const shiftB = document.getElementById('shiftB'); | |
| switch(preset) { | |
| case 'default': | |
| strengthSlider.value = 120; | |
| notchWhiten.checked = true; | |
| shiftR.value = 9; | |
| shiftG.value = 15; | |
| shiftB.value = 27; | |
| break; | |
| case 'subtle': | |
| strengthSlider.value = 60; | |
| notchWhiten.checked = false; | |
| shiftR.value = 5; | |
| shiftG.value = 8; | |
| shiftB.value = 12; | |
| break; | |
| case 'strong': | |
| strengthSlider.value = 180; | |
| notchWhiten.checked = true; | |
| shiftR.value = 15; | |
| shiftG.value = 25; | |
| shiftB.value = 40; | |
| break; | |
| case 'auto': | |
| if (currentFile) { | |
| autoDetectShiftBase(); | |
| return; | |
| } else { | |
| showError('Please select an image first for auto-detection'); | |
| return; | |
| } | |
| } | |
| strengthValue.textContent = strengthSlider.value; | |
| console.log('Preset applied:', preset); | |
| } | |
| async function autoDetectShiftBase() { | |
| if (!currentFile) { | |
| showError('Please select an image first'); | |
| return; | |
| } | |
| try { | |
| showInfo('Auto-detecting optimal shift base values...'); | |
| console.log('Starting auto-detection with image data...'); | |
| const inputBase64 = await fileToBase64(currentFile); | |
| const imageData = await getImageDataFromBase64(inputBase64); | |
| const detectedShift = detectShiftBase(imageData); | |
| document.getElementById('shiftR').value = Math.round(detectedShift[0]); | |
| document.getElementById('shiftG').value = Math.round(detectedShift[1]); | |
| document.getElementById('shiftB').value = Math.round(detectedShift[2]); | |
| showInfo(`Auto-detected shift base: R=${Math.round(detectedShift[0])}, G=${Math.round(detectedShift[1])}, B=${Math.round(detectedShift[2])}`); | |
| } catch (error) { | |
| showError('Auto-detection failed: ' + error.message); | |
| console.error('Auto-detection error:', error); | |
| } | |
| } | |
| async function processImage() { | |
| console.log('Process image button clicked'); | |
| if (!currentFile) { | |
| console.error('No file selected'); | |
| showError('Please select an image first'); | |
| return; | |
| } | |
| try { | |
| clearMessages(); | |
| showInfo('Processing image...'); | |
| console.log('Starting image processing...'); | |
| const startTime = performance.now(); | |
| const options = { | |
| strength: parseInt(document.getElementById('strength').value), | |
| notchWhiten: document.getElementById('notchWhiten').checked, | |
| shiftBase: [ | |
| parseInt(document.getElementById('shiftR').value), | |
| parseInt(document.getElementById('shiftG').value), | |
| parseInt(document.getElementById('shiftB').value) | |
| ] | |
| }; | |
| console.log('Processing options:', options); | |
| processedBase64 = await processFileBase64(currentFile, options); | |
| const endTime = performance.now(); | |
| const processingTime = Math.round(endTime - startTime); | |
| console.log('Processing completed in', processingTime, 'ms'); | |
| console.log('Result base64 length:', processedBase64.length); | |
| document.getElementById('processedImage').src = processedBase64; | |
| document.getElementById('downloadBtn').disabled = false; | |
| // Show stats | |
| showStats(options, processingTime, currentFile); | |
| showInfo(`Image processed successfully in ${processingTime}ms`); | |
| } catch (error) { | |
| console.error('Processing failed:', error); | |
| showError('Failed to process image: ' + error.message); | |
| } | |
| } | |
| function downloadResult() { | |
| if (!processedBase64) { | |
| showError('No processed image to download'); | |
| return; | |
| } | |
| const link = document.createElement('a'); | |
| link.download = 'processed_' + (currentFile.name || 'image.png'); | |
| link.href = processedBase64; | |
| link.click(); | |
| } | |
| function showStats(options, processingTime, file) { | |
| const statsContent = document.getElementById('statsContent'); | |
| const fileSizeKB = Math.round(file.size / 1024); | |
| statsContent.innerHTML = ` | |
| <strong>File:</strong> ${file.name} (${fileSizeKB} KB)<br> | |
| <strong>Processing Time:</strong> ${processingTime}ms<br> | |
| <strong>Strength:</strong> ${options.strength}<br> | |
| <strong>Notch Whiten:</strong> ${options.notchWhiten ? 'Yes' : 'No'}<br> | |
| <strong>Shift Base:</strong> R=${options.shiftBase[0]}, G=${options.shiftBase[1]}, B=${options.shiftBase[2]}<br> | |
| <strong>File Type:</strong> ${file.type} | |
| `; | |
| document.getElementById('stats').style.display = 'block'; | |
| } | |
| function showError(message) { | |
| const container = document.getElementById('errorContainer'); | |
| container.innerHTML = `<div class="error">${message}</div>`; | |
| } | |
| function showInfo(message) { | |
| const container = document.getElementById('infoContainer'); | |
| container.innerHTML = `<div class="info">${message}</div>`; | |
| } | |
| function clearMessages() { | |
| document.getElementById('errorContainer').innerHTML = ''; | |
| document.getElementById('infoContainer').innerHTML = ''; | |
| } | |
| // Initialize page | |
| document.addEventListener('DOMContentLoaded', function() { | |
| console.log('DOMContentLoaded - Initializing page...'); | |
| console.log('Function availability check:', { | |
| detectShiftBase: typeof detectShiftBase, | |
| processBase64ToBase64: typeof processBase64ToBase64, | |
| processFileBase64: typeof processFileBase64, | |
| getImageDataFromBase64: typeof getImageDataFromBase64, | |
| fileToBase64: typeof fileToBase64 | |
| }); | |
| console.log('UI Elements check:', { | |
| fileInput: !!document.getElementById('fileInput'), | |
| strengthSlider: !!document.getElementById('strength'), | |
| processBtn: !!document.getElementById('processBtn'), | |
| originalImage: !!document.getElementById('originalImage'), | |
| processedImage: !!document.getElementById('processedImage') | |
| }); | |
| setupEventListeners(); | |
| loadDefaultImage(); | |
| showInfo('Page loaded successfully! Default image (fish.png) loaded. You can select a different image or process the default one.'); | |
| console.log('DeSepAI Test Page fully initialized and ready!'); | |
| }); | |
| // Load default fish.png image | |
| async function loadDefaultImage() { | |
| try { | |
| console.log('Loading default fish.png image...'); | |
| console.log('Current working directory:', window.location.href); | |
| // Fetch fish.png and convert to File object | |
| const response = await fetch('./fish.png'); | |
| console.log('Fetch response status:', response.status, response.statusText); | |
| if (!response.ok) { | |
| throw new Error(`Failed to fetch fish.png: ${response.status} ${response.statusText}`); | |
| } | |
| const blob = await response.blob(); | |
| console.log('Blob created, size:', blob.size, 'type:', blob.type); | |
| const file = new File([blob], 'fish.png', { type: 'image/png' }); | |
| console.log('File object created:', file.name, file.size, file.type); | |
| // Set as current file | |
| currentFile = file; | |
| console.log('currentFile set to:', currentFile.name); | |
| // Show original image | |
| const base64 = await fileToBase64(file); | |
| console.log('Base64 conversion successful, length:', base64.length); | |
| const originalImg = document.getElementById('originalImage'); | |
| const resultsDiv = document.getElementById('results'); | |
| const processBtn = document.getElementById('processBtn'); | |
| if (!originalImg || !resultsDiv || !processBtn) { | |
| throw new Error('Required DOM elements not found'); | |
| } | |
| originalImg.src = base64; | |
| resultsDiv.style.display = 'grid'; | |
| processBtn.disabled = false; | |
| console.log('Default fish.png image loaded successfully'); | |
| showInfo('Fish.png loaded as default image. Ready to process!'); | |
| } catch (error) { | |
| console.error('Failed to load default image:', error); | |
| showError('Failed to load default fish.png image: ' + error.message); | |
| showInfo('Select an image file to begin processing'); | |
| } | |
| } | |
| </script> | |
| </body> | |
| </html> |
Author
twobob
commented
Oct 17, 2025
fish.png
the html is to show the kind of the things the .ts can do
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment