#!/usr/bin/env python3 """ Analizador de capturas de pantalla del canvas con QR codes de referencia Lee QR codes de las capturas para detectar problemas de resolución y posicionamiento. Uso: python analyze_canvas_screenshot.py """ import sys import json from PIL import Image import cv2 import numpy as np from pyzbar import pyzbar import argparse from datetime import datetime def analyze_qr_codes(image_path): """Analiza los QR codes en una imagen y extrae información de posición""" try: # Cargar imagen image = Image.open(image_path) image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR) # Detectar QR codes qr_codes = pyzbar.decode(image_cv) if not qr_codes: return { "status": "error", "message": "No se encontraron QR codes en la imagen", } results = { "status": "success", "image_path": image_path, "image_size": image.size, "analysis_timestamp": datetime.now().isoformat(), "qr_codes_found": len(qr_codes), "qr_data": [], } print(f"📸 Analizando imagen: {image_path}") print(f"📐 Tamaño de imagen: {image.size[0]} × {image.size[1]} píxeles") print(f"🔍 QR codes encontrados: {len(qr_codes)}") print() for i, qr in enumerate(qr_codes): try: # Decodificar datos JSON del QR qr_json = json.loads(qr.data.decode("utf-8")) # Información de posición del QR en la imagen qr_rect = qr.rect qr_center_pixels = ( qr_rect.left + qr_rect.width // 2, qr_rect.top + qr_rect.height // 2, ) # Información del QR qr_info = { "index": i + 1, "label": qr_json.get("label", "Unknown"), "expected_position_meters": qr_json.get("position", [0, 0]), "found_position_pixels": qr_center_pixels, "qr_size_pixels": (qr_rect.width, qr_rect.height), "original_data": qr_json, } results["qr_data"].append(qr_info) print(f"🎯 QR #{i+1}: {qr_info['label']}") print( f" 📍 Posición esperada: {qr_info['expected_position_meters']} metros" ) print(f" 📱 Encontrado en píxeles: {qr_center_pixels}") print(f" 📏 Tamaño QR: {qr_rect.width} × {qr_rect.height} px") # Calcular escala si tenemos la información original if "pixels_per_meter" in qr_json: original_ppm = qr_json["pixels_per_meter"] expected_canvas_size = qr_json.get("canvas_size", [78.31, 54.53]) # Calcular escala actual basada en el canvas if image.size[0] > 0 and expected_canvas_size[0] > 0: current_ppm = image.size[0] / expected_canvas_size[0] scale_factor = current_ppm / original_ppm print(f" 📊 Escala original: {original_ppm:.1f} px/m") print(f" 📊 Escala actual: {current_ppm:.1f} px/m") print(f" 📊 Factor de escala: {scale_factor:.3f}") qr_info["scale_analysis"] = { "original_pixels_per_meter": original_ppm, "current_pixels_per_meter": current_ppm, "scale_factor": scale_factor, } print() except (json.JSONDecodeError, KeyError) as e: print(f"❌ Error decodificando QR #{i+1}: {e}") continue # Análisis de distribución espacial if len(results["qr_data"]) >= 2: print("📏 Análisis de distribución espacial:") analyze_spatial_distribution(results["qr_data"], results) return results except Exception as e: return {"status": "error", "message": f"Error analizando imagen: {e}"} def analyze_spatial_distribution(qr_data, results): """Analiza la distribución espacial de los QR codes para detectar distorsiones""" # Buscar QR codes de esquinas conocidas para análisis de distorsión corners = {} for qr in qr_data: label = qr["label"] if label in ["TL", "TR", "BL", "BR"]: # Top-Left, Top-Right, etc. corners[label] = qr if len(corners) >= 2: print(" 🔍 Detectando distorsión basada en esquinas...") # Calcular distancias esperadas vs reales if "TL" in corners and "TR" in corners: tl_pos = corners["TL"]["found_position_pixels"] tr_pos = corners["TR"]["found_position_pixels"] width_pixels = abs(tr_pos[0] - tl_pos[0]) # Comparar con ancho esperado del canvas if corners["TL"]["original_data"].get("canvas_size"): expected_width_meters = corners["TL"]["original_data"]["canvas_size"][0] current_ppm = width_pixels / expected_width_meters print( f" 📐 Ancho detectado: {width_pixels} px = {expected_width_meters}m" ) print(f" 📊 Resolución horizontal: {current_ppm:.1f} px/m") if "TL" in corners and "BL" in corners: tl_pos = corners["TL"]["found_position_pixels"] bl_pos = corners["BL"]["found_position_pixels"] height_pixels = abs(bl_pos[1] - tl_pos[1]) if corners["TL"]["original_data"].get("canvas_size"): expected_height_meters = corners["TL"]["original_data"]["canvas_size"][ 1 ] current_ppm = height_pixels / expected_height_meters print( f" 📐 Alto detectado: {height_pixels} px = {expected_height_meters}m" ) print(f" 📊 Resolución vertical: {current_ppm:.1f} px/m") # Verificar área de objetos si existe el QR "OBJECTS" objects_qr = next((qr for qr in qr_data if qr["label"] == "OBJECTS"), None) if objects_qr: obj_pos = objects_qr["found_position_pixels"] print(f" 🎯 Área de objetos detectada en: {obj_pos} px") # Calcular si está en la posición esperada relativa expected_pos = objects_qr["expected_position_meters"] print(f" 🎯 Posición esperada: {expected_pos} metros") def main(): """Función principal""" parser = argparse.ArgumentParser( description="Analiza capturas del canvas con QR de referencia" ) parser.add_argument("image", help="Ruta a la imagen de captura a analizar") parser.add_argument("--output", "-o", help="Archivo JSON de salida para resultados") parser.add_argument("--verbose", "-v", action="store_true", help="Salida detallada") args = parser.parse_args() # Verificar que existe la imagen try: with open(args.image, "rb"): pass except FileNotFoundError: print(f"❌ Error: No se encontró la imagen '{args.image}'") return 1 # Analizar imagen results = analyze_qr_codes(args.image) if results["status"] == "error": print(f"❌ {results['message']}") return 1 # Mostrar resumen print("=" * 60) print("📋 RESUMEN DEL ANÁLISIS:") print(f"✅ QR codes válidos encontrados: {results['qr_codes_found']}") if results["qr_data"]: labels_found = [qr["label"] for qr in results["qr_data"]] print(f"🏷️ Etiquetas detectadas: {', '.join(labels_found)}") # Guardar resultados si se especifica if args.output: try: with open(args.output, "w") as f: json.dump(results, f, indent=2) print(f"💾 Resultados guardados en: {args.output}") except Exception as e: print(f"❌ Error guardando resultados: {e}") print("✅ Análisis completado") return 0 if __name__ == "__main__": exit(main())