Spaces:
Runtime error
Runtime error
File size: 7,339 Bytes
fe35ddf | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 | # -*- coding: utf-8 -*-
"""Модуль для работы с моделью YOLO и детекцией объектов"""
import os
from pathlib import Path
import torch
import cv2
import numpy as np
from PIL import Image, ImageDraw, ImageFont
from PyQt5.QtCore import QThread, pyqtSignal
try:
from ultralytics import YOLO
YOLO_AVAILABLE = True
except ImportError:
YOLO_AVAILABLE = False
print("⚠️ Установите ultralytics: pip install ultralytics")
def check_yolo_availability():
"""Проверяет доступность библиотеки YOLO"""
return YOLO_AVAILABLE
class ModelLoader:
"""Класс для загрузки и управления моделью YOLO"""
def __init__(self):
self.model = None
self.class_names = {}
self.model_path = None
def find_model_file(self, default_path="C:/PycharmProjects/XVL/src/model/best.pt"):
"""Ищет файл модели в различных местах"""
possible_paths = [
Path(default_path),
Path("model/best.pt"),
Path("best.pt"),
Path.cwd() / "best.pt",
]
for path in possible_paths:
if path.exists():
print(f"✅ Найдена модель: {path}")
return str(path)
return None
def load_model(self, model_path=None):
"""Загружает модель YOLO"""
if not YOLO_AVAILABLE:
raise ImportError("Библиотека ultralytics не установлена!")
if model_path:
self.model_path = model_path
else:
self.model_path = self.find_model_file()
if not self.model_path:
raise FileNotFoundError("Файл модели best.pt не найден")
try:
self.model = YOLO(self.model_path)
print(f"✅ Модель загружена: {self.model_path}")
if hasattr(self.model, 'names'):
self.class_names = self.model.names
print(f"📊 Классы модели: {self.class_names}")
return True
except Exception as e:
raise Exception(f"Не удалось загрузить модель: {str(e)}")
def predict(self, image_path, confidence_threshold=0.25):
"""Выполняет предсказание на изображении"""
if self.model is None:
raise ValueError("Модель не загружена")
# Загружаем изображение
img = cv2.imread(str(image_path))
if img is None:
raise FileNotFoundError(f"Не удалось загрузить изображение: {image_path}")
img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Выполняем предсказание
results = self.model(img_rgb, conf=confidence_threshold)
# Извлекаем результаты
boxes = []
classes = []
confidences = []
if results and len(results) > 0:
result = results[0]
if result.boxes is not None:
for box in result.boxes:
# Координаты
x1, y1, x2, y2 = box.xyxy[0].cpu().numpy()
boxes.append([x1, y1, x2, y2])
# Класс
class_id = int(box.cls[0])
classes.append(class_id)
# Уверенность
conf = float(box.conf[0])
confidences.append(conf)
return img_rgb, boxes, classes, confidences
class DetectionThread(QThread):
"""Поток для выполнения детекции в фоновом режиме"""
detection_finished = pyqtSignal(np.ndarray, list, list, list) # изображение, боксы, классы, уверенность
detection_error = pyqtSignal(str)
def __init__(self, model_loader, image_path, confidence_threshold=0.25):
super().__init__()
self.model_loader = model_loader
self.image_path = image_path
self.confidence_threshold = confidence_threshold
def run(self):
try:
# Выполняем предсказание
img_rgb, boxes, classes, confidences = self.model_loader.predict(
self.image_path,
self.confidence_threshold
)
# Отправляем результаты
self.detection_finished.emit(img_rgb, boxes, classes, confidences)
except Exception as e:
self.detection_error.emit(f"Ошибка детекции: {str(e)}")
class ResultVisualizer:
"""Класс для визуализации результатов детекции"""
COLORS = [
(255, 0, 0), # Красный
(0, 255, 0), # Зеленый
(0, 0, 255), # Синий
(255, 255, 0), # Желтый
(255, 0, 255), # Пурпурный
(0, 255, 255), # Голубой
(255, 165, 0), # Оранжевый
(128, 0, 128), # Фиолетовый
]
@staticmethod
def draw_boxes(image, boxes, classes, confidences, class_names):
"""Рисует bounding boxes на изображении"""
img_pil = Image.fromarray(image)
draw = ImageDraw.Draw(img_pil)
# Загружаем шрифт
try:
font = ImageFont.truetype("arial.ttf", 16)
except:
font = ImageFont.load_default()
for i, (box, class_id, confidence) in enumerate(zip(boxes, classes, confidences)):
x1, y1, x2, y2 = box
# Выбираем цвет для класса
color = ResultVisualizer.COLORS[class_id % len(ResultVisualizer.COLORS)]
# Рисуем прямоугольник
draw.rectangle([x1, y1, x2, y2], outline=color, width=3)
# Подготовка текста
class_name = class_names.get(class_id, f"Дефект {class_id}")
label = f"{class_name}: {confidence:.1%}"
# Рисуем фон для текста
text_bbox = draw.textbbox((x1, y1), label, font=font)
draw.rectangle(text_bbox, fill=color)
# Рисуем текст
draw.text((x1, y1), label, fill=(255, 255, 255), font=font)
return np.array(img_pil)
@staticmethod
def get_statistics_text(classes, class_names):
"""Формирует текстовую статистику по результатам"""
if not classes:
return "Дефектов не обнаружено"
class_counts = {}
for class_id in classes:
class_name = class_names.get(class_id, f"Дефект {class_id}")
class_counts[class_name] = class_counts.get(class_name, 0) + 1
stats_parts = [f"{name}: {count}" for name, count in class_counts.items()]
return " | ".join(stats_parts) |