389 lines
16 KiB
C#
389 lines
16 KiB
C#
using Microsoft.AspNetCore.Mvc;
|
|
using OpenCvSharp;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Threading.Tasks;
|
|
using CircleDetectionApi.Helpers;
|
|
|
|
namespace CircleDetectionApi.Controllers
|
|
{
|
|
[ApiController]
|
|
[Route("api/[controller]")]
|
|
public class Circle1DetectionController : ControllerBase
|
|
{
|
|
private readonly IWebHostEnvironment _environment;
|
|
private readonly VehicleDetectionHelper _vehicleDetector;
|
|
|
|
public Circle1DetectionController(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<IActionResult> 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);
|
|
|
|
// 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
|
|
);
|
|
|
|
// Também tentar o método de detecção avançado
|
|
var wheelDetections = VehicleDetectionHelper.DetectWheelsAndAxles(mat);
|
|
|
|
// Criar lista para armazenar os círculos detectados
|
|
var detectedCircles = new List<object>();
|
|
|
|
// 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 (4 ou 6)
|
|
if (wheelCount == 0 && vehicleClass.ToLower() == "truck")
|
|
{
|
|
wheelCount = 4; // Valor padrão conservador
|
|
}
|
|
|
|
// Retornar o resultado como JSON
|
|
return Ok(new
|
|
{
|
|
count = detectedCircles.Count,
|
|
estimatedWheels = wheelCount,
|
|
estimatedAxles = estimatedAxles,
|
|
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}" });
|
|
}
|
|
}
|
|
|
|
[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<object>();
|
|
|
|
// 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;
|
|
|
|
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;
|
|
|
|
// 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 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:
|
|
if (wheelCount <= 0) return $"{vehicleClass} (rodas não detectadas)";
|
|
return $"{vehicleClass} com {wheelCount} rodas";
|
|
}
|
|
}
|
|
}
|
|
} |