{"id":782,"date":"2025-03-29T03:30:32","date_gmt":"2025-03-29T03:30:32","guid":{"rendered":"https:\/\/divbydev.com\/?page_id=782"},"modified":"2025-03-29T14:14:06","modified_gmt":"2025-03-29T14:14:06","slug":"edge-cleanup-tool","status":"publish","type":"page","link":"https:\/\/divbydev.com\/index.php\/edge-cleanup-tool\/","title":{"rendered":"Edge Cleanup Tool"},"content":{"rendered":"\n<p>Stuff<\/p>\n\n\n\n<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n  <meta charset=\"UTF-8\" \/>\n  <title>Advanced Edge Cleanup<\/title>\n  <style>\n    body {\n      font-family: sans-serif;\n      margin: 20px;\n    }\n    #controls {\n      margin-bottom: 15px;\n    }\n    label {\n      display: inline-block;\n      margin-right: 10px;\n    }\n    #imageCanvas {\n      display: block;\n      border: 1px solid #ccc;\n      margin-top: 10px;\n      max-width: 100%;\n    }\n    .control-group {\n      margin-bottom: 10px;\n    }\n    button {\n      margin-right: 5px;\n    }\n    #previewCanvas {\n      display: none; \/* Hidden preview canvas for processing *\/\n    }\n  <\/style>\n<\/head>\n<body>\n  <h1>Advanced Edge Cleanup<\/h1>\n  <div id=\"controls\">\n    <div class=\"control-group\">\n      <input type=\"file\" id=\"fileInput\" accept=\"image\/*\" \/>\n    <\/div>\n    <div class=\"control-group\">\n      <label for=\"thresholdRange\">Threshold (0-255): <span id=\"thresholdValue\">220<\/span><\/label>\n      <input type=\"range\" id=\"thresholdRange\" min=\"0\" max=\"255\" value=\"220\" \/>\n    <\/div>\n    <div class=\"control-group\">\n      <label for=\"blurRange\">Blur Radius: <span id=\"blurValue\">5<\/span><\/label>\n      <input type=\"range\" id=\"blurRange\" min=\"0\" max=\"20\" value=\"5\" \/>\n    <\/div>\n    <div class=\"control-group\">\n      <label for=\"toleranceRange\">Tolerance: <span id=\"toleranceValue\">20<\/span><\/label>\n      <input type=\"range\" id=\"toleranceRange\" min=\"0\" max=\"50\" value=\"20\" \/>\n    <\/div>\n    <button id=\"cleanupBtn\">Clean Up Edges<\/button>\n    <button id=\"resetBtn\">Reset Image<\/button>\n    <button id=\"clearBtn\">Clear<\/button>\n    <button id=\"saveBtn\">Save Image<\/button>\n  <\/div>\n  <canvas id=\"imageCanvas\"><\/canvas>\n  <canvas id=\"previewCanvas\"><\/canvas>\n\n  <script>\n    const fileInput = document.getElementById(\"fileInput\");\n    const thresholdRange = document.getElementById(\"thresholdRange\");\n    const thresholdValueDisplay = document.getElementById(\"thresholdValue\");\n    const blurRange = document.getElementById(\"blurRange\");\n    const blurValueDisplay = document.getElementById(\"blurValue\");\n    const toleranceRange = document.getElementById(\"toleranceRange\");\n    const toleranceValueDisplay = document.getElementById(\"toleranceValue\");\n    const cleanupBtn = document.getElementById(\"cleanupBtn\");\n    const resetBtn = document.getElementById(\"resetBtn\");\n    const clearBtn = document.getElementById(\"clearBtn\");\n    const saveBtn = document.getElementById(\"saveBtn\");\n    const canvas = document.getElementById(\"imageCanvas\");\n    const ctx = canvas.getContext(\"2d\");\n    const previewCanvas = document.getElementById(\"previewCanvas\");\n    const previewCtx = previewCanvas.getContext(\"2d\");\n\n    let originalImage = null;\n\n    \/\/ Update display values when sliders change\n    thresholdRange.addEventListener(\"input\", () => {\n      thresholdValueDisplay.textContent = thresholdRange.value;\n    });\n    blurRange.addEventListener(\"input\", () => {\n      blurValueDisplay.textContent = blurRange.value;\n    });\n    toleranceRange.addEventListener(\"input\", () => {\n      toleranceValueDisplay.textContent = toleranceRange.value;\n    });\n\n    \/\/ Load image into canvas\n    fileInput.addEventListener(\"change\", (event) => {\n      const file = event.target.files[0];\n      if (!file) return;\n\n      const img = new Image();\n      img.onload = () => {\n        canvas.width = img.width;\n        canvas.height = img.height;\n        previewCanvas.width = img.width;\n        previewCanvas.height = img.height;\n        ctx.drawImage(img, 0, 0);\n        originalImage = new Image();\n        originalImage.src = img.src; \/\/ Store original image for reset\n      };\n      img.src = URL.createObjectURL(file);\n    });\n\n    \/\/ Reset to original image\n    resetBtn.addEventListener(\"click\", () => {\n      if (originalImage) {\n        ctx.clearRect(0, 0, canvas.width, canvas.height);\n        ctx.drawImage(originalImage, 0, 0);\n      }\n    });\n\n    \/\/ Clear everything\n    clearBtn.addEventListener(\"click\", () => {\n      fileInput.value = \"\";\n      thresholdRange.value = 220;\n      blurRange.value = 5;\n      toleranceRange.value = 20;\n      thresholdValueDisplay.textContent = \"220\";\n      blurValueDisplay.textContent = \"5\";\n      toleranceValueDisplay.textContent = \"20\";\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n      canvas.width = canvas.height = 0;\n      previewCanvas.width = previewCanvas.height = 0;\n      originalImage = null;\n    });\n\n    \/\/ Clean up edges\n    cleanupBtn.addEventListener(\"click\", () => {\n      if (!originalImage) return;\n\n      \/\/ Use preview canvas for processing\n      previewCtx.drawImage(originalImage, 0, 0);\n      const imageData = previewCtx.getImageData(0, 0, canvas.width, canvas.height);\n      const data = imageData.data;\n      const width = imageData.width;\n      const height = imageData.height;\n\n      const threshold = parseInt(thresholdRange.value, 10);\n      const blurRadius = parseInt(blurRange.value, 10);\n      const tolerance = parseInt(toleranceRange.value, 10);\n\n      \/\/ Create mask based on luminance with tolerance\n      const mask = new Float32Array(width * height);\n      for (let i = 0; i < data.length; i += 4) {\n        const r = data[i];\n        const g = data[i + 1];\n        const b = data[i + 2];\n        \/\/ Calculate luminance (weighted RGB)\n        const luminance = 0.2126 * r + 0.7152 * g + 0.0722 * b;\n        const idx = i \/ 4;\n        \/\/ Smooth transition with tolerance\n        let m = (luminance - (threshold - tolerance)) \/ (2 * tolerance);\n        mask[idx] = Math.min(Math.max(m, 0), 1);\n      }\n\n      \/\/ Apply improved Gaussian-like blur if radius > 0\n      const blurredMask = blurRadius > 0 ? gaussianBlur(mask, width, height, blurRadius) : mask;\n\n      \/\/ Apply mask to alpha channel\n      for (let i = 0; i < data.length; i += 4) {\n        const idx = i \/ 4;\n        data[i + 3] = data[i + 3] * (1 - blurredMask[idx]);\n      }\n\n      \/\/ Update main canvas\n      ctx.clearRect(0, 0, canvas.width, canvas.height);\n      ctx.putImageData(imageData, 0, 0);\n    });\n\n    \/\/ Save image\n    saveBtn.addEventListener(\"click\", () => {\n      if (canvas.width === 0 || canvas.height === 0) return;\n      const link = document.createElement(\"a\");\n      link.download = \"cleaned-image.png\";\n      link.href = canvas.toDataURL(\"image\/png\");\n      link.click();\n    });\n\n    \/\/ Improved Gaussian-like blur (approximation with multiple box blurs)\n    function gaussianBlur(mask, width, height, radius) {\n      let tempMask = new Float32Array(mask);\n      \/\/ Three passes of box blur approximate a Gaussian\n      for (let pass = 0; pass < 3; pass++) {\n        tempMask = boxBlur(tempMask, width, height, Math.floor(radius \/ 3));\n      }\n      return tempMask;\n    }\n\n    \/\/ Box blur function\n    function boxBlur(mask, width, height, radius) {\n      const output = new Float32Array(width * height);\n\n      \/\/ Horizontal pass\n      for (let y = 0; y < height; y++) {\n        for (let x = 0; x < width; x++) {\n          let sum = 0;\n          let count = 0;\n          for (let dx = -radius; dx <= radius; dx++) {\n            const x2 = x + dx;\n            if (x2 >= 0 && x2 < width) {\n              sum += mask[y * width + x2];\n              count++;\n            }\n          }\n          output[y * width + x] = sum \/ count;\n        }\n      }\n\n      \/\/ Vertical pass\n      const temp = new Float32Array(output);\n      for (let x = 0; x < width; x++) {\n        for (let y = 0; y < height; y++) {\n          let sum = 0;\n          let count = 0;\n          for (let dy = -radius; dy <= radius; dy++) {\n            const y2 = y + dy;\n            if (y2 >= 0 && y2 < height) {\n              sum += temp[y2 * width + x];\n              count++;\n            }\n          }\n          output[y * width + x] = sum \/ count;\n        }\n      }\n      return output;\n    }\n  <\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>Stuff Advanced Edge Cleanup Advanced Edge Cleanup Threshold (0-255): 220 Blur Radius: 5 Tolerance: 20 Clean Up Edges Reset Image Clear Save Image<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"footnotes":""},"class_list":["post-782","page","type-page","status-publish"],"_links":{"self":[{"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages\/782","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/comments?post=782"}],"version-history":[{"count":3,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages\/782\/revisions"}],"predecessor-version":[{"id":788,"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/pages\/782\/revisions\/788"}],"wp:attachment":[{"href":"https:\/\/divbydev.com\/index.php\/wp-json\/wp\/v2\/media?parent=782"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}