export const steps = [ { title: "1. Upload / Input Image", content: `You upload or use a default grayscale image. This is treated like a 2D slice of a physical object. The input image is a 2D function: $ f(x, y) $ This represents how much X-rays are absorbed at each point.`, }, { title: "2. Radon Transform (Generating the Sinogram)", content: `We rotate a virtual X-ray beam around the image and compute line integrals at each angle — simulating how X-rays pass through. \\[ R(\\theta, s) = \\int_{-\\infty}^{\\infty} f(s \\cos\\theta - t \\sin\\theta,\\ s \\sin\\theta + t \\cos\\theta)\\ dt \\] Where:
- $\\theta$ = projection angle
- $s$ = offset from center ![Radon transform](https://upload.wikimedia.org/wikipedia/commons/9/93/Radon_transform_sinogram.gif)`, }, { title: "3. Sinogram", content: `You now have a 2D image where: - X-axis = detector position - Y-axis = angle Each row is a projection. Math: $\text{Sinogram} = \{ R(\theta_1, s), R(\theta_2, s), \dots, R(\theta_n, s) \}$ ![Sinogram](https://www.researchgate.net/profile/Samuel-Asante-2/publication/299856137/figure/fig3/AS:348226420002817@1460035056245/A-Shepp-Logan-Phantom-and-reconstructed-Image-Sinogram-a-Original-image-b-radon.png)`, }, { title: "4. Optional: Apply Ramp Filter (FBP)", content: `If enabled, we sharpen each projection before back-projecting by amplifying high-frequency content. Math: \\[ P(\\omega) = \\mathcal{F}[R(\\theta, s)] \\\\ P'(\\omega) = |\\omega| \\cdot P(\\omega) \\\\ \\text{Filtered Projection} = \\mathcal{F}^{-1}[P'(\\omega)] \\] ![Ramp filter graph](https://www.researchgate.net/publication/346858231/figure/fig4/AS:967035467091970@1607570630558/Applying-Ramp-filter-to-a-sinogram-preserves-high-frequency-features-The-filter-is.png)`, }, { title: "5. Back Projection", content: `Each (filtered) projection is \"smeared\" back into the image space along its angle. \\[ f'(x, y) = \\int_0^{\\pi} R'(\\theta,\\ x \\cos\\theta + y \\sin\\theta)\\ d\\theta \\]`, }, { title: "6. Final Reconstruction", content: `After all angles are added up, you get a reconstructed image resembling the original. \[ f'(x, y) \approx f(x, y) \]`, }, ]; export const StepAccordion = { view(vnode) { const index = vnode.attrs.index; const step = steps[index]; const expanded = vnode.state.expanded ?? false; return m("div", { class: "border-b border-gray-300 py-1 my-2" }, [ m( "button", { class: "w-full text-left font-bold text-lg text-gray-800 focus:outline-none", onclick: () => { vnode.state.expanded = !vnode.state.expanded; }, }, step.title ), vnode.state.expanded && m( "div", { class: "mt-2 text-gray-700 whitespace-pre-wrap text-sm", onupdate: () => { if (window.MathJax) window.MathJax.typesetPromise(); }, }, m.trust(markdownToHTML(step.content)) ), ]); }, }; // You must provide a markdownToHTML() function or use a library like marked.js function markdownToHTML(text) { return text .replace(/\n/g, "
") .replace( /!\[(.*?)\]\((.*?)\)/g, '$1' ); }