← GUI de cámara PySide6 para visión en Raspberry Pi
Inspección visual (visión artificial)Raspberry Pi + cámara USBUSB/V4L2HMI / Dashboard
GUI de cámara PySide6 para visión en Raspberry Pi — ejemplo completo
Crea una GUI de inspección con PySide6 en Raspberry Pi: un hilo QThread de cámara que solo emite frames con la vista en vivo visible y libera la CPU.
Programa completo y ejecutable para el Raspberry Pi + cámara USB (gui-pyside6-camera-thread.py): incluye cabecera de conexionado, requisitos y notas de integración.
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales
Vista de solo lectura.
# -*- coding: utf-8 -*-
"""
COMPLETE EXAMPLE — Multi-tab PySide6 GUI with asynchronous camera thread
Hardware: Raspberry Pi + USB camera
Based on: visual inspection project in production (gui.py, vision.py)
Requirements:
pip install opencv-python PySide6 numpy
What it does — the same architecture as the shop-floor application:
- Camera QThread: captures frames in its own thread and emits them
as a Qt signal. The GUI never blocks waiting for the camera.
- Key CPU saving on the Raspberry Pi: the thread ONLY emits frames
when the tab with the live view is active (set_live_enabled).
- 3 tabs: Operation (video + Inspect button), References
(capture pattern) and Configuration (persisted sliders).
Without a connected camera it generates synthetic frames: it works on any PC.
Integration with the catalog:
- "Inspect" would call the pipeline + matching from the catalog:
ejemplos/inspeccion-visual/opencv-silkscreen-pipeline.py
ejemplos/inspeccion-visual/rotational-template-matching.py
- The sliders persist in config.json and the references as PNG:
ejemplos/inspeccion-visual/json-config-references.py
"""
import sys
import time
import cv2
import numpy as np
from PySide6.QtCore import QThread, Signal, Qt
from PySide6.QtGui import QImage, QPixmap
from PySide6.QtWidgets import (QApplication, QMainWindow, QTabWidget,
QWidget, QVBoxLayout, QLabel, QPushButton,
QSlider)
class CameraThread(QThread):
"""Capture thread: emits BGR frames only if the live view is active."""
frame_ready = Signal(np.ndarray)
def __init__(self, cam_index=0):
super().__init__()
self._running = True
self._live = True # the GUI toggles it when switching tabs
self._cam_index = cam_index
def set_live_enabled(self, enabled):
# Active tab without video -> no frames emitted -> CPU freed
self._live = enabled
def stop(self):
self._running = False
self.wait()
def run(self):
cap = cv2.VideoCapture(self._cam_index)
use_camera = cap.isOpened()
while self._running:
if not self._live: # live view off:
time.sleep(0.1) # sleep, do not capture or emit
continue
if use_camera:
ok, frame = cap.read()
if not ok: continue
else: # hardware-less demo: synthetic frame
frame = np.full((480, 640, 3), 30, np.uint8)
cv2.putText(frame, time.strftime("%H:%M:%S"), (200, 250),
cv2.FONT_HERSHEY_SIMPLEX, 1.5, (0, 255, 0), 2)
time.sleep(0.033) # ~30 simulated fps
self.frame_ready.emit(frame)
cap.release() # no-op if the camera never opened
class MainWindow(QMainWindow):
def __init__(self):
super().__init__()
self.setWindowTitle("Visual inspection - GUI demo")
# --- Tabs ---------------------------------------------------
self.tabs = QTabWidget()
self.setCentralWidget(self.tabs)
self.tabs.addTab(self._operation_tab(), "Operation")
self.tabs.addTab(self._references_tab(), "References")
self.tabs.addTab(self._config_tab(), "Configuration")
self.tabs.currentChanged.connect(self._on_tab_changed) # live on/off
# --- Camera thread ----------------------------------------------
self.cam = CameraThread()
self.cam.frame_ready.connect(self._show_frame)
self.cam.start()
def _operation_tab(self):
w, layout = QWidget(), QVBoxLayout()
self.video_label = QLabel("Waiting for camera...")
self.video_label.setAlignment(Qt.AlignCenter)
self.result = QLabel("--")
self.result.setAlignment(Qt.AlignCenter)
btn = QPushButton("Inspect part")
btn.clicked.connect(self._inspect)
for widget in (self.video_label, btn, self.result):
layout.addWidget(widget)
w.setLayout(layout)
return w
def _references_tab(self):
w, layout = QWidget(), QVBoxLayout()
btn = QPushButton("Capture reference (saves processed PNG)")
btn.clicked.connect(lambda: print("-> save_reference(), see "
"json-config-references.py"))
layout.addWidget(btn)
w.setLayout(layout)
return w
def _config_tab(self):
w, layout = QWidget(), QVBoxLayout()
layout.addWidget(QLabel("bg_threshold (persists in config.json)"))
s = QSlider(Qt.Horizontal)
s.setRange(0, 255), s.setValue(60)
s.valueChanged.connect(lambda v: print(f"bg_threshold = {v}"))
layout.addWidget(s)
w.setLayout(layout)
return w
def _on_tab_changed(self, index):
# Only tab 0 (Operation) shows live video
self.cam.set_live_enabled(index == 0)
def _show_frame(self, frame):
"""Converts the OpenCV BGR frame to QPixmap and paints it."""
rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
h, w, ch = rgb.shape
qimg = QImage(rgb.data, w, h, ch * w, QImage.Format_RGB888)
self.video_label.setPixmap(QPixmap.fromImage(qimg))
def _inspect(self):
# Here you would call process() + rotational_match() from the catalog
self.result.setText("PASS (score 0.93) - demo")
def closeEvent(self, event):
self.cam.stop() # stop the thread before closing
event.accept()
if __name__ == "__main__":
app = QApplication(sys.argv)
win = MainWindow()
win.resize(720, 600), win.show()
sys.exit(app.exec())
Descarga el pack completo del proyecto — gratisEste ejemplo + los relacionados + lista de materiales