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; namespace CtrEditor.ObjetosSim.Extraccion_Datos { /// /// Interaction logic for ucBuscarCoincidencias.xaml /// /// public partial class osBuscarCoincidencias : osBase, IosBase { private osBase _osMotor = null; private simTransporte SimGeometria; public static string NombreClase() { return "Search Templates"; } private string nombre = NombreClase(); public override string Nombre { get => nombre; set => SetProperty(ref nombre, value); } [ObservableProperty] bool search_templates; partial void OnSearch_templatesChanged(bool oldValue, bool newValue) { if (Search_templates) BuscarCoincidencias(); Search_templates = false; } public override void TopChanged(float value) { base.TopChanged(value); Search_templates = false; } public override void LeftChanged(float value) { base.LeftChanged(value); Search_templates = false; } [ObservableProperty] public float ancho; partial void OnAnchoChanged(float value) { Search_templates = false; } [ObservableProperty] public float alto; partial void OnAltoChanged(float value) { Search_templates = false; } public override void OnTimerAfterMovement() { } [ObservableProperty] public int n_Repeat_X; [ObservableProperty] public int n_Repeat_Y; [ObservableProperty] public float offset_Repeat_X; [ObservableProperty] public float offset_Repeat_Y; [ObservableProperty] public float angulo; [ObservableProperty] string tag_extract; [ObservableProperty] string clase; [ObservableProperty] string tag_name; [ObservableProperty] float opacity_oculto; [ObservableProperty] bool show_debug_ocr; [ObservableProperty] float threshold; public osBuscarCoincidencias() { Ancho = 1; Alto = 1; Angulo = 0; Opacity_oculto = 0.1f; } 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().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; // 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; // Crear y agregar el rectángulo al Canvas (ajustando por la escala) Rectangle matchRect = new Rectangle { Stroke = new SolidColorBrush(Colors.Red), StrokeThickness = 2, Width = width / scaleFactorX, Height = height / scaleFactorY, Tag = "BuscarCoincidencias" }; Canvas.SetLeft(matchRect, col / scaleFactorX); Canvas.SetTop(matchRect, row / scaleFactorY); Canvas.SetZIndex(matchRect, 40); _mainViewModel.MainCanvas.Children.Add(matchRect); ConteoPositivos++; progressDialog.ReportProgress(90); if (ConteoPositivos > 20) return; } } } } } }); } // 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; } } }