VTKJS Image Filters (Sharpening, Finegrain, etc)

Hi,

I was wondering if there’s an efficent way to implement image filters using VTKJS. Specially for medical imaging.

Filters like fine grain, sharpening, CLAHE, etc.

I saw this example in python: https://examples.vtk.org/site/Cxx/ImageProcessing/EnhanceEdges/

It’s pretty good, but there’s nothing in VTKJS that i found.

Here’s my very in-efficent implementation with vtkjs for sharpening

/**
 * Apply image sharpening using Laplacian edge enhancement for 2D images.
 * This function implements edge enhancement by subtracting the Laplacian
 * (edge detection) from the original image to enhance edges.
 *
 * @param imageData - The vtkImageData to sharpen
 * @param intensity - Sharpening intensity (0-1, where 0 is no sharpening, 1 is maximum)
 * @returns The sharpened vtkImageData
 */
function applySharpeningFilter(
{ imageData, intensity = 0.5 }:
{ imageData: vtkImageDataType; intensity?: number }
): vtkImageDataType {
  if (!imageData) {
    return imageData;
  }

  // Clamp intensity to valid range
  intensity = Math.max(0, Math.min(1, intensity));

  if (intensity === 0) {
    // No sharpening needed
    return imageData;
  }

  const dims = imageData.getDimensions();
  const scalars = imageData.getPointData().getScalars();
  const data = scalars.getData();
  const numComponents = scalars.getNumberOfComponents();

  // Create output data
  const outputData = new Float32Array(data.length);

  // 2D Laplacian kernel for edge detection
  // Standard 3x3 Laplacian filter
  const laplacianKernel = [
    0, 1, 0,
    1, -4, 1,
    0, 1, 0
  ];

  const kernelSize = 3;
  const kernelOffset = Math.floor(kernelSize / 2);

  // Apply Laplacian filter and edge enhancement
  for (let z = 0; z < dims[2]; z++) {
    for (let y = 0; y < dims[1]; y++) {
      for (let x = 0; x < dims[0]; x++) {
        for (let c = 0; c < numComponents; c++) {
          const idx = ((z * dims[1] + y) * dims[0] + x) * numComponents + c;
          const originalValue = data[idx];

          let laplacianValue = 0;

          // 2D convolution with Laplacian kernel
          let kernelIdx = 0;
          for (let ky = -kernelOffset; ky <= kernelOffset; ky++) {
            for (let kx = -kernelOffset; kx <= kernelOffset; kx++) {
              const nx = x + kx;
              const ny = y + ky;

              if (nx >= 0 && nx < dims[0] && ny >= 0 && ny < dims[1]) {
                const nIdx = ((z * dims[1] + ny) * dims[0] + nx) * numComponents + c;
                const kernelValue = laplacianKernel[kernelIdx];
                laplacianValue += data[nIdx] * kernelValue;
              }
              kernelIdx++;
            }
          }

          // Apply edge enhancement: original - (laplacian * intensity)
          // This enhances edges by subtracting the detected edges from the original
          outputData[idx] = originalValue - (laplacianValue * intensity);
        }
      }
    }
  }

  // Create new image data with sharpened values
  const outputImageData = vtkImageData.newInstance();
  outputImageData.setDimensions(dims);
  outputImageData.setSpacing(imageData.getSpacing());
  outputImageData.setOrigin(imageData.getOrigin());
  outputImageData.setDirection(imageData.getDirection());

  const outputScalars = vtkDataArray.newInstance({
    numberOfComponents: numComponents,
    values: outputData,
    name: 'Scalars'
  });

  outputImageData.getPointData().setScalars(outputScalars);

  return outputImageData;
}

You can do it on the GPU with the Convolution2D pass:

Alternatively, you can use the VTK.js / ITK.wasm compatibility to do “image processing”.

Thanks, super helpful :smiley: