File size: 6,787 Bytes
e93bfbd
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
import importlib
import os
import subprocess
import sys
import tempfile
from datetime import datetime
from pathlib import Path

from PIL import Image, ImageOps

DATA_DIR = Path("./data")
IMAGES_DIR = DATA_DIR / "images"


def _ensure_dep(package_name, import_name=None):
    if import_name is None:
        import_name = package_name
    try:
        return importlib.import_module(import_name)
    except ImportError:
        from rich.console import Console
        console = Console()
        console.print(f"[yellow]'{package_name}' is required for this feature.[/yellow]")
        import questionary
        install = questionary.confirm(f"Install {package_name} now?", default=True).ask()
        if not install:
            return None
        console.print(f"[cyan]Installing {package_name}...[/cyan]")
        subprocess.check_call([sys.executable, "-m", "pip", "install", package_name])
        return importlib.import_module(import_name)

def _ensure_dirs():
    IMAGES_DIR.mkdir(parents=True, exist_ok=True)

def _save_image(pil_image, prefix="capture"):
    _ensure_dirs()
    timestamp = datetime.now().strftime("%Y%m%d_%H%M%S_%f")
    filename = f"{prefix}_{timestamp}.jpg"
    path = str(IMAGES_DIR / filename)
    if pil_image.mode != "RGB":
        pil_image = pil_image.convert("RGB")
    pil_image.save(path, quality=95)
    return path

# ── Camera ──────────────────────────────────────────────────────

def capture_camera():
    cv2 = _ensure_dep("opencv-python", "cv2")
    if cv2 is None:
        return None, "Camera capture requires opencv-python"

    cap = cv2.VideoCapture(0)
    if not cap.isOpened():
        return None, "No camera detected (could not open index 0)"

    from rich.console import Console
    console = Console()
    console.print("[cyan]Camera opened. Press SPACE to capture, ESC to cancel.[/cyan]")

    import questionary
    input("Press Enter when ready for camera preview...")
    ret, frame = cap.read()
    cap.release()

    if not ret:
        return None, "Failed to capture frame from camera"

    preview_path = tempfile.mktemp(suffix="_preview.jpg")
    cv2.imwrite(preview_path, frame)
    preview = Image.open(preview_path)
    os.unlink(preview_path)

    console.print("[cyan]Image captured from camera.[/cyan]")
    path = _save_image(preview, "camera")
    return path, f"Captured from camera -> {path}"

# ── File browser ───────────────────────────────────────────────

def capture_file():
    try:
        import tkinter as tk
        from tkinter import filedialog
        root = tk.Tk()
        root.withdraw()
        root.attributes("-topmost", True)
        path = filedialog.askopenfilename(
            title="Select an X-ray image",
            filetypes=[
                ("Image files", "*.jpg *.jpeg *.png *.bmp *.tif *.tiff *.dcm"),
                ("All files", "*.*"),
            ],
        )
        root.destroy()
    except Exception as e:
        return None, f"File dialog failed: {e}"

    if not path:
        return None, "No file selected"

    return _open_and_save(path)

# ── Manual path entry ──────────────────────────────────────────

def capture_path():
    from rich.console import Console
    console = Console()
    console.print("[cyan]Enter the path to an X-ray image file.[/cyan]")

    import questionary
    path = questionary.path("Image path:").ask()
    if not path:
        return None, "No path entered"
    return _open_and_save(path)

# ── DICOM ──────────────────────────────────────────────────────

def capture_dicom(path=None):
    pydicom = _ensure_dep("pydicom")
    if pydicom is None:
        return None, "DICOM loading requires pydicom"
    np = _ensure_dep("numpy")
    if np is None:
        return None, "DICOM loading requires numpy"

    if not path:
        try:
            import tkinter as tk
            from tkinter import filedialog
            root = tk.Tk()
            root.withdraw()
            root.attributes("-topmost", True)
            path = filedialog.askopenfilename(
                title="Select a DICOM file",
                filetypes=[("DICOM files", "*.dcm"), ("All files", "*.*")],
            )
            root.destroy()
        except Exception as e:
            return None, f"File dialog failed: {e}"

    if not path:
        return None, "No DICOM file selected"

    try:
        ds = pydicom.dcmread(path)
        arr = ds.pixel_array
        arr = arr - arr.min()
        arr = (arr / arr.max() * 255).astype(np.uint8)
        if len(arr.shape) == 2:
            img = Image.fromarray(arr, mode="L")
            img = ImageOps.equalize(img)
        else:
            img = Image.fromarray(arr)
        result_path = _save_image(img, "dicom")
        return result_path, f"DICOM loaded from {path} -> saved as {result_path}"
    except Exception as e:
        return None, f"Failed to read DICOM: {e}"

# ── Generic open + save ────────────────────────────────────────

def _open_and_save(source_path):
    source_path = str(source_path)
    if source_path.lower().endswith(".dcm"):
        return capture_dicom(source_path)
    try:
        img = Image.open(source_path)
        path = _save_image(img, "import")
        return path, f"Imported from {source_path} -> {path}"
    except Exception as e:
        return None, f"Failed to open image: {e}"

# ── Top-level picker ───────────────────────────────────────────

def pick_image():
    from rich.console import Console
    import questionary

    console = Console()
    method = questionary.select(
        "How do you want to provide the X-ray image?",
        choices=[
            "Browse files on computer",
            "Enter file path manually",
            "Capture from camera",
            "Load DICOM file",
        ],
        pointer=">",
    ).ask()

    result = None
    if method == "Browse files on computer":
        result = capture_file()
    elif method == "Enter file path manually":
        result = capture_path()
    elif method == "Capture from camera":
        result = capture_camera()
    elif method == "Load DICOM file":
        result = capture_dicom()

    return result


if __name__ == "__main__":
    path, msg = pick_image()
    print(msg)