2024-06-06 11:53:00 -03:00
|
|
|
|
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;
|
2024-06-09 05:39:31 -03:00
|
|
|
|
using Rect = System.Windows.Rect;
|
|
|
|
|
using System.ComponentModel;
|
2024-06-09 12:33:09 -03:00
|
|
|
|
using Newtonsoft.Json;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
|
|
|
|
|
namespace CtrEditor.ObjetosSim.Extraccion_Datos
|
|
|
|
|
{
|
|
|
|
|
/// <summary>
|
|
|
|
|
/// Interaction logic for ucBuscarCoincidencias.xaml
|
|
|
|
|
/// </summary>
|
|
|
|
|
///
|
|
|
|
|
|
|
|
|
|
public partial class osBuscarCoincidencias : osBase, IosBase
|
|
|
|
|
{
|
2024-06-09 12:33:09 -03:00
|
|
|
|
[JsonIgnore]
|
|
|
|
|
public float offsetY;
|
|
|
|
|
[JsonIgnore]
|
|
|
|
|
public float offsetX;
|
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
[ObservableProperty]
|
|
|
|
|
List<Rect> search_rectangles;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
|
|
|
|
|
public static string NombreClase()
|
|
|
|
|
{
|
|
|
|
|
return "Search Templates";
|
|
|
|
|
}
|
|
|
|
|
private string nombre = NombreClase();
|
2024-06-09 05:39:31 -03:00
|
|
|
|
|
2024-06-06 11:53:00 -03:00
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
[ObservableProperty]
|
|
|
|
|
bool export_ocr;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
|
|
|
|
|
[ObservableProperty]
|
2024-06-09 05:39:31 -03:00
|
|
|
|
string text_export_ocr;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
partial void OnExport_ocrChanged(bool value)
|
2024-06-06 11:53:00 -03:00
|
|
|
|
{
|
2024-06-09 05:39:31 -03:00
|
|
|
|
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;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
public override void TopChanging(float oldValue, float newValue)
|
2024-06-06 11:53:00 -03:00
|
|
|
|
{
|
2024-06-09 12:33:09 -03:00
|
|
|
|
offsetY = newValue - oldValue;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
}
|
2024-06-09 05:39:31 -03:00
|
|
|
|
public override void LeftChanging(float oldValue, float newValue)
|
|
|
|
|
{
|
2024-06-09 12:33:09 -03:00
|
|
|
|
offsetX = newValue - oldValue;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
[ObservableProperty]
|
2024-06-09 05:39:31 -03:00
|
|
|
|
[property: Description("Width of the object.")]
|
|
|
|
|
[property: Category("Position:")]
|
|
|
|
|
public float ancho;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
|
|
|
|
|
[ObservableProperty]
|
2024-06-09 05:39:31 -03:00
|
|
|
|
[property: Description("Height of the object.")]
|
|
|
|
|
[property: Category("Position:")]
|
|
|
|
|
public float alto;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
|
|
|
|
|
[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;
|
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
[ObservableProperty]
|
|
|
|
|
float coincidencias;
|
|
|
|
|
|
|
|
|
|
|
2024-06-06 11:53:00 -03:00
|
|
|
|
public osBuscarCoincidencias()
|
|
|
|
|
{
|
|
|
|
|
Ancho = 1;
|
|
|
|
|
Alto = 1;
|
|
|
|
|
Angulo = 0;
|
|
|
|
|
Opacity_oculto = 0.1f;
|
2024-06-09 05:39:31 -03:00
|
|
|
|
Threshold = 0.6f;
|
2024-06-06 11:53:00 -03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
|
2024-06-06 11:53:00 -03:00
|
|
|
|
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;
|
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
// Lista para mantener áreas ya aceptadas
|
|
|
|
|
List<Rectangle> acceptedRectangles = new List<Rectangle>();
|
|
|
|
|
|
|
|
|
|
if (search_rectangles != null)
|
|
|
|
|
search_rectangles.Clear();
|
|
|
|
|
else
|
|
|
|
|
search_rectangles = new List<Rect>();
|
|
|
|
|
|
2024-06-06 11:53:00 -03:00
|
|
|
|
// 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;
|
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
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
|
2024-06-06 11:53:00 -03:00
|
|
|
|
Rectangle matchRect = new Rectangle
|
|
|
|
|
{
|
|
|
|
|
Stroke = new SolidColorBrush(Colors.Red),
|
|
|
|
|
StrokeThickness = 2,
|
2024-06-09 05:39:31 -03:00
|
|
|
|
Width = newRect.Width,
|
|
|
|
|
Height = newRect.Height,
|
2024-06-06 11:53:00 -03:00
|
|
|
|
Tag = "BuscarCoincidencias"
|
|
|
|
|
};
|
2024-06-09 05:39:31 -03:00
|
|
|
|
|
|
|
|
|
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-06 11:53:00 -03:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-09 05:39:31 -03:00
|
|
|
|
|
2024-06-06 11:53:00 -03:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|