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

190
LINES

< > BotCompany Repo | #1015444 // class SimpleRecognizer v1 - recognizes a line of text - works, but slowly

JavaX fragment (include)

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

Author comment

Began life as a copy of #1006108

download  show line numbers  debug dex  old transpilations   

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

No comments. add comment

Snippet ID: #1015444
Snippet name: class SimpleRecognizer v1 - recognizes a line of text - works, but slowly
Eternal ID of this version: #1015444/1
Text MD5: 643bb1d800a7b79993812b9da91ef543
Author: stefan
Category: javax / ocr
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2018-05-14 01:04:14
Source code size: 6653 bytes / 190 lines
Pitched / IR pitched: No / No
Views / Downloads: 295 / 333
Referenced in: [show references]