machine_archivist/camera_handler.py
2025-04-06 19:58:31 +02:00

116 lines
3.7 KiB
Python

import tkinter as tk
from PIL import Image, ImageTk, ImageEnhance
import cv2
import time
import os
def scan(image_path=None, camera_index=None, hold_preview=False, preview_scale=1.0, timeout=10):
"""
Captures an image with a beautiful, high-quality Tkinter preview.
Features a centered, large countdown before capture.
"""
assert image_path is not None, "Image path not provided."
if camera_index is None:
camera_index = 0 # Default camera index
# Open camera
camera = cv2.VideoCapture(camera_index)
if not camera.isOpened():
print("❌ Error: Could not access the webcam")
return None, None
# Get highest available resolution
camera.set(cv2.CAP_PROP_FRAME_WIDTH, 9999)
camera.set(cv2.CAP_PROP_FRAME_HEIGHT, 9999)
max_width = int(camera.get(cv2.CAP_PROP_FRAME_WIDTH))
max_height = int(camera.get(cv2.CAP_PROP_FRAME_HEIGHT))
print(f"🔍 Using resolution: {max_width}x{max_height}")
# Calculate preview dimensions
preview_width = int(max_width * preview_scale)
preview_height = int(max_height * preview_scale)
# Initialize Tkinter window
root = tk.Tk()
root.title("📸 Capture Preview")
root.geometry(f"{preview_width}x{preview_height}") # Adjust to preview size
root.configure(bg="black")
root.overrideredirect(True) # Borderless fullscreen effect
# Create canvas for overlay
canvas = tk.Canvas(root, width=preview_width, height=preview_height, highlightthickness=0)
canvas.pack()
countdown_label = tk.Label(root, text="4", font=("Helvetica", 100, "bold"), fg="white", bg="black")
countdown_label.place(x=preview_width // 2, y=preview_height // 2, anchor="center") # Center countdown
frame = None
countdown_start = time.time()
def update_preview():
"""Update the camera preview & countdown."""
nonlocal frame
ret, frame = camera.read()
if not ret:
print("❌ Error: Failed to read from camera.")
root.destroy()
return
# Scale down only for preview
frame_preview = cv2.resize(frame, (preview_width, preview_height))
frame_rgb = cv2.cvtColor(frame_preview, cv2.COLOR_BGR2RGB)
img = Image.fromarray(frame_rgb)
# Apply slight dimming effect
enhancer = ImageEnhance.Brightness(img)
img = enhancer.enhance(0.6)
img_tk = ImageTk.PhotoImage(img)
canvas.create_image(0, 0, anchor=tk.NW, image=img_tk)
canvas.img_tk = img_tk # Keep reference
# Update countdown timer
elapsed = int(time.time() - countdown_start)
remaining = max(0, 4 - elapsed)
countdown_label.config(text=str(remaining - 1))
if remaining == 0:
capture() # Capture automatically at 0
root.after(100, update_preview)
def capture(event=None):
"""Capture image and close window."""
nonlocal frame
if frame is not None:
cv2.imwrite(image_path, frame)
print(f"✅ Image saved: {image_path}")
camera.release()
root.quit()
def check_timeout():
"""Exit if hold_preview is enabled and timeout is reached."""
if hold_preview and time.time() - countdown_start > timeout:
print("⌛ Timeout reached. Exiting...")
root.quit()
root.after(1000, check_timeout)
# Bind Enter key to capture function
root.bind("<Return>", capture)
# Start preview loop
update_preview()
check_timeout()
root.mainloop()
return frame, image_path if os.path.exists(image_path) else (None, None)
if __name__ == '__main__':
scan("./scans/debug_img.png", hold_preview=False, preview_scale=0.5, timeout=10)