Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,18 @@ new `rcN` heading when that rc is published to TestPyPI.

## Unreleased

- ShowDiffraction detection denoise: center refinement and spot/ring detection
now run on a denoised view of the frame (`detect_denoise`, default `"auto"`:
Anscombe for sparse counting data, light Gaussian for moderate-SNR data,
identity when clean). All fits and measurements keep using the raw frame, so
positions and radii are never biased by the smoothing.
- ShowDiffraction `detect_spots` exposes its shot-noise contrast floor as
`noise_sigma`; lower it on frames whose diffuse scattering or detector
shadows inflate the robust noise estimate past real peak contrast.
- ShowDiffraction display denoise: a view-only `denoise` trait (including the
new Poisson non-local means `nlm` filter, which keeps spots sharp where the
detection blur softens them) and a `show_detection_view` toggle that
displays what detection saw; both leave stored data and measurements raw.
- Add `ChooseLattice`, an interactive 2D selector for choosing an ordered
origin, a1, and a2 and exposing their `(row, col)` coordinates and derived
lattice vectors for downstream analysis.
Expand Down
4 changes: 2 additions & 2 deletions docs/api/showdiffraction.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,11 +58,11 @@ no console error, no NaN frame).
| Contrast histogram (dual-thumb slider) | `dp_vmin_pct`, `dp_vmax_pct` | Drag either thumb (mouse or touch) for a live preview; traits update once on release |
| Center mode dropdown | `center_mode` | `auto` re-detects the BF disk; `manual` enables click-to-set |
| Click to set center (manual) | `center_row`, `center_col` | Crosshair moves; spot d-spacings recompute |
| Detect spots | `_detect_spots_request`, `spots` | Auto-finds every isolated peak with contrast at least 10% of the strongest (`min_relative`); no count cap |
| Detect spots | `_detect_spots_request`, `spots` | Auto-finds every isolated peak with contrast at least 10% of the strongest (`min_relative`); candidates come from the `detect_denoise` view, positions are refined on raw data; no count cap |
| Add / remove spot (click) | `_spot_add_request`, `_spot_remove_request`, `spots` | Marker placed/removed; d-spacing updates |
| Move spot (Move + drag) | `_spot_move_request`, `spots` | Re-picks the spot at the drop position; stale hkl clears |
| Spot pick dropdown | `spot_refine`, `snap_enabled`, `snap_radius` | Clicked spots are Gaussian-fitted, snapped to the local maximum, or kept exactly as clicked |
| Detect rings | `_detect_rings_request`, `rings` | Auto-finds all Debye–Scherrer rings above the profile prominence threshold; no count cap |
| Detect rings | `_detect_rings_request`, `rings` | Auto-finds all Debye–Scherrer rings above the profile prominence threshold, on the `detect_denoise` view; ring fits stay on the raw profile; no count cap |
| Add / remove ring | `_ring_add_request`, `_ring_remove_request`, `rings` | Ring overlay; ring d-spacing updates |
| Calibrate from spot / ring | `_calibrate_from_spot_request`, `_calibrate_from_ring_request`, `k_pixel_size` | Sets k-space pixel size from a known d |
| Auto button | `_auto_request`, `analysis_status` | Runs center, rings, calibration, fit, and indexing in one pass; status reports failed steps only |
Expand Down
42 changes: 28 additions & 14 deletions docs/tutorials/showdiffraction.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
"quantem showdiffraction --demo # no data needed: real Fe3O4 SAED\n",
"```\n",
"\n",
"The same analysis is a few lines in a notebook. `showdiffraction_fe3o4` fetches the tutorial pattern (a packaged copy is used offline): Fe3O4 nanoparticles on an amorphous carbon support. The broad support halo near d = 3.6 Å is skipped with `exclude_radius`; Auto refines the center, detects rings, calibrates from the Fe3O4 phase, fits ring profiles, and indexes hkl."
"The same analysis is a few lines in a notebook. `showdiffraction_fe3o4` fetches the tutorial pattern (a packaged copy is used offline): Fe3O4 nanoparticles on an amorphous carbon support. The broad support halo near d = 3.6 \u00c5 is skipped with `exclude_radius`; Auto refines the center, detects rings, calibrates from the Fe3O4 phase, fits ring profiles, and indexes hkl."
]
},
{
Expand All @@ -51,7 +51,7 @@
"version_minor": 1
},
"text/plain": [
"ShowDiffraction(shape=(1, 512, 512), sampling=(1.0 Å, 0.004992728220367598 1/Å), frame=0/1, title='Fe3O4 SAED (real)')"
"ShowDiffraction(shape=(1, 512, 512), sampling=(1.0 \u00c5, 0.004992728220367598 1/\u00c5), frame=0/1, title='Fe3O4 SAED (real)')"
]
},
"execution_count": 1,
Expand Down Expand Up @@ -96,7 +96,7 @@
"\n",
"- One-click pipeline: Auto chains center refinement, ring detection, phase calibration, profile fitting, and hkl indexing; every step is also a plain Python call.\n",
"- Exact crystallography: d-spacings and interplanar angles from the full metric tensor for any crystal system, systematic absences for the common structure types, and a phase library of 100+ standards with cited lattice constants.\n",
"- Honest identification: candidates are ranked by plain facts (matched lines, mean Δd, missing strong lines), and ranking stays a verification aid; nothing is applied silently.\n",
"- Honest identification: candidates are ranked by plain facts (matched lines, mean \u0394d, missing strong lines), and ranking stays a verification aid; nothing is applied silently.\n",
"- Distortion-aware: a fitted elliptical distortion corrects every radius, profile, and calibration rather than being a separate pass.\n",
"- Reproducible: JSON state save/load, CSV/JSON measurement tables, and the same analysis from the notebook, the exported HTML, or the `quantem` CLI."
]
Expand Down Expand Up @@ -190,7 +190,7 @@
"version_minor": 1
},
"text/plain": [
"ShowDiffraction(shape=(1, 256, 256), sampling=(1.0 Å, 0.018 1/Å), frame=0/1, spots=12, title='Single-crystal SAED')"
"ShowDiffraction(shape=(1, 256, 256), sampling=(1.0 \u00c5, 0.018 1/\u00c5), frame=0/1, spots=12, title='Single-crystal SAED')"
]
},
"execution_count": 3,
Expand Down Expand Up @@ -246,7 +246,7 @@
"version_minor": 1
},
"text/plain": [
"ShowDiffraction(shape=(1, 512, 512), sampling=(1.0 Å, 0.003988000818768712 1/Å), frame=0/1, title='Magnetite-like rings')"
"ShowDiffraction(shape=(1, 512, 512), sampling=(1.0 \u00c5, 0.003988000818768712 1/\u00c5), frame=0/1, title='Magnetite-like rings')"
]
},
"execution_count": 4,
Expand Down Expand Up @@ -294,18 +294,18 @@
"name": "stdout",
"output_type": "stream",
"text": [
"Fe3O4: 5/5 lines, mean Δd 0.82%, missing strong n/a\n",
"γ-Fe2O3: 5/5 lines, mean Δd 0.87%, missing strong n/a\n",
"α-Fe2O3 (hematite): 5/5 lines, mean Δd 1.26%, missing strong n/a\n",
"α-Fe: 1/5 lines, mean Δd 2.78%, missing strong n/a\n",
"Fe3O4: 5/5 lines, mean \u0394d 0.82%, missing strong n/a\n",
"\u03b3-Fe2O3: 5/5 lines, mean \u0394d 0.87%, missing strong n/a\n",
"\u03b1-Fe2O3 (hematite): 5/5 lines, mean \u0394d 1.26%, missing strong n/a\n",
"\u03b1-Fe: 1/5 lines, mean \u0394d 2.78%, missing strong n/a\n",
"Fe3O4\n"
]
}
],
"source": [
"from quantem.widget import library_phase\n",
"\n",
"expected = [library_phase(n) for n in (\"Fe3O4\", \"γ-Fe2O3\", \"α-Fe2O3 (hematite)\", \"α-Fe\")]\n",
"expected = [library_phase(n) for n in (\"Fe3O4\", \"\u03b3-Fe2O3\", \"\u03b1-Fe2O3 (hematite)\", \"\u03b1-Fe\")]\n",
"verified = real.identify_phase(expected)\n",
"\n",
"for candidate in verified:\n",
Expand All @@ -314,7 +314,7 @@
" missing = candidate[\"n_missing_strong\"]\n",
" print(\n",
" f\"{candidate['name']}: {candidate['matched']}/{candidate['n_obs']} lines, \"\n",
" f\"mean Δd {error_text}, \"\n",
" f\"mean \u0394d {error_text}, \"\n",
" f\"missing strong {'n/a' if missing is None else missing}\"\n",
" )\n",
"\n",
Expand All @@ -323,6 +323,20 @@
"print(candidates[0][\"name\"])"
]
},
{
"cell_type": "markdown",
"id": "29b98a84",
"metadata": {},
"source": "## Detection denoise\n\nThe real Fe3O4 SAED thinned to a median of 0.5 counts per pixel, analyzed twice side by side. Detection and Auto run on a matched-filter denoised view (`detect_denoise`, default `auto`; Anscombe on counting data) while ring fits and measurements stay on the raw counts. The right panel also sets the display-only `denoise = \"nlm\"` so the pattern itself is readable; `show_detection_view` shows what detection saw instead."
},
{
"cell_type": "code",
"execution_count": null,
"id": "8edfbcc8",
"metadata": {},
"outputs": [],
"source": "from ipywidgets import HBox, Layout\n\nfrom quantem.widget.data import showdiffraction_fe3o4\n\nfe3o4_saed = showdiffraction_fe3o4(verbose=False)\nscale = 0.5 / np.median(fe3o4_saed[fe3o4_saed > 0])\nsparse_saed = np.random.default_rng(2).poisson(np.clip(fe3o4_saed, 0, None) * scale).astype(np.float32)\n\n\ndef low_dose_auto(mode, title):\n w = ShowDiffraction(sparse_saed, title=title, offline=True, verbose=False, panel_width_px=430)\n w.detect_denoise = mode\n if mode != \"none\":\n w.denoise = \"nlm\"\n w.phase_name = \"Fe3O4\"\n w.run_auto()\n indexed = sum(1 for r in w.rings if r.get(\"hkl\"))\n print(f\"{title}: calibration rms {w.calibration_rms_px:.2f} px, {indexed}/{len(w.rings)} rings indexed\")\n return w\n\n\nraw = low_dose_auto(\"none\", \"raw\")\ndenoised = low_dose_auto(\"auto\", \"denoised\")\nHBox([raw, denoised], layout=Layout(overflow=\"auto\"))"
},
{
"cell_type": "markdown",
"id": "sddsave01",
Expand Down Expand Up @@ -351,9 +365,9 @@
"output_type": "stream",
"text": [
"Magnetite-like rings\n",
"════════════════════════════════\n",
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\n",
"Frames: 1 (showing #0)\n",
"Detector: 512×512 (0.0040 1/Å/px)\n",
"Detector: 512\u00d7512 (0.0040 1/\u00c5/px)\n",
"Calibration: phase (rms 0.62 px)\n",
"Center: (255.5, 255.5) BF r=13.1 px\n",
"Spots: 0\n",
Expand Down Expand Up @@ -406,4 +420,4 @@
},
"nbformat": 4,
"nbformat_minor": 5
}
}
Loading