/
Dev_Devil
/
ComputerVision
Обзор
Документация
Войти
/
Dev_Devil
/
ComputerVision
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
Shapiro/part3/python/abi.py
726 строк
30 KB
nikitatia
shapiro part 3
24 ноя 2025, 22:31
24 ноя 2025, 22:31
4f76e9b
Код
Авторство
О чём код?
import cv2 import numpy as np import matplotlib.pyplot as plt import networkx as nx from typing import Tuple, List, Set, Dict from collections import deque import seaborn as sns class BinaryImageVisualizer: def __init__(self): self.fig_size = (15, 10) self.cmap_binary = 'gray' self.cmap_labels = 'tab10' def create_test_image(self) -> np.ndarray: image = np.zeros((100, 100), dtype=np.uint8) cv2.rectangle(image, (10, 10), (30, 30), 1, -1) cv2.rectangle(image, (50, 20), (70, 40), 1, -1) cv2.circle(image, (80, 25), 10, 1, -1) cv2.circle(image, (40, 60), 12, 1, -1) triangle = np.array([[20, 70], [10, 85], [30, 85]], np.int32) cv2.fillPoly(image, [triangle], 1) cv2.ellipse(image, (70, 70), (15, 10), 0, 0, 360, 1, -1) complex_shape = np.array([[80, 50], [90, 45], [95, 55], [90, 65], [80, 70], [75, 60]], np.int32) cv2.fillPoly(image, [complex_shape], 1) return image * 255 def visualize_mask_application(self, analyzer): """Визуализация применения масок""" test_image = self.create_test_image() masks = { 'Единичная 3x3': np.ones((3, 3), dtype=np.float32), 'Гауссова 3x3': np.array([[1, 2, 1], [2, 4, 2], [1, 2, 1]], dtype=np.float32), 'Прямоугольная 5x3': np.ones((5, 3), dtype=np.float32) } fig, axes = plt.subplots(2, len(masks) + 1, figsize=(18, 8)) axes[0, 0].imshow(test_image, cmap=self.cmap_binary) axes[0, 0].set_title('Исходное изображение') axes[0, 0].axis('off') axes[1, 0].axis('off') for i, (mask_name, mask) in enumerate(masks.items(), 1): axes[0, i].imshow(mask, cmap='viridis', interpolation='nearest') axes[0, i].set_title(f'Маска: {mask_name}') axes[0, i].axis('off') result = analyzer.apply_mask(test_image, mask, normalize=True) axes[1, i].imshow(result, cmap=self.cmap_binary) axes[1, i].set_title(f'Результат {mask_name}') axes[1, i].axis('off') plt.tight_layout() plt.show() def visualize_object_counting(self, analyzer): """Визуализация подсчета объектов методом углов""" # Создание изображения с несколькими объектами image = np.zeros((80, 80), dtype=np.uint8) cv2.rectangle(image, (10, 10), (25, 25), 1, -1) cv2.rectangle(image, (40, 10), (55, 25), 1, -1) cv2.circle(image, (30, 50), 10, 1, -1) image = image * 255 # Подсчет объектов count = analyzer.count_objects_corners(image // 255) # Визуализация углов corners_image = image.copy() # Поиск и отметка углов for i in range(image.shape[0] - 1): for j in range(image.shape[1] - 1): window = (image[i:i+2, j:j+2] // 255).astype(np.uint8) # Внешние углы (красные) if np.sum(window == 1) == 1: cv2.circle(corners_image, (j, i), 2, 128, -1) # Внутренние углы (синие) if np.sum(window == 1) == 3: cv2.circle(corners_image, (j, i), 2, 200, -1) fig, axes = plt.subplots(1, 2, figsize=(12, 5)) axes[0].imshow(image, cmap=self.cmap_binary) axes[0].set_title('Исходное бинарное изображение') axes[0].axis('off') axes[1].imshow(corners_image, cmap=self.cmap_binary) axes[1].set_title(f'Обнаруженные углы\nКоличество объектов: {count}') axes[1].axis('off') # Легенда для углов axes[1].legend(loc='upper right') plt.tight_layout() plt.show() def visualize_connected_components(self, analyzer): """Визуализация маркировки связных компонент""" test_image = self.create_test_image() // 255 # Применение алгоритмов labeled_recursive = analyzer.iterative_connected_components(test_image) labeled_union_find = analyzer.union_find_connected_components(test_image) # Визуализация fig, axes = plt.subplots(2, 2, figsize=(15, 12)) # Исходное изображение axes[0, 0].imshow(test_image, cmap=self.cmap_binary) axes[0, 0].set_title('Исходное бинарное изображение') axes[0, 0].axis('off') # Итеративный алгоритм im1 = axes[0, 1].imshow(labeled_recursive, cmap=self.cmap_labels) axes[0, 1].set_title(f'Итеративный алгоритм\nКомпонент: {np.max(labeled_recursive)}') axes[0, 1].axis('off') plt.colorbar(im1, ax=axes[0, 1]) # Union-Find алгоритм im2 = axes[1, 0].imshow(labeled_union_find, cmap=self.cmap_labels) axes[1, 0].set_title(f'Union-Find алгоритм\nКомпонент: {np.max(labeled_union_find)}') axes[1, 0].axis('off') plt.colorbar(im2, ax=axes[1, 0]) # Разница между алгоритмами diff = labeled_recursive - labeled_union_find im3 = axes[1, 1].imshow(diff, cmap='coolwarm', vmin=-np.max(np.abs(diff)) if np.max(np.abs(diff)) > 0 else -1, vmax=np.max(np.abs(diff)) if np.max(np.abs(diff)) > 0 else 1) axes[1, 1].set_title('Разница между алгоритмами') axes[1, 1].axis('off') plt.colorbar(im3, ax=axes[1, 1]) plt.tight_layout() plt.show() def visualize_morphology_operations(self, analyzer): """Визуализация морфологических операций""" # Создание тестового изображения с шумом и отверстиями image = np.zeros((100, 100), dtype=np.uint8) cv2.rectangle(image, (20, 20), (50, 50), 1, -1) cv2.circle(image, (70, 70), 20, 1, -1) # Добавление шума noise_positions = np.random.randint(0, 100, (10, 2)) for pos in noise_positions: image[pos[0], pos[1]] = 1 # Добавление отверстия image[30:35, 30:35] = 0 image = image * 255 # Применение морфологических операций operations = { 'Исходное': image, 'Наращивание': analyzer.binary_morphology(image, 'dilation'), 'Эрозия': analyzer.binary_morphology(image, 'erosion'), 'Размыкание': analyzer.binary_morphology(image, 'opening'), 'Замыкание': analyzer.binary_morphology(image, 'closing') } # Визуализация fig, axes = plt.subplots(2, 3, figsize=(15, 10)) axes = axes.ravel() for i, (op_name, result) in enumerate(operations.items()): axes[i].imshow(result, cmap=self.cmap_binary) axes[i].set_title(op_name) axes[i].axis('off') # Условное наращивание seed_points = np.zeros_like(image) seed_points[25, 25] = 255 # начальная точка conditional_result = analyzer.conditional_dilation(image, seed_points) axes[5].imshow(conditional_result, cmap=self.cmap_binary) axes[5].set_title('Условное наращивание') axes[5].axis('off') plt.tight_layout() plt.show() def visualize_region_properties(self, analyzer): """Визуализация свойств областей""" test_image = self.create_test_image() // 255 labeled_image = analyzer.iterative_connected_components(test_image) properties = analyzer.region_properties(labeled_image) # Создание цветного изображения для визуализации colored_image = np.zeros((test_image.shape[0], test_image.shape[1], 3), dtype=np.uint8) # Генерация случайных цветов для каждой области colors = {} for prop in properties: colors[prop['label']] = np.random.randint(50, 255, 3) for i in range(labeled_image.shape[0]): for j in range(labeled_image.shape[1]): label = labeled_image[i, j] if label > 0: colored_image[i, j] = colors[label] # Визуализация fig, axes = plt.subplots(2, 2, figsize=(15, 12)) # Исходное изображение axes[0, 0].imshow(test_image, cmap=self.cmap_binary) axes[0, 0].set_title('Исходное изображение') axes[0, 0].axis('off') # Маркированное изображение axes[0, 1].imshow(colored_image) axes[0, 1].set_title('Маркированные области') axes[0, 1].axis('off') # Визуализация свойств properties_image = colored_image.copy() for prop in properties: centroid = prop['centroid'] bbox = prop['bounding_box'] label = prop['label'] # Центр тяжести cv2.circle(properties_image, (int(centroid[1]), int(centroid[0])), 3, (255, 255, 255), -1) # Ограничивающий прямоугольник cv2.rectangle(properties_image, (bbox[0], bbox[1]), (bbox[2], bbox[3]), (255, 255, 255), 1) # Подпись с информацией об области cv2.putText(properties_image, f'{label}', (bbox[0], bbox[1] - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.4, (255, 255, 255), 1) axes[1, 0].imshow(properties_image) axes[1, 0].set_title('Свойства областей\n(центры и bounding boxes)') axes[1, 0].axis('off') # Таблица свойств axes[1, 1].axis('off') if properties: table_data = [] for prop in properties[:6]: # Ограничим количество строк для читаемости table_data.append([ prop['label'], prop['area'], f"({prop['centroid'][0]:.1f}, {prop['centroid'][1]:.1f})", prop['perimeter'], f"{prop['circularity']:.2f}" ]) table = axes[1, 1].table( cellText=table_data, colLabels=['Метка', 'Площадь', 'Центр', 'Периметр', 'Округлость'], loc='center', cellLoc='center' ) table.auto_set_font_size(False) table.set_fontsize(8) table.scale(1, 1.5) axes[1, 1].set_title('Свойства областей (первые 6)') plt.tight_layout() plt.show() def visualize_region_adjacency_graph(self, analyzer): """Визуализация графа смежности областей""" test_image = self.create_test_image() // 255 labeled_image = analyzer.iterative_connected_components(test_image) rag = analyzer.region_adjacency_graph(labeled_image) # Создание графа G = nx.Graph() # Добавление узлов и ребер for node, neighbors in rag.items(): G.add_node(node) for neighbor in neighbors: G.add_edge(node, neighbor) # Позиции узлов на основе центроид областей properties = analyzer.region_properties(labeled_image) pos = {} for prop in properties: if prop['label'] in G.nodes(): pos[prop['label']] = (prop['centroid'][1], -prop['centroid'][0]) # Визуализация fig, axes = plt.subplots(1, 2, figsize=(15, 6)) # Маркированное изображение colored_image = np.zeros((test_image.shape[0], test_image.shape[1], 3), dtype=np.uint8) colors = {} for prop in properties: colors[prop['label']] = np.random.randint(50, 255, 3) for i in range(labeled_image.shape[0]): for j in range(labeled_image.shape[1]): label = labeled_image[i, j] if label > 0: colored_image[i, j] = colors[label] axes[0].imshow(colored_image) axes[0].set_title('Маркированные области') axes[0].axis('off') def visualize_otsu_thresholding(self, analyzer): gray_image = np.zeros((100, 100), dtype=np.uint8) cv2.rectangle(gray_image, (15, 15), (40, 40), 50, -1) cv2.circle(gray_image, (70, 30), 15, 60, -1) cv2.rectangle(gray_image, (50, 50), (85, 85), 200, -1) cv2.ellipse(gray_image, (30, 70), (20, 15), 0, 0, 360, 180, -1) noise = np.random.normal(0, 10, gray_image.shape).astype(np.uint8) gray_image = cv2.add(gray_image, noise) threshold, binary_image = analyzer.otsu_threshold(gray_image) hist = cv2.calcHist([gray_image], [0], None, [256], [0, 256]) hist_norm = hist / hist.sum() # Визуализация fig, axes = plt.subplots(2, 2, figsize=(15, 10)) # Полутоновое изображение axes[0, 0].imshow(gray_image, cmap='gray') axes[0, 0].set_title('Полутоновое изображение') axes[0, 0].axis('off') # Гистограмма axes[0, 1].plot(hist_norm, color='black') axes[0, 1].axvline(x=threshold, color='red', linestyle='--', label=f'Порог Оцу: {threshold}') axes[0, 1].set_title('Гистограмма изображения') axes[0, 1].set_xlabel('Яркость') axes[0, 1].set_ylabel('Вероятность') axes[0, 1].legend() axes[0, 1].grid(True, alpha=0.3) # Бинаризованное изображение axes[1, 0].imshow(binary_image, cmap='gray') axes[1, 0].set_title(f'Бинаризация (порог = {threshold})') axes[1, 0].axis('off') # Сравнение comparison = np.hstack([gray_image, binary_image]) axes[1, 1].imshow(comparison, cmap='gray') axes[1, 1].set_title('Сравнение: исходное vs бинарное') axes[1, 1].axis('off') axes[1, 1].text(50, 5, 'Исходное', color='white', ha='center', fontsize=8) axes[1, 1].text(150, 5, 'Бинарное', color='black', ha='center', fontsize=8) plt.tight_layout() plt.show() def comprehensive_demo(self, analyzer): """Комплексная демонстрация всех методов""" print("=== КОМПЛЕКСНАЯ ДЕМОНСТРАЦИЯ АНАЛИЗА БИНАРНЫХ ИЗОБРАЖЕНИЙ ===") # 1. Создание и визуализация тестового изображения print("\n1. Создание тестового изображения...") test_image = self.create_test_image() plt.figure(figsize=(12, 8)) plt.subplot(2, 3, 1) plt.imshow(test_image, cmap=self.cmap_binary) plt.title('Тестовое бинарное изображение') plt.axis('off') # 2. Подсчет объектов print("2. Подсчет объектов методом углов...") count = analyzer.count_objects_corners(test_image // 255) plt.subplot(2, 3, 2) plt.imshow(test_image, cmap=self.cmap_binary) plt.title(f'Подсчет объектов: {count}') plt.axis('off') # 3. Маркировка связных компонент print("3. Маркировка связных компонент...") labeled = analyzer.iterative_connected_components(test_image // 255) plt.subplot(2, 3, 3) plt.imshow(labeled, cmap=self.cmap_labels) plt.title(f'Маркировка: {np.max(labeled)} компонент') plt.axis('off') # 4. Морфологические операции print("4. Применение морфологических операций...") closed = analyzer.binary_morphology(test_image, 'closing') plt.subplot(2, 3, 4) plt.imshow(closed, cmap=self.cmap_binary) plt.title('Морфология: Замыкание') plt.axis('off') # 5. Свойства областей print("5. Вычисление свойств областей...") properties = analyzer.region_properties(labeled) colored = np.zeros((test_image.shape[0], test_image.shape[1], 3)) for prop in properties: mask = (labeled == prop['label']) colored[mask] = np.random.rand(3) plt.subplot(2, 3, 5) plt.imshow(colored) plt.title(f'Свойства: {len(properties)} областей') plt.axis('off') # Основной класс анализатора (исправленный) class BinaryImageAnalyzer: def __init__(self): self.parent = [] self.labels = None def apply_mask(self, image: np.ndarray, mask: np.ndarray, normalize: bool = True) -> np.ndarray: """Применение маски к изображению""" if len(image.shape) == 3: image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) pad_h = mask.shape[0] // 2 pad_w = mask.shape[1] // 2 padded = cv2.copyMakeBorder(image, pad_h, pad_h, pad_w, pad_w, cv2.BORDER_REPLICATE) result = np.zeros_like(image, dtype=np.float32) for i in range(image.shape[0]): for j in range(image.shape[1]): region = padded[i:i+mask.shape[0], j:j+mask.shape[1]] result[i, j] = np.sum(region * mask) if normalize: result = result / np.sum(mask) return result.astype(np.uint8) def count_objects_corners(self, binary_image: np.ndarray) -> int: """Подсчет объектов методом углов""" E = 0 I = 0 for i in range(binary_image.shape[0] - 1): for j in range(binary_image.shape[1] - 1): window = binary_image[i:i+2, j:j+2] if np.sum(window == 1) == 1: E += 1 if np.sum(window == 1) == 3: I += 1 return (E - I) // 4 def iterative_connected_components(self, binary_image: np.ndarray) -> np.ndarray: labeled = np.zeros_like(binary_image, dtype=np.int32) current_label = 0 directions = [(-1, 0), (1, 0), (0, -1), (0, 1)] for i in range(binary_image.shape[0]): for j in range(binary_image.shape[1]): if binary_image[i, j] == 1 and labeled[i, j] == 0: current_label += 1 stack = [(i, j)] while stack: x, y = stack.pop() if 0 <= x < binary_image.shape[0] and 0 <= y < binary_image.shape[1]: if binary_image[x, y] == 1 and labeled[x, y] == 0: labeled[x, y] = current_label for dx, dy in directions: stack.append((x + dx, y + dy)) return labeled def union_find_connected_components(self, binary_image: np.ndarray) -> np.ndarray: """Маркировка с union-find структурой""" LB = np.zeros_like(binary_image, dtype=np.int32) max_labels = binary_image.size // 4 self.parent = [0] * (max_labels + 2) # +2 для запаса label = 0 def find(x): while self.parent[x] != 0: x = self.parent[x] return x def union(x, y): root_x = find(x) root_y = find(y) if root_x != root_y: self.parent[root_y] = root_x # Первый проход for i in range(binary_image.shape[0]): for j in range(binary_image.shape[1]): if binary_image[i, j] == 1: neighbors = [] if i > 0 and binary_image[i-1, j] == 1: neighbors.append(LB[i-1, j]) if j > 0 and binary_image[i, j-1] == 1: neighbors.append(LB[i, j-1]) if not neighbors: label += 1 if label >= len(self.parent): # Расширяем массив при необходимости self.parent.extend([0] * (len(self.parent))) LB[i, j] = label else: min_label = min(neighbors) LB[i, j] = min_label for nbr in neighbors: if nbr != min_label: union(min_label, nbr) # Второй проход for i in range(binary_image.shape[0]): for j in range(binary_image.shape[1]): if LB[i, j] > 0: LB[i, j] = find(LB[i, j]) return LB def binary_morphology(self, binary_image: np.ndarray, operation: str, kernel_size: Tuple[int, int] = (3, 3)) -> np.ndarray: """Морфологические операции""" kernel = np.ones(kernel_size, np.uint8) if operation == 'dilation': return cv2.dilate(binary_image, kernel) elif operation == 'erosion': return cv2.erode(binary_image, kernel) elif operation == 'opening': return cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, kernel) elif operation == 'closing': return cv2.morphologyEx(binary_image, cv2.MORPH_CLOSE, kernel) else: raise ValueError("Неизвестная операция") def conditional_dilation(self, binary_image: np.ndarray, seed_points: np.ndarray, kernel_size: Tuple[int, int] = (3, 3)) -> np.ndarray: """Условное наращивание""" kernel = np.ones(kernel_size, np.uint8) C = seed_points.copy() C_prev = np.zeros_like(C) iterations = 0 max_iterations = 100 while not np.array_equal(C, C_prev) and iterations < max_iterations: C_prev = C.copy() C = cv2.dilate(C, kernel) C = cv2.bitwise_and(C, binary_image) iterations += 1 return C def region_properties(self, labeled_image: np.ndarray) -> List[dict]: """Вычисление свойств областей""" properties = [] max_label = np.max(labeled_image) for label in range(1, max_label + 1): mask = (labeled_image == label) if np.sum(mask) == 0: continue coords = np.argwhere(mask) area = len(coords) if area == 0: continue centroid_r = np.mean(coords[:, 0]) centroid_c = np.mean(coords[:, 1]) perimeter_mask = cv2.morphologyEx(mask.astype(np.uint8), cv2.MORPH_GRADIENT, np.ones((3,3), np.uint8)) perimeter = np.sum(perimeter_mask) circularity = (perimeter ** 2) / area if area > 0 else 0 y, x = np.where(mask) if len(x) > 0 and len(y) > 0: bounding_box = (np.min(x), np.min(y), np.max(x), np.max(y)) else: bounding_box = (0, 0, 0, 0) if len(coords) > 0: r_centered = coords[:, 0] - centroid_r c_centered = coords[:, 1] - centroid_c mu_rr = np.mean(r_centered ** 2) mu_cc = np.mean(c_centered ** 2) mu_rc = np.mean(r_centered * c_centered) else: mu_rr = mu_cc = mu_rc = 0 properties.append({ 'label': label, 'area': area, 'centroid': (centroid_r, centroid_c), 'perimeter': perimeter, 'circularity': circularity, 'bounding_box': bounding_box, 'mu_rr': mu_rr, 'mu_cc': mu_cc, 'mu_rc': mu_rc }) return properties def otsu_threshold(self, gray_image: np.ndarray) -> Tuple[int, np.ndarray]: """Метод Оцу для автоматического выбора порога""" # Используем встроенную функцию OpenCV для надежности threshold, binary_image = cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU) return threshold, binary_image def region_adjacency_graph(self, labeled_image: np.ndarray) -> dict: """Построение графа смежности областей""" rag = {} max_label = np.max(labeled_image) for label in range(1, max_label + 1): rag[label] = set() for i in range(labeled_image.shape[0] - 1): for j in range(labeled_image.shape[1] - 1): current = labeled_image[i, j] if current == 0: continue neighbors = [] if i > 0: neighbors.append(labeled_image[i-1, j]) if i < labeled_image.shape[0] - 1: neighbors.append(labeled_image[i+1, j]) if j > 0: neighbors.append(labeled_image[i, j-1]) if j < labeled_image.shape[1] - 1: neighbors.append(labeled_image[i, j+1]) for nbr in neighbors: if nbr != 0 and nbr != current and nbr not in rag[current]: rag[current].add(nbr) if nbr in rag: rag[nbr].add(current) # Удаляем пустые записи rag = {k: v for k, v in rag.items() if len(v) > 0} return rag # Запуск демонстрации if __name__ == "__main__": analyzer = BinaryImageAnalyzer() visualizer = BinaryImageVisualizer() try: # Комплексная демонстрация visualizer.comprehensive_demo(analyzer) # Индивидуальные визуализации print("\n=== ИНДИВИДУАЛЬНЫЕ ВИЗУАЛИЗАЦИИ ===") print("\n1. Визуализация применения масок...") visualizer.visualize_mask_application(analyzer) print("\n2. Визуализация подсчета объектов...") visualizer.visualize_object_counting(analyzer) print("\n3. Визуализация маркировки связных компонент...") visualizer.visualize_connected_components(analyzer) print("\n4. Визуализация морфологических операций...") visualizer.visualize_morphology_operations(analyzer) print("\n5. Визуализация свойств областей...") visualizer.visualize_region_properties(analyzer) print("\n6. Визуализация пороговой бинаризации...") visualizer.visualize_otsu_thresholding(analyzer) except Exception as e: print(f"Произошла ошибка: {e}") print("Попробуйте запустить отдельные визуализации...") # Попробуем запустить простейшую демонстрацию try: test_image = visualizer.create_test_image() plt.figure(figsize=(10, 5)) plt.subplot(1, 2, 1) plt.imshow(test_image, cmap='gray') plt.title('Тестовое изображение') plt.axis('off') count = analyzer.count_objects_corners(test_image // 255) plt.subplot(1, 2, 2) plt.imshow(test_image, cmap='gray') plt.title(f'Найдено объектов: {count}') plt.axis('off') plt.show() except Exception as e2: print(f"Даже базовая визуализация не сработала: {e2}")