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