Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

182
LINES

< > BotCompany Repo | #1006108 // SimpleRecognizer - recognizes a line of text

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (6241L/41K).

1  
sclass SimpleRecognizer {
2  
  bool useCache1 = true, useCache2 = true;
3  
  Lock lock = lock();
4  
  
5  
  transient IF1<BWImage> wordImagePreprocessor; // optional preprocessor for word images (e.g. auto-contast)
6  
7  
  class GlyphInfo {
8  
    S meaning;
9  
    bool multi; // multiple meanings seen
10  
    
11  
    toString { ret meaning; }
12  
  }
13  
  
14  
  // key = md5
15  
  Map<S, GlyphInfo> glyphInfos = synchroMap();
16  
  
17  
  // optional for full similarity search - character image to MD5
18  
  Map<BWImage, S> fullSearchMap;
19  
  
20  
  S unknownCharacter = ocr_unknownCharacterPlaceholder(); // "\u2666" - diamond suit symbol; used for unknown characters
21  
  
22  
  *() {}
23  
  
24  
  void load(S info) {
25  
    lock lock;
26  
    recognizeGrouped_cache.clear();
27  
    new Matches m;
28  
    for (S s : toLinesFullTrim(info)) {
29  
      if (find3("the images * are the characters *", s, m)) {
30  
        L<S> md5s = splitAtSpace($1);
31  
        L<S> characters = eachCharAsString(dropSpaces($2));
32  
        saveMeanings(md5s, characters);
33  
      } else if (find3("the images * are the grouped characters *", s, m)) {
34  
        L<S> md5s = splitAtSpace($1);
35  
        L<S> characters = ocr_parseGlyphs(dropSpaces($2));
36  
        saveMeanings(md5s, characters);
37  
      } else if (nempty(javaTokC(s))) {
38  
        print("huh? " + s);
39  
      }
40  
    }
41  
    //print("Have " + n(l(glyphInfos), "glyph info"));
42  
    //psl(glyphInfos);
43  
  }
44  
  
45  
  void saveMeaning(S md5, S meaning) {
46  
    GlyphInfo info = getGlyphInfo(md5);
47  
    if (info.multi) ret;
48  
    if (hasDifferent(info.meaning, meaning)) {
49  
      //info.meaning = null;
50  
      info.meaning = meaning;
51  
      info.multi = true;
52  
      //print("multi");
53  
    } else
54  
      info.meaning = meaning;
55  
  }
56  
  
57  
  // gets or creates GlyphInfo
58  
  GlyphInfo getGlyphInfo(S md5) {
59  
    synchronized(glyphInfos) {
60  
      GlyphInfo info = glyphInfos.get(md5);
61  
      if (info == null)
62  
        glyphInfos.put(md5, info = new GlyphInfo);
63  
      ret info;
64  
    }
65  
  }
66  
  
67  
  void saveMeanings(L<S> md5s, L<S> characters) {
68  
    if (l(md5s) != l(characters)) { print("huh?"); ret; }
69  
    for i over md5s:
70  
      saveMeaning(md5s.get(i), characters.get(i));
71  
  }
72  
  
73  
  S recognize(BWImage img) {
74  
    ret ocr_joinGroups(recognizeGrouped(img));
75  
  }
76  
  
77  
  Scored<S> recognizeScored(BWImage img) {
78  
    Scored<L<S>> s = recognizeGrouped(img, null);
79  
    ret scored(ocr_joinGroups(s!), s);
80  
  }
81  
  
82  
  L<S> recognizeGrouped(BWImage img) {
83  
    ret getVar(recognizeGrouped(img, null));
84  
  }
85  
  
86  
  // md5 -> recognition result
87  
  Map<S, Scored<LS>> recognizeGrouped_cache = synchroMap();
88  
  int cantCache, cacheHits, cacheMisses;
89  
90  
  Scored<LS> recognizeGrouped(BWImage img, L<Rect> clips_out) {
91  
    S md5 = null;
92  
    if (clips_out != null || !useCache1) ++cantCache;
93  
    else {
94  
      img = callFOrKeep(wordImagePreprocessor, img);
95  
      md5 = md5OfBWImage(img);
96  
      Scored<LS> result = recognizeGrouped_cache.get(md5);
97  
      if (result != null) {
98  
        ++cacheHits;
99  
        ret result;
100  
      } else ++cacheMisses;
101  
    }
102  
    
103  
    Scored<LS> result = recognizeGrouped_uncached(img, clips_out);
104  
    if (md5 != null) recognizeGrouped_cache.put(md5, result);
105  
    ret result;
106  
  }
107  
    
108  
  Scored<LS> recognizeGrouped_uncached(BWImage img, L<Rect> clips_out) {
109  
    new LS buf;
110  
    L<Rect> rects = horizontalAutoSplit2ThenAutoCrop(img);
111  
    if (empty(rects)) ret scored((L<S>) emptyList(), 0.99);
112  
    new L<Scored> scores;
113  
    iLoop: for (int i = 0; i < l(rects); i++) {
114  
      Rect r = null;
115  
      for (int j = i; j < l(rects); j++) {
116  
        r = rectUnion(r, rects.get(j));
117  
        BWImage cImg = img.clip(r);
118  
        Scored<GlyphInfo> scored = recognizeGlyph(cImg, false);
119  
        GlyphInfo info = getVar(scored);
120  
        if (info != null && info.meaning != null) {
121  
          buf.add(info.meaning);
122  
          buf.addAll(rep("_", j-i));
123  
          if (clips_out != null) clips_out.addAll(rep(r, j-i+1));
124  
          scores.add(scored);
125  
          i = j;
126  
          continue iLoop;
127  
        }
128  
      }
129  
      r = rects.get(i);
130  
      Scored<GlyphInfo> scored = recognizeGlyph(img.clip(r), true);
131  
      GlyphInfo info = getVar(scored);
132  
      if (info != null && info.meaning != null)
133  
        buf.add(info.meaning);
134  
      else
135  
        buf.add(unknownCharacter);
136  
      if (clips_out != null) clips_out.add(r);
137  
      scores.add(scored);
138  
    }
139  
    ret scored(buf, averageScore(scores));
140  
  }
141  
  
142  
  // md5 -> recognition result
143  
  Map<S, Scored<GlyphInfo>> recognizeGlyph_cache = synchroMap();
144  
  static int cacheHits2, cacheMisses2;
145  
  
146  
  Scored<GlyphInfo> recognizeGlyph(BWImage img) {
147  
    ret recognizeGlyph(img, true);
148  
  }
149  
  
150  
  Scored<GlyphInfo> recognizeGlyph(BWImage img, bool fullSearch) {
151  
    S md5 = md5OfBWImage(img);
152  
    {
153  
      //lock lock;
154  
      GlyphInfo info = glyphInfos.get(md5);
155  
      if (info != null || !fullSearch || fullSearchMap == null) ret fullScored(info);
156  
      if (useCache2) {
157  
        Scored<GlyphInfo> result = recognizeGlyph_cache.get(md5);
158  
        if (result != null) { ++cacheHits2; ret result; }
159  
        cacheMisses2++;
160  
      }
161  
    }
162  
    
163  
    new Best<S> best;
164  
    for (BWImage cImg : /*concurrentlyIterateKeys*/keys(fullSearchMap)) {
165  
      float sim = bwImageSimilarityResized(img, cImg, (float) best.bestScore());
166  
      best.put(fullSearchMap.get(cImg), sim);
167  
    }
168  
    Scored<GlyphInfo> result = !best.has() ? null : scored(glyphInfos.get(best!), best.score());
169  
    if (useCache2)
170  
      recognizeGlyph_cache.put(md5, result);
171  
    ret result;
172  
  }
173  
  
174  
  S cacheStats() {
175  
    //ret "Cache size: " + l(recognizeGrouped_cache) + ", hits: " + cacheHits + ", misses: " + cacheMisses + ", uncachable: " + cantCache;
176  
    ret "Cache size: " + l(recognizeGlyph_cache) + ", hits: " + cacheHits2 + ", misses: " + cacheMisses2 + ", full search map: " + l(fullSearchMap);
177  
  }
178  
  
179  
  S sizeStats() {
180  
    ret l(glyphInfos) + "/" + l(fullSearchMap);
181  
  }
182  
}

Author comment

Began life as a copy of #1006103

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1006108
Snippet name: SimpleRecognizer - recognizes a line of text
Eternal ID of this version: #1006108/41
Text MD5: 1015f4609a0f4e17fa05ad438b036d60
Transpilation MD5: 1d2bcf46dbd08a869e56a96ee2f27137
Author: stefan
Category: javax / ocr
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2019-11-21 14:21:42
Source code size: 5918 bytes / 182 lines
Pitched / IR pitched: No / No
Views / Downloads: 1148 / 1936
Version history: 40 change(s)
Referenced in: [show references]