Schrift erkennen

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 5 Antworten in diesem Thema. Der letzte Beitrag () ist von Bartosz.

    Schrift erkennen

    Hallo liebes Forum,

    da ich Nachhilfe gebe, scanne ich oft ein DIN A4 Blatt ein. Dieses schneide ich mit Picasa zurecht (siehe Anhang). Ich möchte nun, dass ein Programm das macht. Daher muss ich die Schrift erkennen. Ich habe festgestellt, dass im Bild des Scanners einige Fehlpixel enthalten sind (sie sind dunkel statt weiß) trotz .PNG von vornherein.
    Dann habe ich mir gedacht, fang ich doch ganz grob an, und erkenne erstmal ein schwarzes Rechteck. Das klappte mittelmäßig. Aufgrund der Fehlpixel / Pixelfehler wird das umrandete Rechteck nach rechts und unten etwas zu groß. Da kam mir die Idee, dass ich checken muss, ob die letzten 5 Pixel dunkel waren! Und siehe da, gelöst! Aber nun mein Problem, denn es geht ja um die Schrift. Die Schrift ist so dünn, dass ich nicht (immer) fragen kann "Habe ich 5 dunkle Pixel gesehen?".

    Das nächste Ziel ist dann, wie oben genannt, das erkannte Rechteck mit der Schrift darin zu cutten und als einzelnes Bild zu speichern.

    Wie würdet ihr das angehen?

    PS: Option Strict On habe ich in den Projekteigenschaften gesetzt.

    VB.NET-Quellcode

    1. Imports Microsoft.WindowsAPICodePack.Dialogs
    2. Public NotInheritable Class Form1
    3. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    4. Me.BackColor = Color.FromArgb(163, 226, 255)
    5. Button1.BackColor = Color.FromArgb(207, 240, 255)
    6. NumericUpDown1.Value = 2
    7. End Sub
    8. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    9. TextBox_Xmin.Text = ""
    10. TextBox_Xmax.Text = ""
    11. TextBox_Ymin.Text = ""
    12. TextBox_Ymax.Text = ""
    13. Application.DoEvents()
    14. Dim Pfad As String = ""
    15. Using OFD As New CommonOpenFileDialog
    16. OFD.Title = "Bild auswählen, das beschnitten werden soll"
    17. OFD.Filters.Add(New CommonFileDialogFilter("JPEG", ".jpg"))
    18. OFD.Filters.Add(New CommonFileDialogFilter("Bitmap", ".bmp"))
    19. OFD.Filters.Add(New CommonFileDialogFilter("PNG", ".png"))
    20. OFD.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
    21. OFD.IsFolderPicker = False
    22. If OFD.ShowDialog() = DialogResult.OK Then
    23. Pfad = System.IO.Path.GetFullPath(OFD.FileName)
    24. Else
    25. Exit Sub
    26. End If
    27. End Using
    28. Using Image As New Bitmap(Pfad)
    29. Dim White As Color = Color.FromArgb(255, 255, 255)
    30. Dim Black As Color = Color.FromArgb(0, 0, 0)
    31. Dim akt_Color As Color
    32. Dim tolerance As Int16 = CShort(NumericUpDown1.Value)
    33. Dim Red_min, Red_max As Int16
    34. Dim Green_min, Green_max As Int16
    35. Dim Blue_min, Blue_max As Int16
    36. Red_min = CShort(Math.Max(Black.R - tolerance, 0))
    37. Red_max = CShort(Math.Min(Black.R + tolerance, 255))
    38. Green_min = CShort(Math.Max(Black.G - tolerance, 0))
    39. Green_max = CShort(Math.Min(Black.G + tolerance, 255))
    40. Blue_min = CShort(Math.Max(Black.B - tolerance, 0))
    41. Blue_max = CShort(Math.Min(Black.B + tolerance, 255))
    42. Dim Cnt As Int32 = 0
    43. Dim XMIN As Int16 = CShort(0)
    44. Dim YMIN As Int16 = CShort(0)
    45. Dim XMAX As Int16 = CShort(0)
    46. Dim YMAX As Int16 = CShort(0)
    47. For X As Int16 = 0 To CShort(Image.Size.Width - 1) Step 1
    48. For Y As Int16 = 0 To CShort(Image.Size.Height - 1) Step 1
    49. akt_Color = Image.GetPixel(X, Y)
    50. If (akt_Color.R >= Red_min AndAlso akt_Color.R <= Red_max) AndAlso
    51. (akt_Color.G >= Green_min AndAlso akt_Color.G <= Green_max) AndAlso
    52. (akt_Color.B >= Blue_min AndAlso akt_Color.B <= Blue_max) Then
    53. If Cnt = 0 Then 'Allererste Werte
    54. XMIN = X
    55. TextBox_Xmin.Text = XMIN.ToString
    56. YMIN = Y
    57. TextBox_Ymin.Text = YMIN.ToString
    58. Application.DoEvents()
    59. End If
    60. Cnt += 1
    61. If X > XMAX Then 'Allerhöchste Werte
    62. XMAX = X
    63. TextBox_Xmax.Text = XMAX.ToString
    64. Application.DoEvents()
    65. End If
    66. If Y > YMAX Then
    67. YMAX = Y
    68. TextBox_Ymax.Text = YMAX.ToString
    69. Application.DoEvents()
    70. End If
    71. End If
    72. Next
    73. Next
    74. Using g_img As Graphics = Graphics.FromImage(Image)
    75. g_img.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy
    76. Using Pen1 = New Pen(Color.FromArgb(191, 191, 0), CSng(3.0F))
    77. Dim myRect As New Rectangle(XMIN, YMIN, XMAX, YMAX)
    78. g_img.DrawRectangle(Pen1, myRect)
    79. Dim Substr As String = Pfad.Substring(0, Pfad.LastIndexOf("\") + 1)
    80. Dim Bildname As String = Pfad.Substring(Pfad.LastIndexOf("\") + 1, Pfad.LastIndexOf(".") - (Pfad.LastIndexOf("\") + 1))
    81. Image.Save(Substr & Bildname & " - Kopie.png", System.Drawing.Imaging.ImageFormat.Png)
    82. End Using
    83. End Using
    84. End Using
    85. GC.Collect()
    86. End Sub
    87. End Class
    Bilder
    • IMG_20200625_0001 - Kopie.png

      1,64 MB, 1.276×1.754, 109 mal angesehen
    • Beim Zuschneiden - Kopie.jpg

      41,9 kB, 1.052×621, 96 mal angesehen
    • fast weiß, schwarzes Rechteck - Kopie.png

      473,48 kB, 1.276×1.754, 98 mal angesehen
    • Form.jpg

      24,09 kB, 533×252, 100 mal angesehen
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.
    Kurzer Einwurf, da GetPixel doch vergleichsweise langsam bei großer Pixelmenge ist: schau mal beim LockBits-Tutorial rein

    Bartosz schrieb:

    PS: Option Strict On habe ich in den Projekteigenschaften gesetzt.
    Das wird hier für weitere Hilfe auch vorausgesetzt. ;)
    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „VaporiZed“, mal wieder aus Grammatikgründen.

    Aufgrund spontaner Selbsteintrübung sind all meine Glaskugeln beim Hersteller. Lasst mich daher bitte nicht den Spekulatiusbackmodus wechseln.
    @Bartosz Das Zauberwort heißt Bildverarbeitung.
    Über die Pixel-Rohdaten musst Du zunächst einen Erosions- und danach einen Dilationsfilter darüber jagen, das behebt mindestens 90% dieser Probleme.
    www9.in.tum.de/seminare/ps.SS0…6-morphologie-karakoc.pdf
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    @VaporiZed Ok, schau ich mir an.
    @RodFromGermany ich habe mir
    mittels Nuget das Paket Accord.imaging.Filters heruntergeladen (siehe
    Anhang) und nutze die Filter.

    Verschiedene Ergebnisse:
    • Bei dem schwarzen Testrechteckbild wird das vom Programm gezeichnete Rechteck kleiner. Insgesamt unten und rechts noch zu groß, aber schon nah dra :)
    • bei der Schrift wird das vom Programm gezeichnete Rechteck größer. Es ist schlimmer geworden.


    VB.NET-Quellcode

    1. Imports Microsoft.WindowsAPICodePack.Dialogs
    2. Imports Accord.Imaging.Filters
    3. Public NotInheritable Class Form1
    4. Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
    5. Me.BackColor = Color.FromArgb(163, 226, 255)
    6. Button1.BackColor = Color.FromArgb(207, 240, 255)
    7. NumericUpDown1.Value = 2
    8. End Sub
    9. Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    10. TextBox_Xmin.Text = ""
    11. TextBox_Xmax.Text = ""
    12. TextBox_Ymin.Text = ""
    13. TextBox_Ymax.Text = ""
    14. Application.DoEvents()
    15. Dim Pfad As String = ""
    16. Using OFD As New CommonOpenFileDialog
    17. OFD.Title = "Bild auswählen, das beschnitten werden soll"
    18. OFD.Filters.Add(New CommonFileDialogFilter("JPEG", ".jpg"))
    19. OFD.Filters.Add(New CommonFileDialogFilter("Bitmap", ".bmp"))
    20. OFD.Filters.Add(New CommonFileDialogFilter("PNG", ".png"))
    21. OFD.InitialDirectory = Environment.GetFolderPath(Environment.SpecialFolder.Desktop)
    22. OFD.IsFolderPicker = False
    23. If OFD.ShowDialog() = DialogResult.OK Then
    24. Pfad = System.IO.Path.GetFullPath(OFD.FileName)
    25. Else
    26. Exit Sub
    27. End If
    28. End Using
    29. Dim erosion_filter As Erosion = New Erosion()
    30. Dim einmal_gefiltertes_image As Bitmap = erosion_filter.Apply(New Bitmap(Pfad))
    31. Dim dilation_filter As Dilation = New Dilation()
    32. Dim zweifach_gefiltertes_image As Bitmap = dilation_filter.Apply(einmal_gefiltertes_image)
    33. 'Using Image As New Bitmap(Pfad)
    34. Dim Black As Color = Color.FromArgb(0, 0, 0)
    35. Dim akt_Color As Color
    36. Dim tolerance As Int16 = CShort(NumericUpDown1.Value)
    37. Dim Red_min, Red_max As Int16
    38. Dim Green_min, Green_max As Int16
    39. Dim Blue_min, Blue_max As Int16
    40. Red_min = CShort(Math.Max(Black.R - tolerance, 0))
    41. Red_max = CShort(Math.Min(Black.R + tolerance, 255))
    42. Green_min = CShort(Math.Max(Black.G - tolerance, 0))
    43. Green_max = CShort(Math.Min(Black.G + tolerance, 255))
    44. Blue_min = CShort(Math.Max(Black.B - tolerance, 0))
    45. Blue_max = CShort(Math.Min(Black.B + tolerance, 255))
    46. Dim Cnt As Int32 = 0
    47. Dim XMIN As Int16 = 0
    48. Dim YMIN As Int16 = 0
    49. Dim XMAX As Int16 = 0
    50. Dim YMAX As Int16 = 0
    51. For X As Int16 = 0 To CShort(zweifach_gefiltertes_image.Size.Width - 1) Step 1
    52. For Y As Int16 = 0 To CShort(zweifach_gefiltertes_image.Size.Height - 1) Step 1
    53. akt_Color = zweifach_gefiltertes_image.GetPixel(X, Y)
    54. If (akt_Color.R >= Red_min AndAlso akt_Color.R <= Red_max) AndAlso
    55. (akt_Color.G >= Green_min AndAlso akt_Color.G <= Green_max) AndAlso
    56. (akt_Color.B >= Blue_min AndAlso akt_Color.B <= Blue_max) Then
    57. If Cnt = 0 Then 'Allererste Werte
    58. XMIN = X
    59. TextBox_Xmin.Text = XMIN.ToString
    60. YMIN = Y
    61. TextBox_Ymin.Text = YMIN.ToString
    62. Application.DoEvents()
    63. End If
    64. Cnt += 1
    65. If X > XMAX Then 'Allerhöchste Werte
    66. XMAX = X
    67. TextBox_Xmax.Text = XMAX.ToString
    68. Application.DoEvents()
    69. End If
    70. If Y > YMAX Then
    71. YMAX = Y
    72. TextBox_Ymax.Text = YMAX.ToString
    73. Application.DoEvents()
    74. End If
    75. End If
    76. Next
    77. Next
    78. Using g_img As Graphics = Graphics.FromImage(zweifach_gefiltertes_image)
    79. g_img.CompositingMode = System.Drawing.Drawing2D.CompositingMode.SourceCopy
    80. Using Pen1 = New Pen(Color.FromArgb(191, 191, 0), 3.0F)
    81. [color=#ff6600]Dim myRect As New Rectangle(XMIN, YMIN, XMAX - XMIN, YMAX - YMIN)[/color]
    82. g_img.DrawRectangle(Pen1, myRect)
    83. Dim Substr As String = Pfad.Substring(0, Pfad.LastIndexOf("\") + 1)
    84. Dim Bildname As String =
    85. Pfad.Substring(Pfad.LastIndexOf("\") + 1, Pfad.LastIndexOf(".") -
    86. (Pfad.LastIndexOf("\") + 1))
    87. zweifach_gefiltertes_image.Save(Substr & Bildname & " -
    88. Kopie.png", System.Drawing.Imaging.ImageFormat.Png)
    89. End Using
    90. End Using
    91. 'End Using
    92. GC.Collect()
    93. End Sub
    94. End Class

    Bilder
    • Verweise nur zur Info.jpg

      41,89 kB, 252×446, 102 mal angesehen
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Bartosz“ ()

    Feststellung:
    Bei Toleranz = 79 kommt man dem Ziel näher (s. Anhang)
    Bilder
    • IMG_20200625_0001 3 - Kopie.png

      1,65 MB, 1.276×1.754, 98 mal angesehen
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.
    zugeschnitten bekomme ich das Bild auch. Habe ich hierhier
    Bild automatisch zuschneiden

    VB.NET-Quellcode

    1. If CheckBox_schneiden.Checked Then
    2. Using zugeschnittenes_Bild As New Bitmap(myRect.Width, myRect.Height)
    3. Using gr As Graphics = Graphics.FromImage(zugeschnittenes_Bild)
    4. gr.DrawImage(Image, New Rectangle(0, 0, myRect.Width, myRect.Height), myRect, GraphicsUnit.Pixel)
    5. zugeschnittenes_Bild.Save(Substr & Bildname & " - zugeschnitten.png", System.Drawing.Imaging.ImageFormat.Png)
    6. End Using
    7. End Using
    8. End If


    Jetzt fehlen nur noch die Feinheiten in der Erkennung
    An die Neulinge: Nutzt Option Strict On und Option Infer Off. Dadurch kommt ihr mit Datentypumwandlungen nicht durcheinander und der Code verbessert sich um Einiges! Solche Fehler à la Dim Beispiel As Integer = "123" können nicht mehr passieren.