Drei Unschärfe-Varianten — Gauß, anisotrop, Bewegung

Dasselbe Bild durch drei verschiedene Unschärfe-Kerne, untereinander gestapelt zum direkten Vergleich: - **Gauß** — die klassische isotrope Weichzeichnung mit gleichem Sigma in beiden Achsen. - **Anisotrop** — unabhängige X-/Y-Sigmas. Mit X viel größer als Y entsteht ein horizontaler Streifen, der die vertikalen Details scharf lässt — der „gestreckte Lichter"-Look von Linsen. - **Motion** — gerichtete Linearunschärfe entlang eines Winkels und einer Länge. Simuliert Kameraverwacklung oder Bewegung des Motivs in eine bestimmte Richtung. Alle drei sind reine Pixel-Operationen in der Engine — keine Erweiterungen nötig. Mit den Parameter-Reglern siehst du, wie sich jede Variante vom naiven Gauß unterscheidet.

INPUT
INPUT — Drei Unschärfe-Varianten — Gauß, anisotrop, Bewegung
Three blurs
Three blurs — Drei Unschärfe-Varianten — Gauß, anisotrop, Bewegung
JavaScript
// Three blur flavours side-by-side — gaussian / anisotropic / motion
// demo_blur_variants.js
//!INPUT: INPUT
//!OUTPUT: OUTPUT
//!PARAM: GAUSSIAN_SIGMA:number=4,min=0,max=40
//!PARAM: ANISO_SIGMA_X:number=12,min=0,max=40
//!PARAM: ANISO_SIGMA_Y:number=2,min=0,max=40
//!PARAM: MOTION_ANGLE:number=30,min=-180,max=180
//!PARAM: MOTION_LENGTH:integer=24,min=0,max=80
//!PARAM: LABEL_SIZE:number=22,min=10,max=64

// Renders the same input through three different blur kernels in
// one composite, so the visual difference is immediately obvious:
//
//   1. Isotropic Gaussian — the everyday "soft focus" blur. Equal
//      sigma in both axes, separable two-pass.
//   2. Anisotropic Gaussian — independent sigmas. Stretching one
//      axis gives lens-style streaks (sigma_x ≫ sigma_y) without
//      losing detail in the other axis.
//   3. Motion blur — a directional linear average along an angle,
//      not a Gaussian. Simulates camera shake / subject motion.
//      Bilinear-sampled along the trail for smooth gradient.
//
// All three are pure pixel ops with no Rust extensions needed; the
// engine ships them in `bridge/src/ops/filter.rs`.

const src = Engine.loadImage(INPUT);
const SRC_W = src.width;
const SRC_H = src.height;

// One panel = the source size. Three panels stacked vertically with
// a small label band on top of each so the comparison is clear.
const BAND_H = LABEL_SIZE + 8;
const PANEL_H = SRC_H;
const TOTAL_H = (BAND_H + PANEL_H) * 3;

const canvas = Engine.createImage(1, 1);
canvas.setPixel(px(0, 0), new Pixel(0.05, 0.05, 0.07, 1.0));
canvas.resize(SRC_W, TOTAL_H);

// Helper: draw a label on the canvas at the start of a band.
function label(text, y) {
  canvas.drawText(text, 16, y + LABEL_SIZE - 2, {
    size: LABEL_SIZE,
    color: NamedColor.white,
  });
}

// ── Panel 1: isotropic Gaussian ──────────────────────────────────
{
  const tile = src.clone();
  tile.gaussianBlur(GAUSSIAN_SIGMA);
  label(`gaussianBlur(${GAUSSIAN_SIGMA})`, 0);
  canvas.blendAt(tile, px(0, BAND_H), 1.0, BlendMode.Over);
  tile.free();
}

// ── Panel 2: anisotropic Gaussian ─────────────────────────────────
{
  const tile = src.clone();
  tile.anisotropicBlur(ANISO_SIGMA_X, ANISO_SIGMA_Y);
  const y = (BAND_H + PANEL_H);
  label(`anisotropicBlur(${ANISO_SIGMA_X}, ${ANISO_SIGMA_Y})`, y);
  canvas.blendAt(tile, px(0, y + BAND_H), 1.0, BlendMode.Over);
  tile.free();
}

// ── Panel 3: motion blur ──────────────────────────────────────────
{
  const tile = src.clone();
  tile.motionBlur(MOTION_ANGLE, MOTION_LENGTH);
  const y = 2 * (BAND_H + PANEL_H);
  label(`motionBlur(${MOTION_ANGLE}°, ${MOTION_LENGTH}px)`, y);
  canvas.blendAt(tile, px(0, y + BAND_H), 1.0, BlendMode.Over);
  tile.free();
}

src.free();
canvas.save(OUTPUT);
canvas.free();

// © 2026 Michael Lechner · mlc OpticScript · https://mlcgo.eu · Elastic License 2.0