CtrEditor/ObjetosSim/Extraccion Datos/ucBuscarCoincidencias.xaml.cs

446 lines
17 KiB
C#
Raw Normal View History

using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.Simulacion;
using System.IO;
using System.Windows.Media.Imaging;
using Tesseract;
using Emgu.CV.CvEnum;
using Emgu.CV.Structure;
using Emgu.CV;
using System.Drawing;
using Image = System.Windows.Controls.Image;
using Point = System.Drawing.Point;
using Rectangle = System.Windows.Shapes.Rectangle;
using Size = System.Drawing.Size;
using Ookii.Dialogs.Wpf;
using Rect = System.Windows.Rect;
using System.ComponentModel;
using Newtonsoft.Json;
2024-06-10 06:07:25 -03:00
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
using System.ComponentModel;
namespace CtrEditor.ObjetosSim.Extraccion_Datos
{
/// <summary>
/// Interaction logic for ucBuscarCoincidencias.xaml
/// </summary>
///
public partial class osBuscarCoincidencias : osBase, IosBase
{
[JsonIgnore]
public float offsetY;
[JsonIgnore]
public float offsetX;
[ObservableProperty]
List<Rect> search_rectangles;
public static string NombreClase()
{
return "Search Templates";
}
private string nombre = NombreClase();
public override string Nombre
{
get => nombre;
set => SetProperty(ref nombre, value);
}
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
bool search_templates;
partial void OnSearch_templatesChanged(bool oldValue, bool newValue)
{
if (Search_templates)
BuscarCoincidencias();
Search_templates = false;
}
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
bool export_ocr;
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
string text_export_ocr;
partial void OnExport_ocrChanged(bool value)
{
if (Export_ocr)
{
Text_export_ocr = "";
if (!string.IsNullOrEmpty(Nombre) && _mainViewModel != null)
{
foreach (var objetoSimulable in _mainViewModel.ObjetosSimulables)
{
if (objetoSimulable != this && objetoSimulable.Group_Panel == Nombre)
{
if (objetoSimulable is osExtraccionTag osExtraccionTag)
{
osExtraccionTag.CaptureImageAreaAndDoOCR();
Text_export_ocr += osExtraccionTag.Tag_extract;
}
}
}
}
}
Export_ocr = false;
}
public override void TopChanging(float oldValue, float newValue)
{
offsetY = newValue - oldValue;
}
public override void LeftChanging(float oldValue, float newValue)
{
offsetX = newValue - oldValue;
}
[ObservableProperty]
[property: Description("Width of the object.")]
2024-06-10 06:07:25 -03:00
[property: Category("Layout:")]
public float ancho;
[ObservableProperty]
[property: Description("Height of the object.")]
2024-06-10 06:07:25 -03:00
[property: Category("Layout:")]
public float alto;
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Layout:")]
public float angulo;
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
string tag_extract;
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
string clase;
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
string tag_name;
[ObservableProperty]
float opacity_oculto;
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
bool show_debug_ocr;
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
float threshold;
[ObservableProperty]
2024-06-10 06:07:25 -03:00
[property: Category("Tag Extraction:")]
[property: ReadOnly(true)]
float coincidencias;
public osBuscarCoincidencias()
{
Ancho = 1;
Alto = 1;
Angulo = 0;
Opacity_oculto = 0.1f;
Threshold = 0.6f;
}
private void ShowPreviewWindow(Stream imageStream)
{
// Create a new window for preview
Window previewWindow = new Window
{
Title = "Preview Captured Image",
Width = 500,
Height = 500,
Content = new Image
{
Source = BitmapFrame.Create(imageStream, BitmapCreateOptions.None, BitmapCacheOption.OnLoad),
Stretch = Stretch.Uniform
}
};
previewWindow.ShowDialog();
}
private async void BuscarCoincidencias()
{
var progressDialog = new ProgressDialog
{
WindowTitle = "Procesando",
Text = "Buscando coincidencias...",
ShowTimeRemaining = true,
ShowCancelButton = false
};
progressDialog.DoWork += (sender, e) => BuscarCoincidenciasAsync(progressDialog);
progressDialog.RunWorkerCompleted += (sender, e) =>
{
if (e.Error != null)
{
MessageBox.Show(e.Error.Message, "Error", MessageBoxButton.OK, MessageBoxImage.Error);
}
};
progressDialog.Show();
}
private void BuscarCoincidenciasAsync(ProgressDialog progressDialog)
{
// Reset the Canvas children
Application.Current.Dispatcher.Invoke(() =>
{
var clearShapes = _mainViewModel.MainCanvas.Children.OfType<System.Windows.Shapes.Shape>().Where(s => s.Tag as string == "BuscarCoincidencias").ToList();
foreach (var shape in clearShapes)
{
_mainViewModel.MainCanvas.Children.Remove(shape);
}
if (_mainViewModel?.MainCanvas.Children[0] is Image imagenDeFondo)
{
// Asegurarse de que la imagen origen está disponible
if (imagenDeFondo.Source is BitmapSource bitmapSource)
{
progressDialog.ReportProgress(10);
// Obtener los DPI de la imagen original
float originalDpiX = (float)bitmapSource.DpiX;
float originalDpiY = (float)bitmapSource.DpiY;
// Estándar DPI en el que el Canvas renderiza la imagen
float canvasDpiX = 96; // WPF usually renders at 96 DPI
float canvasDpiY = 96;
// Calcular el ratio de escala entre el Canvas y la imagen original
float scaleFactorX = originalDpiX / canvasDpiX;
float scaleFactorY = originalDpiY / canvasDpiY;
// Ajustar las coordenadas de recorte en función del ratio de escala
int x = (int)MeterToPixels(Left * scaleFactorX);
int y = (int)MeterToPixels(Top * scaleFactorY);
int width = (int)MeterToPixels(Ancho * scaleFactorX);
int height = (int)MeterToPixels(Alto * scaleFactorY);
// Validar y ajustar el tamaño del recorte para que se mantenga dentro de los límites de la imagen
if (x < 0) x = 0;
if (y < 0) y = 0;
if (x + width > bitmapSource.PixelWidth) width = bitmapSource.PixelWidth - x;
if (y + height > bitmapSource.PixelHeight) height = bitmapSource.PixelHeight - y;
// Recortar el área deseada utilizando las coordenadas ajustadas
CroppedBitmap croppedBitmap = new CroppedBitmap(bitmapSource, new Int32Rect(x, y, width, height));
// Convertir CroppedBitmap a Mat directamente
Mat templateMat = BitmapSourceToMat(croppedBitmap);
int scale = 4;
// Convertir la plantilla a escala de grises y redimensionarla
Mat templateGray = new Mat();
CvInvoke.CvtColor(templateMat, templateGray, ColorConversion.Bgr2Gray);
Mat templateGrayResized = new Mat();
CvInvoke.Resize(templateGray, templateGrayResized, new Size(templateGray.Width / scale, templateGray.Height / scale), 0, 0, Inter.Linear);
progressDialog.ReportProgress(20);
// Cargar la imagen principal completa en un Mat
Mat mainImageMat = BitmapSourceToMat(bitmapSource);
// Convertir la imagen principal a escala de grises y redimensionarla
Mat mainImageGray = new Mat();
CvInvoke.CvtColor(mainImageMat, mainImageGray, ColorConversion.Bgr2Gray);
Mat mainImageGrayResized = new Mat();
CvInvoke.Resize(mainImageGray, mainImageGrayResized, new Size(mainImageGray.Width / scale, mainImageGray.Height / scale), 0, 0, Inter.Linear);
progressDialog.ReportProgress(50);
// Realizar la coincidencia de plantillas
Mat result = new Mat();
CvInvoke.MatchTemplate(mainImageGray, templateGray, result, TemplateMatchingType.CcoeffNormed);
// Establecer un umbral de coincidencia
if (Threshold < 0.4)
Threshold = 0.4f;
double threshold = Threshold;
int ConteoPositivos = 0;
// Lista para mantener áreas ya aceptadas
2024-06-10 06:07:25 -03:00
List<Rectangle> acceptedRectangles = new List<Rectangle>();
if (search_rectangles != null)
search_rectangles.Clear();
else
search_rectangles = new List<Rect>();
// Obtener los puntos que superan el umbral
float[] resultArray = result.GetData(false) as float[];
if (resultArray != null)
{
for (int i = 0; i < resultArray.Length; i++)
{
if (resultArray[i] >= threshold)
{
int row = i / result.Cols;
int col = i % result.Cols;
Rect newRect = new Rect();
newRect.X = col / scaleFactorX;
newRect.Y = row / scaleFactorY;
newRect.Width = width / scaleFactorX;
newRect.Height = height / scaleFactorY;
// Crear un rectángulo para la coincidencia actual
Rectangle matchRect = new Rectangle
{
Stroke = new SolidColorBrush(Colors.Red),
StrokeThickness = 2,
Width = newRect.Width,
Height = newRect.Height,
Tag = "BuscarCoincidencias"
};
Canvas.SetLeft(matchRect, newRect.X);
Canvas.SetTop(matchRect, newRect.Y);
// Verificar si la coincidencia actual está dentro de algún rectángulo aceptado
bool isOverlap = acceptedRectangles.Any(r =>
new Rect(Canvas.GetLeft(r), Canvas.GetTop(r), r.Width, r.Height).IntersectsWith(
new Rect(Canvas.GetLeft(matchRect), Canvas.GetTop(matchRect), matchRect.Width, matchRect.Height)
));
// Si no hay superposición, agregar el rectángulo al Canvas y a la lista de aceptados
if (!isOverlap)
{
Canvas.SetZIndex(matchRect, 40);
_mainViewModel.MainCanvas.Children.Add(matchRect);
acceptedRectangles.Add(matchRect);
search_rectangles.Add(newRect);
ConteoPositivos++;
Coincidencias = ConteoPositivos;
progressDialog.ReportProgress(90);
if (ConteoPositivos > 20)
return;
}
}
}
2024-06-10 06:07:25 -03:00
PopularTagExtraction();
}
}
}
});
}
2024-06-10 06:07:25 -03:00
private void PopularTagExtraction()
{
var objetosSimulablesCopy = new List<osBase>(_mainViewModel.ObjetosSimulables);
foreach (var obj in objetosSimulablesCopy)
if (obj is osExtraccionTag objExtraccionTag)
if (objExtraccionTag.Id_Search_Templates == this.Nombre && objExtraccionTag.Cloned)
_mainViewModel.ObjetosSimulables.Remove(objExtraccionTag);
var objetosSimulables2Copy = new List<osBase>(_mainViewModel.ObjetosSimulables);
foreach (var rectangle in search_rectangles)
{
float offsetX = PixelsToMeters((float)rectangle.X) - Left;
float offsetY = PixelsToMeters((float)rectangle.Y) - Top;
foreach (var eTag in objetosSimulables2Copy)
{
if (eTag is osExtraccionTag objExtraccionTag)
{
if (objExtraccionTag.Id_Search_Templates == this.Nombre)
{
osExtraccionTag newObj = (osExtraccionTag)_mainViewModel.DuplicarObjeto(objExtraccionTag, offsetX, offsetY);
if (newObj != null)
newObj.Cloned = true;
}
}
}
}
}
// Método para convertir BitmapSource a Mat
private Mat BitmapSourceToMat(BitmapSource bitmapSource)
{
Bitmap bitmap;
using (MemoryStream outStream = new MemoryStream())
{
BitmapEncoder enc = new BmpBitmapEncoder();
enc.Frames.Add(BitmapFrame.Create(bitmapSource));
enc.Save(outStream);
bitmap = new Bitmap(outStream);
}
return bitmap.ToMat();
}
public override void ucLoaded()
{
// El UserControl ya se ha cargado y podemos obtener las coordenadas para
// crear el objeto de simulacion
base.ucLoaded();
}
}
public partial class ucBuscarCoincidencias : UserControl, IDataContainer
{
public osBase? Datos { get; set; }
public ucBuscarCoincidencias()
{
InitializeComponent();
this.Loaded += OnLoaded;
this.Unloaded += OnUnloaded;
}
private void OnLoaded(object sender, RoutedEventArgs e)
{
Datos?.ucLoaded();
}
private void OnUnloaded(object sender, RoutedEventArgs e)
{
Datos?.ucUnLoaded();
}
public void Resize(float width, float height)
{
if (Datos is osBuscarCoincidencias datos)
{
datos.Ancho = PixelToMeter.Instance.calc.PixelsToMeters(width);
datos.Alto = PixelToMeter.Instance.calc.PixelsToMeters(height);
}
}
public void Move(float LeftPixels, float TopPixels)
{
if (Datos != null)
{
Datos.Left = PixelToMeter.Instance.calc.PixelsToMeters(LeftPixels);
Datos.Top = PixelToMeter.Instance.calc.PixelsToMeters(TopPixels);
}
}
public void Rotate(float Angle)
{
if (Datos != null)
if (Datos is osBuscarCoincidencias datos)
datos.Angulo = Angle;
}
public void Highlight(bool State) { }
public int ZIndex()
{
return 1;
}
}
}