Add video playback functionality to MainWindow and update app configuration
This commit is contained in:
@@ -6,12 +6,13 @@ from typing import Any
|
||||
|
||||
import cv2
|
||||
import numpy as np
|
||||
from PySide6.QtCore import Qt, Slot
|
||||
from PySide6.QtCore import Qt, QTimer, Slot
|
||||
from PySide6.QtGui import QAction, QImage, QPixmap
|
||||
from PySide6.QtWidgets import (
|
||||
QApplication,
|
||||
QHBoxLayout,
|
||||
QLabel,
|
||||
QFileDialog,
|
||||
QMainWindow,
|
||||
QMessageBox,
|
||||
QPushButton,
|
||||
@@ -42,6 +43,10 @@ class MainWindow(QMainWindow):
|
||||
self.fps_frame_count = 0
|
||||
self.fps_last_time = time.monotonic()
|
||||
self.display_fps = 0.0
|
||||
self.video_capture: cv2.VideoCapture | None = None
|
||||
self.video_timer = QTimer(self)
|
||||
self.video_timer.timeout.connect(self._read_video_frame)
|
||||
self.video_playing = False
|
||||
self.media_store = MediaStore(self.config, self.app_config)
|
||||
self.video_recorder = VideoRecorder(self.config, self.app_config)
|
||||
|
||||
@@ -119,12 +124,19 @@ class MainWindow(QMainWindow):
|
||||
)
|
||||
toolbar_layout = QHBoxLayout(self.toolbar)
|
||||
toolbar_layout.setContentsMargins(8, 6, 8, 6)
|
||||
self.load_video_button = self._tool_button(QStyle.SP_DirOpenIcon, "Wczytaj film")
|
||||
self.video_play_button = self._tool_button(QStyle.SP_MediaPlay, "Play/pauza filmu")
|
||||
self.photo_button = self._tool_button(QStyle.SP_DialogSaveButton, "Zrob zdjecie")
|
||||
self.record_button = self._tool_button(QStyle.SP_MediaPlay, "Start/stop nagrywania")
|
||||
self.settings_button = self._tool_button(QStyle.SP_FileDialogDetailedView, "Ustawienia obrazu")
|
||||
toolbar_layout.addWidget(self.load_video_button)
|
||||
toolbar_layout.addWidget(self.video_play_button)
|
||||
toolbar_layout.addWidget(self.photo_button)
|
||||
toolbar_layout.addWidget(self.record_button)
|
||||
toolbar_layout.addWidget(self.settings_button)
|
||||
self.video_play_button.setEnabled(False)
|
||||
self.load_video_button.clicked.connect(self.load_video)
|
||||
self.video_play_button.clicked.connect(self.toggle_video_playback)
|
||||
self.photo_button.clicked.connect(self.take_photo)
|
||||
self.record_button.clicked.connect(self.toggle_recording)
|
||||
self.settings_button.clicked.connect(self.open_settings)
|
||||
@@ -158,9 +170,10 @@ class MainWindow(QMainWindow):
|
||||
def closeEvent(self, event: Any) -> None:
|
||||
if self.video_recorder.is_recording:
|
||||
self.video_recorder.stop(self.current_metadata("video"))
|
||||
self.worker.stop()
|
||||
self.video_timer.stop()
|
||||
self._close_video_capture()
|
||||
self._stop_camera_worker()
|
||||
self.detection_worker.stop()
|
||||
self.worker.wait(2000)
|
||||
self.detection_worker.wait(2000)
|
||||
super().closeEvent(event)
|
||||
|
||||
@@ -223,6 +236,41 @@ class MainWindow(QMainWindow):
|
||||
self.record_button.setIcon(self.style().standardIcon(QStyle.SP_MediaStop))
|
||||
self.statusBar().showMessage(f"Nagrywanie: {path}", 5000)
|
||||
|
||||
def load_video(self) -> None:
|
||||
path, _ = QFileDialog.getOpenFileName(
|
||||
self,
|
||||
"Wczytaj film",
|
||||
"",
|
||||
"Filmy (*.mp4 *.avi *.mov *.mkv *.m4v);;Wszystkie pliki (*)",
|
||||
)
|
||||
if not path:
|
||||
return
|
||||
|
||||
capture = cv2.VideoCapture(path)
|
||||
if not capture.isOpened():
|
||||
QMessageBox.warning(self, "Film", "Nie mozna otworzyc pliku wideo")
|
||||
capture.release()
|
||||
return
|
||||
|
||||
if self.video_recorder.is_recording:
|
||||
self.video_recorder.stop(self.current_metadata("video"))
|
||||
self.record_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
|
||||
|
||||
self._stop_camera_worker()
|
||||
self._close_video_capture()
|
||||
self.video_capture = capture
|
||||
self.video_play_button.setEnabled(True)
|
||||
self._set_video_playing(False)
|
||||
self.overlay_result = None
|
||||
self.last_detection = None
|
||||
self.result_text.setPlainText(f"Wczytano film: {path}")
|
||||
self._read_video_frame()
|
||||
|
||||
def toggle_video_playback(self) -> None:
|
||||
if self.video_capture is None:
|
||||
return
|
||||
self._set_video_playing(not self.video_playing)
|
||||
|
||||
def open_settings(self) -> None:
|
||||
dialog = SettingsDialog(self.config, self)
|
||||
dialog.settings_saved.connect(self.save_camera_settings)
|
||||
@@ -232,7 +280,54 @@ class MainWindow(QMainWindow):
|
||||
def save_camera_settings(self, camera_config: dict[str, Any]) -> None:
|
||||
self.config["camera"] = camera_config
|
||||
self.app_config.save(self.config)
|
||||
self.worker.update_camera_config(camera_config)
|
||||
if self.worker is not None:
|
||||
self.worker.update_camera_config(camera_config)
|
||||
|
||||
def _read_video_frame(self) -> None:
|
||||
if self.video_capture is None:
|
||||
return
|
||||
|
||||
ok, frame = self.video_capture.read()
|
||||
if not ok or frame is None:
|
||||
self._set_video_playing(False)
|
||||
self.video_capture.set(cv2.CAP_PROP_POS_FRAMES, 0)
|
||||
self.statusBar().showMessage("Koniec filmu", 3000)
|
||||
return
|
||||
|
||||
self.on_frame_ready(frame)
|
||||
|
||||
def _set_video_playing(self, playing: bool) -> None:
|
||||
self.video_playing = playing
|
||||
if self.video_capture is None:
|
||||
self.video_timer.stop()
|
||||
self.video_play_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
|
||||
self.video_play_button.setEnabled(False)
|
||||
return
|
||||
|
||||
if playing:
|
||||
fps = self.video_capture.get(cv2.CAP_PROP_FPS)
|
||||
if fps <= 0:
|
||||
fps = float(self.config["camera"].get("fps", 30))
|
||||
interval_ms = max(1, int(round(1000 / fps)))
|
||||
self.video_timer.start(interval_ms)
|
||||
self.video_play_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPause))
|
||||
else:
|
||||
self.video_timer.stop()
|
||||
self.video_play_button.setIcon(self.style().standardIcon(QStyle.SP_MediaPlay))
|
||||
|
||||
def _close_video_capture(self) -> None:
|
||||
self._set_video_playing(False)
|
||||
if self.video_capture is not None:
|
||||
self.video_capture.release()
|
||||
self.video_capture = None
|
||||
self.video_play_button.setEnabled(False)
|
||||
|
||||
def _stop_camera_worker(self) -> None:
|
||||
if self.worker is None:
|
||||
return
|
||||
self.worker.stop()
|
||||
self.worker.wait(2000)
|
||||
self.worker = None
|
||||
|
||||
def _maybe_request_detection(self, frame: np.ndarray) -> None:
|
||||
if not self.detecting:
|
||||
@@ -277,10 +372,18 @@ class MainWindow(QMainWindow):
|
||||
lines.append(f"Komunikat: {result.error}")
|
||||
if result.confidence is not None:
|
||||
lines.append(f"YOLO confidence: {result.confidence:.3f}")
|
||||
if result.ocr_engine:
|
||||
lines.append(f"OCR: {result.ocr_engine}")
|
||||
if result.ocr_confidence is not None:
|
||||
lines.append(f"OCR confidence: {result.ocr_confidence:.3f}")
|
||||
if result.ocr_elapsed_ms is not None:
|
||||
lines.append(f"OCR czas: {result.ocr_elapsed_ms:.0f} ms")
|
||||
if result.parsed:
|
||||
lines.append(f"Zamowienie: {result.parsed.order_number or '-'}")
|
||||
lines.append(f"Kolor: {result.parsed.color_code or '-'}")
|
||||
lines.append(f"Model: {result.parsed.product_model or '-'}")
|
||||
color_score = _format_score(result.parsed.color_score)
|
||||
model_score = _format_score(result.parsed.product_model_score)
|
||||
lines.append(f"Kolor: {result.parsed.color_code or '-'}{color_score}")
|
||||
lines.append(f"Model: {result.parsed.product_model or '-'}{model_score}")
|
||||
if result.raw_text:
|
||||
lines.append("")
|
||||
lines.append(result.raw_text)
|
||||
@@ -326,7 +429,7 @@ class MainWindow(QMainWindow):
|
||||
|
||||
def _draw_fps(self, frame_bgr: np.ndarray) -> None:
|
||||
label = f"FPS: {self.display_fps:.1f}"
|
||||
cv2.rectangle(frame_bgr, (12, 12), (122, 46), (0, 0, 0), -1)
|
||||
cv2.rectangle(frame_bgr, (12, 12), (142, 46), (0, 0, 0), -1)
|
||||
cv2.putText(
|
||||
frame_bgr,
|
||||
label,
|
||||
@@ -344,3 +447,9 @@ def run_app(app_config: AppConfig) -> int:
|
||||
window = MainWindow(app_config)
|
||||
window.show()
|
||||
return app.exec()
|
||||
|
||||
|
||||
def _format_score(score: float | None) -> str:
|
||||
if score is None:
|
||||
return ""
|
||||
return f" ({score:.2f})"
|
||||
|
||||
@@ -20,19 +20,32 @@
|
||||
}
|
||||
},
|
||||
"detection": {
|
||||
"model_path": "models/best.pt",
|
||||
"model_path": "models/best_v1.pt",
|
||||
"confidence_threshold": 0.25,
|
||||
"mode": "best",
|
||||
"frame_stride": 5,
|
||||
"frame_stride": 30,
|
||||
"image_size": 640,
|
||||
"device": "cpu"
|
||||
},
|
||||
"ocr": {
|
||||
"enabled": true,
|
||||
"engine": "paddle",
|
||||
"language": "eng",
|
||||
"tesseract_cmd": null,
|
||||
"psm": 6,
|
||||
"margin": 0,
|
||||
"threshold": true,
|
||||
"scale": 2.0
|
||||
"paddle_threshold": false,
|
||||
"scale": 2.0,
|
||||
"config": "",
|
||||
"use_angle_cls": true,
|
||||
"paddle": {
|
||||
"enable_mkldnn": false,
|
||||
"lang": "en",
|
||||
"use_doc_orientation_classify": false,
|
||||
"use_doc_unwarping": false,
|
||||
"use_textline_orientation": false
|
||||
}
|
||||
},
|
||||
"capture": {
|
||||
"photos_dir": "captures/photos",
|
||||
@@ -45,13 +58,33 @@
|
||||
"show_fps": true
|
||||
},
|
||||
"label_data": {
|
||||
"model_min_score": 0.72,
|
||||
"color_min_score": 0.72,
|
||||
"models": [
|
||||
"Regius",
|
||||
"Duvell"
|
||||
"Regius 6",
|
||||
"Regius 7",
|
||||
"Duvell 6",
|
||||
"Duvell 7",
|
||||
"Duvell Elite 6",
|
||||
"Duvell Elite 7"
|
||||
],
|
||||
"colors": [
|
||||
"T-NF-BLK-OUT-BST-G",
|
||||
"T-BLK-G"
|
||||
"T-BLK-G",
|
||||
"T-BLK-S",
|
||||
"T-BLK-M",
|
||||
"M-BLK-G",
|
||||
"M-BLK-S",
|
||||
"M-BLK-M",
|
||||
"T-CST-G",
|
||||
"T-CST-S",
|
||||
"T-CST-M",
|
||||
"T-ANTIQUE-G",
|
||||
"T-ANTIQUE-S",
|
||||
"T-ANTIQUE-M",
|
||||
"T-NAT-G",
|
||||
"T-NAT-S",
|
||||
"T-NAT-M"
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user