| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113 |
- """
- Locule Worker Module
- Worker thread for async locule segmentation and counting.
- """
- from typing import Optional
- import logging
- from PyQt5.QtCore import pyqtSignal
- from PyQt5.QtGui import QImage
- from workers.base_worker import BaseWorker, WorkerSignals
- from models.locule_model import LoculeModel
- from utils.config import get_device
- logger = logging.getLogger(__name__)
- class LoculeWorkerSignals(WorkerSignals):
- """
- Signals specific to locule counting.
-
- Signals:
- result_ready: Emitted when counting is complete
- (annotated_image: QImage, locule_count: int, detections: list)
- """
- result_ready = pyqtSignal(QImage, int, list)
- class LoculeWorker(BaseWorker):
- """
- Worker for processing images and counting locules.
-
- Runs LoculeModel inference in a background thread without blocking UI.
-
- Attributes:
- image_path: Path to image file
- model: LoculeModel instance
- signals: LoculeWorkerSignals for emitting results
- """
-
- def __init__(self, image_path: str, model: Optional[LoculeModel] = None):
- """
- Initialize the locule worker.
-
- Args:
- image_path: Path to cross-section image to process
- model: LoculeModel instance (if None, creates new one)
- """
- super().__init__()
- self.image_path = image_path
- self.model = model
-
- # Replace base signals with locule-specific signals
- self.signals = LoculeWorkerSignals()
-
- # If no model provided, create and load one
- if self.model is None:
- device = get_device()
- self.model = LoculeModel(device=device)
- self.model.load()
-
- logger.info(f"LoculeWorker created for: {image_path}")
-
- def process(self):
- """
- Process the image and count locules.
-
- Emits result_ready signal with counting results.
- """
- if self.is_cancelled():
- logger.info("LoculeWorker cancelled before processing")
- return
-
- # Update progress
- self.emit_progress(10, "Loading image...")
-
- if not self.model.is_loaded:
- logger.warning("Model not loaded, loading now...")
- self.emit_progress(30, "Loading model...")
- if not self.model.load():
- raise RuntimeError("Failed to load locule model")
-
- # Process image
- self.emit_progress(50, "Segmenting locules...")
-
- result = self.model.predict(self.image_path)
-
- if self.is_cancelled():
- logger.info("LoculeWorker cancelled during processing")
- return
-
- if not result['success']:
- raise RuntimeError(result['error'])
-
- self.emit_progress(90, "Finalizing results...")
-
- # Make a copy of the QImage to ensure thread safety
- # QImage data can be garbage collected if not copied
- annotated_image_copy = result['annotated_image'].copy()
-
- # Emit results
- self.signals.result_ready.emit(
- annotated_image_copy,
- result['locule_count'],
- result['detections']
- )
-
- self.emit_progress(100, "Complete!")
-
- logger.info(f"LoculeWorker completed: {result['locule_count']} locules detected")
|