# µScan V2.1.0 import tkinter as tk from tkinter import ttk, simpledialog, messagebox import cv2 from PIL import Image, ImageTk from pyzbar.pyzbar import decode from datetime import datetime, timedelta import threading import queue import json import os class PfandScanner: def __init__(self, window, window_title): self.window = window self.window.title(window_title) self.data_file = "quantities.json" self.load_json() self.barcode_times = {} self.prompted_barcodes = set() self.camera_frame = ttk.Frame(window) self.camera_frame.pack(side="left", padx=10, pady=5) self.control_frame = ttk.Frame(window) self.control_frame.pack(side="left", padx=10, pady=5, fill="y") self.info_frame = ttk.Frame(window) self.info_frame.pack(side="right", padx=10, pady=5, fill="both", expand=True) self.camera_label = ttk.Label(self.camera_frame) self.camera_label.pack() focus_frame = ttk.LabelFrame(self.control_frame, text="Camera Controls") focus_frame.pack(pady=5, padx=5, fill="x") ttk.Label(focus_frame, text="Focus:").pack(pady=2) self.focus_slider = ttk.Scale(focus_frame, from_=0, to=255, orient="horizontal") self.focus_slider.set(0) self.focus_slider.pack(pady=2, padx=5, fill="x") self.autofocus_var = tk.BooleanVar(value=True) self.autofocus_check = ttk.Checkbutton( focus_frame, text="Autofocus", variable=self.autofocus_var, command=self.toggle_autofocus) self.autofocus_check.pack(pady=2) process_frame = ttk.LabelFrame(self.control_frame, text="Image Processing") process_frame.pack(pady=5, padx=5, fill="x") ttk.Label(process_frame, text="Brightness:").pack(pady=2) self.brightness_slider = ttk.Scale(process_frame, from_=0, to=100, orient="horizontal") self.brightness_slider.set(50) self.brightness_slider.pack(pady=2, padx=5, fill="x") ttk.Label(process_frame, text="Contrast:").pack(pady=2) self.contrast_slider = ttk.Scale(process_frame, from_=0, to=100, orient="horizontal") self.contrast_slider.set(50) self.contrast_slider.pack(pady=2, padx=5, fill="x") self.tree = ttk.Treeview(self.info_frame, columns=("Time", "Barcode", "Type", "Deposit"), show="headings") self.tree.heading("Time", text="Time") self.tree.heading("Barcode", text="Barcode") self.tree.heading("Type", text="Type") self.tree.heading("Deposit", text="Deposit (€)") self.tree.column("Time", width=150) self.tree.column("Barcode", width=200) self.tree.column("Type", width=100) self.tree.column("Deposit", width=100) self.tree.pack(fill="both", expand=True) scrollbar = ttk.Scrollbar(self.info_frame, orient="vertical", command=self.tree.yview) scrollbar.pack(side="right", fill="y") self.tree.configure(yscrollcommand=scrollbar.set) self.cap = cv2.VideoCapture(0) self.cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1280) self.cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 720) self.cap.set(cv2.CAP_PROP_AUTOFOCUS, 0) self.cap.set(cv2.CAP_PROP_FOCUS, 0) self.queue = queue.Queue() self.pfand_values = { "EINWEG": 0.25, "MEHRWEG": 0.15, "DOSE": 0.25, } self.process_video() self.window.protocol("WM_DELETE_WINDOW", self.on_closing) self.process_queue() def load_json(self): if os.path.exists(self.data_file): with open(self.data_file, 'r') as f: self.quantities = json.load(f) else: self.quantities = {} def save_json(self): with open(self.data_file, 'w') as f: json.dump(self.quantities, f, indent=4) def toggle_autofocus(self): if self.autofocus_var.get(): self.cap.set(cv2.CAP_PROP_AUTOFOCUS, 1) self.focus_slider.state(['disabled']) else: self.cap.set(cv2.CAP_PROP_AUTOFOCUS, 0) self.focus_slider.state(['!disabled']) self.cap.set(cv2.CAP_PROP_FOCUS, self.focus_slider.get()) def adjust_image(self, frame): brightness = self.brightness_slider.get() / 50.0 - 1.0 contrast = self.contrast_slider.get() / 50.0 adjusted = cv2.convertScaleAbs(frame, alpha=contrast, beta=brightness * 127) gray = cv2.cvtColor(adjusted, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (5, 5), 0) binary = cv2.adaptiveThreshold(blurred, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 11, 2) return binary def process_video(self): try: ret, frame = self.cap.read() if ret: if not self.autofocus_var.get(): self.cap.set(cv2.CAP_PROP_FOCUS, self.focus_slider.get()) processed_frame = self.adjust_image(frame) barcodes = decode(processed_frame) or decode(frame) for barcode in barcodes: points = barcode.polygon if len(points) == 4: pts = [(p.x, p.y) for p in points] cv2.polylines(frame, [cv2.convexHull(cv2.UMat(cv2.Mat(pts))).get()], True, (0, 0, 255), 2) barcode_data = barcode.data.decode('utf-8') self.queue.put(barcode_data) cv2image = cv2.cvtColor(frame, cv2.COLOR_BGR2RGBA) img = Image.fromarray(cv2image) imgtk = ImageTk.PhotoImage(image=img) self.camera_label.imgtk = imgtk self.camera_label.configure(image=imgtk) except Exception as e: print(f"Error in processing video: {e}") self.window.after(100, self.process_video) def show_product_selection(self, barcode_data): if hasattr(self, 'product_win') and self.product_win.winfo_exists(): return self.product_win = tk.Toplevel(self.window) self.product_win.title("Produktwahl") ttk.Label(self.product_win, text=f"Welches Produkt soll dem Barcode '{barcode_data}' zugeordnet werden?").pack(pady=5) selected_product = tk.StringVar() for prod in self.quantities: ttk.Radiobutton(self.product_win, text=prod, variable=selected_product, value=prod).pack(anchor='w') def confirm(): prod = selected_product.get() if prod: self.quantities[prod] += 1 self.save_json() self.product_win.destroy() else: messagebox.showwarning("Keine Auswahl", "Bitte ein Produkt auswählen.") ttk.Button(self.product_win, text="Bestätigen", command=confirm).pack(pady=5) def process_queue(self): try: barcode_data = self.queue.get(timeout=0.1) now = datetime.now() if barcode_data in self.barcode_times: timestamps = self.barcode_times[barcode_data] timestamps = [t for t in timestamps if now - t <= timedelta(seconds=5)] if len(timestamps) >= 3: return timestamps.append(now) self.barcode_times[barcode_data] = timestamps else: self.barcode_times[barcode_data] = [now] current_time = now.strftime("%Y-%m-%d %H:%M:%S") if len(barcode_data) == 13: pfand_type = "EINWEG" elif len(barcode_data) == 8: pfand_type = "MEHRWEG" else: pfand_type = "DOSE" deposit = self.pfand_values.get(pfand_type, 0.00) self.tree.insert("", 0, values=(current_time, barcode_data, pfand_type, f"{deposit:.2f}")) if barcode_data not in self.prompted_barcodes: self.prompted_barcodes.add(barcode_data) self.window.after(0, self.show_product_selection, barcode_data) except queue.Empty: pass except Exception as e: print(f"Error in queue processing: {e}") finally: self.window.after(100, self.process_queue) # freez delay def on_closing(self): if self.cap.isOpened(): self.cap.release() self.window.destroy() if __name__ != "__main__": def launch_pfand_scanner(): scanner_window = tk.Toplevel() PfandScanner(scanner_window, "µScan V2.1.0")