From 26e860d168ee3a925daec5855238bb1dd6134d90 Mon Sep 17 00:00:00 2001 From: Peter Stockings Date: Sat, 17 May 2025 09:52:48 +1000 Subject: [PATCH] Make generated singogram easier to see --- public/UploadImageComponent.js | 1 + public/sinogram.js | 60 ++++++++++++++++++++-------------- 2 files changed, 36 insertions(+), 25 deletions(-) diff --git a/public/UploadImageComponent.js b/public/UploadImageComponent.js index a2d45ba..3f1266e 100644 --- a/public/UploadImageComponent.js +++ b/public/UploadImageComponent.js @@ -230,6 +230,7 @@ export const UploadImageComponent = { src: this.sinogramUrl, alt: "Sinogram", class: "rounded shadow max-w-full h-auto mx-auto", + style: "width: 100%; max-height: 300px;", }), ]), diff --git a/public/sinogram.js b/public/sinogram.js index 5b86b9f..98165d3 100644 --- a/public/sinogram.js +++ b/public/sinogram.js @@ -18,51 +18,49 @@ export async function generateSinogram( for (let angle = 0; angle < angles; angle++) { const theta = (angle * Math.PI) / angles; - // 🔁 Call visual overlay for this angle if (drawAngleCallback) drawAngleCallback(theta); - - // (Optional: add delay for animation) await new Promise((r) => setTimeout(r, 0.01)); - // Clear canvas ctx.clearRect(0, 0, size, size); - - // Transform and draw rotated image ctx.save(); ctx.translate(size / 2, size / 2); ctx.rotate(theta); ctx.drawImage(image, -image.width / 2, -image.height / 2); ctx.restore(); - // Read pixel data const { data } = ctx.getImageData(0, 0, size, size); - // Sum brightness vertically (simulate X-ray projection) const projection = []; for (let x = 0; x < size; x++) { let sum = 0; for (let y = 0; y < size; y++) { const i = (y * size + x) * 4; - const gray = data[i]; // red channel (since grayscale) - sum += gray; + sum += data[i]; // grayscale from red channel } - projection.push(sum / size); // normalize + projection.push(sum / size); } projections.push(projection); } - // Create sinogram canvas + const isHorizontal = angles >= size; + + // Create rotated canvas accordingly const sinogramCanvas = Object.assign(document.createElement("canvas"), { - width: size, - height: angles, + width: isHorizontal ? size : angles, + height: isHorizontal ? angles : size, }); const sinCtx = sinogramCanvas.getContext("2d"); - const imgData = sinCtx.createImageData(size, angles); + const imgData = sinCtx.createImageData( + sinogramCanvas.width, + sinogramCanvas.height + ); - for (let y = 0; y < angles; y++) { + for (let angle = 0; angle < angles; angle++) { for (let x = 0; x < size; x++) { - const val = projections[y][x]; - const i = (y * size + x) * 4; + const val = projections[angle][x]; + const px = isHorizontal ? x : angle; + const py = isHorizontal ? angle : x; + const i = (py * sinogramCanvas.width + px) * 4; imgData.data[i + 0] = val; imgData.data[i + 1] = val; imgData.data[i + 2] = val; @@ -95,7 +93,18 @@ export async function reconstructImageFromSinogram( sinogramImage.height ).data; - size = sinogramImage.width; // match size to sinogram resolution + // Detect orientation + let width, angles; + const isVertical = sinogramImage.height > sinogramImage.width; + if (isVertical) { + angles = sinogramImage.width; + width = sinogramImage.height; + } else { + angles = sinogramImage.height; + width = sinogramImage.width; + } + + size = width; const outputCanvas = Object.assign(document.createElement("canvas"), { width: size, height: size, @@ -104,17 +113,18 @@ export async function reconstructImageFromSinogram( const accum = new Float32Array(size * size); const center = size / 2; - const angles = sinogramImage.height; - const width = sinogramImage.width; - for (let angle = 0; angle < angles; angle++) { const theta = (angle * Math.PI) / angles; let projection = []; for (let x = 0; x < width; x++) { - const i = (angle * width + x) * 4; + const i = isVertical + ? (x * angles + angle) * 4 // transposed layout + : (angle * width + x) * 4; // normal layout + projection.push(sinogramData[i]); } + if (useFBP) { projection = applyRampFilter(projection); } @@ -122,7 +132,7 @@ export async function reconstructImageFromSinogram( for (let y = 0; y < size; y++) { for (let x = 0; x < size; x++) { const x0 = x - center; - const y0 = center - y; // flip y + const y0 = center - y; const s = Math.round( x0 * Math.cos(theta) + y0 * Math.sin(theta) + width / 2 ); @@ -133,7 +143,6 @@ export async function reconstructImageFromSinogram( } if (onFrame) { - // normalize and draw current frame let maxVal = 0; for (let i = 0; i < accum.length; i++) { if (accum[i] > maxVal) maxVal = accum[i]; @@ -154,6 +163,7 @@ export async function reconstructImageFromSinogram( imageData.data[i * 4 + 2] = b; imageData.data[i * 4 + 3] = 255; } + outputCtx.putImageData(imageData, 0, 0); await new Promise((r) => setTimeout(r, 1)); onFrame(angle, outputCanvas.toDataURL());