Questo tipo di tag e' utilizzato per oggetti in movimento che si possono allontanare od avvicinare troppo alla camera rendendo inutile un singolo tag..per questo vengono utlizzati tag piramidali
Generatore (si il codice e' stato completamente generato da Claude.ai)
import cv2
import numpy as np
from matplotlib import pyplot as plt
class FractalArucoGenerator:
"""
Generate fractal ArUco markers where the marker contains smaller
versions of itself recursively.
"""
def __init__(self, dictionary=cv2.aruco.DICT_6X6_250):
self.aruco_dict = cv2.aruco.getPredefinedDictionary(dictionary)
def generate_base_marker(self, marker_id, size):
"""Generate a basic ArUco marker."""
marker_img = cv2.aruco.generateImageMarker(self.aruco_dict, marker_id, size)
return marker_img
def create_fractal_marker(self, marker_id, base_size, levels=2, scale_factor=0.25):
"""
Create a fractal ArUco marker with recursive smaller versions.
Args:
marker_id: ArUco marker ID
base_size: Size of the base marker in pixels
levels: Number of fractal levels (recursion depth)
scale_factor: Size reduction factor for each level
"""
# Generate base marker
marker = self.generate_base_marker(marker_id, base_size)
if levels <= 1:
return marker
# Create canvas with white border
border = int(base_size * 0.1)
canvas_size = base_size + 2 * border
canvas = np.ones((canvas_size, canvas_size), dtype=np.uint8) * 255
# Place main marker in center
canvas[border:border+base_size, border:border+base_size] = marker
# Add smaller fractal markers in corners
small_size = int(base_size * scale_factor)
small_marker = self.create_fractal_marker(marker_id, small_size, levels-1, scale_factor)
# Calculate positions for corner markers
positions = [
(border//4, border//4), # Top-left
(canvas_size - border//4 - small_marker.shape[1], border//4), # Top-right
(border//4, canvas_size - border//4 - small_marker.shape[0]), # Bottom-left
(canvas_size - border//4 - small_marker.shape[1],
canvas_size - border//4 - small_marker.shape[0]) # Bottom-right
]
# Place smaller markers in corners
for y, x in positions:
h, w = small_marker.shape
if y + h <= canvas_size and x + w <= canvas_size:
canvas[y:y+h, x:x+w] = small_marker
return canvas
def save_printable(self, marker_img, filename, physical_size_mm=100, dpi=300):
"""
Save marker as printable PDF/PNG with exact physical dimensions.
Args:
marker_img: The marker image
filename: Output filename
physical_size_mm: Physical size in millimeters
dpi: Dots per inch for printing
"""
# Calculate required pixel size for exact physical dimensions
inches = physical_size_mm / 25.4
pixel_size = int(inches * dpi)
# Resize marker to exact pixel dimensions
marker_resized = cv2.resize(marker_img, (pixel_size, pixel_size),
interpolation=cv2.INTER_NEAREST)
# Save as high-quality PNG
cv2.imwrite(filename, marker_resized)
# Also create a figure for PDF output
fig, ax = plt.subplots(figsize=(inches, inches))
ax.imshow(marker_resized, cmap='gray', interpolation='nearest')
ax.axis('off')
plt.subplots_adjust(left=0, right=1, top=1, bottom=0)
pdf_filename = filename.rsplit('.', 1)[0] + '.pdf'
plt.savefig(pdf_filename, dpi=dpi, bbox_inches='tight', pad_inches=0)
plt.close()
return marker_resized, pixel_size
def main():
"""Generate and save fractal ArUco marker ID=0, 100mm size."""
generator = FractalArucoGenerator(dictionary=cv2.aruco.DICT_6X6_250)
# Generate fractal marker (ID=0)
print("Generating fractal ArUco marker ID=0...")
# Create fractal marker with 3 levels
base_size = 400 # Base resolution for generation
fractal_marker = generator.create_fractal_marker(
marker_id=0,
base_size=base_size,
levels=3, # 3 levels of recursion
scale_factor=0.2 # Each level is 20% the size of previous
)
# Save as printable with exact 100mm physical size
print("Saving printable versions (100mm x 100mm)...")
marker_resized, pixel_size = generator.save_printable(
fractal_marker,
'aruco_fractal_id0_100mm.png',
physical_size_mm=100,
dpi=300
)
print(f"✓ Saved: aruco_fractal_id0_100mm.png ({pixel_size}x{pixel_size} pixels)")
print(f"✓ Saved: aruco_fractal_id0_100mm.pdf")
print(f" Physical size: 100mm x 100mm at 300 DPI")
print(f" Ready for printing!")
# Also save a simple version without fractals for comparison
simple_marker = generator.generate_base_marker(0, base_size)
generator.save_printable(
simple_marker,
'aruco_simple_id0_100mm.png',
physical_size_mm=100,
dpi=300
)
print(f"✓ Saved: aruco_simple_id0_100mm.png (non-fractal version)")
# Display preview
plt.figure(figsize=(12, 6))
plt.subplot(1, 2, 1)
plt.imshow(fractal_marker, cmap='gray', interpolation='nearest')
plt.title('Fractal ArUco ID=0\n(100mm @ 300 DPI)')
plt.axis('off')
plt.subplot(1, 2, 2)
plt.imshow(simple_marker, cmap='gray', interpolation='nearest')
plt.title('Simple ArUco ID=0\n(100mm @ 300 DPI)')
plt.axis('off')
plt.tight_layout()
plt.savefig('aruco_comparison.png', dpi=150, bbox_inches='tight')
print("✓ Saved: aruco_comparison.png (preview)")
plt.show()
print("\n" + "="*50)
print("PRINTING INSTRUCTIONS:")
print("="*50)
print("1. Open aruco_fractal_id0_100mm.pdf")
print("2. Print at 100% scale (do NOT scale to fit)")
print("3. Verify the printed size is exactly 100mm x 100mm")
print("4. Use thick white paper for best results")
print("="*50)
if __name__ == "__main__":
main()
Lettore
import cv2
import numpy as np
from typing import List, Tuple, Dict
import time
class FractalArucoDetector:
"""
A fractal/hierarchical ArUco marker detector that searches for markers
at multiple scales using a pyramid approach for robust detection.
"""
def __init__(self,
dictionary=cv2.aruco.DICT_6X6_250,
num_scales=4,
scale_factor=0.7,
min_marker_perimeter=20):
"""
Initialize the fractal ArUco detector.
Args:
dictionary: ArUco dictionary to use
num_scales: Number of scales in the pyramid
scale_factor: Scale reduction factor between levels
min_marker_perimeter: Minimum marker perimeter to consider
"""
self.aruco_dict = cv2.aruco.getPredefinedDictionary(dictionary)
self.aruco_params = cv2.aruco.DetectorParameters()
self.detector = cv2.aruco.ArucoDetector(self.aruco_dict, self.aruco_params)
self.num_scales = num_scales
self.scale_factor = scale_factor
self.min_marker_perimeter = min_marker_perimeter
def build_gaussian_pyramid(self, image: np.ndarray) -> List[np.ndarray]:
"""Build a Gaussian pyramid of the image."""
pyramid = [image]
current = image.copy()
for i in range(1, self.num_scales):
scale = self.scale_factor ** i
width = int(image.shape[1] * scale)
height = int(image.shape[0] * scale)
if width < 50 or height < 50: # Stop if too small
break
current = cv2.resize(image, (width, height),
interpolation=cv2.INTER_LINEAR)
pyramid.append(current)
return pyramid
def detect_at_scale(self,
image: np.ndarray,
scale: float) -> Tuple[List, List, List]:
"""
Detect ArUco markers at a specific scale.
Returns:
corners, ids, rejected candidates
"""
corners, ids, rejected = self.detector.detectMarkers(image)
# Scale corners back to original image coordinates
if corners is not None and len(corners) > 0:
scaled_corners = []
for corner in corners:
scaled_corner = corner / scale
scaled_corners.append(scaled_corner)
return scaled_corners, ids, rejected
return [], [], []
def merge_detections(self,
all_detections: List[Dict]) -> Tuple[List, List]:
"""
Merge detections from multiple scales, removing duplicates.
Uses spatial proximity to identify duplicate detections.
"""
if not all_detections:
return [], []
merged_corners = []
merged_ids = []
# Flatten all detections
for detection in all_detections:
corners = detection['corners']
ids = detection['ids']
if ids is not None and len(ids) > 0:
for corner, marker_id in zip(corners, ids):
merged_corners.append(corner)
merged_ids.append(marker_id[0])
if not merged_corners:
return [], []
# Remove duplicates based on spatial proximity
unique_corners = []
unique_ids = []
for i, (corner, marker_id) in enumerate(zip(merged_corners, merged_ids)):
is_duplicate = False
center = np.mean(corner[0], axis=0)
for j, existing_corner in enumerate(unique_corners):
if unique_ids[j] == marker_id:
existing_center = np.mean(existing_corner[0], axis=0)
distance = np.linalg.norm(center - existing_center)
# If centers are close, it's a duplicate
if distance < 50: # threshold in pixels
is_duplicate = True
break
if not is_duplicate:
unique_corners.append(corner)
unique_ids.append(marker_id)
return unique_corners, np.array(unique_ids).reshape(-1, 1)
def detect_markers(self,
image: np.ndarray,
visualize=False) -> Tuple[List, List, np.ndarray]:
"""
Detect ArUco markers using fractal/multi-scale approach.
Args:
image: Input image (color or grayscale)
visualize: If True, return annotated image
Returns:
corners, ids, annotated_image (if visualize=True)
"""
# Convert to grayscale if needed
if len(image.shape) == 3:
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
else:
gray = image
# Build pyramid
pyramid = self.build_gaussian_pyramid(gray)
# Detect at each scale
all_detections = []
for level, scaled_image in enumerate(pyramid):
scale = self.scale_factor ** level
corners, ids, rejected = self.detect_at_scale(scaled_image, scale)
if ids is not None and len(ids) > 0:
all_detections.append({
'corners': corners,
'ids': ids,
'scale': scale,
'level': level
})
# Merge detections from all scales
final_corners, final_ids = self.merge_detections(all_detections)
# Create visualization if requested
annotated = None
if visualize:
annotated = image.copy() if len(image.shape) == 3 else cv2.cvtColor(image, cv2.COLOR_GRAY2BGR)
if final_corners and final_ids is not None:
cv2.aruco.drawDetectedMarkers(annotated, final_corners, final_ids)
# Add scale information
for corner, marker_id in zip(final_corners, final_ids):
center = np.mean(corner[0], axis=0).astype(int)
cv2.putText(annotated, f"ID: {marker_id[0]}",
tuple(center), cv2.FONT_HERSHEY_SIMPLEX,
0.6, (0, 255, 0), 2)
return final_corners, final_ids, annotated
# Example usage
def main():
"""Example: Detect ArUco markers from webcam or image."""
# Initialize detector
detector = FractalArucoDetector(
dictionary=cv2.aruco.DICT_6X6_250,
num_scales=5,
scale_factor=0.75
)
# Option 1: Use webcam
cap = cv2.VideoCapture(0)
print("Press 'q' to quit, 's' to save detection")
while True:
ret, frame = cap.read()
if not ret:
break
# Detect markers
start_time = time.time()
corners, ids, annotated = detector.detect_markers(frame, visualize=True)
detection_time = (time.time() - start_time) * 1000
# Display results
if annotated is not None:
cv2.putText(annotated, f"Detection time: {detection_time:.1f}ms",
(10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
if ids is not None:
cv2.putText(annotated, f"Markers found: {len(ids)}",
(10, 60), cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)
cv2.imshow('Fractal ArUco Detection', annotated)
key = cv2.waitKey(1) & 0xFF
if key == ord('q'):
break
elif key == ord('s') and annotated is not None:
cv2.imwrite('aruco_detection.png', annotated)
print("Detection saved!")
cap.release()
cv2.destroyAllWindows()
# Option 2: Use static image
# image = cv2.imread('your_image.jpg')
# corners, ids, annotated = detector.detect_markers(image, visualize=True)
# cv2.imshow('Detection', annotated)
# cv2.waitKey(0)
# cv2.destroyAllWindows()
if __name__ == "__main__":
main()

Nessun commento:
Posta un commento