Noch mal OCR, aber nur Zahlen

  • VB.NET

Es gibt 18 Antworten in diesem Thema. Der letzte Beitrag () ist von Skino.

    Noch mal OCR, aber nur Zahlen



    Ich möchte diese Zahlen gern in mein VB Programm übernehmen. Das Programm, das diese Werte erzeugt bietet leider keine passende Schnittstelle. Diese Werte werden als Bild auf dem Monitor dargestellt, immer an der selben Position.
    Ich habe schon einige Ansätze hier gelesen, leider waren die Links zu den verwendeten APIs, DLLs oder ähnlichem tot und auch eine Googlesuche danach brachte nichts.
    Ich hatte auch den Ansatz gelesen jeden Balken auf Farbveränderung zu überwachen und daraus dann die passende 7 Segment Anzeige zu rekonstruieren aber das ist ziemlich aufwändig und unelegant, wenn auch clever.
    Gibt es eine einfache praktikable Lösung, über OCR die Zahlen zu übernehmen?
    Man(n) kann auch ohne Hunde leben, aber es lohnt nicht (Heinz Rühmann)
    @Skino Ich hab mal iwo was gesehen, dass es genügt, gezielt 4 oder 5 gerade und schräge Linien über die Zahlenpixel zu legen und die Maxima zu suchen.
    Anhand der Anzahl und der Lage zueinander kann so die Ziffer erkannt werden.
    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!
    Auf GitHub findest du unter dem Stichwort "7 segment OCR" jede Menge Projekte, die das implementieren, allerdings größtenteils mit Python (was aber auch nicht wirklich ein Problem ist, wenn du das dann einfach als Konsolenanwendung anbindest). Mit .NET habe ich nichts finden können.
    Danke erstmal für eure Antworten.
    Leider habe ich bisher nichts brauchbares gefunden. Ich fürchte auch das das mein VB Wissen deutlich übersteigt.

    EDIT: Auf Youtube gefunden https://www.youtube.com/watch?v=Kjdu8SjEtG0

    Könnte exakt sein was ich benötige. Mal sehen.
    Man(n) kann auch ohne Hunde leben, aber es lohnt nicht (Heinz Rühmann)

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

    Skino schrieb:

    Könnte exakt sein was ich benötige.
    Inwieweit schießt das über die Problembeschreibung hinaus?
    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!
    @Skino Du willstz eine 7-Segment-Anzeige OCR-konvertieren, das Video konvertiert komplexe Texte, ist für Dein Problem also völlig oversized.
    Wenn Du das nicht stemmen kannst, kommst Du hier ggf. mit Problemen, die Du nicht hättest, wenn Du Dich auf die 7-Segment-Anzeige beschränkst.
    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!
    Das ist natürlich richtig aber die Lösung im Video ist fertig und muss nur minimal angepasst werden. Alles andere würde sicher deutlich mehr Aufwand und Wissen erfordern.
    Die 7 Segment, die ich einlesen möchte sind auch in einer Picturebox. und die zusätzliche Software ist verfügbar. Also eigentlich perfekt für mich. Interessant wäre natürlich welche Systemlast damit erzeugt wird, da ich die Anzeigen ca. 2x pro Sekunde einlesen möchte.
    Diese 7 Segment Phyton Lösung habe ich mir angesehen aber so auf Anhieb wüsste ich auch nicht wie ich das vernünftig einbinden könnte.
    Man(n) kann auch ohne Hunde leben, aber es lohnt nicht (Heinz Rühmann)

    Skino schrieb:

    Diese 7 Segment Phyton Lösung
    Kannst Du da mal einen Link oder den Code direkt posten?
    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!
    Wenn du Code aus einer Sprache wie Python nutzen möchtest, die nicht einfach direkt ansprechbar ist, ist oft der einfachste Weg, ne Konsolenanwendung in dieser Sprache zu erstellen, die die Daten in einer Datei entgegen nimmt und in einer weiteren ausgibt - in diesem Fall also ein Python-Skript, welches einen Pfad zu einem Bild entgegen nimmt und dann entweder das Ergebnis in die Konsole schreibt die du dann ausliest oder in eine weitere Datei.

    Natürlich gibt es auch fast nichts was dagegen spräche, das gleiche wie in den Python-Projekten in .NET zu implementieren, aber aufwandstechnisch ist eine Einbindung auf die Art sicherlich erstmal einfacher.
    Man(n) kann auch ohne Hunde leben, aber es lohnt nicht (Heinz Rühmann)

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

    @Skino OK, das Pyton-Zeugs ist nicht das, was ich erhofft hatte.
    Aber mit der Google-Anfrage csharp 7-segment ocr fand ich diese Seite (der erste Treffer):
    github.com/FANMixco/7-segment-ocr-reader
    Mal eben nach WinForm konvertiert und dies:
    Spoiler anzeigen

    C#-Quellcode

    1. using System;
    2. using System.Collections.Generic;
    3. using System.Diagnostics;
    4. using System.Drawing;
    5. using System.Threading.Tasks;
    6. // https://github.com/FANMixco/7-segment-ocr-reader
    7. namespace WindowsFormsApp1
    8. {
    9. class SevenSegmentOCR
    10. {
    11. const int BLACK = 0;
    12. const int WHITE = 255;
    13. const int DEFAULT = 140;
    14. const int GREEN_SCREEN = 50;
    15. const int LIGHT_GREEN = 115;
    16. const int BLUE = 70;
    17. private class Limits
    18. {
    19. public int x1 { get; set; }
    20. public int x2 { get; set; }
    21. public int y1 { get; set; }
    22. public int y2 { get; set; }
    23. }
    24. private class Pair
    25. {
    26. public int v1 { get; set; }
    27. public int v2 { get; set; }
    28. public Pair()
    29. {
    30. }
    31. public Pair(int V1, int V2)
    32. {
    33. v1 = V1;
    34. v2 = V2;
    35. }
    36. }
    37. Bitmap MyBmp;
    38. public SevenSegmentOCR(Bitmap bmp)
    39. {
    40. MyBmp = bmp;
    41. }
    42. public Bitmap BlackWhite(int type = 0, bool faded = false)
    43. {
    44. byte limit = 0;
    45. switch (type)
    46. {
    47. case 0:
    48. limit = DEFAULT;
    49. break;
    50. case 1:
    51. limit = GREEN_SCREEN;
    52. break;
    53. case 2:
    54. limit = BLUE;
    55. break;
    56. }
    57. if (faded)
    58. {
    59. limit += 75;
    60. }
    61. if (limit > 255)
    62. {
    63. limit = 255;
    64. }
    65. var img1 = MyBmp;
    66. int width1 = img1.Width;
    67. int height1 = img1.Height;
    68. Parallel.For(0, width1, i =>
    69. {
    70. Parallel.For(0, height1, j =>
    71. {
    72. Color color1 = MyBmp.GetPixel(i, j);
    73. int r1 = (int)color1.R;
    74. int g1 = (int)color1.G;
    75. int b1 = (int)color1.B;
    76. byte a, r, g, b;
    77. int ans = (r1 + g1 + b1) / 3;
    78. if (ans < limit)
    79. {
    80. a = BLACK;
    81. r = BLACK;
    82. g = BLACK;
    83. b = BLACK;
    84. }
    85. else
    86. {
    87. a = WHITE;
    88. r = WHITE;
    89. g = WHITE;
    90. b = WHITE;
    91. }
    92. color1 = Color.FromArgb(a, r, g, b);
    93. img1.SetPixel(i, j, color1);
    94. });
    95. });
    96. return MyBmp;
    97. }
    98. public string RecognizeNumber()
    99. {
    100. Stopwatch sw = new Stopwatch();
    101. sw.Start();
    102. try
    103. {
    104. string number = String.Empty;
    105. List<Limits> numbers = new List<Limits>();
    106. Pair boundariesX = new Pair(0, 0);
    107. Pair boundariesY = new Pair(0, 0);
    108. int i = 0;
    109. while (true)
    110. {
    111. if (sw.ElapsedMilliseconds > 1000 * 60 * 10)
    112. {
    113. goto Exit;
    114. }
    115. try
    116. {
    117. if (numbers.Count > 0)
    118. {
    119. boundariesX = getBoundariesX(numbers[i - 1].x2);
    120. }
    121. else
    122. {
    123. boundariesX = getBoundariesX();
    124. }
    125. boundariesY = GetBoundariesY(boundariesX);
    126. numbers.Add(new Limits() { x1 = boundariesX.v1, x2 = boundariesX.v2, y1 = boundariesY.v1, y2 = boundariesY.v2 });
    127. i++;
    128. if (i > 6)
    129. {
    130. goto Exit;
    131. }
    132. }
    133. //If there is no more data to look up, it breaks the while
    134. catch
    135. {
    136. break;
    137. }
    138. }
    139. #region Check Numbers
    140. sw = new Stopwatch();
    141. foreach (Limits val in numbers)
    142. {
    143. if (sw.ElapsedMilliseconds > 1000 * 60 * 10)
    144. {
    145. goto Exit;
    146. }
    147. if ((val.x2 - val.x1) <= (int)(val.y2 - val.y1) / 4)
    148. {
    149. // A digit that has a width of less than one quarter of it's height is recognized as a one.
    150. number += "1";
    151. }
    152. else if ((val.y2 - val.y1) <= (int)(val.x2 - val.x1) / 3)
    153. {
    154. // To recognize a minus sign a method similar to recognizing the digit one is used. If a digit is less high than 1/3 of its width, it is considered a minus sign.
    155. number += "-";
    156. }
    157. else if ((val.y2 - val.y1) <= (val.x2 - val.x1))
    158. {
    159. // To recognize a decimal point, e.g. of a digital scale, the size of each digit (that was not recognized as a one already) is compared with the maximum digit width and height. If a digit is significantly smaller than that, it is assumed to be a decimal point. The decimal point or thousands separators count towards the number of digits to recognize.
    160. number += ".";
    161. }
    162. else
    163. {
    164. // Every digit found by the segmentation is classified as follows: A vertical scan is started in the center top pixel of the digit to find the three horizontal segments.Any foreground pixel in the upper third is counted as part of the top segment, those in the second third as part of the middle and those in the last third as part of the bottom segment.
    165. // To examine the vertical segments two horizontal scanlines starting on the left margin of the digit are used.The first starts a quarter of the digit height from the top, the other from a quarter of the digit height from the bottom.Foreground pixels in the left resp.right half represent left resp.right segments.
    166. // The recognized segments are then used to identify the displayed digit using a table lookup(implemented as a switch statement).
    167. number += DetectNumber(val);
    168. }
    169. }
    170. #endregion
    171. sw.Stop();
    172. return number;
    173. }
    174. catch
    175. {
    176. sw.Stop();
    177. return "unknown";
    178. }
    179. Exit:
    180. sw.Stop();
    181. return "unknown";
    182. }
    183. private Pair getBoundariesX(int Start = BLACK)
    184. {
    185. int x1 = getNewLocationX(Start, 0);
    186. int x2 = 0;
    187. for (int x = x1; x < MyBmp.Width; x++)
    188. {
    189. int avg = 0;
    190. x2 = x;
    191. for (int y = 0; y < MyBmp.Height; y++)
    192. {
    193. avg += getColor(x, y);
    194. }
    195. if (avg / MyBmp.Height == 255)
    196. {
    197. break;
    198. }
    199. }
    200. return new Pair(x1, x2--);
    201. }
    202. private int getNewLocationX(int Start = 0, byte color = BLACK)
    203. {
    204. for (int x = Start; x < MyBmp.Width; x++)
    205. {
    206. for (int y = 0; y < MyBmp.Height; y++)
    207. {
    208. if (getColor(x, y) == color)
    209. {
    210. return x;
    211. }
    212. }
    213. }
    214. return -1;
    215. }
    216. private Pair GetBoundariesY(Pair xLimits)
    217. {
    218. int y1 = 0;
    219. int y2 = 0;
    220. bool found = false;
    221. for (int y = 0; y < MyBmp.Height; y++)
    222. {
    223. for (int x = xLimits.v1; x < xLimits.v2; x++)
    224. {
    225. if (getColor(x, y) == BLACK)
    226. {
    227. y1 = y;
    228. found = true;
    229. break;
    230. }
    231. }
    232. if (found)
    233. {
    234. break;
    235. }
    236. }
    237. found = false;
    238. for (int y = MyBmp.Height - 1; y >= 0; y--)
    239. {
    240. for (int x = xLimits.v1; x < xLimits.v2; x++)
    241. {
    242. if (getColor(x, y) == BLACK)
    243. {
    244. y2 = y;
    245. found = true;
    246. break;
    247. }
    248. }
    249. if (found)
    250. {
    251. break;
    252. }
    253. }
    254. return new Pair(y1, y2);
    255. }
    256. private string DetectNumber(Limits number)
    257. {
    258. int[] YResults = new int[3];
    259. YResults = GetYResults(number);
    260. int[,] XResultsUp = new int[1, 2];//[0,0]=left location, [0,1]=right location
    261. XResultsUp = GetXResults(number, true);
    262. int[,] XResultsDown = new int[1, 2];//[0,0]=left location, [0,1]=right location
    263. XResultsDown = GetXResults(number, false);
    264. if (YResults[0] == 1 && YResults[1] == 0 && YResults[2] == 1 && XResultsUp[0, 0] == 1 && XResultsUp[0, 1] == 1 && XResultsDown[0, 0] == 1 && XResultsDown[0, 1] == 1)
    265. return "0";
    266. else if (YResults[0] == 1 && YResults[1] == 1 && YResults[2] == 1 && XResultsUp[0, 0] == 0 && XResultsUp[0, 1] == 1 && XResultsDown[0, 0] == 1 && XResultsDown[0, 1] == 0)
    267. return "2";
    268. else if (YResults[0] == 1 && YResults[1] == 1 && YResults[2] == 1 && XResultsUp[0, 0] == 0 && XResultsUp[0, 1] == 1 && XResultsDown[0, 0] == 0 && XResultsDown[0, 1] == 1)
    269. return "3";
    270. else if (YResults[0] == 0 && YResults[1] == 1 && YResults[2] == 0 && XResultsUp[0, 0] == 1 && XResultsUp[0, 1] == 1 && XResultsDown[0, 0] == 0 && XResultsDown[0, 1] == 1)
    271. return "4";
    272. else if (YResults[0] == 1 && YResults[1] == 1 && YResults[2] == 1 && XResultsUp[0, 0] == 1 && XResultsUp[0, 1] == 0 && XResultsDown[0, 0] == 0 && XResultsDown[0, 1] == 1)
    273. return "5";
    274. else if (YResults[0] == 1 && YResults[1] == 1 && YResults[2] == 1 && XResultsUp[0, 0] == 1 && XResultsUp[0, 1] == 0 && XResultsDown[0, 0] == 1 && XResultsDown[0, 1] == 1)
    275. return "6";
    276. else if (YResults[0] == 1 && YResults[1] == 0 && YResults[2] == 0 && XResultsUp[0, 0] == 0 && XResultsUp[0, 1] == 1 && XResultsDown[0, 0] == 0 && XResultsDown[0, 1] == 1)
    277. return "7";
    278. else if (YResults[0] == 1 && YResults[1] == 1 && YResults[2] == 1 && XResultsUp[0, 0] == 1 && XResultsUp[0, 1] == 1 && XResultsDown[0, 0] == 1 && XResultsDown[0, 1] == 1)
    279. return "8";
    280. else if (YResults[0] == 1 && YResults[1] == 1 && YResults[2] == 1 && XResultsUp[0, 0] == 1 && XResultsUp[0, 1] == 1 && XResultsDown[0, 0] == 0 && XResultsDown[0, 1] == 1)
    281. return "9";
    282. else
    283. return "―";
    284. }
    285. private int[] GetYResults(Limits number)
    286. {
    287. int center = (number.x1 + number.x2) / 2;
    288. int[] total = new int[3] { 0, 0, 0 };
    289. for (int y = 0; y < MyBmp.Height / 4; y++)
    290. {
    291. if (getColor(center, y) == BLACK)
    292. {
    293. total[0] = 1;
    294. break;
    295. }
    296. }
    297. for (int y = MyBmp.Height / 2; y < MyBmp.Height - MyBmp.Height / 4; y++)
    298. {
    299. if (getColor(center, y) == BLACK)
    300. {
    301. total[1] = 1;
    302. break;
    303. }
    304. }
    305. for (int y = MyBmp.Height - 1; y >= MyBmp.Height - MyBmp.Height / 4; y--)
    306. {
    307. if (getColor(center, y) == BLACK)
    308. {
    309. total[2] = 1;
    310. break;
    311. }
    312. }
    313. return total;
    314. }
    315. private int GetNewLocationY(int Center, int Start = 0, byte color = WHITE)
    316. {
    317. for (int y = Start; y < MyBmp.Height; y++)
    318. {
    319. if (getColor(Center, y) == color)
    320. return y;
    321. }
    322. return -1;
    323. }
    324. private int[,] GetXResults(Limits number, bool Up)
    325. {
    326. int[,] XResults = new int[1, 2];
    327. int value = (number.y2 - number.y1) / 3;
    328. if (Up == false)
    329. {
    330. value = number.y2 - value;
    331. }
    332. else
    333. {
    334. value += number.y1;
    335. }
    336. for (int x = number.x1; x < number.x1 + (number.x2 - number.x1) / 2; x++)
    337. {
    338. if (getColor(x, value) == BLACK)
    339. {
    340. XResults[0, 0] = 1;
    341. break;
    342. }
    343. }
    344. for (int x = number.x2; x >= number.x2 - (number.x2 - number.x1) / 2; x--)
    345. {
    346. if (getColor(x, value) == BLACK)
    347. {
    348. XResults[0, 1] = 1;
    349. break;
    350. }
    351. }
    352. return XResults;
    353. }
    354. private int getColor(int x, int y)
    355. {
    356. return (int)(MyBmp.GetPixel(x, y).R + MyBmp.GetPixel(x, y).G + MyBmp.GetPixel(x, y).B) / 3;
    357. }
    358. }
    359. }
    Aufruf:

    C#-Quellcode

    1. Bitmap bmp = new Bitmap("c:\\Temp\\TestOcr4-2.png");
    2. SevenSegmentOCR ocr = new SevenSegmentOCR(bmp);
    3. string txt = ocr.RecognizeNumber();
    4. MessageBox.Show(txt);
    Input:

    Ergebnis:

    Das verwendete Bild ist ein Auisschnitt aus einem verwiesenen Bild, das ich mit meiner Bildverarbeitung ein wenig "aufpoliert" habe.
    Die Zahlen von Dir konnte er nicht, die waren ggf. zu klein.
    Folgende Erläuterung war dabei:
    Spoiler anzeigen
    # 7-segment-ocr-reader
    Class for reading 7 segment displays with C#

    Inspired by:

    **Seven Segment Optical Character Recognition**

    unix-ag.uni-kl.de/~auerswal/ssocr/

    ![ocr](unix-ag.uni-kl.de/~auerswal/ssocr/six_digits.png)

    And their visualization algorithm:

    ![ocr_preview](unix-ag.uni-kl.de/~auerswal/ssocr/illustrate_algo.png)

    All images in this Read Me are property of [Technische Universität Kaiserslautern](uni-kl.de)


    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!
    Ich hätte das ja auch wahrscheinlich eher per Pixelscan genacht. Pro Ziffer muss man ja nur 7 Pixel checken, also 1 pro Balken. Was mich bei Post#1 etwas verwirrt ist links die 110. Die erste 1 ist anscheinend linksbündig, die 2. rechtsbündig. Wie sieht denn da ne 888 aus? Oder habe ich die Ziffern falsch interpretiert?
    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.

    VaporiZed schrieb:

    Oder habe ich die Ziffern falsch interpretiert?
    Ich hab da durchaus einige Bilder probiert, dieses war das erste, wo was rauskam.

    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!
    Das mit der Pixelabfrage habe ich auch überlegt. Mache ich immer, wenn virtuelle LEDs abgefragt werden sollen. Aber bei 16 Ziffern ist der Aufwand beachtlich und auch irgendwie unelegant. Aber Danke für den Tipp.
    @RodFromGermany: Lässt sich der Quelltext in VB übersetzen? Eher nicht denke ich, sieht eher zu kompliziert aus. Muss ich da noch irgenwelche DLLs oder APIs einbinden , konnte nicht auf Github finden.
    Man(n) kann auch ohne Hunde leben, aber es lohnt nicht (Heinz Rühmann)

    Skino schrieb:

    Aber bei 16 Ziffern ist der Aufwand beachtlich und auch irgendwie unelegant.
    Denn? Die Erkennung einer Ziffer ist eine Aufgabe. Die Festlegung, wo alle Ziffern sind, eine andere, aber einfachere. Und an den jeweiligen Positionen wird Aufgabe 1 ausgeführt.
    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.

    Skino schrieb:

    Lässt sich der Quelltext in VB übersetzen?
    Kopiere den C#-Code, öffne den in meiner Signatur angegebenen Konverter und schwups (testen musst Du selbst, den Namespace kannst Du rausnemen oder Deinem anpassen):
    Spoiler anzeigen

    VB.NET-Quellcode

    1. Imports System
    2. Imports System.Collections.Generic
    3. Imports System.Diagnostics
    4. Imports System.Drawing
    5. Imports System.Threading.Tasks
    6. Namespace WindowsFormsApp1
    7. Class SevenSegmentOCR
    8. Const BLACK As Integer = 0
    9. Const WHITE As Integer = 255
    10. Const [DEFAULT] As Integer = 140
    11. Const GREEN_SCREEN As Integer = 50
    12. Const LIGHT_GREEN As Integer = 115
    13. Const BLUE As Integer = 70
    14. Private Class Limits
    15. Public Property x1 As Integer
    16. Public Property x2 As Integer
    17. Public Property y1 As Integer
    18. Public Property y2 As Integer
    19. End Class
    20. Private Class Pair
    21. Public Property v1 As Integer
    22. Public Property v2 As Integer
    23. Public Sub New()
    24. End Sub
    25. Public Sub New(ByVal V1 As Integer, ByVal V2 As Integer)
    26. v1 = V1
    27. v2 = V2
    28. End Sub
    29. End Class
    30. Private MyBmp As Bitmap
    31. Public Sub New(ByVal bmp As Bitmap)
    32. MyBmp = bmp
    33. End Sub
    34. Public Function BlackWhite(ByVal Optional type As Integer = 0, ByVal Optional faded As Boolean = False) As Bitmap
    35. Dim limit As Byte = 0
    36. Select Case type
    37. Case 0
    38. limit = [DEFAULT]
    39. Case 1
    40. limit = GREEN_SCREEN
    41. Case 2
    42. limit = BLUE
    43. End Select
    44. If faded Then
    45. limit += 75
    46. End If
    47. If limit > 255 Then
    48. limit = 255
    49. End If
    50. Dim img1 = MyBmp
    51. Dim width1 As Integer = img1.Width
    52. Dim height1 As Integer = img1.Height
    53. Parallel.[For](0, width1, Sub(i)
    54. Parallel.[For](0, height1, Sub(j)
    55. Dim color1 As Color = MyBmp.GetPixel(i, j)
    56. Dim r1 As Integer = CInt(color1.R)
    57. Dim g1 As Integer = CInt(color1.G)
    58. Dim b1 As Integer = CInt(color1.B)
    59. Dim a, r, g, b As Byte
    60. Dim ans As Integer = (r1 + g1 + b1) / 3
    61. If ans < limit Then
    62. a = BLACK
    63. r = BLACK
    64. g = BLACK
    65. b = BLACK
    66. Else
    67. a = WHITE
    68. r = WHITE
    69. g = WHITE
    70. b = WHITE
    71. End If
    72. color1 = Color.FromArgb(a, r, g, b)
    73. img1.SetPixel(i, j, color1)
    74. End Sub)
    75. End Sub)
    76. Return MyBmp
    77. End Function
    78. Public Function RecognizeNumber() As String
    79. Dim sw As Stopwatch = New Stopwatch()
    80. sw.Start()
    81. Try
    82. Dim number As String = String.Empty
    83. Dim numbers As List(Of Limits) = New List(Of Limits)()
    84. Dim boundariesX As Pair = New Pair(0, 0)
    85. Dim boundariesY As Pair = New Pair(0, 0)
    86. Dim i As Integer = 0
    87. While True
    88. If sw.ElapsedMilliseconds > 1000 * 60 * 10 Then
    89. GoTo [Exit]
    90. End If
    91. Try
    92. If numbers.Count > 0 Then
    93. boundariesX = getBoundariesX(numbers(i - 1).x2)
    94. Else
    95. boundariesX = getBoundariesX()
    96. End If
    97. boundariesY = GetBoundariesY(boundariesX)
    98. numbers.Add(New Limits() With {
    99. .x1 = boundariesX.v1,
    100. .x2 = boundariesX.v2,
    101. .y1 = boundariesY.v1,
    102. .y2 = boundariesY.v2
    103. })
    104. i += 1
    105. If i > 6 Then
    106. GoTo [Exit]
    107. End If
    108. Catch
    109. Exit While
    110. End Try
    111. End While
    112. sw = New Stopwatch()
    113. For Each val As Limits In numbers
    114. If sw.ElapsedMilliseconds > 1000 * 60 * 10 Then
    115. GoTo [Exit]
    116. End If
    117. If (val.x2 - val.x1) <= CInt((val.y2 - val.y1)) / 4 Then
    118. number += "1"
    119. ElseIf (val.y2 - val.y1) <= CInt((val.x2 - val.x1)) / 3 Then
    120. number += "-"
    121. ElseIf (val.y2 - val.y1) <= (val.x2 - val.x1) Then
    122. number += "."
    123. Else
    124. number += DetectNumber(val)
    125. End If
    126. Next
    127. sw.[Stop]()
    128. Return number
    129. Catch
    130. sw.[Stop]()
    131. Return "unknown"
    132. End Try
    133. [Exit]:
    134. sw.[Stop]()
    135. Return "unknown"
    136. End Function
    137. Private Function getBoundariesX(ByVal Optional Start As Integer = BLACK) As Pair
    138. Dim x1 As Integer = getNewLocationX(Start, 0)
    139. Dim x2 As Integer = 0
    140. For x As Integer = x1 To MyBmp.Width - 1
    141. Dim avg As Integer = 0
    142. x2 = x
    143. For y As Integer = 0 To MyBmp.Height - 1
    144. avg += getColor(x, y)
    145. Next
    146. If avg / MyBmp.Height = 255 Then
    147. Exit For
    148. End If
    149. Next
    150. Return New Pair(x1, Math.Max(System.Threading.Interlocked.Decrement(x2), x2 + 1))
    151. End Function
    152. Private Function getNewLocationX(ByVal Optional Start As Integer = 0, ByVal Optional color As Byte = BLACK) As Integer
    153. For x As Integer = Start To MyBmp.Width - 1
    154. For y As Integer = 0 To MyBmp.Height - 1
    155. If getColor(x, y) = color Then
    156. Return x
    157. End If
    158. Next
    159. Next
    160. Return -1
    161. End Function
    162. Private Function GetBoundariesY(ByVal xLimits As Pair) As Pair
    163. Dim y1 As Integer = 0
    164. Dim y2 As Integer = 0
    165. Dim found As Boolean = False
    166. For y As Integer = 0 To MyBmp.Height - 1
    167. For x As Integer = xLimits.v1 To xLimits.v2 - 1
    168. If getColor(x, y) = BLACK Then
    169. y1 = y
    170. found = True
    171. Exit For
    172. End If
    173. Next
    174. If found Then
    175. Exit For
    176. End If
    177. Next
    178. found = False
    179. For y As Integer = MyBmp.Height - 1 To 0
    180. For x As Integer = xLimits.v1 To xLimits.v2 - 1
    181. If getColor(x, y) = BLACK Then
    182. y2 = y
    183. found = True
    184. Exit For
    185. End If
    186. Next
    187. If found Then
    188. Exit For
    189. End If
    190. Next
    191. Return New Pair(y1, y2)
    192. End Function
    193. Private Function DetectNumber(ByVal number As Limits) As String
    194. Dim YResults As Integer() = New Integer(2) {}
    195. YResults = GetYResults(number)
    196. Dim XResultsUp As Integer(,) = New Integer(0, 1) {}
    197. XResultsUp = GetXResults(number, True)
    198. Dim XResultsDown As Integer(,) = New Integer(0, 1) {}
    199. XResultsDown = GetXResults(number, False)
    200. If YResults(0) = 1 AndAlso YResults(1) = 0 AndAlso YResults(2) = 1 AndAlso XResultsUp(0, 0) = 1 AndAlso XResultsUp(0, 1) = 1 AndAlso XResultsDown(0, 0) = 1 AndAlso XResultsDown(0, 1) = 1 Then
    201. Return "0"
    202. ElseIf YResults(0) = 1 AndAlso YResults(1) = 1 AndAlso YResults(2) = 1 AndAlso XResultsUp(0, 0) = 0 AndAlso XResultsUp(0, 1) = 1 AndAlso XResultsDown(0, 0) = 1 AndAlso XResultsDown(0, 1) = 0 Then
    203. Return "2"
    204. ElseIf YResults(0) = 1 AndAlso YResults(1) = 1 AndAlso YResults(2) = 1 AndAlso XResultsUp(0, 0) = 0 AndAlso XResultsUp(0, 1) = 1 AndAlso XResultsDown(0, 0) = 0 AndAlso XResultsDown(0, 1) = 1 Then
    205. Return "3"
    206. ElseIf YResults(0) = 0 AndAlso YResults(1) = 1 AndAlso YResults(2) = 0 AndAlso XResultsUp(0, 0) = 1 AndAlso XResultsUp(0, 1) = 1 AndAlso XResultsDown(0, 0) = 0 AndAlso XResultsDown(0, 1) = 1 Then
    207. Return "4"
    208. ElseIf YResults(0) = 1 AndAlso YResults(1) = 1 AndAlso YResults(2) = 1 AndAlso XResultsUp(0, 0) = 1 AndAlso XResultsUp(0, 1) = 0 AndAlso XResultsDown(0, 0) = 0 AndAlso XResultsDown(0, 1) = 1 Then
    209. Return "5"
    210. ElseIf YResults(0) = 1 AndAlso YResults(1) = 1 AndAlso YResults(2) = 1 AndAlso XResultsUp(0, 0) = 1 AndAlso XResultsUp(0, 1) = 0 AndAlso XResultsDown(0, 0) = 1 AndAlso XResultsDown(0, 1) = 1 Then
    211. Return "6"
    212. ElseIf YResults(0) = 1 AndAlso YResults(1) = 0 AndAlso YResults(2) = 0 AndAlso XResultsUp(0, 0) = 0 AndAlso XResultsUp(0, 1) = 1 AndAlso XResultsDown(0, 0) = 0 AndAlso XResultsDown(0, 1) = 1 Then
    213. Return "7"
    214. ElseIf YResults(0) = 1 AndAlso YResults(1) = 1 AndAlso YResults(2) = 1 AndAlso XResultsUp(0, 0) = 1 AndAlso XResultsUp(0, 1) = 1 AndAlso XResultsDown(0, 0) = 1 AndAlso XResultsDown(0, 1) = 1 Then
    215. Return "8"
    216. ElseIf YResults(0) = 1 AndAlso YResults(1) = 1 AndAlso YResults(2) = 1 AndAlso XResultsUp(0, 0) = 1 AndAlso XResultsUp(0, 1) = 1 AndAlso XResultsDown(0, 0) = 0 AndAlso XResultsDown(0, 1) = 1 Then
    217. Return "9"
    218. Else
    219. Return "―"
    220. End If
    221. End Function
    222. Private Function GetYResults(ByVal number As Limits) As Integer()
    223. Dim center As Integer = (number.x1 + number.x2) / 2
    224. Dim total As Integer() = New Integer(2) {0, 0, 0}
    225. For y As Integer = 0 To MyBmp.Height / 4 - 1
    226. If getColor(center, y) = BLACK Then
    227. total(0) = 1
    228. Exit For
    229. End If
    230. Next
    231. For y As Integer = MyBmp.Height / 2 To MyBmp.Height - MyBmp.Height / 4 - 1
    232. If getColor(center, y) = BLACK Then
    233. total(1) = 1
    234. Exit For
    235. End If
    236. Next
    237. For y As Integer = MyBmp.Height - 1 To MyBmp.Height - MyBmp.Height / 4
    238. If getColor(center, y) = BLACK Then
    239. total(2) = 1
    240. Exit For
    241. End If
    242. Next
    243. Return total
    244. End Function
    245. Private Function GetNewLocationY(ByVal Center As Integer, ByVal Optional Start As Integer = 0, ByVal Optional color As Byte = WHITE) As Integer
    246. For y As Integer = Start To MyBmp.Height - 1
    247. If getColor(Center, y) = color Then Return y
    248. Next
    249. Return -1
    250. End Function
    251. Private Function GetXResults(ByVal number As Limits, ByVal Up As Boolean) As Integer(,)
    252. Dim XResults As Integer(,) = New Integer(0, 1) {}
    253. Dim value As Integer = (number.y2 - number.y1) / 3
    254. If Up = False Then
    255. value = number.y2 - value
    256. Else
    257. value += number.y1
    258. End If
    259. For x As Integer = number.x1 To number.x1 + (number.x2 - number.x1) / 2 - 1
    260. If getColor(x, value) = BLACK Then
    261. XResults(0, 0) = 1
    262. Exit For
    263. End If
    264. Next
    265. For x As Integer = number.x2 To number.x2 - (number.x2 - number.x1) / 2
    266. If getColor(x, value) = BLACK Then
    267. XResults(0, 1) = 1
    268. Exit For
    269. End If
    270. Next
    271. Return XResults
    272. End Function
    273. Private Function getColor(ByVal x As Integer, ByVal y As Integer) As Integer
    274. Return CInt((MyBmp.GetPixel(x, y).R + MyBmp.GetPixel(x, y).G + MyBmp.GetPixel(x, y).B)) / 3
    275. End Function
    276. End Class
    277. End Namespace
    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!