Spaces:
Running
on
Zero
Running
on
Zero
File size: 5,757 Bytes
611206a 5888da9 611206a 5888da9 611206a de894d3 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a de894d3 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a 5888da9 611206a |
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 |
import cv2
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.patheffects as path_effects
from typing import Any, List, Dict, Tuple, Optional
import io
from PIL import Image
class VisualizationHelper:
"""Helper class for visualizing detection results"""
@staticmethod
def visualize_detection(image: Any, result: Any, color_mapper: Optional[Any] = None,
figsize: Tuple[int, int] = (12, 12),
return_pil: bool = False,
filter_classes: Optional[List[int]] = None) -> Optional[Image.Image]:
"""
Visualize detection results on a single image
Args:
image: Image path or numpy array
result: Detection result object
color_mapper: ColorMapper instance for consistent colors
figsize: Figure size
return_pil: If True, returns a PIL Image object
Returns:
PIL Image if return_pil is True, otherwise displays the plot
"""
if result is None:
print('No data for visualization')
return None
# Read image if path is provided
if isinstance(image, str):
img = cv2.imread(image)
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
else:
img = image
if len(img.shape) == 3 and img.shape[2] == 3:
# Check if BGR format (OpenCV) and convert to RGB if needed
if isinstance(img, np.ndarray):
# Assuming BGR format from OpenCV
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Create figure
fig, ax = plt.subplots(figsize=figsize)
ax.imshow(img)
# Get bounding boxes, classes and confidences
boxes = result.boxes.xyxy.cpu().numpy()
classes = result.boxes.cls.cpu().numpy()
confs = result.boxes.conf.cpu().numpy()
# Get class names
names = result.names
# Create a default color mapper if none is provided
if color_mapper is None:
# For backward compatibility, fallback to a simple color function
from matplotlib import colormaps
cmap = colormaps['tab10']
def get_color(class_id):
return cmap(class_id % 10)
else:
# Use the provided color mapper
def get_color(class_id):
hex_color = color_mapper.get_color(class_id)
# Convert hex to RGB float values for matplotlib
hex_color = hex_color.lstrip('#')
return tuple(int(hex_color[i:i+2], 16) / 255 for i in (0, 2, 4)) + (1.0,)
# Draw detection results
for box, cls, conf in zip(boxes, classes, confs):
x1, y1, x2, y2 = box
cls_id = int(cls)
if filter_classes and cls_id not in filter_classes:
continue
cls_name = names[cls_id]
# Get color for this class
box_color = get_color(cls_id)
box_width = x2 - x1
box_height = y2 - y1
box_area = box_width * box_height
# 根據框大小調整字體大小,但有限制
adaptive_fontsize = max(10, min(14, int(10 + box_area / 10000)))
ax.text(x1, y1 - 8, f'{cls_name}: {conf:.2f}',
color='white', fontsize=adaptive_fontsize, fontweight="bold",
bbox=dict(facecolor=box_color[:3], alpha=0.85, pad=3, boxstyle="round,pad=0.3"),
path_effects=[path_effects.withStroke(linewidth=1.5, foreground="black")])
# Add bounding box
ax.add_patch(plt.Rectangle((x1, y1), x2-x1, y2-y1,
fill=False, edgecolor=box_color[:3], linewidth=2))
ax.axis('off')
# ax.set_title('Detection Result')
plt.tight_layout()
if return_pil:
# Convert plot to PIL Image
buf = io.BytesIO()
fig.savefig(buf, format='png', bbox_inches='tight', pad_inches=0)
buf.seek(0)
pil_img = Image.open(buf)
plt.close(fig)
return pil_img
else:
plt.show()
return None
@staticmethod
def create_summary(result: Any) -> Dict:
"""
Create a summary of detection results
Args:
result: Detection result object
Returns:
Dictionary with detection summary statistics
"""
if result is None:
return {"error": "No detection result provided"}
# Get classes and confidences
classes = result.boxes.cls.cpu().numpy().astype(int)
confidences = result.boxes.conf.cpu().numpy()
names = result.names
# Count detections by class
class_counts = {}
for cls, conf in zip(classes, confidences):
cls_name = names[int(cls)]
if cls_name not in class_counts:
class_counts[cls_name] = {"count": 0, "confidences": []}
class_counts[cls_name]["count"] += 1
class_counts[cls_name]["confidences"].append(float(conf))
# Calculate average confidence for each class
for cls_name, stats in class_counts.items():
if stats["confidences"]:
stats["average_confidence"] = float(np.mean(stats["confidences"]))
stats.pop("confidences") # Remove detailed confidences list to keep summary concise
# Prepare summary
summary = {
"total_objects": len(classes),
"class_counts": class_counts,
"unique_classes": len(class_counts)
}
return summary
|