diff --git a/CtrEditor.csproj b/CtrEditor.csproj
index 5fd72e8..365e2d3 100644
--- a/CtrEditor.csproj
+++ b/CtrEditor.csproj
@@ -89,6 +89,7 @@
+
@@ -172,4 +173,11 @@
+
+
+
+
+
+
+
diff --git a/Documentation/PaddleOCRSharpAPI.txt b/Documentation/PaddleOCRSharpAPI.txt
new file mode 100644
index 0000000..17a020e
Binary files /dev/null and b/Documentation/PaddleOCRSharpAPI.txt differ
diff --git a/Documentation/paddleocr-api.md b/Documentation/paddleocr-api.md
new file mode 100644
index 0000000..b980c6a
--- /dev/null
+++ b/Documentation/paddleocr-api.md
@@ -0,0 +1,623 @@
+
+
+
+ PaddleOCRSharp
+
+
+
+
+ Base class for engine objects
+
+
+
+
+ Custom loading path for PaddleOCR.dll, default is empty. If specified, it needs to be assigned before engine instantiation.
+
+
+
+
+ Initialization
+
+
+
+
+ Get the current path of the program
+
+
+
+
+
+ Get the current path of the program
+
+
+
+
+
+ Convert Image to Byte[]
+
+
+
+
+
+
+ Release memory
+
+
+
+
+ Get underlying error information
+
+
+
+
+
+ Json helper class
+
+
+
+
+ Json deserialization
+
+
+
+
+
+
+
+ Model configuration object
+
+
+
+
+ det_infer model path
+
+
+
+
+ cls_infer model path
+
+
+
+
+ rec_infer model path
+
+
+
+
+ Full path of ppocr_keys.txt file
+
+
+
+
+ Table model configuration object
+
+
+
+
+ table_model_dir model path
+
+
+
+
+ Table recognition dictionary
+
+
+
+
+ OCR recognition parameters
+
+
+
+
+ Whether to use GPU; default false
+
+
+
+
+ GPU id, effective when using GPU; default 0
+
+
+
+
+ Requested GPU memory; default 4000
+
+
+
+
+ Number of threads for CPU prediction. When the machine has sufficient cores, the higher this value, the faster the prediction; default 10
+
+
+
+
+ Whether to use mkldnn library; default true
+
+
+
+
+ Whether to perform text detection; default true
+
+
+
+
+ Whether to perform text recognition; default true
+
+
+
+
+ Whether to perform text orientation classification; default false
+
+
+
+
+ When the input image length and width are greater than 960, the image is scaled proportionally so that the longest side is 960; default 960
+
+
+
+
+ Used to filter the binary image predicted by DB. Setting to 0.-0.3 has no significant effect on results; default 0.3
+
+
+
+
+ DB post-processing threshold for filtering boxes. If detection has missing boxes, this can be reduced accordingly; default 0.5
+
+
+
+
+ Represents the tightness of the text box. Smaller values mean the text box is closer to the text; default 1.6
+
+
+
+
+ Whether to use dilation on the output map, default false
+
+
+
+
+ true: use polygon box to calculate bbox; false: use rectangle box. Rectangle calculation is faster, polygon boxes are more accurate for curved text areas.
+
+
+
+
+ Whether to visualize the results. If true, the prediction results will be saved as an ocr_vis.png file in the current directory. Default false
+
+
+
+
+ Whether to use direction classifier, default false
+
+
+
+
+ Score threshold for direction classifier, default 0.9
+
+
+
+
+ Direction classifier batchsize, default 1
+
+
+
+
+ Recognition model batchsize, default 6
+
+
+
+
+ Recognition model input image height, default 48
+
+
+
+
+ Recognition model input image width, default 320
+
+
+
+
+ Whether to display prediction results, default false
+
+
+
+
+ When using GPU prediction, whether to enable tensorrt, default false
+
+
+
+
+ OCR modifiable parameters
+
+
+
+
+ Dynamically modify whether to detect. When OCRParameter.det=true, m_det can dynamically turn off the det parameter
+
+
+
+
+ Dynamically modify whether to recognize. When OCRParameter.rec=true, m_rec can dynamically turn off the rec parameter
+
+
+
+
+ When the input image length and width are greater than 960, the image is scaled proportionally so that the longest side is 960; default 960
+
+
+
+
+ Used to filter the binary image predicted by DB. Setting to 0.-0.3 has no significant effect on results; default 0.3. Effective when m_det=true
+
+
+
+
+ DB post-processing threshold for filtering boxes. If detection has missing boxes, this can be reduced accordingly; default 0.5. Effective when m_det=true
+
+
+
+
+ Represents the tightness of the text box. Smaller values mean the text box is closer to the text; default 1.6. Effective when m_det=true
+
+
+
+
+ OCR recognition result
+
+
+
+
+ List of text blocks
+
+
+
+
+ Recognition result text
+
+
+
+
+ Recognition result text in Json format
+
+
+
+
+ Return string format
+
+
+
+
+ Recognized text block
+
+
+
+
+ List of coordinate vertices around the text block
+
+
+
+
+ Text block content
+
+
+
+
+ Text recognition confidence
+
+
+
+
+ Angle classification confidence
+
+
+
+
+ Angle classification label
+
+
+
+
+ Return string format
+
+
+
+
+ Point object
+
+
+
+
+ X coordinate, in pixels
+
+
+
+
+ Y coordinate, in pixels
+
+
+
+
+ Default constructor
+
+
+
+
+ Constructor
+
+
+
+
+
+
+ Return string format
+
+
+
+
+ OCR structured recognition result
+
+
+
+
+ Table recognition result
+
+
+
+
+ Number of rows
+
+
+
+
+ Number of columns
+
+
+
+
+ List of cells
+
+
+
+
+ List of text blocks
+
+
+
+
+ Cell
+
+
+
+
+ Cell constructor
+
+
+
+
+ Row number
+
+
+
+
+ Column number
+
+
+
+
+ Text blocks
+
+
+
+
+ Recognized text
+
+
+
+
+ PaddleOCR text recognition engine object
+
+
+
+
+ Initialize OCR engine object with default parameters
+
+
+
+
+ Initialize OCR engine object with default parameters
+
+ Model configuration object, if null then default values are used
+
+
+
+ PaddleOCR recognition engine object initialization
+
+ Model configuration object, if null then default values are used
+ Recognition parameters, if null then default values are used
+
+
+
+ PaddleOCR recognition engine object initialization
+
+ Model configuration object, if null then default values are used
+ Recognition parameters in json string format
+
+
+
+ Get default configuration
+
+ Root directory
+
+
+
+ Perform text recognition on image file
+
+ Image file
+ OCR recognition result
+
+
+
+ Perform text recognition on image object
+
+ Image
+ OCR recognition result
+
+
+
+ Text recognition
+
+ Image memory stream
+ OCR recognition result
+
+
+
+ Text recognition
+
+ Image base64
+ OCR recognition result
+
+
+
+ Text recognition
+
+ Image memory address
+ Image width
+ Image height
+ Image channel, usually 3 or 1
+ OCR recognition result
+
+
+
+ Result parsing
+
+
+
+
+
+
+ Structured text recognition
+
+ Image
+ Table recognition result
+
+
+
+ Calculate table splitting
+
+
+
+
+
+
+
+ Dynamically modify parameters after initialization
+
+ Modifiable parameter object
+ Whether successful, calling before initialization will result in failure
+
+
+
+ Whether to enable rectangular processing of detection results. Single characters are easily detected as diamond shapes, processing them as rectangles can improve recognition accuracy. Only applicable for horizontal text.
+
+
+
+
+
+ Release object
+
+
+
+
+ PaddleOCR table recognition engine object
+
+
+
+
+ PaddleStructureEngine recognition engine object initialization
+
+
+
+
+ PaddleStructureEngine recognition engine object initialization
+
+ Model configuration object, if null then default values are used
+
+
+
+ PaddleStructureEngine recognition engine object initialization
+
+ Model configuration object, if null then default values are used
+ Recognition parameters, if null then default values are used
+
+
+
+ PaddleStructureEngine recognition engine object initialization
+
+ Model configuration object, if null then default values are used
+ Recognition parameters in Json format, if null then default values are used
+
+
+
+ Get default configuration
+
+ Root directory
+
+
+
+ Perform table text recognition on image file
+
+ Image file
+ Table recognition result
+
+
+
+ Perform table text recognition on image object
+
+ Image
+ Table recognition result
+
+
+
+ Perform table text recognition on image Byte array
+
+ Image byte array
+ Table recognition result
+
+
+
+ Perform table text recognition on image Base64
+
+ Image Base64
+ Table recognition result
+
+
+
+ Result parsing
+
+
+
+
+
+
+ Release object
+
+
+
+
+ OCR recognition parameters
+
+
+
+
+ When the input image length and width are greater than 488, the image is scaled proportionally, default 488
+
+
+
+
+ Whether to merge empty cells
+
+
+
+
+ Batch recognition quantity
+
+
+
+
diff --git a/FuncionesBase/TagPattern.cs b/FuncionesBase/TagPattern.cs
index 893e648..5d2b2d8 100644
--- a/FuncionesBase/TagPattern.cs
+++ b/FuncionesBase/TagPattern.cs
@@ -1,6 +1,7 @@
using System;
using System.Text.RegularExpressions;
using System.Collections.Generic;
+using System.Linq;
using Xceed.Wpf.Toolkit.PropertyGrid.Attributes;
namespace CtrEditor.FuncionesBase
@@ -15,8 +16,10 @@ namespace CtrEditor.FuncionesBase
{ "DESCRIPCION", "All uppercase text" },
{ "Siemens IO Input", "Format: Exxxxx.y (x: 0-65535, y: 0-7)" },
{ "Siemens IO Output", "Format: Axxxxx.y (x: 0-65535, y: 0-7)" },
- { "LETRASNUMEROS", "Format: ABC...123... (1-10 letters + numbers)" },
- { "LETRASNUMEROSESPACIOS", "Format: ABC...123... (letters, numbers and spaces allowed)" },
+ { "LETRASNUMEROS", "Format: ABC...123... (letters and numbers only, other chars become _)" },
+ { "LETRASNUMEROS/*-+", "Format: ABC...123... (letters, numbers, and /*-+ chars only, other chars become _)" },
+ { "LETRASNUMEROSESPACIOS", "Format: ABC...123... (letters, numbers and spaces only, other chars become _)" },
+ { "LETRASNUMEROSESPACIOS/*-+", "Format: ABC...123... (letters, numbers, spaces, and /*-+ chars only, other chars become _)" },
{ "Numero", "Numeric value" }
};
@@ -36,7 +39,9 @@ namespace CtrEditor.FuncionesBase
"Siemens IO Input" => ApplySiemensPattern(text, "E"),
"Siemens IO Output" => ApplySiemensPattern(text, "A"),
"LETRASNUMEROS" => ApplyLetrasNumerosPattern(text),
+ "LETRASNUMEROS/*-+" => ApplyLetrasNumerosSpecialPattern(text),
"LETRASNUMEROSESPACIOS" => ApplyLetrasNumerosEspaciosPattern(text),
+ "LETRASNUMEROSESPACIOS/*-+" => ApplyLetrasNumerosEspaciosSpecialPattern(text),
"Numero" => ApplyNumberPattern(text),
_ => text
};
@@ -54,7 +59,7 @@ namespace CtrEditor.FuncionesBase
if (match.Success)
{
int address = Math.Min(int.Parse(match.Groups[1].Value), 65535);
- int bit = match.Groups[2].Success ?
+ int bit = match.Groups[2].Success ?
Math.Min(int.Parse(match.Groups[2].Value), 7) : 0;
return $"{prefix}{address}.{bit}";
}
@@ -63,29 +68,67 @@ namespace CtrEditor.FuncionesBase
private static string ApplyLetrasNumerosPattern(string text)
{
- // Extract letters and numbers from the text
- var letters = new string(text.Where(c => char.IsLetter(c)).Take(10).ToArray());
- var numbers = new string(text.Where(c => char.IsDigit(c)).ToArray());
+ // Replace any character that is not a letter or digit with "_"
+ char[] result = new char[text.Length];
- // If no letters found, return "A" as default
- if (string.IsNullOrEmpty(letters))
- letters = "A";
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (char.IsLetter(text[i]) || char.IsDigit(text[i]))
+ result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
+ else
+ result[i] = '_';
+ }
- // If no numbers found, return "0" as default
- if (string.IsNullOrEmpty(numbers))
- numbers = "0";
-
- // Combine letters (uppercase) and numbers
- return $"{letters.ToUpper()}{numbers}";
+ return new string(result);
}
private static string ApplyLetrasNumerosEspaciosPattern(string text)
{
- // Keep only letters, numbers and spaces
- var cleanedText = new string(text.Where(c => char.IsLetterOrDigit(c) || char.IsWhiteSpace(c)).ToArray());
-
- // Convert to uppercase
- return cleanedText.ToUpper();
+ // Replace any character that is not a letter, digit, or space with "_"
+ char[] result = new char[text.Length];
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (char.IsLetter(text[i]) || char.IsDigit(text[i]) || char.IsWhiteSpace(text[i]))
+ result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
+ else
+ result[i] = '_';
+ }
+
+ return new string(result);
+ }
+
+ private static string ApplyLetrasNumerosSpecialPattern(string text)
+ {
+ // Replace any character that is not a letter, digit, or one of /*-+ with "_"
+ char[] result = new char[text.Length];
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (char.IsLetter(text[i]) || char.IsDigit(text[i]) || text[i] == '/' || text[i] == '*' || text[i] == '-' || text[i] == '+')
+ result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
+ else
+ result[i] = '_';
+ }
+
+ return new string(result);
+ }
+
+ private static string ApplyLetrasNumerosEspaciosSpecialPattern(string text)
+ {
+ // Replace any character that is not a letter, digit, space, or one of /*-+ with "_"
+ char[] result = new char[text.Length];
+
+ for (int i = 0; i < text.Length; i++)
+ {
+ if (char.IsLetter(text[i]) || char.IsDigit(text[i]) || char.IsWhiteSpace(text[i]) ||
+ text[i] == '/' || text[i] == '*' || text[i] == '-' || text[i] == '+')
+ result[i] = char.ToUpper(text[i]); // Convert letters to uppercase
+ else
+ result[i] = '_';
+ }
+
+ return new string(result);
}
private static string ApplyNumberPattern(string text)
@@ -107,4 +150,4 @@ namespace CtrEditor.FuncionesBase
return items;
}
}
-}
+}
\ No newline at end of file
diff --git a/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs b/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs
index 565cddf..a2d2a37 100644
--- a/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs
+++ b/ObjetosSim/Extraccion Datos/ucExtraccionTag.xaml.cs
@@ -165,7 +165,7 @@ namespace CtrEditor.ObjetosSim.Extraccion_Datos
public void CaptureImageAreaAndDoOCR()
{
- string extractedText = CaptureImageAreaAndDoOCR(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window);
+ string extractedText = CaptureImageAreaAndDoOCRPPaddle(Left, Top, Ancho, Alto, Angulo, Show_Debug_Window);
// Clean up the extracted text if eliminar_enters is true
if (Eliminar_enters && !string.IsNullOrEmpty(extractedText))
diff --git a/ObjetosSim/osBase.cs b/ObjetosSim/osBase.cs
index 774da08..1a0df2e 100644
--- a/ObjetosSim/osBase.cs
+++ b/ObjetosSim/osBase.cs
@@ -1,16 +1,17 @@
using CommunityToolkit.Mvvm.ComponentModel;
using CtrEditor.FuncionesBase;
using CtrEditor.Serialization;
+using CtrEditor.Services;
using CtrEditor.Simulacion;
using LibS7Adv;
using nkast.Aether.Physics2D.Common;
+using PaddleOCRSharp;
using Siemens.Simatic.Simulation.Runtime;
using System.ComponentModel;
using System.Diagnostics;
using System.IO;
using System.Windows;
using System.Windows.Controls;
-using System.Windows.Forms;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Media.Imaging;
@@ -414,7 +415,7 @@ namespace CtrEditor.ObjetosSim
using (var engine = new TesseractEngine(tesseractPath, "eng", EngineMode.Default))
{
// Configuraciones para mejorar el OCR de una sola letra
- engine.SetVariable("tessedit_char_whitelist", " ABCDEFGHIJKLMNÑOPQRSTUVWXYZabcdefghijklmnñopqrstuvwxyz0123456789-."); // Lista blanca de caracteres
+ engine.SetVariable("tessedit_char_whitelist", " ABCDEFGHIJKLMNÑOPQRSTUVWXYZabcdefghijklmnñopqrstuvwxyz0123456789-./"); // Lista blanca de caracteres
var result = engine.Process(img);
return result.GetText();
}
@@ -427,6 +428,98 @@ namespace CtrEditor.ObjetosSim
return "";
}
+
+ // Reemplaza el método existente CaptureImageAreaAndDoOCR con esta implementación
+ public string CaptureImageAreaAndDoOCRPPaddle(float Left, float Top, float Ancho, float Alto, float Angulo = 0, bool ShowPreview = false)
+ {
+ if (_mainViewModel?.MainCanvas.Children[0] is System.Windows.Controls.Image imagenDeFondo)
+ {
+ if (imagenDeFondo.Source is BitmapSource bitmapSource)
+ {
+ float originalDpiX = (float)bitmapSource.DpiX;
+ float originalDpiY = (float)bitmapSource.DpiY;
+
+ float canvasDpiX = 96;
+ float canvasDpiY = 96;
+
+ float scaleFactorX = originalDpiX / canvasDpiX;
+ float scaleFactorY = originalDpiY / canvasDpiY;
+
+ int x = (int)MeterToPixels(Left * scaleFactorX);
+ int y = (int)MeterToPixels(Top * scaleFactorY);
+ int width = (int)MeterToPixels(Ancho * scaleFactorX);
+ int height = (int)MeterToPixels(Alto * scaleFactorY);
+
+ 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;
+
+ CroppedBitmap croppedBitmap = new CroppedBitmap(bitmapSource, new Int32Rect(x, y, width, height));
+
+ TransformedBitmap transformedBitmap = new TransformedBitmap();
+ transformedBitmap.BeginInit();
+ transformedBitmap.Source = croppedBitmap;
+
+ if (Angulo != 0)
+ {
+ RotateTransform rotateTransform = new RotateTransform(-Angulo);
+ transformedBitmap.Transform = rotateTransform;
+ }
+
+ transformedBitmap.EndInit();
+
+ PngBitmapEncoder encoder = new PngBitmapEncoder();
+ encoder.Frames.Add(BitmapFrame.Create(transformedBitmap));
+
+ using (MemoryStream memoryStream = new MemoryStream())
+ {
+ encoder.Save(memoryStream);
+ memoryStream.Seek(0, SeekOrigin.Begin);
+
+ if (ShowPreview) ShowPreviewWindow(memoryStream);
+
+ try
+ {
+ // Primero convertimos a un array de bytes
+ byte[] imageBytes = memoryStream.ToArray();
+
+ // Obtener el motor PaddleOCR
+ var ocrEngine = PaddleOCRManager.GetEngine();
+
+ // Usar la sobrecarga que acepta un array de bytes en lugar de Bitmap
+ OCRResult result = ocrEngine.DetectText(imageBytes);
+
+ if (result != null && result.TextBlocks != null && result.TextBlocks.Count > 0)
+ {
+ // Lista de caracteres permitidos (similar a la whitelist de Tesseract)
+ string allowedChars = " ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-./";
+
+ // Filtrar texto por lista de caracteres permitidos
+ var filteredBlocks = result.TextBlocks
+ .Where(block => block.Score > 0.5) // Solo bloques con confianza > 50%
+ .Select(block => new {
+ Text = new string(block.Text.Where(c => allowedChars.Contains(c)).ToArray()),
+ Score = block.Score
+ })
+ .OrderByDescending(block => block.Score);
+
+ string recognizedText = string.Join(" ", filteredBlocks.Select(b => b.Text));
+ return recognizedText.Trim();
+ }
+ return "";
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"Error en OCR: {ex.Message}");
+ return "";
+ }
+ }
+ }
+ }
+ return "";
+ }
+
// Método para obtener un color más claro y saturado
public static Color ObtenerColorMasClaroYSaturado(Color colorOriginal, double incrementoL, double incrementoS)
{
diff --git a/Services/PaddleOCRManager.cs b/Services/PaddleOCRManager.cs
new file mode 100644
index 0000000..5db8d8c
--- /dev/null
+++ b/Services/PaddleOCRManager.cs
@@ -0,0 +1,88 @@
+using System;
+using System.IO;
+using System.Drawing;
+using System.Windows.Media;
+using System.Windows.Media.Imaging;
+using System.Windows;
+using System.Linq;
+using PaddleOCRSharp;
+
+namespace CtrEditor.Services
+{
+ // Clase auxiliar para gestionar PaddleOCR
+ public static class PaddleOCRManager
+ {
+ private static PaddleOCREngine _engine;
+ private static bool _initialized = false;
+ private static readonly object _lockObj = new object();
+
+ public static PaddleOCREngine GetEngine()
+ {
+ if (!_initialized)
+ {
+ lock (_lockObj)
+ {
+ if (!_initialized)
+ {
+ InitializeEngine();
+ _initialized = true;
+ }
+ }
+ }
+ return _engine;
+ }
+
+ private static void InitializeEngine()
+ {
+ try
+ {
+ string baseDir = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "paddleocr");
+
+ OCRModelConfig config = new OCRModelConfig
+ {
+ // Rutas a modelos de inferencia
+ det_infer = Path.Combine(baseDir, "det", "inference"),
+ cls_infer = Path.Combine(baseDir, "cls", "inference"),
+ rec_infer = Path.Combine(baseDir, "rec", "inference"),
+ keys = Path.Combine(baseDir, "keys", "ppocr_keys.txt")
+ };
+
+ OCRParameter parameter = new OCRParameter
+ {
+ // Configurar parámetros de OCR
+ use_angle_cls = true,
+ cls_thresh = 0.9f,
+ det_db_thresh = 0.3f,
+ det_db_box_thresh = 0.6f,
+ rec_batch_num = 6,
+ rec_img_h = 48,
+ enable_mkldnn = true,
+ use_gpu = false,
+ cpu_math_library_num_threads = Environment.ProcessorCount
+ };
+
+ _engine = new PaddleOCREngine(config, parameter);
+ }
+ catch (Exception ex)
+ {
+ System.Diagnostics.Debug.WriteLine($"Error al inicializar PaddleOCR: {ex.Message}");
+ // Fallback - inicializar con configuración predeterminada
+ _engine = new PaddleOCREngine();
+ }
+ }
+
+ public static void Cleanup()
+ {
+ lock (_lockObj)
+ {
+ if (_initialized && _engine != null)
+ {
+ _engine.Dispose();
+ _engine = null;
+ _initialized = false;
+ }
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/ViewModels/MatrixPreviewViewModel.cs b/ViewModels/MatrixPreviewViewModel.cs
index 95eb5b6..8223023 100644
--- a/ViewModels/MatrixPreviewViewModel.cs
+++ b/ViewModels/MatrixPreviewViewModel.cs
@@ -88,21 +88,41 @@ namespace CtrEditor.PopUps
MatrixRows.Clear();
_dataGrid.Columns.Clear();
- if (!MatrixItems.Any()) return;
+ if (!MatrixItems.Any())
+ {
+ Debug.WriteLine("No matrix items found.");
+ return;
+ }
+
+ Debug.WriteLine($"Total matrix items: {MatrixItems.Count}");
+
+ // Add Image column first
+ var imageColumn = new DataGridTextColumn
+ {
+ Header = "Image",
+ Binding = new Binding("[Image]"),
+ Width = DataGridLength.Auto
+ };
+ _dataGrid.Columns.Add(imageColumn);
// Group items by source image and then by column number
- var itemsByImage = MatrixItems.GroupBy(x => x.Source_Image);
+ var itemsByImage = MatrixItems.GroupBy(x => x.Source_Image).ToList();
+ Debug.WriteLine($"Images in matrix: {itemsByImage.Count}");
- // Ordenar items por número de columna
+ // Sort items by column number
var orderedItems = MatrixItems
.GroupBy(x => x.ColumnNumber)
.OrderBy(g => g.Key)
.Select(g => g.First())
.ToList();
- // Crear columnas
+ Debug.WriteLine($"Distinct columns: {orderedItems.Count}");
+
+ // Create data grid columns
foreach (var item in orderedItems)
{
+ if (item.ColumnNumber <= 0) continue; // Skip invalid column numbers
+
var column = new DataGridTextColumn
{
Header = $"{item.ColumnNumber}:{item.ColumnName}",
@@ -115,22 +135,25 @@ namespace CtrEditor.PopUps
// Process each image's data
foreach (var imageGroup in itemsByImage)
{
+ Debug.WriteLine($"Processing image: {imageGroup.Key} with {imageGroup.Count()} items");
+
// Calculate max rows needed for this image
int maxRows = 1;
var clonedItems = imageGroup.Where(x => x.IsCloned).ToList();
if (clonedItems.Any())
{
maxRows = clonedItems.Max(x => x.Copy_Number) + 1;
+ Debug.WriteLine($"Found cloned items, max rows: {maxRows}");
}
// Create rows for this image
for (int row = 0; row < maxRows; row++)
{
var rowData = new Dictionary();
-
+
// Add image identifier
rowData["Image"] = imageGroup.Key;
-
+
// Add fixed (non-cloned) values in all rows
foreach (var item in imageGroup.Where(x => !x.IsCloned))
{
@@ -152,6 +175,8 @@ namespace CtrEditor.PopUps
MatrixRows.Add(rowData);
}
}
+
+ Debug.WriteLine($"Total matrix rows created: {MatrixRows.Count}");
}
private void UpdateColumnHeaders()
@@ -183,13 +208,22 @@ namespace CtrEditor.PopUps
{
// Store current image
var currentImage = _mainViewModel.SelectedImage;
-
+
// Change to target image and wait for UI update
_mainViewModel.SelectedImage = imageName;
await _mainViewModel.WaitForUIUpdateAsync();
// Analyze current page
var items = AnalyzePage();
+ if (items.Count == 0)
+ {
+ Debug.WriteLine($"Warning: No items found for image {imageName}");
+ }
+ else
+ {
+ Debug.WriteLine($"Found {items.Count} items for image {imageName}");
+ }
+
foreach (var item in items)
{
item.Source_Image = imageName;
@@ -204,11 +238,17 @@ namespace CtrEditor.PopUps
}
}
}
+ catch (Exception ex)
+ {
+ Debug.WriteLine($"Error in AnalyzeMatrixMultiPage: {ex.Message}");
+ MessageBox.Show($"Error analyzing matrix: {ex.Message}", "Error", MessageBoxButton.OK, MessageBoxImage.Error);
+ }
finally
{
_mainViewModel.HasUnsavedChanges = originalHasUnsavedChanges;
}
+ Debug.WriteLine($"Total items collected: {MatrixItems.Count}");
ResolveColumnConflicts();
UpdateMatrixPreview();
}
@@ -251,13 +291,17 @@ namespace CtrEditor.PopUps
// Get base tags that are linked to Search Templates
var osExtraccionTagBaseGrouped_List = _mainViewModel.ObjetosSimulables
.OfType()
- .Where(tag => tag.Show_On_This_Page && !tag.Cloned && tag.Id_Search_Templates != null && tag.Id_Search_Templates != "")
+ .Where(tag => tag.Show_On_This_Page &&
+ !tag.Cloned &&
+ !string.IsNullOrEmpty(tag.Id_Search_Templates))
.ToList();
// Get fixed tags (not linked to Search Templates)
var osExtraccionTagBaseFix_List = _mainViewModel.ObjetosSimulables
.OfType()
- .Where(tag => tag.Show_On_This_Page && !tag.Cloned && (tag.Id_Search_Templates == null || tag.Id_Search_Templates == ""))
+ .Where(tag => tag.Show_On_This_Page &&
+ !tag.Cloned &&
+ string.IsNullOrEmpty(tag.Id_Search_Templates))
.ToList();
// Get cloned tags (created by Search Templates)
@@ -266,6 +310,11 @@ namespace CtrEditor.PopUps
.Where(tag => tag.Show_On_This_Page && tag.Cloned)
.ToList();
+ // Log counts for debugging
+ Debug.WriteLine($"Fixed tags: {osExtraccionTagBaseFix_List.Count}");
+ Debug.WriteLine($"Grouped tags: {osExtraccionTagBaseGrouped_List.Count}");
+ Debug.WriteLine($"Cloned tags: {osExtraccionTagCloned_List.Count}");
+
// Process fixed tags
foreach (var tag in osExtraccionTagBaseFix_List)
{
@@ -278,7 +327,9 @@ namespace CtrEditor.PopUps
Value = tag.Tag_extract,
Type = "Fixed",
IsCloned = false,
- Id = tag.Id
+ Id = tag.Id,
+ Source_Image = _mainViewModel.SelectedImage,
+ Copy_Number = 0
});
}
@@ -294,7 +345,9 @@ namespace CtrEditor.PopUps
Value = tag.Tag_extract,
Type = $"Grouped ({tag.Id_Search_Templates})",
IsCloned = false,
- Id = tag.Id
+ Id = tag.Id,
+ Source_Image = _mainViewModel.SelectedImage,
+ Copy_Number = 0
});
}
@@ -311,6 +364,7 @@ namespace CtrEditor.PopUps
Type = "Cloned",
IsCloned = true,
Id = tag.Id,
+ Source_Image = _mainViewModel.SelectedImage,
Copy_Number = tag.Copy_Number
});
}
diff --git a/paddleocr/cls/inference/._inference.pdiparams b/paddleocr/cls/inference/._inference.pdiparams
new file mode 100644
index 0000000..30c9fb5
Binary files /dev/null and b/paddleocr/cls/inference/._inference.pdiparams differ
diff --git a/paddleocr/cls/inference/._inference.pdiparams.info b/paddleocr/cls/inference/._inference.pdiparams.info
new file mode 100644
index 0000000..30c9fb5
Binary files /dev/null and b/paddleocr/cls/inference/._inference.pdiparams.info differ
diff --git a/paddleocr/cls/inference/._inference.pdmodel b/paddleocr/cls/inference/._inference.pdmodel
new file mode 100644
index 0000000..30c9fb5
Binary files /dev/null and b/paddleocr/cls/inference/._inference.pdmodel differ
diff --git a/paddleocr/cls/inference/inference.pdiparams b/paddleocr/cls/inference/inference.pdiparams
new file mode 100644
index 0000000..e3b3215
Binary files /dev/null and b/paddleocr/cls/inference/inference.pdiparams differ
diff --git a/paddleocr/cls/inference/inference.pdiparams.info b/paddleocr/cls/inference/inference.pdiparams.info
new file mode 100644
index 0000000..341c265
Binary files /dev/null and b/paddleocr/cls/inference/inference.pdiparams.info differ
diff --git a/paddleocr/cls/inference/inference.pdmodel b/paddleocr/cls/inference/inference.pdmodel
new file mode 100644
index 0000000..7e0540f
Binary files /dev/null and b/paddleocr/cls/inference/inference.pdmodel differ
diff --git a/paddleocr/det/inference/.DS_Store b/paddleocr/det/inference/.DS_Store
new file mode 100644
index 0000000..5b91d33
Binary files /dev/null and b/paddleocr/det/inference/.DS_Store differ
diff --git a/paddleocr/det/inference/._.DS_Store b/paddleocr/det/inference/._.DS_Store
new file mode 100644
index 0000000..f8ab1f9
Binary files /dev/null and b/paddleocr/det/inference/._.DS_Store differ
diff --git a/paddleocr/det/inference/._inference.pdiparams b/paddleocr/det/inference/._inference.pdiparams
new file mode 100644
index 0000000..b86c9ea
Binary files /dev/null and b/paddleocr/det/inference/._inference.pdiparams differ
diff --git a/paddleocr/det/inference/._inference.pdiparams.info b/paddleocr/det/inference/._inference.pdiparams.info
new file mode 100644
index 0000000..b86c9ea
Binary files /dev/null and b/paddleocr/det/inference/._inference.pdiparams.info differ
diff --git a/paddleocr/det/inference/._inference.pdmodel b/paddleocr/det/inference/._inference.pdmodel
new file mode 100644
index 0000000..b86c9ea
Binary files /dev/null and b/paddleocr/det/inference/._inference.pdmodel differ
diff --git a/paddleocr/det/inference/inference.pdiparams b/paddleocr/det/inference/inference.pdiparams
new file mode 100644
index 0000000..a21c592
Binary files /dev/null and b/paddleocr/det/inference/inference.pdiparams differ
diff --git a/paddleocr/det/inference/inference.pdiparams.info b/paddleocr/det/inference/inference.pdiparams.info
new file mode 100644
index 0000000..00e756f
Binary files /dev/null and b/paddleocr/det/inference/inference.pdiparams.info differ
diff --git a/paddleocr/det/inference/inference.pdmodel b/paddleocr/det/inference/inference.pdmodel
new file mode 100644
index 0000000..1c8e6ce
Binary files /dev/null and b/paddleocr/det/inference/inference.pdmodel differ
diff --git a/paddleocr/download-paddleocr-models.ps1 b/paddleocr/download-paddleocr-models.ps1
new file mode 100644
index 0000000..f31d53e
--- /dev/null
+++ b/paddleocr/download-paddleocr-models.ps1
@@ -0,0 +1,132 @@
+# Script para descargar e instalar modelos PaddleOCR
+# Guardar como: download-paddleocr-models.ps1
+
+param(
+ [string]$OutputDirectory = ".\paddleocr",
+ [switch]$ForceDownload = $false
+)
+
+# URLs de modelos PaddleOCR (inglés por defecto)
+$modelUrls = @{
+ "det" = "https://paddleocr.bj.bcebos.com/PP-OCRv3/english/en_PP-OCRv3_det_slim_infer.tar"
+ "rec" = "https://paddleocr.bj.bcebos.com/PP-OCRv3/english/en_PP-OCRv3_rec_slim_infer.tar"
+ "cls" = "https://paddleocr.bj.bcebos.com/dygraph_v2.0/ch/ch_ppocr_mobile_v2.0_cls_slim_infer.tar"
+ "keys" = "https://raw.githubusercontent.com/PaddlePaddle/PaddleOCR/release/2.6/ppocr/utils/en_dict.txt"
+}
+
+# Crear directorios si no existen
+function EnsureDirectory($path) {
+ if (-not (Test-Path $path)) {
+ New-Item -Path $path -ItemType Directory | Out-Null
+ Write-Host "Creado directorio: $path" -ForegroundColor Green
+ }
+}
+
+# Descargar archivo con barra de progreso
+function DownloadFile($url, $outputFile) {
+ $webClient = New-Object System.Net.WebClient
+ $webClient.Encoding = [System.Text.Encoding]::UTF8
+
+ $downloadExists = Test-Path $outputFile
+ if ($downloadExists -and -not $ForceDownload) {
+ Write-Host "El archivo ya existe: $outputFile. Use -ForceDownload para sobrescribir." -ForegroundColor Yellow
+ return $false
+ }
+
+ Write-Host "Descargando $url..." -ForegroundColor Cyan
+ try {
+ $webClient.DownloadFile($url, $outputFile)
+ Write-Host "Descarga completada: $outputFile" -ForegroundColor Green
+ return $true
+ } catch {
+ Write-Host "Error al descargar $url : $_" -ForegroundColor Red
+ return $false
+ }
+}
+
+# Extraer archivo TAR
+function ExtractTarFile($tarFile, $destDir) {
+ Write-Host "Extrayendo $tarFile..." -ForegroundColor Cyan
+
+ # Verificar si tar está disponible
+ $tarAvailable = $null -ne (Get-Command "tar" -ErrorAction SilentlyContinue)
+
+ if ($tarAvailable) {
+ # Usar tar nativo
+ try {
+ tar -xf $tarFile -C $destDir
+ Write-Host "Extracción completada: $tarFile" -ForegroundColor Green
+ return $true
+ } catch {
+ Write-Host "Error al extraer con tar: $_" -ForegroundColor Red
+ }
+ }
+
+ # Fallback a .NET
+ try {
+ Add-Type -AssemblyName System.IO.Compression.FileSystem
+
+ # Descomprimir TAR con .NET
+ # Nota: Esto es un ejemplo simplificado. Necesitarías una biblioteca específica para TAR
+ Write-Host "TAR nativo no disponible. Se requiere una biblioteca para manejar archivos TAR." -ForegroundColor Yellow
+
+ # Aquí puedes agregar código para usar SharpCompress u otra biblioteca para TAR
+ # Por ejemplo:
+ # [Reflection.Assembly]::LoadFrom("path\to\SharpCompress.dll")
+ # $reader = [SharpCompress.Readers.ReaderFactory]::Open($tarFile)
+ # ...
+
+ return $false
+ } catch {
+ Write-Host "Error al extraer $tarFile : $_" -ForegroundColor Red
+ return $false
+ }
+}
+
+# Crear directorio principal
+EnsureDirectory $OutputDirectory
+
+# Crear estructura de directorios
+$modelTypes = @("det", "rec", "cls", "keys")
+foreach ($type in $modelTypes) {
+ $typeDir = Join-Path $OutputDirectory $type
+ EnsureDirectory $typeDir
+
+ if ($type -ne "keys") {
+ $inferenceDir = Join-Path $typeDir "inference"
+ EnsureDirectory $inferenceDir
+ }
+}
+
+# Descargar y procesar cada modelo
+foreach ($entry in $modelUrls.GetEnumerator()) {
+ $type = $entry.Key
+ $url = $entry.Value
+ $typeDir = Join-Path $OutputDirectory $type
+
+ if ($type -eq "keys") {
+ # Para el archivo de claves, solo descargarlo directamente
+ $outputFile = Join-Path $typeDir "ppocr_keys.txt"
+ DownloadFile $url $outputFile
+ } else {
+ # Para los modelos, descargar TAR y extraerlo
+ $tarFile = Join-Path $env:TEMP "$type.tar"
+
+ $downloaded = DownloadFile $url $tarFile
+ if ($downloaded) {
+ $extracted = ExtractTarFile $tarFile $typeDir
+
+ # Limpiar archivo temporal
+ if (Test-Path $tarFile) {
+ Remove-Item $tarFile -Force
+ }
+ }
+ }
+}
+
+Write-Host "`nInstalación de modelos PaddleOCR completada en: $OutputDirectory" -ForegroundColor Green
+Write-Host "Para usar estos modelos, configura PaddleOCREngine con las siguientes rutas:" -ForegroundColor Yellow
+Write-Host " - det_infer: $OutputDirectory\det\inference" -ForegroundColor White
+Write-Host " - rec_infer: $OutputDirectory\rec\inference" -ForegroundColor White
+Write-Host " - cls_infer: $OutputDirectory\cls\inference" -ForegroundColor White
+Write-Host " - keys: $OutputDirectory\keys\ppocr_keys.txt" -ForegroundColor White
diff --git a/paddleocr/keys/ppocr_keys.txt b/paddleocr/keys/ppocr_keys.txt
new file mode 100644
index 0000000..7677d31
--- /dev/null
+++ b/paddleocr/keys/ppocr_keys.txt
@@ -0,0 +1,95 @@
+0
+1
+2
+3
+4
+5
+6
+7
+8
+9
+:
+;
+<
+=
+>
+?
+@
+A
+B
+C
+D
+E
+F
+G
+H
+I
+J
+K
+L
+M
+N
+O
+P
+Q
+R
+S
+T
+U
+V
+W
+X
+Y
+Z
+[
+\
+]
+^
+_
+`
+a
+b
+c
+d
+e
+f
+g
+h
+i
+j
+k
+l
+m
+n
+o
+p
+q
+r
+s
+t
+u
+v
+w
+x
+y
+z
+{
+|
+}
+~
+!
+"
+#
+$
+%
+&
+'
+(
+)
+*
++
+,
+-
+.
+/
+
diff --git a/paddleocr/rec/inference/inference.pdiparams b/paddleocr/rec/inference/inference.pdiparams
new file mode 100644
index 0000000..baa7670
Binary files /dev/null and b/paddleocr/rec/inference/inference.pdiparams differ
diff --git a/paddleocr/rec/inference/inference.pdiparams.info b/paddleocr/rec/inference/inference.pdiparams.info
new file mode 100644
index 0000000..357f09b
Binary files /dev/null and b/paddleocr/rec/inference/inference.pdiparams.info differ
diff --git a/paddleocr/rec/inference/inference.pdmodel b/paddleocr/rec/inference/inference.pdmodel
new file mode 100644
index 0000000..8d5230a
Binary files /dev/null and b/paddleocr/rec/inference/inference.pdmodel differ