defect_worker.py 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. """
  2. Defect Worker Module
  3. Worker thread for async defect detection and quality assessment.
  4. """
  5. from typing import Optional
  6. import logging
  7. from PyQt5.QtCore import pyqtSignal
  8. from PyQt5.QtGui import QImage
  9. from workers.base_worker import BaseWorker, WorkerSignals
  10. from models.defect_model import DefectModel
  11. from utils.config import get_device
  12. logger = logging.getLogger(__name__)
  13. class DefectWorkerSignals(WorkerSignals):
  14. """
  15. Signals specific to defect detection.
  16. Signals:
  17. result_ready: Emitted when detection is complete
  18. (annotated_image: QImage, primary_class: str,
  19. class_counts: dict, total_detections: int)
  20. """
  21. result_ready = pyqtSignal(QImage, str, dict, int)
  22. class DefectWorker(BaseWorker):
  23. """
  24. Worker for processing images and detecting defects.
  25. Runs DefectModel inference in a background thread without blocking UI.
  26. Attributes:
  27. image_path: Path to image file
  28. model: DefectModel instance
  29. signals: DefectWorkerSignals for emitting results
  30. """
  31. def __init__(self, image_path: str, model: Optional[DefectModel] = None):
  32. """
  33. Initialize the defect worker.
  34. Args:
  35. image_path: Path to image file to process
  36. model: DefectModel instance (if None, creates new one)
  37. """
  38. super().__init__()
  39. self.image_path = image_path
  40. self.model = model
  41. # Replace base signals with defect-specific signals
  42. self.signals = DefectWorkerSignals()
  43. # If no model provided, create and load one
  44. if self.model is None:
  45. device = get_device()
  46. self.model = DefectModel(device=device)
  47. self.model.load()
  48. logger.info(f"DefectWorker created for: {image_path}")
  49. def process(self):
  50. """
  51. Process the image and detect defects.
  52. Emits result_ready signal with detection results.
  53. """
  54. if self.is_cancelled():
  55. logger.info("DefectWorker cancelled before processing")
  56. return
  57. # Update progress
  58. self.emit_progress(10, "Loading image...")
  59. if not self.model.is_loaded:
  60. logger.warning("Model not loaded, loading now...")
  61. self.emit_progress(30, "Loading model...")
  62. if not self.model.load():
  63. raise RuntimeError("Failed to load defect model")
  64. # Process image
  65. self.emit_progress(50, "Detecting defects...")
  66. result = self.model.predict(self.image_path)
  67. if self.is_cancelled():
  68. logger.info("DefectWorker cancelled during processing")
  69. return
  70. if not result['success']:
  71. raise RuntimeError(result['error'])
  72. self.emit_progress(90, "Finalizing results...")
  73. # Make a copy of the QImage to ensure thread safety
  74. # QImage data can be garbage collected if not copied
  75. annotated_image_copy = result['annotated_image'].copy()
  76. # Emit results
  77. self.signals.result_ready.emit(
  78. annotated_image_copy,
  79. result['primary_class'],
  80. result['class_counts'],
  81. result['total_detections']
  82. )
  83. self.emit_progress(100, "Complete!")
  84. logger.info(f"DefectWorker completed: {result['primary_class']} ({result['total_detections']} detections)")