{"id":88,"date":"2025-10-29T21:38:32","date_gmt":"2025-10-29T13:38:32","guid":{"rendered":"http:\/\/photocaloric.com\/?p=88"},"modified":"2025-11-01T20:29:01","modified_gmt":"2025-11-01T12:29:01","slug":"%e6%8b%89%e6%9b%bc%e5%85%89%e8%b0%b1%e6%89%a3%e9%99%a4%e8%83%8c%e6%99%af%e7%9a%84airpls%e7%ae%97%e6%b3%95","status":"publish","type":"post","link":"http:\/\/photocaloric.com\/index.php\/2025\/10\/29\/%e6%8b%89%e6%9b%bc%e5%85%89%e8%b0%b1%e6%89%a3%e9%99%a4%e8%83%8c%e6%99%af%e7%9a%84airpls%e7%ae%97%e6%b3%95\/","title":{"rendered":"\u62c9\u66fc\u5149\u8c31\u6263\u9664\u80cc\u666f\u7684airPLS\u7b97\u6cd5(\u9644\u5728\u7ebf\u5904\u7406\u529f\u80fd)"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">\u62c9\u66fc\u5149\u8c31\u6263\u9664\u80cc\u666f\u4e00\u76f4\u662f\u4e00\u4e2a\u5f88\u9ebb\u70e6\u7684\u4e1c\u897f\uff0c\u5c24\u5176\u662f\u5149\u8c31\u5f88\u591a\u7684\u8bdd\uff0c\u4e00\u4e2a\u4e00\u4e2a\u653e\u5230origin\u91cc\u5904\u7406\u592a\u8d39\u4e8b\u4e86\u3002\u6709\u6ca1\u6709\u4ec0\u4e48\u81ea\u52a8\u5316\u7684\u65b9\u6cd5\u5462\uff1f<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u6709\u7684\u5144\u5f1f\uff0c\u6709\u7684\u3002\u4e00\u4e2a\u53eb\u505aairPLS\u7684\u7b97\u6cd5\u89e3\u541b\u6101\uff01<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5047\u8bbe\u6211\u4eec\u7684\u539f\u59cb\u5149\u8c31\u662f\\(I_{tot}(k)=I_{Raman}(k)+I_{background}(k)\\)\uff0c\u5176\u4e2d\\(I_{Raman}(k)\\)\u662f\u62c9\u66fc\u5cf0\u7684\u7ec4\u5408\uff0c\u6211\u4eec\u8ba4\u4e3a\u5b83\u7a84\u800c\u9ad8\uff0c\\(I_{background}(k)\\)\u662f\u80cc\u666f\uff0c\u5b83\u53ef\u80fd\u662f\u73af\u5883\u6742\u6563\u5149\uff0c\u6216\u8005\u4e00\u4e9b\u8367\u5149\u5cf0\uff0c\u6211\u4eec\u8ba4\u4e3a\u5b83\u4eec\u4f4e\u800c\u77ee\u3002\u6211\u4eec\u9700\u8981\u6709\u4e00\u4e2a\u7b97\u6cd5\u80fd\u591f\u62df\u5408\u51fa\\(I_{background}(k)\\)\uff0c\u4ee5\u4fbf\u6211\u4eec\u5c06\u5176\u51cf\u53bb\u4ee5\u5f97\u5230\u53ea\u6709\\(I_{Raman}(k)\\)\u7684\u7ed3\u679c\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u89e3\u51b3\u601d\u8def\u5f88\u7b80\u5355\uff1a\u8003\u8651\u5728x\u8f74\u6709\u4e00\u4e2a\u6c14\u7403\uff0c\u5c06\u5176\u5411\u4e0a\u5439\uff0c\u5b83\u4f1a\u5c3d\u53ef\u80fd\u8d34\u5408\u5149\u8c31\u7684\u5e95\u90e8\uff0c\u4f46\u7531\u4e8e\u6c14\u7403\u5177\u6709\u6709\u9650\u7684\u8868\u9762\u5f20\u529b\uff0c\u5b83\u4e0d\u4f1a\u4f38\u5165\u5149\u8c31\u5e95\u90e8\u7684\u7ec6\u5c0f\u7f1d\u9699\u4e2d\u2014\u2014\u5373\u62c9\u66fc\u5cf0\u4e2d\u3002\u8fd9\u6837\uff0c\u6211\u4eec\u5c31\u80fd\u8bf4\u8fd9\u4e2a\u6c14\u7403\u7684\u5f62\u72b6\u5c31\u5f88\u63a5\u8fd1\u80cc\u666f\\(I_{background}(k)\\)\u4e86\u3002<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">\u5177\u4f53\u7b97\u6cd5\u5b9e\u73b0\u662f\uff0c\u8003\u8651\u4e00\u4e2a\u8fed\u4ee3\u8fc7\u7a0b\uff0c\u76ee\u6807\u51fd\u6570\u662f\\(I_{target}(k)\\)<\/p>\n\n\n\n<!DOCTYPE html>\n<html lang=\"zh\">\n<head>\n<meta charset=\"UTF-8\">\n<title>airPLS<\/title>\n<script src=\"https:\/\/cdn.jsdelivr.net\/npm\/chart.js\"><\/script>\n<style>\n  body { font-family: \"Microsoft YaHei\", sans-serif; margin: 40px; background: #f8f9fb; }\n  h2 { color: #333; }\n  textarea { width: 100%; height: 120px; font-family: monospace; }\n  input, button { margin: 6px 0; padding: 5px 10px; }\n  .param-box { background: #fff; border-radius: 8px; padding: 12px; margin: 10px 0; box-shadow: 0 1px 4px rgba(0,0,0,0.1);}\n  label { display: inline-block; width: 90px; }\n  canvas#chart {\n      background: white;\n      border: 1px solid #ccc;\n      border-radius: 8px;\n      margin-top: 20px;\n      position: relative !important;\n      width: 100% !important;\n      max-width: 900px !important;\n      height: 400px !important;\n      display: block !important;\n      z-index: 0 !important;\n  }\n<\/style>\n<\/head>\n\n<body>\n\n<h2>\ud83d\udca8 airPLS<\/h2>\n<p>\u53ef\u7c98\u8d34\u6570\u636e\uff08\u4e24\u5217 x, y\uff09\u6216\u5bfc\u5165 CSV \u6587\u4ef6\u3002\u82e5\u65e0\u8f93\u5165\uff0c\u8bf7\u76f4\u63a5\u70b9\u51fb\u201c\u8fd0\u884c\u793a\u4f8b\u201d\u3002<\/p>\n\n<textarea id=\"dataInput\" placeholder=\"\u7c98\u8d34\u4e24\u5217\u6570\u636e\uff0c\u5982\uff1a\n740   10.2\n740.5 12.1\n741   20.3\n...\"><\/textarea><br>\n<input type=\"file\" id=\"fileInput\" accept=\".txt,.csv\"><br>\n\n<div class=\"param-box\">\n  <h4>\u2699\ufe0f \u53c2\u6570\u8c03\u8282<\/h4>\n  <label>\u03bb (\u5e73\u6ed1\u5ea6):<\/label>\n  <input type=\"range\" id=\"lambdaSlider\" min=\"4\" max=\"8\" step=\"0.1\" value=\"4\" oninput=\"updateLambdaLabel()\">\n  <span id=\"lambdaValue\">1e4<\/span><br>\n\n  <label>p (\u5f20\u529b):<\/label>\n  <input type=\"range\" id=\"pSlider\" min=\"0.001\" max=\"0.1\" step=\"0.001\" value=\"0.1\" oninput=\"updatePLabel()\">\n  <span id=\"pValue\">0.100<\/span>\n<\/div>\n\n<button onclick=\"runExample()\">\u8fd0\u884c\u793a\u4f8b<\/button>\n<button onclick=\"runBaseline()\">\u8fd0\u884c\u5206\u6790<\/button>\n<button onclick=\"downloadResult()\">\u4e0b\u8f7d\u7ed3\u679c CSV<\/button>\n\n<canvas id=\"chart\" height=\"350\"><\/canvas>\n\n<script>\n\/\/ ======== AsLS \u6838\u5fc3\u7b97\u6cd5 ========\nfunction asls(y, lambda=1e4, p=0.1, niter=15) {\n    const m = y.length;\n    \/\/ \u6784\u9020\u4e8c\u9636\u5dee\u5206\u77e9\u9635 D\n    let D = Array(m-2).fill(0).map((_,i)=>{\n        let row = Array(m).fill(0);\n        row[i] = 1; row[i+1] = -2; row[i+2] = 1;\n        return row;\n    });\n    \/\/ H = \u03bb D\u1d57D\n    let H = Array(m).fill(0).map(()=>Array(m).fill(0));\n    for (let i=0;i<m;i++){\n        for (let j=0;j<m;j++){\n            let s=0;\n            for (let k=0;k<m-2;k++) s += D[k][i]*D[k][j];\n            H[i][j] = lambda*s;\n        }\n    }\n    let w = Array(m).fill(1);\n    let z = Array(m).fill(0);\n    for (let iter=0; iter<niter; iter++) {\n        \/\/ \u6784\u9020 C = W + H\n        let C = Array(m).fill(0).map(()=>Array(m).fill(0));\n        for (let i=0;i<m;i++){\n            for (let j=0;j<m;j++){\n                C[i][j] = (i===j?w[i]:0) + H[i][j];\n            }\n        }\n        let b = y.map((yi,i)=>w[i]*yi);\n        z = solve(C,b);\n        let d = y.map((yi,i)=>yi - z[i]);\n        w = d.map(v => v>0 ? p : 1-p);\n    }\n    return z;\n}\n\n\/\/ \u7b80\u5355\u9ad8\u65af\u6d88\u5143\nfunction solve(A,b){\n    const n = A.length;\n    A = A.map(r=>r.slice());\n    b = b.slice();\n    for (let i=0;i<n;i++){\n        let maxRow=i;\n        for (let k=i+1;k<n;k++)\n            if(Math.abs(A[k][i])>Math.abs(A[maxRow][i])) maxRow=k;\n        [A[i],A[maxRow]]=[A[maxRow],A[i]];\n        [b[i],b[maxRow]]=[b[maxRow],b[i]];\n        let div=A[i][i];\n        for(let j=i;j<n;j++) A[i][j]\/=div;\n        b[i]\/=div;\n        for(let k=i+1;k<n;k++){\n            let factor=A[k][i];\n            for(let j=i;j<n;j++) A[k][j]-=factor*A[i][j];\n            b[k]-=factor*b[i];\n        }\n    }\n    let x=Array(n).fill(0);\n    for(let i=n-1;i>=0;i--){\n        x[i]=b[i];\n        for(let j=i+1;j<n;j++) x[i]-=A[i][j]*x[j];\n    }\n    return x;\n}\n\n\/\/ ======== \u6570\u636e\u89e3\u6790 ========\nfunction parseData(txt){\n    let lines = txt.trim().split(\/\\r?\\n\/);\n    let x=[],y=[];\n    for(let line of lines){\n        let parts = line.trim().split(\/[\\s,]+\/);\n        if(parts.length>=2){\n            x.push(parseFloat(parts[0]));\n            y.push(parseFloat(parts[1]));\n        }\n    }\n    return {x,y};\n}\n\n\/\/ ======== \u53c2\u6570\u66f4\u65b0\u663e\u793a ========\nfunction updateLambdaLabel(){\n    let logv = parseFloat(document.getElementById(\"lambdaSlider\").value);\n    let val = Math.pow(10, logv).toExponential(0);\n    document.getElementById(\"lambdaValue\").innerText = val;\n}\nfunction updatePLabel(){\n    let val = parseFloat(document.getElementById(\"pSlider\").value);\n    document.getElementById(\"pValue\").innerText = val.toFixed(3);\n}\n\n\/\/ ======== \u4e3b\u8fd0\u884c\u51fd\u6570 ========\nlet chart;\nfunction runBaseline(){\n    let txt = document.getElementById(\"dataInput\").value;\n    if(!txt.trim()){ alert(\"\u8bf7\u8f93\u5165\u6216\u5bfc\u5165\u6570\u636e\uff0c\u6216\u70b9\u51fb\u201c\u8fd0\u884c\u793a\u4f8b\u201d\u3002\"); return; }\n    let {x,y} = parseData(txt);\n    if(x.length<10){ alert(\"\u6570\u636e\u592a\u5c11\uff01\"); return; }\n\n    const lambda = Math.pow(10, parseFloat(document.getElementById(\"lambdaSlider\").value));\n    const p = parseFloat(document.getElementById(\"pSlider\").value);\n\n    let baseline = asls(y, lambda, p, 20);\n    let corrected = y.map((v,i)=>Math.max(v-baseline[i],0));\n    drawPlot(x,y,baseline,corrected);\n    window.currentResult = {x,y,baseline,corrected};\n}\n\n\/\/ ======== \u7ed8\u56fe ========\nfunction drawPlot(x,y,baseline,corrected){\n    if(chart) chart.destroy();\n    const ctx = document.getElementById(\"chart\");\n    chart = new Chart(ctx, {\n        type: 'line',\n        data: {\n            labels: x,\n            datasets: [\n                {label:'\u539f\u59cb\u5149\u8c31', data:y, borderColor:'black', borderWidth:1, pointRadius:0},\n                {label:'\u62df\u5408\u57fa\u7ebf', data:baseline, borderColor:'red', borderWidth:1.5, pointRadius:0},\n                {label:'\u53bb\u57fa\u7ebf\u540e\u5149\u8c31', data:corrected, borderColor:'blue', borderWidth:1, pointRadius:0}\n            ]\n        },\n        options: {\n            animation:false,\n            scales:{\n                x:{title:{display:true,text:'\u6ce2\u957f \/ \u5355\u4f4d'}},\n                y:{title:{display:true,text:'\u5f3a\u5ea6'}}\n            }\n        }\n    });\n}\n\n\/\/ ======== \u793a\u4f8b\u6570\u636e ========\nfunction runExample(){\n    const x=[], y=[];\n    for(let i=0;i<400;i++){\n        let xi=740+20*i\/400;\n        let base=200*Math.exp(-Math.pow(xi-750,2)\/(2*4*4));\n        let small=0;\n        for(let k=0;k<10;k++){\n            let ctr=742+16*Math.random(), amp=5+20*Math.random();\n            small += amp*Math.exp(-Math.pow(xi-ctr,2)\/(2*0.05*0.05));\n        }\n        y.push(base+small+1.5*(Math.random()-0.5));\n        x.push(xi);\n    }\n    document.getElementById(\"dataInput\").value = x.map((xi,i)=>xi+\"\\t\"+y[i]).join(\"\\n\");\n    runBaseline();\n}\n\n\/\/ ======== \u6587\u4ef6\u5bfc\u5165 ========\ndocument.getElementById(\"fileInput\").addEventListener(\"change\", e=>{\n    const file = e.target.files[0];\n    if(!file) return;\n    const reader = new FileReader();\n    reader.onload = ev => { document.getElementById(\"dataInput\").value = ev.target.result; };\n    reader.readAsText(file);\n});\n\n\/\/ ======== \u4e0b\u8f7d\u7ed3\u679c ========\nfunction downloadResult(){\n    if(!window.currentResult){ alert(\"\u8bf7\u5148\u8fd0\u884c\u5206\u6790\u3002\"); return; }\n    const {x,y,baseline,corrected} = window.currentResult;\n    let csv = \"x,y,baseline,corrected\\n\";\n    for(let i=0;i<x.length;i++)\n        csv += `${x[i]},${y[i]},${baseline[i]},${corrected[i]}\\n`;\n    const blob = new Blob([csv],{type:'text\/csv'});\n    const url = URL.createObjectURL(blob);\n    const a = document.createElement('a');\n    a.href = url;\n    a.download = 'baseline_corrected.csv';\n    a.click();\n}\n<\/script>\n\n<\/body>\n<\/html>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>\u62c9\u66fc\u5149\u8c31\u6263\u9664\u80cc\u666f\u4e00\u76f4\u662f\u4e00\u4e2a\u5f88\u9ebb\u70e6\u7684\u4e1c\u897f\uff0c\u5c24\u5176\u662f\u5149\u8c31\u5f88\u591a\u7684\u8bdd\uff0c\u4e00\u4e2a\u4e00\u4e2a\u653e\u5230origin\u91cc\u5904\u7406\u592a\u8d39\u4e8b\u4e86\u3002\u6709\u6ca1\u6709\u4ec0\u4e48\u81ea\u52a8\u5316\u7684\u65b9\u6cd5\u5462\uff1f \u6709 &#8230;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"emotion":"","emotion_color":"","title_style":"","license":"","footnotes":""},"categories":[1],"tags":[],"class_list":["post-88","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/posts\/88","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/comments?post=88"}],"version-history":[{"count":8,"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/posts\/88\/revisions"}],"predecessor-version":[{"id":117,"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/posts\/88\/revisions\/117"}],"wp:attachment":[{"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/media?parent=88"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/categories?post=88"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/photocaloric.com\/index.php\/wp-json\/wp\/v2\/tags?post=88"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}