{"id":6706,"date":"2026-09-01T06:10:40","date_gmt":"2026-09-01T06:10:40","guid":{"rendered":"https:\/\/meiling412.com\/?page_id=6706"},"modified":"2026-09-01T06:12:56","modified_gmt":"2026-09-01T06:12:56","slug":"photo-lineart","status":"publish","type":"page","link":"https:\/\/lin608.meiling412.com\/ms\/photo-lineart\/","title":{"rendered":"\u7167\u7247\u8b8a\u7dda\u7a3f"},"content":{"rendered":"\n<!DOCTYPE html>\n<html lang=\"zh-Hant\">\n<head>\n<meta charset=\"UTF-8\">\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n<title>\u7167\u7247\u8b8a\u7dda\u7a3f\uff5c\u514d\u8cbb\u8457\u8272\u9801\u7522\u751f\u5668\uff5c\u5bf6\u8c9d\u7684\u9748\u7ce7\u5c4b<\/title>\n<meta name=\"description\" content=\"\u4e0a\u50b3\u4e00\u5f35\u7167\u7247\uff0c30 \u79d2\u8b8a\u6210\u4e00\u5f35\u9ed1\u767d\u7dda\u7a3f\u5716\u3002\u514d\u8cbb\u3001\u7121\u9650\u6b21\u4f7f\u7528\uff0c\u5716\u7247\u5168\u7a0b\u53ea\u5728\u4f60\u7684\u88dd\u7f6e\u4e0a\u8655\u7406\uff0c\u4e0d\u6703\u4e0a\u50b3\u4f3a\u670d\u5668\u3002\">\n<style>\n  :root {\n    --ink: #3d3a50;\n    --sub: #8a86a0;\n    --accent: #6c5ce7;\n    --accent-2: #a29bfe;\n    --bg: #f7f5ff;\n    --card: #ffffff;\n    --line: #e5e2f2;\n    --warm: #fff8ef;\n  }\n  * { box-sizing: border-box; }\n  html, body { margin: 0; padding: 0; }\n  body {\n    font-family: \"Segoe UI\", \"Microsoft JhengHei\", \"PingFang TC\", \"Noto Sans TC\", sans-serif;\n    background: var(--bg);\n    color: var(--ink);\n    line-height: 1.7;\n  }\n  .wrap { max-width: 1060px; margin: 0 auto; padding: 34px 20px 70px; }\n\n  .hero { text-align: center; padding: 8px 0 26px; }\n  .hero .tag {\n    display: inline-block;\n    background: #efeaff;\n    color: var(--accent);\n    border-radius: 999px;\n    padding: 4px 14px;\n    font-size: 13px;\n    font-weight: 600;\n    margin-bottom: 14px;\n  }\n  .hero h1 { font-size: 30px; margin: 0 0 10px; }\n  .hero p { max-width: 640px; margin: 0 auto; color: var(--sub); font-size: 16px; }\n\n  .steps { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin: 8px 0 24px; }\n  @media (max-width: 720px) { .steps { grid-template-columns: 1fr; } }\n  .step {\n    background: var(--card);\n    border: 1px solid var(--line);\n    border-radius: 14px;\n    padding: 16px 14px;\n    text-align: center;\n  }\n  .step .num {\n    width: 30px; height: 30px;\n    margin: 0 auto 8px;\n    background: var(--accent);\n    color: #fff;\n    border-radius: 50%;\n    font-weight: 700;\n    line-height: 30px;\n  }\n  .step b { display: block; font-size: 15px; }\n  .step small { color: var(--sub); }\n\n  .grid { display: grid; grid-template-columns: 330px 1fr; gap: 20px; align-items: start; }\n  @media (max-width: 860px) { .grid { grid-template-columns: 1fr; } }\n\n  .card {\n    background: var(--card);\n    border: 1px solid var(--line);\n    border-radius: 16px;\n    padding: 18px;\n    box-shadow: 0 6px 24px rgba(108, 92, 231, .07);\n  }\n  .drop {\n    border: 2px dashed var(--accent-2);\n    border-radius: 14px;\n    padding: 24px 12px;\n    text-align: center;\n    cursor: pointer;\n    background: #fbfaff;\n    transition: background .15s;\n  }\n  .drop:hover, .drop.over { background: #f1edff; }\n  .drop .big { font-size: 32px; }\n  .drop p { margin: 6px 0 2px; font-weight: 600; }\n  .drop small { color: var(--sub); }\n  .row { display: flex; gap: 10px; margin-top: 12px; }\n  button {\n    flex: 1;\n    border: none;\n    border-radius: 10px;\n    padding: 10px 12px;\n    font-size: 14px;\n    cursor: pointer;\n    background: #efecff;\n    color: var(--accent);\n    font-weight: 600;\n  }\n  button.primary { background: var(--accent); color: #fff; }\n  button:disabled { opacity: .45; cursor: not-allowed; }\n  label { display: block; margin-top: 14px; font-size: 13px; color: var(--sub); }\n  input[type=range] { width: 100%; margin-top: 4px; accent-color: var(--accent); }\n  .val { float: right; color: var(--ink); font-weight: 600; }\n  .check { margin-top: 12px; font-size: 13px; color: var(--sub); }\n  .check input { accent-color: var(--accent); }\n  .info { margin-top: 12px; font-size: 13px; color: var(--sub); min-height: 1.4em; }\n  .note { margin-top: 14px; font-size: 12px; color: var(--sub); line-height: 1.7; }\n\n  .preview { text-align: center; }\n  .preview h2 { font-size: 15px; margin: 0 0 10px; color: var(--sub); font-weight: 600; }\n  .canvas-box {\n    background: #fff;\n    border: 1px solid var(--line);\n    border-radius: 12px;\n    padding: 10px;\n    min-height: 260px;\n    display: flex;\n    align-items: center;\n    justify-content: center;\n    position: relative;\n  }\n  .canvas-box canvas { max-width: 100%; height: auto; border-radius: 8px; }\n  .placeholder { color: var(--sub); font-size: 14px; }\n  .side { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }\n  .side .mini h3 { font-size: 12px; color: var(--sub); margin: 0 0 6px; text-align: center; }\n  .side canvas { width: 100%; height: auto; border: 1px solid var(--line); border-radius: 8px; background: #fff; }\n\n  .cta {\n    margin-top: 26px;\n    background: linear-gradient(135deg, #6c5ce7, #a29bfe);\n    color: #fff;\n    border-radius: 18px;\n    padding: 26px 24px;\n    text-align: center;\n  }\n  .cta h2 { margin: 0 0 8px; font-size: 22px; }\n  .cta p { margin: 0 auto 18px; max-width: 560px; opacity: .95; }\n  .cta ul { text-align: left; max-width: 430px; margin: 0 auto 18px; padding-left: 20px; }\n  .cta a.line {\n    display: inline-block;\n    background: #fff;\n    color: var(--accent);\n    font-weight: 700;\n    padding: 12px 28px;\n    border-radius: 999px;\n    text-decoration: none;\n    font-size: 16px;\n  }\n  .cta .small { font-size: 12px; opacity: .85; margin-top: 12px; }\n\n  .uses { margin-top: 28px; }\n  .uses h2 { font-size: 20px; text-align: center; margin: 0 0 16px; }\n  .chips { display: flex; flex-wrap: wrap; gap: 10px; justify-content: center; }\n  .chip {\n    background: var(--card);\n    border: 1px solid var(--line);\n    border-radius: 999px;\n    padding: 8px 16px;\n    font-size: 14px;\n  }\n\n  .faq { margin-top: 30px; }\n  .faq h2 { font-size: 20px; text-align: center; margin: 0 0 16px; }\n  .qa {\n    background: var(--card);\n    border: 1px solid var(--line);\n    border-radius: 12px;\n    padding: 14px 16px;\n    margin-bottom: 10px;\n  }\n  .qa b { display: block; margin-bottom: 2px; }\n  .qa span { color: var(--sub); font-size: 14px; }\n\n  footer {\n    margin-top: 34px;\n    text-align: center;\n    color: var(--sub);\n    font-size: 13px;\n    border-top: 1px solid var(--line);\n    padding-top: 18px;\n  }\n  footer a { color: var(--accent); text-decoration: none; }\n<\/style>\n<\/head>\n<body>\n<div class=\"wrap\">\n\n  <section class=\"hero\">\n    <div class=\"tag\">\u514d\u8cbb\u30fb\u514d\u5b89\u88dd\u30fb\u5716\u7247\u4e0d\u4e0a\u50b3<\/div>\n    <h1>\u7167\u7247\u8b8a\u7dda\u7a3f\uff5c\u514d\u8cbb\u8457\u8272\u9801\u7522\u751f\u5668<\/h1>\n    <p>\u4e0a\u50b3\u4e00\u5f35\u7167\u7247\uff0c30 \u79d2\u8b8a\u6210\u4e00\u5f35\u9ed1\u767d\u7dda\u7a3f\u5716\u3002\u53ef\u4ee5\u505a\u6210\u5b69\u5b50\u7684\u8457\u8272\u9801\u3001\u795d\u798f\u5361\u5716\u6848\uff0c\u7121\u9650\u6b21\u514d\u8cbb\u4f7f\u7528\u3002<\/p>\n  <\/section>\n\n  <section class=\"steps\">\n    <div class=\"step\"><div class=\"num\">1<\/div><b>\u4e0a\u50b3\u7167\u7247<\/b><small>\u62d6\u66f3\u6216\u9ede\u9078\uff0cJPG\uff0fPNG \u90fd\u53ef\u4ee5<\/small><\/div>\n    <div class=\"step\"><div class=\"num\">2<\/div><b>\u8abf\u6574\u7dda\u689d<\/b><small>\u654f\u611f\u5ea6\u3001\u7c97\u7d30\u3001\u5e73\u6ed1\u5ea6\u96a8\u4f60\u8abf<\/small><\/div>\n    <div class=\"step\"><div class=\"num\">3<\/div><b>\u4e0b\u8f09\u7dda\u7a3f<\/b><small>\u4e00\u9375\u5b58\u6210 PNG\uff0c\u99ac\u4e0a\u5c31\u80fd\u7528<\/small><\/div>\n  <\/section>\n\n  <div class=\"grid\">\n    <div class=\"card\">\n      <div class=\"drop\" id=\"drop\">\n        <div class=\"big\">\ud83d\uddbc\ufe0f<\/div>\n        <p>\u628a\u5716\u7247\u62d6\u5230\u9019\u88e1\uff0c\u6216\u6309\u4e00\u4e0b\u9078\u64c7<\/p>\n        <small>\u5efa\u8b70\u4e0d\u8d85\u904e 2000px\uff0c\u8655\u7406\u90fd\u5728\u4f60\u88dd\u7f6e\u4e0a\u5b8c\u6210<\/small>\n        <input type=\"file\" id=\"file\" accept=\"image\/*\" hidden>\n      <\/div>\n      <div class=\"row\">\n        <button id=\"sampleBtn\">\ud83c\udfa8 \u7528\u7bc4\u4f8b\u5716\u8a66\u8a66<\/button>\n      <\/div>\n      <label>\u7dda\u689d\u654f\u611f\u5ea6 <span class=\"val\" id=\"sensVal\">10%<\/span><\/label>\n      <input type=\"range\" id=\"sens\" min=\"2\" max=\"40\" step=\"1\" value=\"10\">\n      <label>\u7dda\u689d\u7c97\u7d30 <span class=\"val\" id=\"thickVal\">1<\/span><\/label>\n      <input type=\"range\" id=\"thick\" min=\"1\" max=\"4\" step=\"1\" value=\"1\">\n      <label>\u5e73\u6ed1\u5ea6\uff08\u53bb\u96dc\u8a0a\uff09 <span class=\"val\" id=\"blurVal\">1<\/span><\/label>\n      <input type=\"range\" id=\"blur\" min=\"0\" max=\"3\" step=\"1\" value=\"1\">\n      <div class=\"check\">\n        <label style=\"margin-top:0\"><input type=\"checkbox\" id=\"mark\" checked> \u5728\u7dda\u7a3f\u4e0b\u65b9\u52a0\u4e0a\u4f86\u6e90\u6587\u5b57\uff08\u63a8\u85a6\uff0c\u65b9\u4fbf\u5206\u4eab\u6642\u627e\u5230\u6211\u5011\uff09<\/label>\n      <\/div>\n      <div class=\"bar\">\n        <button class=\"primary\" id=\"download\" disabled>\u2b07\ufe0f \u4e0b\u8f09\u7dda\u7a3f PNG<\/button>\n      <\/div>\n      <div class=\"info\" id=\"status\"><\/div>\n      <div class=\"note\">\n        \ud83d\udd12 \u96b1\u79c1\u4fdd\u8b49\uff1a\u5716\u7247\u5168\u7a0b\u53ea\u5728\u4f60\u7684\u700f\u89bd\u5668\u88e1\u8655\u7406\uff0c<b>\u4e0d\u6703\u4e0a\u50b3\u5230\u4efb\u4f55\u4f3a\u670d\u5668<\/b>\uff0c\u95dc\u6389\u9801\u9762\u5c31\u5b8c\u5168\u6d88\u5931\u3002\n      <\/div>\n    <\/div>\n\n    <div class=\"card preview\">\n      <div class=\"side\">\n        <div class=\"mini\">\n          <h3>\u539f\u5716<\/h3>\n          <canvas id=\"orig\"><\/canvas>\n        <\/div>\n        <div class=\"mini\">\n          <h3>\u7dda\u7a3f\u7d50\u679c<\/h3>\n          <canvas id=\"result\"><\/canvas>\n        <\/div>\n      <\/div>\n      <div class=\"canvas-box\" id=\"bigBox\">\n        <canvas id=\"big\" style=\"display:none\"><\/canvas>\n        <div class=\"placeholder\" id=\"placeholder\">\u4e0a\u50b3\u5716\u7247\u5f8c\uff0c\u9019\u88e1\u6703\u986f\u793a\u653e\u5927\u7248\u7dda\u7a3f<\/div>\n      <\/div>\n    <\/div>\n  <\/div>\n\n  <section class=\"cta\">\n    <h2>\u60f3\u8981\u66f4\u7cbe\u7dfb\u3001\u53ef\u4ee5\u5370\u5237\u7684\u7dda\u7a3f\uff1f<\/h2>\n    <p>\u514d\u8cbb\u7248\u9069\u5408\u65e5\u5e38\u73a9\u6a02\u8207\u5206\u4eab\u3002\u5982\u679c\u4f60\u9700\u8981 AI \u7cbe\u4fee\u7684\u8d85\u4e7e\u6de8\u7dda\u689d\u3001300 dpi \u5370\u5237\u7d1a\u89e3\u6790\u5ea6\uff1a<\/p>\n    <ul>\n      <li>AI \u7cbe\u4fee\uff1a\u7dda\u689d\u66f4\u4e7e\u6de8\u3001\u96dc\u8a0a\u66f4\u5c11\u3001\u8f2a\u5ed3\u66f4\u6e96<\/li>\n      <li>\u9ad8\u89e3\u6790\u5ea6\uff1a\u9069\u5408\u5370\u5237\u8f38\u51fa\uff08300 dpi\uff09<\/li>\n      <li>\u53ef\u9078\u98a8\u683c\uff1a\u7d93\u5178\u8457\u8272\uff0f\u5361\u901a\uff0f\u7c21\u7d04\u63d2\u756b<\/li>\n    <\/ul>\n    <a class=\"line\" href=\"https:\/\/lin.ee\/NkXKGOb\" target=\"_blank\" rel=\"noopener\">\ud83d\udcac \u52a0 LINE \u50b3\u5716\u7d66\u6211\u5011<\/a>\n    <div class=\"small\">\u8a73\u7d30\u65b9\u6848\u8207\u50f9\u683c\uff0c\u6211\u5011\u5728 LINE \u4e0a\u804a\uff08\u6700\u5feb\u7576\u5929\u4ea4\u4ef6\uff09<\/div>\n  <\/section>\n\n  <section class=\"uses\">\n    <h2>\u4e00\u5f35\u7dda\u7a3f\uff0c\u53ef\u4ee5\u62ff\u4f86\u505a\u4ec0\u9ebc\uff1f<\/h2>\n    <div class=\"chips\">\n      <span class=\"chip\">\ud83d\udc76 \u89aa\u5b50\u8457\u8272\u9801<\/span>\n      <span class=\"chip\">\u26ea \u4e3b\u65e5\u5b78\uff0f\u5152\u7ae5\u805a\u6703\u6559\u6750<\/span>\n      <span class=\"chip\">\ud83c\udf82 \u751f\u65e5\u5361\u3001\u795d\u798f\u5361\u5716\u6848<\/span>\n      <span class=\"chip\">\ud83d\udcd6 \u6545\u4e8b\u66f8\u63d2\u756b\u8349\u7a3f<\/span>\n      <span class=\"chip\">\ud83d\udd8d\ufe0f \u9577\u8f29\u966a\u4f34\u5857\u9d09<\/span>\n    <\/div>\n  <\/section>\n\n  <section class=\"faq\">\n    <h2>\u5e38\u898b\u554f\u984c<\/h2>\n    <div class=\"qa\"><b>\u9700\u8981\u8a3b\u518a\u6216\u5b89\u88dd\u55ce\uff1f<\/b><span>\u4e0d\u7528\u3002\u6253\u958b\u7db2\u9801\u5c31\u80fd\u7528\uff0c\u4e5f\u4e0d\u9700\u8981\u4e0b\u8f09\u4efb\u4f55 App\u3002<\/span><\/div>\n    <div class=\"qa\"><b>\u6211\u7684\u7167\u7247\u6703\u88ab\u5132\u5b58\u55ce\uff1f<\/b><span>\u4e0d\u6703\u3002\u6240\u6709\u904b\u7b97\u90fd\u5728\u4f60\u81ea\u5df1\u7684\u88dd\u7f6e\u4e0a\u5b8c\u6210\uff0c\u7167\u7247\u4e0d\u6703\u4e0a\u50b3\u5230\u4efb\u4f55\u4f3a\u670d\u5668\u3002<\/span><\/div>\n    <div class=\"qa\"><b>\u514d\u8cbb\u7248\u53ef\u4ee5\u4e0b\u8f09\u5e7e\u6b21\uff1f<\/b><span>\u7121\u9650\u6b21\u3002\u60f3\u8abf\u6574\u5e7e\u6b21\u3001\u4e0b\u8f09\u5e7e\u6b21\u90fd\u53ef\u4ee5\u3002<\/span><\/div>\n    <div class=\"qa\"><b>\u7dda\u7a3f\u53ef\u4ee5\u5546\u7528\u55ce\uff1f<\/b><span>\u500b\u4eba\u8207\u6559\u6703\u4f7f\u7528\u6c92\u554f\u984c\uff1b\u5982\u679c\u8981\u5927\u91cf\u5546\u696d\u88fd\u4f5c\uff08\u4f8b\u5982\u5370\u88fd\u8ca9\u552e\uff09\uff0c\u5efa\u8b70\u4f7f\u7528 AI \u7cbe\u4fee\u7248\uff0c\u7d30\u7bc0\u53ef\u5728 LINE \u4e0a\u8a62\u554f\u3002<\/span><\/div>\n  <\/section>\n\n  <footer>\n    \u00a9 \u5bf6\u8c9d\u7684\u9748\u7ce7\u5c4b\uff5c\u8a69\u6b4c\u30fb\u8056\u7d93\u6545\u4e8b\u30fb\u89aa\u5b50\u9748\u4fee<br>\n    <a href=\"https:\/\/lin.ee\/NkXKGOb\" target=\"_blank\" rel=\"noopener\">\u52a0\u5165\u6211\u5011\u7684 LINE<\/a> \u30fb <a href=\"https:\/\/lin608.meiling412.com\/zh_tw\/subscribe-to-us\/\">\u8a02\u95b1\u6211\u5011<\/a>\n  <\/footer>\n<\/div>\n\n<script>\n\"use strict\";\nconst fileInput = document.getElementById(\"file\");\nconst drop = document.getElementById(\"drop\");\nconst sampleBtn = document.getElementById(\"sampleBtn\");\nconst downloadBtn = document.getElementById(\"download\");\nconst sensInput = document.getElementById(\"sens\");\nconst thickInput = document.getElementById(\"thick\");\nconst blurInput = document.getElementById(\"blur\");\nconst markInput = document.getElementById(\"mark\");\nconst origCv = document.getElementById(\"orig\");\nconst resultCv = document.getElementById(\"result\");\nconst bigCv = document.getElementById(\"big\");\nconst placeholder = document.getElementById(\"placeholder\");\nconst statusEl = document.getElementById(\"status\");\n\nlet srcImage = null;\nconst MAX_DIM = 1600;\n\nconst sensVal = document.getElementById(\"sensVal\");\nconst thickVal = document.getElementById(\"thickVal\");\nconst blurVal = document.getElementById(\"blurVal\");\n\nfunction showVals() {\n  sensVal.textContent = sensInput.value + \"%\";\n  thickVal.textContent = thickInput.value;\n  blurVal.textContent = blurInput.value;\n}\n[sensInput, thickInput, blurInput].forEach(el => el.addEventListener(\"input\", () => {\n  showVals();\n  if (srcImage) process();\n}));\n\ndrop.addEventListener(\"click\", () => fileInput.click());\ndrop.addEventListener(\"dragover\", e => { e.preventDefault(); drop.classList.add(\"over\"); });\ndrop.addEventListener(\"dragleave\", () => drop.classList.remove(\"over\"));\ndrop.addEventListener(\"drop\", e => {\n  e.preventDefault();\n  drop.classList.remove(\"over\");\n  const f = e.dataTransfer.files && e.dataTransfer.files[0];\n  if (f && f.type.startsWith(\"image\/\")) loadFile(f);\n});\nfileInput.addEventListener(\"change\", () => {\n  if (fileInput.files[0]) loadFile(fileInput.files[0]);\n});\n\nfunction loadFile(f) {\n  const url = URL.createObjectURL(f);\n  const img = new Image();\n  img.onload = () => { srcImage = img; process(); URL.revokeObjectURL(url); };\n  img.src = url;\n}\n\nsampleBtn.addEventListener(\"click\", () => {\n  const cv = document.createElement(\"canvas\");\n  cv.width = 640; cv.height = 480;\n  drawSampleScene(cv.getContext(\"2d\"));\n  const img = new Image();\n  img.onload = () => { srcImage = img; process(); };\n  img.src = cv.toDataURL(\"image\/png\");\n});\n\ndownloadBtn.addEventListener(\"click\", () => {\n  const a = document.createElement(\"a\");\n  a.download = \"lineart-\" + Date.now() + \".png\";\n  a.href = bigCv.toDataURL(\"image\/png\");\n  a.click();\n});\n\nfunction process() {\n  statusEl.textContent = \"\u8655\u7406\u4e2d\u2026\";\n  requestAnimationFrame(() => setTimeout(() => {\n    const scale = Math.min(1, MAX_DIM \/ Math.max(srcImage.width, srcImage.height));\n    const w = Math.max(1, Math.round(srcImage.width * scale));\n    const h = Math.max(1, Math.round(srcImage.height * scale));\n\n    const off = document.createElement(\"canvas\");\n    off.width = w; off.height = h;\n    const octx = off.getContext(\"2d\", { willReadFrequently: true });\n    octx.drawImage(srcImage, 0, 0, w, h);\n\n    const data = octx.getImageData(0, 0, w, h).data;\n    const lum = new Float32Array(w * h);\n    for (let i = 0; i < w * h; i++) {\n      lum[i] = 0.299 * data[i*4] + 0.587 * data[i*4+1] + 0.114 * data[i*4+2];\n    }\n\n    const blurR = parseInt(blurInput.value, 10);\n    let smooth = lum;\n    if (blurR > 0) smooth = boxBlur(lum, w, h, blurR, 2);\n\n    const mag = sobel(smooth, w, h);\n    const sensitivity = parseInt(sensInput.value, 10) \/ 100;\n    const thr = percentile(mag, 1 - sensitivity);\n    let edge = new Uint8Array(w * h);\n    for (let i = 0; i < w * h; i++) edge[i] = mag[i] > thr ? 255 : 0;\n\n    const thickN = parseInt(thickInput.value, 10);\n    if (thickN > 1) edge = dilate(edge, w, h, thickN);\n\n    const out = new Uint8ClampedArray(w * h * 4);\n    for (let i = 0; i < w * h; i++) {\n      const v = edge[i] > 0 ? 0 : 255;\n      out[i*4] = v; out[i*4+1] = v; out[i*4+2] = v; out[i*4+3] = 255;\n    }\n\n    origCv.width = w; origCv.height = h;\n    origCv.getContext(\"2d\").drawImage(srcImage, 0, 0, w, h);\n\n    [resultCv, bigCv].forEach(cv => {\n      cv.width = w; cv.height = h;\n      const ctx = cv.getContext(\"2d\");\n      const id = ctx.createImageData(w, h);\n      id.data.set(out);\n      ctx.putImageData(id, 0, 0);\n      if (markInput.checked && cv === bigCv) drawMark(ctx, w, h);\n    });\n    bigCv.style.display = \"block\";\n    placeholder.style.display = \"none\";\n    downloadBtn.disabled = false;\n    statusEl.textContent = \"\u5b8c\u6210\uff01\u5c3a\u5bf8 \" + w + \" \u00d7 \" + h + \" px\uff0c\u53ef\u4ee5\u76f4\u63a5\u4e0b\u8f09\u3002\";\n  }, 20));\n}\n\nfunction drawMark(ctx, w, h) {\n  const text = \"\u514d\u8cbb\u88fd\u4f5c\uff5c\u5bf6\u8c9d\u7684\u9748\u7ce7\u5c4b meiling412.com\";\n  const fs = Math.max(13, Math.round(w * 0.022));\n  ctx.font = fs + \"px sans-serif\";\n  const tw = ctx.measureText(text).width;\n  const x = (w - tw) \/ 2;\n  const y = h - fs * 1.6;\n  ctx.fillStyle = \"rgba(0,0,0,.45)\";\n  ctx.fillText(text, x, y);\n}\n\n\/* ---------- image math ---------- *\/\n\nfunction boxBlur(src, w, h, r, passes) {\n  let a = src;\n  for (let p = 0; p < passes; p++) a = blurPass(a, w, h, r);\n  return a;\n}\n\nfunction blurPass(src, w, h, r) {\n  const tmp = new Float32Array(w * h);\n  const out = new Float32Array(w * h);\n  const win = r * 2 + 1;\n  for (let y = 0; y < h; y++) {\n    let sum = 0;\n    for (let x = -r; x <= r; x++) sum += src[y * w + Math.min(w - 1, Math.max(0, x))];\n    for (let x = 0; x < w; x++) {\n      tmp[y * w + x] = sum \/ win;\n      const add = src[y * w + Math.min(w - 1, x + r + 1)];\n      const sub = src[y * w + Math.max(0, x - r)];\n      sum += add - sub;\n    }\n  }\n  for (let x = 0; x < w; x++) {\n    let sum = 0;\n    for (let y = -r; y <= r; y++) sum += tmp[Math.min(h - 1, Math.max(0, y)) * w + x];\n    for (let y = 0; y < h; y++) {\n      out[y * w + x] = sum \/ win;\n      const add = tmp[Math.min(h - 1, y + r + 1) * w + x];\n      const sub = tmp[Math.max(0, y - r) * w + x];\n      sum += add - sub;\n    }\n  }\n  return out;\n}\n\nfunction sobel(src, w, h) {\n  const mag = new Float32Array(w * h);\n  for (let y = 0; y < h; y++) {\n    const y0 = Math.max(0, y - 1), y1 = y, y2 = Math.min(h - 1, y + 1);\n    for (let x = 0; x < w; x++) {\n      const x0 = Math.max(0, x - 1), x1 = x, x2 = Math.min(w - 1, x + 1);\n      const a = src[y0 * w + x0], b = src[y0 * w + x1], c = src[y0 * w + x2];\n      const d = src[y1 * w + x0],             f = src[y1 * w + x2];\n      const g = src[y2 * w + x0], hh = src[y2 * w + x1], i = src[y2 * w + x2];\n      const gx = (c + 2*f + i) - (a + 2*d + g);\n      const gy = (g + 2*hh + i) - (a + 2*b + c);\n      mag[y * w + x] = Math.sqrt(gx * gx + gy * gy);\n    }\n  }\n  return mag;\n}\n\nfunction percentile(values, p) {\n  const hist = new Uint32Array(256);\n  for (let i = 0; i < values.length; i++) hist[Math.min(255, values[i] | 0)]++;\n  const target = values.length * p;\n  let acc = 0;\n  for (let v = 0; v < 256; v++) {\n    acc += hist[v];\n    if (acc >= target) return v;\n  }\n  return 255;\n}\n\nfunction dilate(src, w, h, r) {\n  const tmp = new Uint8Array(w * h);\n  const out = new Uint8Array(w * h);\n  for (let y = 0; y < h; y++) {\n    for (let x = 0; x < w; x++) {\n      let m = 0;\n      for (let k = -r; k <= r; k++) {\n        const xx = Math.min(w - 1, Math.max(0, x + k));\n        if (src[y * w + xx] > m) m = src[y * w + xx];\n      }\n      tmp[y * w + x] = m;\n    }\n  }\n  for (let x = 0; x < w; x++) {\n    for (let y = 0; y < h; y++) {\n      let m = 0;\n      for (let k = -r; k <= r; k++) {\n        const yy = Math.min(h - 1, Math.max(0, y + k));\n        if (tmp[yy * w + x] > m) m = tmp[yy * w + x];\n      }\n      out[y * w + x] = m;\n    }\n  }\n  return out;\n}\n\n\/* ---------- sample scene ---------- *\/\n\nfunction drawSampleScene(ctx) {\n  const W = 640, H = 480;\n  ctx.fillStyle = \"#9dcff0\";\n  ctx.fillRect(0, 0, W, H);\n  ctx.fillStyle = \"#8cc968\";\n  ctx.fillRect(0, 360, W, 120);\n  ctx.fillStyle = \"#ffe65a\";\n  ctx.strokeStyle = \"#c87800\";\n  ctx.lineWidth = 4;\n  ctx.beginPath(); ctx.arc(565, 95, 45, 0, Math.PI * 2); ctx.fill(); ctx.stroke();\n  ctx.strokeStyle = \"#f0961e\";\n  ctx.lineWidth = 6;\n  for (let i = 0; i < 8; i++) {\n    const a = i * Math.PI \/ 4;\n    ctx.beginPath();\n    ctx.moveTo(565 + 58 * Math.cos(a), 95 + 58 * Math.sin(a));\n    ctx.lineTo(565 + 78 * Math.cos(a), 95 + 78 * Math.sin(a));\n    ctx.stroke();\n  }\n  ctx.fillStyle = \"#ffffff\";\n  ctx.strokeStyle = \"#969696\";\n  ctx.lineWidth = 3;\n  [[110, 85, 45], [175, 70, 55], [250, 88, 42]].forEach(([cx, cy, r]) => {\n    ctx.beginPath(); ctx.arc(cx, cy, r, 0, Math.PI * 2); ctx.fill(); ctx.stroke();\n  });\n  ctx.fillStyle = \"#ffc878\";\n  ctx.strokeStyle = \"#783c14\";\n  ctx.lineWidth = 5;\n  ctx.fillRect(80, 220, 180, 140); ctx.strokeRect(80, 220, 180, 140);\n  ctx.fillStyle = \"#c85a46\";\n  ctx.beginPath(); ctx.moveTo(55, 220); ctx.lineTo(170, 150); ctx.lineTo(285, 220); ctx.closePath(); ctx.fill(); ctx.stroke();\n  ctx.fillStyle = \"#785028\";\n  ctx.fillRect(140, 275, 60, 85); ctx.strokeRect(140, 275, 60, 85);\n  ctx.fillStyle = \"#5a3c19\";\n  ctx.fillRect(160, 310, 20, 50);\n  ctx.fillStyle = \"#96643c\";\n  ctx.strokeStyle = \"#50321a\";\n  ctx.lineWidth = 4;\n  ctx.fillRect(360, 260, 40, 100); ctx.strokeRect(360, 260, 40, 100);\n  ctx.fillStyle = \"#46a046\";\n  ctx.beginPath(); ctx.arc(380, 225, 62, 0, Math.PI * 2); ctx.fill(); ctx.stroke();\n  ctx.beginPath(); ctx.arc(360, 165, 48, 0, Math.PI * 2); ctx.fill(); ctx.stroke();\n  [[470, 390], [530, 370], [585, 400]].forEach(([cx, cy]) => {\n    ctx.strokeStyle = \"#287828\";\n    ctx.lineWidth = 5;\n    ctx.beginPath(); ctx.moveTo(cx, cy); ctx.lineTo(cx, cy + 35); ctx.stroke();\n    ctx.fillStyle = \"#f58caa\";\n    ctx.strokeStyle = \"#962850\";\n    ctx.lineWidth = 3;\n    for (let i = 0; i < 6; i++) {\n      const a = i * Math.PI \/ 3;\n      ctx.beginPath();\n      ctx.arc(cx + 14 * Math.cos(a), cy + 14 * Math.sin(a), 8, 0, Math.PI * 2);\n      ctx.fill(); ctx.stroke();\n    }\n    ctx.fillStyle = \"#ffdc3c\";\n    ctx.strokeStyle = \"#966400\";\n    ctx.beginPath(); ctx.arc(cx, cy, 8, 0, Math.PI * 2); ctx.fill(); ctx.stroke();\n  });\n  ctx.fillStyle = \"#ffd2aa\";\n  ctx.strokeStyle = \"#50281a\";\n  ctx.lineWidth = 4;\n  ctx.beginPath(); ctx.ellipse(120, 420, 30, 30, 0, 0, Math.PI * 2); ctx.fill(); ctx.stroke();\n  ctx.fillStyle = \"#3c2814\";\n  ctx.beginPath(); ctx.ellipse(120, 408, 32, 16, 0, Math.PI, Math.PI * 2); ctx.fill();\n  ctx.strokeStyle = \"#50281a\";\n  ctx.beginPath(); ctx.arc(120, 422, 16, 0.15 * Math.PI, 0.85 * Math.PI); ctx.stroke();\n  ctx.fillStyle = \"#5a8cdc\";\n  ctx.fillRect(70, 450, 100, 28); ctx.strokeRect(70, 450, 100, 28);\n  ctx.strokeStyle = \"#284896\";\n  ctx.lineWidth = 6;\n  ctx.beginPath(); ctx.moveTo(80, 455); ctx.lineTo(60, 478); ctx.stroke();\n  ctx.beginPath(); ctx.moveTo(160, 455); ctx.lineTo(180, 478); ctx.stroke();\n}\n<\/script>\n<\/body>\n<\/html>\n","protected":false},"excerpt":{"rendered":"<p>\u7167\u7247\u8b8a\u7dda\u7a3f\uff5c\u514d\u8cbb\u8457\u8272\u9801\u7522\u751f\u5668\uff5c\u5bf6\u8c9d\u7684\u9748\u7ce7\u5c4b \u514d\u8cbb\u30fb\u514d\u5b89\u88dd\u30fb\u5716\u7247\u4e0d\u4e0a\u50b3 \u7167\u7247\u8b8a\u7dda\u7a3f\uff5c\u514d\u8cbb\u8457\u8272\u9801\u7522\u751f\u5668 \u4e0a\u50b3\u4e00\u5f35\u7167 [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"","meta":{"ocean_post_layout":"","ocean_both_sidebars_style":"","ocean_both_sidebars_content_width":0,"ocean_both_sidebars_sidebars_width":0,"ocean_sidebar":"","ocean_second_sidebar":"","ocean_disable_margins":"enable","ocean_add_body_class":"","ocean_shortcode_before_top_bar":"","ocean_shortcode_after_top_bar":"","ocean_shortcode_before_header":"","ocean_shortcode_after_header":"","ocean_has_shortcode":"","ocean_shortcode_after_title":"","ocean_shortcode_before_footer_widgets":"","ocean_shortcode_after_footer_widgets":"","ocean_shortcode_before_footer_bottom":"","ocean_shortcode_after_footer_bottom":"","ocean_display_top_bar":"default","ocean_display_header":"default","ocean_header_style":"","ocean_center_header_left_menu":"","ocean_custom_header_template":"","ocean_custom_logo":0,"ocean_custom_retina_logo":0,"ocean_custom_logo_max_width":0,"ocean_custom_logo_tablet_max_width":0,"ocean_custom_logo_mobile_max_width":0,"ocean_custom_logo_max_height":0,"ocean_custom_logo_tablet_max_height":0,"ocean_custom_logo_mobile_max_height":0,"ocean_header_custom_menu":"","ocean_menu_typo_font_family":"","ocean_menu_typo_font_subset":"","ocean_menu_typo_font_size":0,"ocean_menu_typo_font_size_tablet":0,"ocean_menu_typo_font_size_mobile":0,"ocean_menu_typo_font_size_unit":"px","ocean_menu_typo_font_weight":"","ocean_menu_typo_font_weight_tablet":"","ocean_menu_typo_font_weight_mobile":"","ocean_menu_typo_transform":"","ocean_menu_typo_transform_tablet":"","ocean_menu_typo_transform_mobile":"","ocean_menu_typo_line_height":0,"ocean_menu_typo_line_height_tablet":0,"ocean_menu_typo_line_height_mobile":0,"ocean_menu_typo_line_height_unit":"","ocean_menu_typo_spacing":0,"ocean_menu_typo_spacing_tablet":0,"ocean_menu_typo_spacing_mobile":0,"ocean_menu_typo_spacing_unit":"","ocean_menu_link_color":"","ocean_menu_link_color_hover":"","ocean_menu_link_color_active":"","ocean_menu_link_background":"","ocean_menu_link_hover_background":"","ocean_menu_link_active_background":"","ocean_menu_social_links_bg":"","ocean_menu_social_hover_links_bg":"","ocean_menu_social_links_color":"","ocean_menu_social_hover_links_color":"","ocean_disable_title":"default","ocean_disable_heading":"default","ocean_post_title":"","ocean_post_subheading":"","ocean_post_title_style":"","ocean_post_title_background_color":"","ocean_post_title_background":0,"ocean_post_title_bg_image_position":"","ocean_post_title_bg_image_attachment":"","ocean_post_title_bg_image_repeat":"","ocean_post_title_bg_image_size":"","ocean_post_title_height":0,"ocean_post_title_bg_overlay":0.5,"ocean_post_title_bg_overlay_color":"","ocean_disable_breadcrumbs":"default","ocean_breadcrumbs_color":"","ocean_breadcrumbs_separator_color":"","ocean_breadcrumbs_links_color":"","ocean_breadcrumbs_links_hover_color":"","ocean_display_footer_widgets":"default","ocean_display_footer_bottom":"default","ocean_custom_footer_template":"","footnotes":""},"class_list":["post-6706","page","type-page","status-publish","hentry","entry","owp-thumbs-layout-horizontal","owp-btn-normal","owp-tabs-layout-horizontal","has-no-thumbnails","has-product-nav"],"_hostinger_reach_plugin_has_subscription_block":false,"_hostinger_reach_plugin_is_elementor":false,"jetpack_sharing_enabled":true,"rttpg_featured_image_url":null,"rttpg_author":{"display_name":"Meiling","author_link":"https:\/\/lin608.meiling412.com\/ms\/author\/lin576688103gmail-com\/"},"rttpg_comment":0,"rttpg_category":null,"rttpg_excerpt":"\u7167\u7247\u8b8a\u7dda\u7a3f\uff5c\u514d\u8cbb\u8457\u8272\u9801\u7522\u751f\u5668\uff5c\u5bf6\u8c9d\u7684\u9748\u7ce7\u5c4b \u514d\u8cbb\u30fb\u514d\u5b89\u88dd\u30fb\u5716\u7247\u4e0d\u4e0a\u50b3 \u7167\u7247\u8b8a\u7dda\u7a3f\uff5c\u514d\u8cbb\u8457\u8272\u9801\u7522\u751f\u5668 \u4e0a\u50b3\u4e00\u5f35\u7167&hellip;","_links":{"self":[{"href":"https:\/\/lin608.meiling412.com\/ms\/wp-json\/wp\/v2\/pages\/6706","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/lin608.meiling412.com\/ms\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/lin608.meiling412.com\/ms\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/lin608.meiling412.com\/ms\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/lin608.meiling412.com\/ms\/wp-json\/wp\/v2\/comments?post=6706"}],"version-history":[{"count":1,"href":"https:\/\/lin608.meiling412.com\/ms\/wp-json\/wp\/v2\/pages\/6706\/revisions"}],"predecessor-version":[{"id":6707,"href":"https:\/\/lin608.meiling412.com\/ms\/wp-json\/wp\/v2\/pages\/6706\/revisions\/6707"}],"wp:attachment":[{"href":"https:\/\/lin608.meiling412.com\/ms\/wp-json\/wp\/v2\/media?parent=6706"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}