Install any skill in seconds. Free to start, no credit card required.
Get Started Free →Filter selection decision tree, noise identification, edge detection priority, and kernel parameter rules
.claude/skills/aeren23-preprocessing-decisions/SKILL.md| Test case | Without → With | Effect | Δ tokens | Δ turns |
|---|---|---|---|---|
| case-02 | ✗→✓ | ▲ Improved | 21% | 0% |
| case-22 | ✗→✓ | ▲ Improved | 23% | 0% |
| case-16 | ✓→✓ | = Same ✓ | 80% | 0% |
| case-03 | ✓→✓ | = Same ✓ | 67% | 0% |
| case-04 | ✓→✓ | = Same ✓ | 48% | 0% |
What type of noise?
├── Salt & Pepper (random black/white dots)
│ └── ✅ Median Filter (cv2.medianBlur) — BEST IN THE WORLD for this
│
├── Gaussian noise (camera sensor heat, general grain)
│ └── ✅ Gaussian Blur (cv2.GaussianBlur)
│
├── Unknown noise + must preserve edges
│ └── ✅ Bilateral Filter (cv2.bilateralFilter) — kills noise, keeps edges
│
├── General smoothing (no specific noise type)
│ └── ✅ Mean Filter (cv2.blur) — simplest, fastest
│
└── Medical image with bias field / Rician noise
└── ✅ Non-Local Means (cv2.fastNlMeansDenoising)| Filter | Speed | Edge Preservation | Noise Removal | Best For | |--------|-------|-------------------|---------------|----------| | Mean (cv2.blur) | ⚡⚡⚡ | ❌ Poor | ⭐⭐ | General smoothing | | Gaussian (cv2.GaussianBlur) | ⚡⚡⚡ | ⭐ Fair | ⭐⭐⭐ | Gaussian noise, pre-Canny | | Median (cv2.medianBlur) | ⚡⚡ | ⭐⭐ Good | ⭐⭐⭐⭐⭐ (S&P) | Salt & Pepper noise | | Bilateral (cv2.bilateralFilter) | ⚡ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ | Edge-aware denoising |
> Rule of thumb: If you don't know the noise type, start with Gaussian. If edges matter, use Bilateral. If you see random black/white dots, use Median — nothing else comes close.
What do you need to detect?
├── General edges (most use cases)
│ └── ✅ Canny (cv2.Canny) — gold standard, 5-step pipeline
│
├── Directional edges (horizontal OR vertical)
│ └── ✅ Sobel (cv2.Sobel) — first derivative, specify dx/dy
│
├── Fine detail + corners + all boundaries
│ └── ✅ Laplacian (cv2.Laplacian) — second derivative, zero-crossing
│
└── Text/document character edges (OCR preprocessing)
└── ✅ Prewitt — better than Sobel for text sharpness| Detector | Derivative | Output | Strengths | Weaknesses | |----------|-----------|--------|-----------|------------| | Sobel | 1st | Directional gradient map | Clean directional edges | Misses some corners | | Prewitt | 1st | Similar to Sobel | Better for text/documents | Noisier than Sobel | | Laplacian | 2nd | All edges via zero-crossing | Catches finest details | Very noise-sensitive | | Canny | Multi-step | Thin binary edges | Best general-purpose | Sensitive to parameters |
Canny is extremely noise-sensitive. Without pre-blurring, it will detect noise as edges.
python# WRONG — will produce noisy edges edges = cv2.Canny(img, 100, 200) # CORRECT — always blur first blurred = cv2.GaussianBlur(img, (5, 5), 0) edges = cv2.Canny(blurred, 100, 200)
Every kernel/filter size in OpenCV must be an odd number (3, 5, 7, 9...). Even numbers will crash.
python# CRASHES cv2.GaussianBlur(img, (4, 4), 0) # Error! # CORRECT cv2.GaussianBlur(img, (5, 5), 0)
Bilateral preserves edges beautifully but is significantly slower than other filters. For real-time video, prefer Gaussian or Median.
python# Bilateral parameters: (src, diameter, sigmaColor, sigmaSpace) cv2.bilateralFilter(img, 9, 75, 75) # diameter=9: neighborhood size # sigmaColor=75: color range for blending # sigmaSpace=75: spatial distance for blending
Unlike other filters that take a tuple (5, 5), median takes just one integer:
python# WRONG cv2.medianBlur(img, (5, 5)) # Error! # CORRECT cv2.medianBlur(img, 5)
In filter functions, ddepth=-1 means "output same depth as input." For float precision:
pythoncv2.filter2D(img, -1, kernel) # Output = same type as input cv2.filter2D(img, cv2.CV_64F, kernel) # Output = 64-bit float
| Frequency | Visual Appearance | Examples | |-----------|-------------------|----------| | Low frequency | Smooth, gradual changes | Background, skin, sky | | High frequency | Sharp, sudden changes | Edges, textures, noise |
| Noise Type | Visual Pattern | Cause | Best Filter | |------------|---------------|-------|-------------| | Salt & Pepper | Random pure white and pure black pixels | Sensor errors, transmission | Median | | Gaussian | Uniform grain/static across image | Sensor heat, low light | Gaussian Blur | | Speckle | Multiplicative granular noise | Ultrasound, SAR radar | Bilateral | | MRI Bias Field | Smooth intensity variation across image | B0 field inhomogeneity | Non-Local Means |
python# threshold1 = lower bound, threshold2 = upper bound # Ratio recommendation: 1:2 or 1:3 edges = cv2.Canny(blurred, 50, 150) # 1:3 ratio
| Case | Status | Duration (ms) | Turns | Tokens | Tool calls | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Without | With | Δ | Without | With | Δ | Without | With | Δ | Without | With | Δ | ||
case-01 | fail→fail | 13,267 | 8,877 | -33% | 1 | 1 | 0% | 2,552 | 3,563 | +40% | 0 | 0 | — |
case-16 | pass→pass | 9,091 | 7,891 | -13% | 1 | 1 | 0% | 1,799 | 3,233 | +80% | 0 | 0 | — |
case-02 | fail→pass | 15,582 | 10,232 | -34% | 1 | 1 | 0% | 3,115 | 3,774 | +21% | 0 | 0 | — |
case-03 | pass→pass | 10,879 | 8,996 | -17% | 1 | 1 | 0% | 2,184 | 3,652 | +67% | 0 | 0 | — |
case-04 | pass→pass | 10,235 | 7,530 | -26% | 1 | 1 | 0% | 2,256 | 3,336 | +48% | 0 | 0 | — |
case-05 | pass→pass | 11,000 | 6,384 | -42% | 1 | 1 | 0% | 2,249 | 3,167 | +41% | 0 | 0 | — |
case-06 | pass→pass | 12,326 | 5,861 | -52% | 1 | 1 | 0% | 2,273 | 2,787 | +23% | 0 | 0 | — |
case-17 | pass→pass | 3,847 | 2,089 | -46% | 1 | 1 | 0% | 772 | 2,044 | +165% | 0 | 0 | — |
case-07 | pass→pass | 5,355 | 3,255 | -39% | 1 | 1 | 0% | 1,203 | 2,328 | +94% | 0 | 0 | — |
case-08 | pass→pass | 6,655 | 4,965 | -25% | 1 | 1 | 0% | 1,440 | 2,616 | +82% | 0 | 0 | — |
case-09 | pass→pass | 6,107 | 4,654 | -24% | 1 | 1 | 0% | 1,249 | 2,473 | +98% | 0 | 0 | — |
case-10 | pass→pass | 9,979 | 5,476 | -45% | 1 | 1 | 0% | 2,016 | 2,747 | +36% | 0 | 0 | — |
case-11 | pass→pass | 6,109 | 3,943 | -35% | 1 | 1 | 0% | 1,201 | 2,469 | +106% | 0 | 0 | — |
case-12 | pass→pass | 8,792 | 4,016 | -54% | 1 | 1 | 0% | 1,329 | 2,275 | +71% | 0 | 0 | — |
case-13 | pass→pass | 13,717 | 4,287 | -69% | 1 | 1 | 0% | 2,639 | 2,536 | -4% | 0 | 0 | — |
case-14 | pass→pass | 10,049 | 4,185 | -58% | 1 | 1 | 0% | 2,080 | 2,487 | +20% | 0 | 0 | — |
case-15 | pass→pass | 5,324 | 3,619 | -32% | 1 | 1 | 0% | 1,166 | 2,266 | +94% | 0 | 0 | — |
case-18 | pass→pass | 8,771 | 7,277 | -17% | 1 | 1 | 0% | 1,880 | 3,083 | +64% | 0 | 0 | — |
case-19 | pass→pass | 10,644 | 5,636 | -47% | 1 | 1 | 0% | 2,011 | 2,805 | +39% | 0 | 0 | — |
case-20 | pass→pass | 13,896 | 7,339 | -47% | 1 | 1 | 0% | 2,730 | 3,074 | +13% | 0 | 0 | — |
case-21 | pass→pass | 8,682 | 5,723 | -34% | 1 | 1 | 0% | 1,707 | 2,855 | +67% | 0 | 0 | — |
case-22 | fail→pass | 10,944 | 4,387 | -60% | 1 | 1 | 0% | 2,037 | 2,496 | +23% | 0 | 0 | — |
DecimalAI ran this skill against gemini-3.6-flash twice over the same eval suite — once with the skill loaded and once without — and compared the two runs case by case. 22 cases were attempted. The headline lift of +9 percentage points is the difference between those two pass rates over the 22 comparable cases.
Without the skill loaded, the model failed this case. With it loaded, the same prompt on the same model passed. This is one improved case from the latest verified run; every case, including any that regressed, is in the table above.
Other measured skills in the registry, with their headline benchmark lift.