|
|
import gradio as gr |
|
|
import numpy as np |
|
|
import pydicom |
|
|
from skimage import exposure, measure, filters |
|
|
from skimage.metrics import structural_similarity as ssim |
|
|
from skimage.metrics import peak_signal_noise_ratio as psnr |
|
|
import json |
|
|
from typing import Dict, List, Tuple |
|
|
import tempfile |
|
|
import os |
|
|
from PIL import Image |
|
|
|
|
|
|
|
|
class VeterinaryDICOMProcessor: |
|
|
"""Veterinärmedizinische DICOM-Bildverbesserung und Qualitätsbewertung""" |
|
|
|
|
|
def __init__(self): |
|
|
|
|
|
self.species_params = { |
|
|
"canine": { |
|
|
"clahe": {"clip_limit": 0.02, "kernel_size": (8, 8)}, |
|
|
"adaptive": {"clip_limit": 0.015, "kernel_size": (6, 6)}, |
|
|
"histogram": {"bins": 256} |
|
|
}, |
|
|
"feline": { |
|
|
"clahe": {"clip_limit": 0.015, "kernel_size": (6, 6)}, |
|
|
"adaptive": {"clip_limit": 0.012, "kernel_size": (4, 4)}, |
|
|
"histogram": {"bins": 256} |
|
|
}, |
|
|
"equine": { |
|
|
"clahe": {"clip_limit": 0.03, "kernel_size": (12, 12)}, |
|
|
"adaptive": {"clip_limit": 0.025, "kernel_size": (10, 10)}, |
|
|
"histogram": {"bins": 256} |
|
|
}, |
|
|
"bovine": { |
|
|
"clahe": {"clip_limit": 0.025, "kernel_size": (10, 10)}, |
|
|
"adaptive": {"clip_limit": 0.02, "kernel_size": (8, 8)}, |
|
|
"histogram": {"bins": 256} |
|
|
} |
|
|
} |
|
|
|
|
|
def load_dicom_or_image(self, file_path: str) -> np.ndarray: |
|
|
"""Lädt DICOM-Datei oder normale Bilddatei und konvertiert zu Numpy Array""" |
|
|
try: |
|
|
|
|
|
if file_path.lower().endswith(('.dcm', '.dicom')): |
|
|
dicom_data = pydicom.dcmread(file_path) |
|
|
image = dicom_data.pixel_array.astype(np.float64) |
|
|
else: |
|
|
|
|
|
pil_image = Image.open(file_path).convert('L') |
|
|
image = np.array(pil_image, dtype=np.float64) |
|
|
|
|
|
|
|
|
if image.max() > image.min(): |
|
|
image = (image - image.min()) / (image.max() - image.min()) |
|
|
|
|
|
return image |
|
|
except Exception as e: |
|
|
raise ValueError(f"Fehler beim Laden der Datei: {e}") |
|
|
|
|
|
def apply_clahe_enhancement(self, image: np.ndarray, species: str) -> np.ndarray: |
|
|
"""Wendet CLAHE (Contrast Limited Adaptive Histogram Equalization) an""" |
|
|
params = self.species_params.get(species, self.species_params["canine"])["clahe"] |
|
|
|
|
|
enhanced = exposure.equalize_adapthist( |
|
|
image, |
|
|
kernel_size=params["kernel_size"], |
|
|
clip_limit=params["clip_limit"], |
|
|
nbins=256 |
|
|
) |
|
|
return enhanced |
|
|
|
|
|
def apply_adaptive_histogram_equalization(self, image: np.ndarray, species: str) -> np.ndarray: |
|
|
"""Wendet Adaptive Histogram Equalization an (lokale Variante)""" |
|
|
params = self.species_params.get(species, self.species_params["canine"])["adaptive"] |
|
|
|
|
|
|
|
|
enhanced = exposure.equalize_adapthist( |
|
|
image, |
|
|
kernel_size=params["kernel_size"], |
|
|
clip_limit=params["clip_limit"], |
|
|
nbins=128 |
|
|
) |
|
|
return enhanced |
|
|
|
|
|
def apply_histogram_equalization(self, image: np.ndarray) -> np.ndarray: |
|
|
"""Standard globale Histogram Equalization""" |
|
|
return exposure.equalize_hist(image) |
|
|
|
|
|
def apply_contrast_stretching(self, image: np.ndarray, percentiles: Tuple[float, float] = (2, 98)) -> np.ndarray: |
|
|
"""Kontrast-Streckung basierend auf Perzentilen""" |
|
|
p_low, p_high = np.percentile(image, percentiles) |
|
|
return exposure.rescale_intensity(image, in_range=(p_low, p_high)) |
|
|
|
|
|
def apply_gamma_correction(self, image: np.ndarray, gamma: float = 1.2) -> np.ndarray: |
|
|
"""Gamma-Korrektur für Helligkeit""" |
|
|
return exposure.adjust_gamma(image, gamma) |
|
|
|
|
|
def calculate_quality_metrics(self, original: np.ndarray, enhanced: np.ndarray) -> Dict[str, float]: |
|
|
"""Berechnet umfassende Bildqualitäts-Metriken""" |
|
|
|
|
|
|
|
|
ssim_score = ssim(original, enhanced, data_range=1.0) |
|
|
|
|
|
|
|
|
psnr_score = psnr(original, enhanced, data_range=1.0) |
|
|
|
|
|
|
|
|
original_entropy = measure.shannon_entropy(original) |
|
|
enhanced_entropy = measure.shannon_entropy(enhanced) |
|
|
|
|
|
|
|
|
original_contrast = np.sqrt(np.mean((original - original.mean()) ** 2)) |
|
|
enhanced_contrast = np.sqrt(np.mean((enhanced - enhanced.mean()) ** 2)) |
|
|
|
|
|
|
|
|
original_edges = np.mean(filters.sobel(original)) |
|
|
enhanced_edges = np.mean(filters.sobel(enhanced)) |
|
|
|
|
|
|
|
|
original_dynamic_range = np.max(original) - np.min(original) |
|
|
enhanced_dynamic_range = np.max(enhanced) - np.min(enhanced) |
|
|
|
|
|
return { |
|
|
"ssim": float(ssim_score), |
|
|
"psnr": float(psnr_score), |
|
|
"original_entropy": float(original_entropy), |
|
|
"enhanced_entropy": float(enhanced_entropy), |
|
|
"entropy_improvement": float(enhanced_entropy - original_entropy), |
|
|
"original_contrast": float(original_contrast), |
|
|
"enhanced_contrast": float(enhanced_contrast), |
|
|
"contrast_improvement": float(enhanced_contrast - original_contrast), |
|
|
"original_edge_density": float(original_edges), |
|
|
"enhanced_edge_density": float(enhanced_edges), |
|
|
"edge_improvement": float(enhanced_edges - original_edges), |
|
|
"original_dynamic_range": float(original_dynamic_range), |
|
|
"enhanced_dynamic_range": float(enhanced_dynamic_range), |
|
|
"dynamic_range_improvement": float(enhanced_dynamic_range - original_dynamic_range) |
|
|
} |
|
|
|
|
|
def array_to_image(self, array: np.ndarray) -> str: |
|
|
"""Konvertiert Numpy Array zu Base64-String für Output""" |
|
|
|
|
|
img_normalized = ((array - array.min()) / (array.max() - array.min()) * 255).astype(np.uint8) |
|
|
|
|
|
|
|
|
pil_image = Image.fromarray(img_normalized, mode='L') |
|
|
|
|
|
|
|
|
with tempfile.NamedTemporaryFile(delete=False, suffix='.png') as tmp_file: |
|
|
pil_image.save(tmp_file, format='PNG') |
|
|
return tmp_file.name |
|
|
|
|
|
def generate_agent_prompt(self, metrics: Dict[str, float], species: str, |
|
|
body_part: str, enhancement_method: str) -> str: |
|
|
"""Generiert strukturierten Prompt für KI-Agent zur Qualitätsbewertung""" |
|
|
|
|
|
prompt = f"""VETERINÄRMEDIZINISCHE BILDQUALITÄTS-ANALYSE |
|
|
|
|
|
PATIENT-INFORMATION: |
|
|
=================== |
|
|
• Spezies: {species.upper()} |
|
|
• Körperregion: {body_part} |
|
|
• Verbesserungsmethode: {enhancement_method} |
|
|
|
|
|
BILDQUALITÄTS-METRIKEN: |
|
|
====================== |
|
|
• SSIM (Strukturelle Ähnlichkeit): {metrics['ssim']:.3f} |
|
|
→ 1.0 = perfekte Ähnlichkeit, 0.0 = völlig unterschiedlich |
|
|
→ Bewertung: {'Ausgezeichnet' if metrics['ssim'] > 0.8 else 'Gut' if metrics['ssim'] > 0.6 else 'Verbesserungsbedürftig'} |
|
|
|
|
|
• PSNR (Signal-Rausch-Verhältnis): {metrics['psnr']:.1f} dB |
|
|
→ >30dB = gut, >25dB = akzeptabel, <20dB = problematisch |
|
|
→ Bewertung: {'Gut' if metrics['psnr'] > 30 else 'Akzeptabel' if metrics['psnr'] > 25 else 'Problematisch'} |
|
|
|
|
|
• Entropie-Verbesserung: {metrics['entropy_improvement']:.3f} |
|
|
→ Positiv = mehr Bildinformation, Negativ = Informationsverlust |
|
|
→ Status: {'Information gewonnen' if metrics['entropy_improvement'] > 0 else 'Information verloren'} |
|
|
|
|
|
• Kontrast-Verbesserung: {metrics['contrast_improvement']:.3f} |
|
|
→ Positiv = besserer Kontrast, Negativ = Kontrastverlust |
|
|
→ Status: {'Kontrast verbessert' if metrics['contrast_improvement'] > 0 else 'Kontrast reduziert'} |
|
|
|
|
|
• Edge-Verbesserung: {metrics['edge_improvement']:.3f} |
|
|
→ Positiv = schärfere Strukturen, Negativ = Unschärfe |
|
|
→ Status: {'Schärfer' if metrics['edge_improvement'] > 0 else 'Unschärfer'} |
|
|
|
|
|
AUFGABEN FÜR KI-AGENT: |
|
|
===================== |
|
|
1. Bewerten Sie die Bildqualität für {species}-Diagnostik (Skala 1-10) |
|
|
2. Ist die Verbesserung für klinische Diagnose ausreichend? |
|
|
3. Welche anatomischen Strukturen sind bei {species} in {body_part} besser/schlechter sichtbar? |
|
|
4. Empfehlen Sie weitere Bildverbesserungsschritte oder alternative Methoden? |
|
|
5. Identifizieren Sie potenzielle Artefakte oder diagnostische Probleme? |
|
|
6. Geben Sie spezies-spezifische Interpretationshilfen? |
|
|
|
|
|
ANTWORT-FORMAT: |
|
|
============== |
|
|
- Strukturiert und klinisch relevant |
|
|
- Fokus auf diagnostischen Nutzen |
|
|
- Spezies-spezifische Besonderheiten berücksichtigen |
|
|
- Konkrete Handlungsempfehlungen |
|
|
""" |
|
|
return prompt |
|
|
|
|
|
|
|
|
processor = VeterinaryDICOMProcessor() |
|
|
|
|
|
def enhance_dicom_image(image_file, species: str, enhancement_method: str, body_part: str = "unbekannt"): |
|
|
""" |
|
|
Verbessert veterinärmedizinische DICOM-Bilder mit verschiedenen Algorithmen. |
|
|
|
|
|
Args: |
|
|
image_file: DICOM-Datei oder Testbild |
|
|
species: Tierart (canine, feline, equine, bovine) |
|
|
enhancement_method: Verbesserungsmethode (clahe, adaptive, histogram, contrast_stretch, gamma) |
|
|
body_part: Körperregion für spezifische Analyse |
|
|
|
|
|
Returns: |
|
|
Verbessertes Bild und detaillierte Qualitätsmetriken |
|
|
""" |
|
|
try: |
|
|
|
|
|
if image_file is None: |
|
|
return None, "❌ Kein Bild hochgeladen" |
|
|
|
|
|
original_image = processor.load_dicom_or_image(image_file) |
|
|
|
|
|
|
|
|
if enhancement_method == "clahe": |
|
|
enhanced_image = processor.apply_clahe_enhancement(original_image, species) |
|
|
elif enhancement_method == "adaptive": |
|
|
enhanced_image = processor.apply_adaptive_histogram_equalization(original_image, species) |
|
|
elif enhancement_method == "histogram": |
|
|
enhanced_image = processor.apply_histogram_equalization(original_image) |
|
|
elif enhancement_method == "contrast_stretch": |
|
|
enhanced_image = processor.apply_contrast_stretching(original_image) |
|
|
elif enhancement_method == "gamma": |
|
|
enhanced_image = processor.apply_gamma_correction(original_image) |
|
|
else: |
|
|
enhanced_image = processor.apply_clahe_enhancement(original_image, species) |
|
|
|
|
|
|
|
|
metrics = processor.calculate_quality_metrics(original_image, enhanced_image) |
|
|
|
|
|
|
|
|
agent_prompt = processor.generate_agent_prompt(metrics, species, body_part, enhancement_method) |
|
|
|
|
|
|
|
|
enhanced_image_path = processor.array_to_image(enhanced_image) |
|
|
|
|
|
|
|
|
report = f""" |
|
|
🔬 VETERINÄRMEDIZINISCHE BILDANALYSE |
|
|
===================================== |
|
|
|
|
|
📊 QUALITÄTS-METRIKEN: |
|
|
• SSIM: {metrics['ssim']:.3f} ({'✅ Gut' if metrics['ssim'] > 0.7 else '⚠️ Prüfen'}) |
|
|
• PSNR: {metrics['psnr']:.1f} dB ({'✅ Gut' if metrics['psnr'] > 25 else '⚠️ Niedrig'}) |
|
|
• Entropie: {metrics['entropy_improvement']:+.3f} ({'✅ Information gewonnen' if metrics['entropy_improvement'] > 0 else '❌ Information verloren'}) |
|
|
• Kontrast: {metrics['contrast_improvement']:+.3f} ({'✅ Verbessert' if metrics['contrast_improvement'] > 0 else '❌ Reduziert'}) |
|
|
• Edge-Definition: {metrics['edge_improvement']:+.3f} ({'✅ Schärfer' if metrics['edge_improvement'] > 0 else '❌ Unschärfer'}) |
|
|
|
|
|
🐾 SPEZIES-ANALYSE: {species.upper()} |
|
|
🎯 METHODE: {enhancement_method.upper()} |
|
|
📍 REGION: {body_part.upper()} |
|
|
|
|
|
🤖 KI-AGENT PROMPT: |
|
|
{agent_prompt} |
|
|
""" |
|
|
|
|
|
return enhanced_image_path, report |
|
|
|
|
|
except Exception as e: |
|
|
return None, f"❌ Fehler bei Bildverbesserung: {str(e)}" |
|
|
|
|
|
def compare_enhancement_methods(image_file, species: str, body_part: str = "thorax"): |
|
|
""" |
|
|
Vergleicht verschiedene Bildverbesserungsmethoden für veterinärmedizinische Analyse. |
|
|
|
|
|
Args: |
|
|
image_file: DICOM-Datei oder Testbild |
|
|
species: Tierart (canine, feline, equine, bovine) |
|
|
body_part: Körperregion für Analyse |
|
|
|
|
|
Returns: |
|
|
Vergleichstabelle aller Methoden mit Qualitätsmetriken |
|
|
""" |
|
|
if image_file is None: |
|
|
return "❌ Kein Bild hochgeladen" |
|
|
|
|
|
try: |
|
|
original_image = processor.load_dicom_or_image(image_file) |
|
|
methods = ["clahe", "adaptive", "histogram", "contrast_stretch", "gamma"] |
|
|
|
|
|
results = [] |
|
|
|
|
|
for method in methods: |
|
|
|
|
|
if method == "clahe": |
|
|
enhanced = processor.apply_clahe_enhancement(original_image, species) |
|
|
elif method == "adaptive": |
|
|
enhanced = processor.apply_adaptive_histogram_equalization(original_image, species) |
|
|
elif method == "histogram": |
|
|
enhanced = processor.apply_histogram_equalization(original_image) |
|
|
elif method == "contrast_stretch": |
|
|
enhanced = processor.apply_contrast_stretching(original_image) |
|
|
elif method == "gamma": |
|
|
enhanced = processor.apply_gamma_correction(original_image) |
|
|
|
|
|
|
|
|
metrics = processor.calculate_quality_metrics(original_image, enhanced) |
|
|
|
|
|
results.append({ |
|
|
"Methode": method.upper(), |
|
|
"SSIM": f"{metrics['ssim']:.3f}", |
|
|
"PSNR (dB)": f"{metrics['psnr']:.1f}", |
|
|
"Entropie Δ": f"{metrics['entropy_improvement']:+.3f}", |
|
|
"Kontrast Δ": f"{metrics['contrast_improvement']:+.3f}", |
|
|
"Edge Δ": f"{metrics['edge_improvement']:+.3f}", |
|
|
"Empfehlung": "✅ Gut" if metrics['ssim'] > 0.7 and metrics['psnr'] > 25 else "⚠️ Prüfen" |
|
|
}) |
|
|
|
|
|
|
|
|
comparison = f""" |
|
|
🔬 METHODEN-VERGLEICH: {species.upper()} - {body_part.upper()} |
|
|
==================================================== |
|
|
|
|
|
""" |
|
|
for result in results: |
|
|
comparison += f""" |
|
|
📊 {result['Methode']}: |
|
|
SSIM: {result['SSIM']} | PSNR: {result['PSNR (dB)']} | {result['Empfehlung']} |
|
|
Entropie: {result['Entropie Δ']} | Kontrast: {result['Kontrast Δ']} | Edges: {result['Edge Δ']} |
|
|
""" |
|
|
|
|
|
|
|
|
best_method = max(results, key=lambda x: float(x['SSIM'])) |
|
|
comparison += f""" |
|
|
🏆 BESTE METHODE: {best_method['Methode']} |
|
|
→ Höchste SSIM: {best_method['SSIM']} |
|
|
→ Empfehlung für {species} {body_part}-Diagnostik |
|
|
|
|
|
💡 VETERINÄR-TIPP: |
|
|
- CLAHE: Optimal für lokale Kontrastverbesserung |
|
|
- ADAPTIVE: Weniger Artefakte, sanfter |
|
|
- HISTOGRAM: Globale Verbesserung, kann übersättigen |
|
|
- CONTRAST_STRETCH: Konservativ, für kritische Diagnosen |
|
|
- GAMMA: Helligkeitsanpassung bei unter-/überbelichteten Bildern |
|
|
""" |
|
|
|
|
|
return comparison |
|
|
|
|
|
except Exception as e: |
|
|
return f"❌ Fehler beim Methodenvergleich: {str(e)}" |
|
|
|
|
|
|
|
|
with gr.Blocks(title="🐾 Veterinary DICOM Enhancement MCP Server") as demo: |
|
|
gr.Markdown(""" |
|
|
# 🐾 Veterinärmedizinische DICOM-Bildverbesserung |
|
|
|
|
|
**MCP Server für KI-Agenten zur automatischen Bildqualitätsbewertung** |
|
|
|
|
|
🔬 Spezies-spezifische Bildverbesserung mit CLAHE, Adaptive & Histogram Equalization |
|
|
""") |
|
|
|
|
|
with gr.Tab("🎯 Einzelne Bildverbesserung"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
input_image = gr.File( |
|
|
label="📁 DICOM-Datei oder Testbild hochladen", |
|
|
file_types=[".dcm", ".dicom", ".png", ".jpg", ".jpeg"] |
|
|
) |
|
|
species = gr.Dropdown( |
|
|
choices=["canine", "feline", "equine", "bovine"], |
|
|
label="🐾 Tierart", |
|
|
value="canine" |
|
|
) |
|
|
enhancement_method = gr.Dropdown( |
|
|
choices=["clahe", "adaptive", "histogram", "contrast_stretch", "gamma"], |
|
|
label="🔧 Verbesserungsmethode", |
|
|
value="clahe" |
|
|
) |
|
|
body_part = gr.Textbox( |
|
|
label="📍 Körperregion", |
|
|
value="thorax", |
|
|
placeholder="z.B. thorax, abdomen, extremität" |
|
|
) |
|
|
|
|
|
with gr.Column(): |
|
|
enhanced_output = gr.Image(label="✨ Verbessertes Bild") |
|
|
metrics_output = gr.Textbox( |
|
|
label="📊 Qualitätsanalyse & KI-Agent Prompt", |
|
|
lines=20, |
|
|
max_lines=30 |
|
|
) |
|
|
|
|
|
enhance_btn = gr.Button("🚀 Bild verbessern", variant="primary") |
|
|
enhance_btn.click( |
|
|
enhance_dicom_image, |
|
|
inputs=[input_image, species, enhancement_method, body_part], |
|
|
outputs=[enhanced_output, metrics_output] |
|
|
) |
|
|
|
|
|
with gr.Tab("📊 Methoden-Vergleich"): |
|
|
with gr.Row(): |
|
|
with gr.Column(): |
|
|
compare_input = gr.File( |
|
|
label="📁 DICOM-Datei oder Testbild hochladen", |
|
|
file_types=[".dcm", ".dicom", ".png", ".jpg", ".jpeg"] |
|
|
) |
|
|
compare_species = gr.Dropdown( |
|
|
choices=["canine", "feline", "equine", "bovine"], |
|
|
label="🐾 Tierart", |
|
|
value="canine" |
|
|
) |
|
|
compare_body_part = gr.Textbox( |
|
|
label="📍 Körperregion", |
|
|
value="thorax" |
|
|
) |
|
|
|
|
|
with gr.Column(): |
|
|
comparison_output = gr.Textbox( |
|
|
label="📈 Methoden-Vergleich", |
|
|
lines=25, |
|
|
max_lines=35 |
|
|
) |
|
|
|
|
|
compare_btn = gr.Button("📊 Alle Methoden vergleichen", variant="primary") |
|
|
compare_btn.click( |
|
|
compare_enhancement_methods, |
|
|
inputs=[compare_input, compare_species, compare_body_part], |
|
|
outputs=[comparison_output] |
|
|
) |
|
|
|
|
|
if __name__ == "__main__": |
|
|
|
|
|
demo.launch( |
|
|
server_name="0.0.0.0", |
|
|
server_port=7860, |
|
|
mcp_server=True, |
|
|
share=False, |
|
|
show_error=True, |
|
|
show_api=True |
|
|
) |