Phase 0: SCAN_INPUT — Input-Scan, Erkennung und Linearitätsprüfung¶
C++ Implementierung:
runner_pipeline.cppPhase-Enum:Phase::SCAN_INPUT
Übersicht¶
Phase 0 ist die Eingangsphase der Pipeline. Sie liest den ersten Frame, erkennt den Bildmodus und das Bayer-Pattern, führt eine optionale Linearitätsprüfung durch und bereitet die Run-Infrastruktur vor.
┌─────────────────────────────────────────────────────────────┐
│ INPUT: Verzeichnis mit FITS-Frames (*.fit*) │
└────────────────────────────┬────────────────────────────────┘
│
┌──────────────▼───────────────┐
│ 1. Frame-Discovery │
│ core::discover_frames() │
│ Sortierung + Limit │
└──────────────┬───────────────┘
│
┌──────────────▼───────────────┐
│ 2. Run-Verzeichnis anlegen │
│ runs/<run_id>/ │
│ ├── logs/ │
│ ├── outputs/ │
│ └── artifacts/ │
└──────────────┬───────────────┘
│
┌──────────────▼───────────────┐
│ 3. Erster Frame lesen │
│ • Dimensionen (W×H) │
│ • NAXIS │
│ • FITS-Header │
└──────────────┬───────────────┘
│
┌──────────────▼───────────────┐
│ 4. Modus-Erkennung │
│ • MONO vs. OSC │
│ • Bayer-Pattern │
│ (RGGB, GRBG, etc.) │
└──────────────┬───────────────┘
│
┌──────────────▼───────────────┐
│ 5. Linearitätsprüfung │
│ • Stichprobe samplen │
│ • validate_linearity() │
│ • Rejection oder Warnung │
└──────────────┬──────────────┘
│
┌──────────────▼───────────────┐
│ OUTPUT: │
│ • frames[] (validiert) │
│ • ColorMode, BayerPattern │
│ • width, height │
│ • linearity_info JSON │
└──────────────────────────────┘
Detaillierter Ablauf¶
1. Frame-Discovery und Sortierung¶
auto frames = core::discover_frames(in_dir, "*.fit*");
std::sort(frames.begin(), frames.end());
if (max_frames > 0 && frames.size() > max_frames)
frames.resize(max_frames);
- Sucht alle Dateien im Input-Verzeichnis die
*.fit*matchen (FITS, FIT, FITS) - Sortiert alphabetisch für deterministische Reihenfolge
- Optional: Beschränkung auf
--max-framesFrames (Debug/Test) - Abbruch wenn keine Frames gefunden
2. Run-Infrastruktur¶
std::string run_id = core::get_run_id(); // Zeitstempel-basierte ID
fs::path run_dir = runs / run_id;
fs::create_directories(run_dir / "logs");
fs::create_directories(run_dir / "outputs");
fs::create_directories(run_dir / "artifacts");
- Eindeutige Run-ID (Zeitstempel-basiert)
- Konfiguration wird als
config.yamlin den Run-Ordner kopiert - Event-Log-Datei:
run_events.jsonl(TeeBuf → stdout + Datei gleichzeitig)
3. Erster Frame — Dimensionen und Header¶
auto [width, height, naxis] = io::get_fits_dimensions(frames.front());
auto first = io::read_fits_float(frames.front());
first_frame = std::move(first.first); // Matrix2Df (Eigen)
first_header = std::move(first.second); // FitsHeader
- Liest den ersten Frame vollständig ein
- Extrahiert Bildbreite, Bildhöhe und NAXIS
- Speichert den Frame als
first_framefür spätere Verwendung - Speichert den FITS-Header für Output-Dateien
4. Farbmodus-Erkennung¶
detected_mode = io::detect_color_mode(first_header, naxis);
detected_bayer = io::detect_bayer_pattern(first_header);
| Modus | Erkennung | Verhalten |
|---|---|---|
| MONO | NAXIS=2 oder kein BAYERPAT | Einzelkanal-Verarbeitung |
| OSC | NAXIS=2 + BAYERPAT vorhanden | CFA-aware Verarbeitung |
- Konfig-Override: Wenn
data.color_modein config gesetzt, wird bei Abweichung gewarnt - Bayer-Pattern: RGGB, GRBG, GBRG, BGGR — wird aus FITS-Header
BAYERPATgelesen - Bei unbekanntem Pattern: Warnung, Fallback auf RGGB
5. Linearitätsprüfung¶
Die Linearitätsprüfung validiert, dass die Frames keine nichtlinearen Operationen (Stretch, Curves) erfahren haben.
if (cfg.linearity.enabled || cfg.data.linear_required) {
auto indices = core::sample_indices(frames.size(), cfg.linearity.max_frames);
for (size_t idx : indices) {
auto res = metrics::validate_linearity_frame(frame_img, cfg.linearity.strictness);
if (!res.is_linear) {
rejected_indices.push_back(idx);
}
}
}
Konfigurationsparameter¶
| Parameter | Beschreibung | Default |
|---|---|---|
linearity.enabled |
Linearitätsprüfung aktivieren | true |
linearity.max_frames |
Maximale Stichprobengröße | 10 |
linearity.strictness |
Strictness-Level für Validierung | 0.5 |
linearity.min_overall_linearity |
Mindest-Linearitäts-Score | 0.8 |
data.linear_required |
Nicht-lineare Frames entfernen | true |
Verhalten bei nicht-linearen Frames¶
linear_required |
Verhalten |
|---|---|
true |
Nicht-lineare Frames werden aus frames[] entfernt |
false |
Warnung, Frames bleiben in der Pipeline |
- Bei
linear_required=trueund alle Frames rejected: Pipeline bricht mit Error ab - Linearity-Info wird als JSON in das
scan_extraEvent geschrieben
Linearity-Info JSON¶
{
"enabled": true,
"sampled_frames": 10,
"overall_linearity": 0.9,
"min_overall_linearity": 0.8,
"failed_frames": 1,
"failed_frame_names": ["frame_0023.fit"],
"flagged_indices": [23],
"action": "removed",
"frames_remaining": 99
}
CHANNEL_SPLIT (Phase 3 — Metadaten-Phase)¶
Direkt nach SCAN_INPUT wird Phase::CHANNEL_SPLIT emittiert. In der C++ Implementierung ist dies eine reine Metadaten-Phase — die eigentliche Kanaltrennung erfolgt deferred während der Normalisierung und Tile-Verarbeitung.
if (detected_mode == ColorMode::OSC) {
extra["mode"] = "OSC";
extra["channels"] = {"R", "G", "B"};
extra["bayer_pattern"] = detected_bayer_str;
extra["note"] = "deferred_to_tile_processing";
} else {
extra["mode"] = "MONO";
extra["channels"] = {"L"};
}
Bei OSC-Daten bleibt das CFA-Mosaik bis zum Debayer in Phase 13 intakt. Die kanalgetrennte Verarbeitung geschieht implizit über Bayer-Offsets in der Normalisierung.
Fehlerbehandlung¶
| Fehler | Verhalten |
|---|---|
| Input-Verzeichnis existiert nicht | Sofortiger Abbruch (return 1) |
| Keine FITS-Frames gefunden | Sofortiger Abbruch (return 1) |
| Erster Frame nicht lesbar | phase_end(error) → run_end(error) → return 1 |
| Alle Frames non-linear | phase_end(error) → run_end(error) → return 1 |
| Config/Header Mismatch | Warnung, Pipeline läuft weiter |
Event-Emitter-Aufrufe¶
run_start(run_id, {config_path, input_dir, run_dir, frames_discovered, dry_run})
phase_start(SCAN_INPUT)
[warnings: linearity, mode mismatch]
phase_end(SCAN_INPUT, "ok", {input_dir, frames_scanned, image_width, image_height,
color_mode, bayer_pattern, linearity})
phase_start(CHANNEL_SPLIT)
phase_end(CHANNEL_SPLIT, "ok", {mode, channels, bayer_pattern})
Nächste Phase¶
→ Phase 1/2: REGISTRATION + PREWARP, danach Phase 4: NORMALIZATION — Hintergrund-Normalisierung