using OpenCvSharp; using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using CircleDetectionApi.Helpers; using Microsoft.AspNetCore.Mvc; namespace CircleDetectionApi.Controllers { [ApiController] [Route("api/[controller]")] public class CircleDetectionController : ControllerBase { private readonly IWebHostEnvironment _environment; private readonly VehicleDetectionHelper _vehicleDetector; public CircleDetectionController(IWebHostEnvironment environment) { _environment = environment; // Caminho para os arquivos do modelo YOLO var modelPath = Path.Combine(environment.ContentRootPath, "Models", "yolov4.weights"); var configPath = Path.Combine(environment.ContentRootPath, "Models", "yolov4.cfg"); var classesPath = Path.Combine(environment.ContentRootPath, "Models", "coco.names"); // Inicializar o detector de veículos _vehicleDetector = new VehicleDetectionHelper(modelPath, configPath, classesPath); } [HttpPost] public async Task DetectCircles(IFormFile image, bool detectVehicle) { // Verificar se a imagem foi enviada if (image == null || image.Length == 0) { return BadRequest(new { error = "Nenhuma imagem foi enviada" }); } // Verificar o tipo do arquivo var allowedExtensions = new[] { ".jpg", ".jpeg", ".png", ".bmp", ".tiff" }; var extension = Path.GetExtension(image.FileName).ToLowerInvariant(); if (!allowedExtensions.Contains(extension)) { return BadRequest(new { error = "Tipo de arquivo não suportado. Por favor, envie uma imagem JPG, PNG, BMP ou TIFF." }); } try { // Ler a imagem em um MemoryStream using var memoryStream = new MemoryStream(); await image.CopyToAsync(memoryStream); memoryStream.Position = 0; // Carregar a imagem usando OpenCvSharp using var mat = Mat.FromImageData(memoryStream.ToArray(), ImreadModes.Color); // Verificar se a detecção de veículos está habilitada //bool detectVehicle = HttpContext.Request.Query.ContainsKey("detectVehicle") && // bool.TryParse(HttpContext.Request.Query["detectVehicle"], out var detectValue) && // detectValue; // Variáveis para armazenar informações sobre o veículo bool isVehicle = false; string vehicleClass = "Não detectado"; Rect vehicleBox = new Rect(); // Detectar veículo (se solicitado) if (detectVehicle) { (isVehicle, vehicleClass, vehicleBox) = _vehicleDetector.DetectVehicle(mat); } // Converter para escala de cinza using var grayMat = new Mat(); Cv2.CvtColor(mat, grayMat, ColorConversionCodes.BGR2GRAY); // Aplicar blur para reduzir ruído using var blurredMat = new Mat(); Cv2.MedianBlur(grayMat, blurredMat, 5); // Usar o algoritmo de Hough Circle para detectar círculos CircleSegment[] circles = Cv2.HoughCircles( blurredMat, HoughModes.Gradient, 1, // Razão entre resolução da imagem e acumulador 240, // Distância mínima entre centros de círculos detectados param1: 250, // Limite superior para detector de bordas Canny param2: 50, // Limite para detecção de centros minRadius: 5, // Raio mínimo do círculo maxRadius: 200 // Raio máximo do círculo ); // Criar lista para armazenar os círculos detectados var detectedCircles = new List(); // Criar uma cópia colorida da imagem para desenhar os círculos using var colorMat = mat.Clone(); // Simplesmente clonar a imagem original // Variável para armazenar contagem de rodas int wheelCount = 0; int estimatedAxles = 0; // Adicionar a variável estimatedAxles if (circles != null && circles.Length > 0) { foreach (var circle in circles) { // Adicionar à lista de resultados detectedCircles.Add(new { center_x = (int)circle.Center.X, center_y = (int)circle.Center.Y, radius = (int)circle.Radius }); // Desenhar o círculo (como no código Python) Cv2.Circle(colorMat, (int)circle.Center.X, (int)circle.Center.Y, (int)circle.Radius, new Scalar(0, 255, 0), 2); // Desenhar o centro do círculo (como no código Python) Cv2.Circle(colorMat, (int)circle.Center.X, (int)circle.Center.Y, 2, new Scalar(0, 0, 255), 1); } // Atualizar contagem de rodas wheelCount = detectedCircles.Count; // Não precisamos mais deste bloco, pois já temos a classe do veículo } // Se o parâmetro saveImage for true, salve a imagem marcada e retorne o URL bool saveImage = HttpContext.Request.Query.ContainsKey("saveImage") && bool.TryParse(HttpContext.Request.Query["saveImage"], out var saveValue) && saveValue; string imageUrl = null; if (saveImage) { // Criar diretório para imagens se não existir var imagesDir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images"); Directory.CreateDirectory(imagesDir); // Salvar a imagem com os círculos marcados var filename = $"circles_{DateTime.Now:yyyyMMddHHmmss}.jpg"; var filePath = Path.Combine(imagesDir, filename); Cv2.ImWrite(filePath, colorMat); // Gerar URL para a imagem imageUrl = $"{Request.Scheme}://{Request.Host}/images/{filename}"; } // Desenhar a caixa delimitadora do veículo, se detectado if (detectVehicle && isVehicle) { _vehicleDetector.DrawVehicleBox(colorMat, vehicleBox, vehicleClass); } // Determinar o tipo de veículo baseado na classe detectada e no número de rodas string vehicleType = GetVehicleTypeFromClassAndWheels(vehicleClass, wheelCount); // Se não detectou nenhuma roda mas identificou um caminhão, estimar um número padrão if (wheelCount == 0 && vehicleClass.ToLower() == "truck") { wheelCount = 4; // Valor padrão conservador para caminhões estimatedAxles = 2; } // Retornar o resultado como JSON com informações detalhadas return Ok(new { count = wheelCount, estimatedWheels = wheelCount, estimatedAxles = estimatedAxles, circles = detectedCircles, imageUrl = imageUrl, vehicleInfo = detectVehicle ? new { isVehicle = isVehicle, detectedClass = vehicleClass, suggestedType = vehicleType, wheelCount = wheelCount, axleCount = estimatedAxles } : null }); } catch (Exception ex) { return StatusCode(500, new { error = $"Erro ao processar a imagem: {ex.Message}" }); } } [HttpGet("file")] public IActionResult DetectCirclesFromFile([FromQuery] string filename) { if (string.IsNullOrEmpty(filename)) { return BadRequest(new { error = "Nome do arquivo não especificado" }); } // Obter o caminho do arquivo var imagesPath = Path.Combine(_environment.ContentRootPath, "Images"); var filePath = Path.Combine(imagesPath, filename); // Verificar se o arquivo existe if (!System.IO.File.Exists(filePath)) { return NotFound(new { error = $"Arquivo {filename} não encontrado" }); } try { // Carregar a imagem usando OpenCvSharp using var mat = Cv2.ImRead(filePath, ImreadModes.Color); // Verificar se a detecção de veículos está habilitada bool detectVehicle = HttpContext.Request.Query.ContainsKey("detectVehicle") && bool.TryParse(HttpContext.Request.Query["detectVehicle"], out var detectValue) && detectValue; // Variáveis para armazenar informações sobre o veículo bool isVehicle = false; string vehicleClass = "Não detectado"; Rect vehicleBox = new Rect(); // Detectar veículo (se solicitado) if (detectVehicle) { (isVehicle, vehicleClass, vehicleBox) = _vehicleDetector.DetectVehicle(mat); } // Converter para escala de cinza using var grayMat = new Mat(); Cv2.CvtColor(mat, grayMat, ColorConversionCodes.BGR2GRAY); // Aplicar blur para reduzir ruído (medianBlur como no código original) using var blurredMat = new Mat(); Cv2.MedianBlur(grayMat, blurredMat, 5); // Usar o algoritmo de Hough Circle para detectar círculos CircleSegment[] circles = Cv2.HoughCircles( blurredMat, HoughModes.Gradient, 1, // Razão entre resolução da imagem e acumulador 240, // Distância mínima entre centros param1: 250, // Limite superior para detector de bordas Canny param2: 50, // Limite para detecção de centros minRadius: 5, // Raio mínimo do círculo maxRadius: 200 // Raio máximo do círculo ); // Preparar o resultado var detectedCircles = new List(); // Criar uma cópia colorida da imagem para desenhar os círculos using var colorMat = new Mat(); Cv2.CvtColor(blurredMat, colorMat, ColorConversionCodes.GRAY2BGR); // Variável para armazenar contagem de rodas int wheelCount = 0; // Processar os círculos detectados if (circles != null && circles.Length > 0) { foreach (var circle in circles) { // Adicionar à lista de resultados detectedCircles.Add(new { center_x = (int)circle.Center.X, center_y = (int)circle.Center.Y, radius = (int)circle.Radius }); // Desenhar o círculo Cv2.Circle(colorMat, (int)circle.Center.X, (int)circle.Center.Y, (int)circle.Radius, new Scalar(0, 255, 0), 2); // Desenhar o centro do círculo Cv2.Circle(colorMat, (int)circle.Center.X, (int)circle.Center.Y, 2, new Scalar(0, 0, 255), 1); } } // Atualizar contagem de rodas wheelCount = detectedCircles.Count; // Se o número de rodas detectadas for ímpar, provavelmente estamos perdendo uma roda // em um eixo, então arredondar para o próximo número par para veículos com mais de 2 rodas if (wheelCount > 2 && wheelCount % 2 == 1) { wheelCount++; } // Tentar estimar o número de eixos (geralmente 2 rodas = 1 eixo) var estimatedAxles = (int)Math.Ceiling(wheelCount / 2.0); // Se o parâmetro saveImage for true, salve a imagem marcada e retorne o URL bool saveImage = HttpContext.Request.Query.ContainsKey("saveImage") && bool.TryParse(HttpContext.Request.Query["saveImage"], out var saveValue) && saveValue; string imageUrl = null; if (saveImage) { // Criar diretório para imagens se não existir var imagesDir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "images"); Directory.CreateDirectory(imagesDir); // Salvar a imagem com os círculos marcados var outputFilename = $"circles_{Path.GetFileNameWithoutExtension(filename)}_{DateTime.Now:yyyyMMddHHmmss}.jpg"; var outputPath = Path.Combine(imagesDir, outputFilename); Cv2.ImWrite(outputPath, colorMat); // Gerar URL para a imagem imageUrl = $"{Request.Scheme}://{Request.Host}/images/{outputFilename}"; } // Desenhar a caixa delimitadora do veículo, se detectado if (detectVehicle && isVehicle) { _vehicleDetector.DrawVehicleBox(colorMat, vehicleBox, vehicleClass); } // Determinar o tipo de veículo baseado na classe detectada e no número de rodas string vehicleType = GetVehicleTypeFromClassAndWheels(vehicleClass, wheelCount); // Retornar o resultado como JSON return Ok(new { count = detectedCircles.Count, circles = detectedCircles, imageUrl = imageUrl, vehicleInfo = detectVehicle ? new { isVehicle = isVehicle, detectedClass = vehicleClass, suggestedType = vehicleType, wheelCount = wheelCount } : null }); } catch (Exception ex) { return StatusCode(500, new { error = $"Erro ao processar a imagem: {ex.Message}" }); } } // Método para determinar o tipo de veículo com base na classe detectada e no número de rodas private string GetVehicleTypeFromClassAndWheels(string vehicleClass, int wheelCount) { if (string.IsNullOrEmpty(vehicleClass) || vehicleClass == "Não detectado") return "Veículo não identificado"; // Combinamos a classe detectada com o número de rodas para uma classificação mais precisa switch (vehicleClass.ToLower()) { case "car": if (wheelCount <= 0) return "Carro (rodas não detectadas)"; if (wheelCount <= 4) return "Carro compacto/Sedan"; return "SUV/Crossover"; case "truck": if (wheelCount <= 0) return "Caminhão (rodas não detectadas)"; if (wheelCount <= 4) return "Pickup"; if (wheelCount <= 6) return "Caminhão pequeno"; if (wheelCount <= 10) return "Caminhão médio"; return "Caminhão grande/Carreta"; case "bus": return "Ônibus"; case "bicycle": return "Bicicleta"; case "motorbike": if (wheelCount == 3) return "Triciclo/Sidecar"; return "Motocicleta"; default: // Se detectamos pelo menos 4 rodas mas a classe não é clara, provavelmente é um caminhão if (wheelCount >= 4) return "Provável caminhão com " + wheelCount + " rodas"; if (wheelCount <= 0) return $"{vehicleClass} (rodas não detectadas)"; return $"{vehicleClass} com {wheelCount} rodas"; } } } }