import { TrustMark } from './dist/index.js' // ====================================================================== // Global variables // ====================================================================== let MODE = 'Q' // TrustMark Available modes: C=compact, Q=quality, B=base let ENCODING_TYPE = TrustMark.encoding.BCH_4 let WM_STRENGTH = 0.4; let JPEG_QUALITY = 0.9; // ====================================================================== // DOM Elements // ====================================================================== let status_element = document.getElementById("status"); let result_element = document.getElementById("result"); let secret_element = document.getElementById('secret'); let image_container = document.getElementById("image_container"); let display_img = document.getElementById("display_img"); let residual_img = document.getElementById("residual_img"); let set_wm_strength_slider = document.getElementById("set_wm_strength"); let wm_strength = document.getElementById('wm_strength') let encode_button = document.getElementById("encode_button"); let erase_button = document.getElementById("erase_button"); let model_type_toggle = document.getElementById("toggle"); let fileUpload = document.getElementById("upload"); let fileDownload = document.getElementById("download"); let tooltip = document.getElementById("tooltip"); let processing_element = document.getElementById('processing'); window.addEventListener('status', (event) => { console.log(event.detail); status_element.textContent = event.detail }); wm_strength.textContent = WM_STRENGTH let current_image; let tm = new TrustMark({ verbose: false, model_type: MODE, encoding_type: ENCODING_TYPE }) showSpinner() status_element.textContent = 'Loading models...' await tm.loadModels() status_element.textContent = 'Trustmark initialized.' hideSpinner() // ====================================================================== // UI functions // ====================================================================== model_type_toggle.addEventListener("click", async b => { if(model_type_toggle.checked===true){await tm.loadModels('P')}; if(model_type_toggle.checked===false){await tm.loadModels('Q')}; }); encode_button.addEventListener("click", async b => { encode().catch(e => { console.error(e) }); }); erase_button.addEventListener("click", async b => { erase().catch(e => { console.error(e) }); }); set_wm_strength_slider.addEventListener("input", b => { WM_STRENGTH = parseInt(set_wm_strength_slider.value) / 10; wm_strength.textContent = WM_STRENGTH }); image_container.addEventListener("dragover", (event) => { event.preventDefault(); image_container.style.backgroundColor = "#e5e7eb"; }); image_container.addEventListener("drop", async (event) => { event.preventDefault() image_container.style.backgroundColor = "unset"; tooltip.style.display = "none"; if (event.dataTransfer.files.length) { let file = event.dataTransfer.files[0]; if (!file) return; display_img.src = URL.createObjectURL(file); current_image = { url: display_img.src, name: parseFilename(file.name).name, extension: parseFilename(file.name).ext, } decode() } }) fileUpload.addEventListener("change", function (b) { image_container.style.backgroundColor = "unset"; fileDownload.style.display = 'none'; tooltip.style.display = "none"; let file = b.target.files[0]; if (!file) return; display_img.src = URL.createObjectURL(file); current_image = { url: display_img.src, name: parseFilename(file.name).name, extension: parseFilename(file.name).ext, } decode().catch(e => { console.error(e) }); }); window.decode_ex = (image) => { display_img.src = image.src current_image = { url: image.src, name: image.name, extension: parseFilename(image.src).ext, } decode() } window.download = async (format) => { let options; if (format == "png") { options = { type: "image/png" } } if (format == "jpeg") { options = { type: "image/jpeg", quality: JPEG_QUALITY } } let download_link = document.createElement("a"); download_link.style.display = "none"; download_link.href = URL.createObjectURL(await current_image.canvas.convertToBlob(options)) download_link.download = current_image.name + current_image.filename_append + '.' + format; status_element.appendChild(download_link); download_link.click() } async function decode() { // Clear UI let ctx = residual_img.getContext("2d"); ctx.clearRect(0, 0, residual_img.width, residual_img.height); fileDownload.style.display = 'none'; tooltip.style.display = "none"; status_element.textContent = 'Analysing for watermark...'; showSpinner() let result = await tm.decode(current_image.url); hideSpinner() if (result.valid) { result_element.textContent = '💦 Watermarked Image'; status_element.textContent = result.binary + '\nSCHEMA:' + result.schema + ' BITFLIPS:' + result.bitflips + ' HEX: ' + result.hex + ' ASCII: ' + result.ascii; } else { result_element.textContent = '🖼️ Input Image' status_element.textContent = "No Watermark found" } } async function encode() { // Clear Residual let ctx = residual_img.getContext("2d"); ctx.clearRect(0, 0, residual_img.width, residual_img.height); let string_secret = secret_element.value; if (!current_image) { return; } current_image.url_backup = display_img.src; // Random secret if empty if (!string_secret) { string_secret = toBinString(Array.from({ length: 68 }, () => Math.round(Math.random()))); secret_element.value = string_secret; } status_element.textContent = 'Injecting the watermark...'; showSpinner(); let result = await tm.encode(current_image.url, string_secret, WM_STRENGTH); hideSpinner(); // Display Residual let residual_data = new ImageData(result.residual, 256, 256); ctx.putImageData(residual_data, 0, 0); current_image.canvas = new OffscreenCanvas(result.width, result.height); current_image.canvas_ctx = current_image.canvas.getContext("2d"); let display_data = new ImageData(result.stego, result.width, result.height); current_image.canvas_ctx.putImageData(display_data, 0, 0); display_img.src = current_image.url = URL.createObjectURL(await current_image.canvas.convertToBlob()); status_element.textContent = 'Verifying the watermark...'; showSpinner() result = await tm.decode(current_image.url); hideSpinner(); if (result.valid) { if (string_secret == result.ascii || string_secret == result.binary) { result_element.textContent = '💦 Watermarked Image'; status_element.textContent = result.binary + '\nSCHEMA:' + result.schema + ' BITFLIPS:' + result.bitflips + ' HEX: ' + result.hex + ' ASCII: ' + result.ascii; current_image.filename_append = "_watermarked"; fileDownload.style.display = 'flex'; } else { display_img.src = current_image.url = current_image.url_backup; result_element.textContent = '🖼️ Input Image'; status_element.textContent = "Watermark secret mismatch, try to change the strength level"; } } else { display_img.src = current_image.url = current_image.url_backup; result_element.textContent = '🖼️ Input Image'; status_element.textContent = "No Watermark found, try to change the strength level"; } } async function erase() { let ctx = residual_img.getContext("2d"); ctx.clearRect(0, 0, residual_img.width, residual_img.height); current_image.url_backup = display_img.src; status_element.textContent = 'Erasing the watermark...' showSpinner() let result = await tm.encode(current_image.url, '', WM_STRENGTH, true); hideSpinner() // Display the residual let residual_data = new ImageData(result.residual, 256, 256) ctx.putImageData(residual_data, 0, 0); // Display erased watermark image current_image.canvas = new OffscreenCanvas(result.width, result.height) current_image.canvas_ctx = current_image.canvas.getContext("2d"); let display_data = new ImageData(result.stego, result.width, result.height) current_image.canvas_ctx.putImageData(display_data, 0, 0); display_img.src = current_image.url = URL.createObjectURL(await current_image.canvas.convertToBlob()) status_element.textContent = 'Verifying...' showSpinner() result = await tm.decode(current_image.url) hideSpinner() if (result.valid) { display_img.src = current_image.url = current_image.url_backup; result_element.textContent = '💦 Watermarked Image'; status_element.textContent = "Watermark not erased, try to change the strength level"; } else { result_element.textContent = '🖼️ Input Image'; status_element.textContent = "Watermark erased."; current_image.filename_append = "_watermark_erased"; fileDownload.style.display = 'flex'; } } // ====================================================================== // Utils // ====================================================================== function showSpinner() { processing_element.style.display = 'block'; } function hideSpinner() { processing_element.style.display = 'none'; } function parseFilename(filename) { let ext = filename.split('.').pop(); return ({ name: filename.replace("." + ext, ''), ext: ext }) } function toBinString(bin_array) { let out = "" for (let i = 0; i < bin_array.length; i++) { out += bin_array[i]; } return out; }