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)

sclass SimpleRecognizer {
  bool useCache1 = true, useCache2 = true;
  
  S initial = [[
  The images "9176714c9a935fc91e14595dbb3adddf 35731666e72dd0c9448f616ff3a7464a da9bc5a24bd503e898f69ad43bb9b92e" are the characters "yet".
  The images "412f9e0f8e177817a4fa285415c5a13f 386b289407599f31e3a7c57c3adb2587 6cd2a2284a5a6fda3a7cad3e3a048671 b0c47c014d665ec5b658f510c258dc47 b273ff598fe6afa04cfed9f9e8fb4109" are the grouped characters "[Ob]ject".
  The images "175116b749670b7b65707c10c935b42c ddaf2b9c198818c49628387ecc2910ed 3777597e39eb0de16c022293c8c91cc0 fad86bda3f716ef4e7fa9d199f6c4383 35731666e72dd0c9448f616ff3a7464a 03b96c30adaaed5b60cea4ab9bc37263" are the characters "images".
  ]];
  
  Lock lock = lock();
  
  class GlyphInfo {
    S meaning;
    bool multi; // multiple meanings seen
  }
  
  // key = md5
  new Map<S, GlyphInfo> glyphInfos;
  
  // optional for full similarity search - character image to MD5
  Map<BWImage, S> fullSearchMap;
  
  S unknownCharacter = diamond(); // "\u2666" - diamond suit symbol; used for unknown characters
  
  *() {
    load(initial);
  }
  
  void load(S info) {
    lock lock;
    recognizeGrouped_cache.clear();
    new Matches m;
    for (S s : toLinesFullTrim(info)) {
      if (find3("the images * are the characters *", s, m)) {
        L<S> md5s = splitAtSpace($1);
        L<S> characters = eachCharAsString(dropSpaces($2));
        saveMeanings(md5s, characters);
      } else if (find3("the images * are the grouped characters *", s, m)) {
        L<S> md5s = splitAtSpace($1);
        L<S> characters = ocr_parseGlyphs(dropSpaces($2));
        saveMeanings(md5s, characters);
      } else if (nempty(javaTokC(s))) {
        print("huh? " + s);
      }
    }
    //print("Have " + n(l(glyphInfos), "glyph info"));
    //psl(glyphInfos);
  }
  
  void saveMeaning(S md5, S meaning) {
    GlyphInfo info = getGlyphInfo(md5);
    if (info.multi) ret;
    if (hasDifferent(info.meaning, meaning)) {
      //info.meaning = null;
      info.meaning = meaning;
      info.multi = true;
      //print("multi");
    } else
      info.meaning = meaning;
  }
  
  // gets or creates GlyphInfo
  GlyphInfo getGlyphInfo(S md5) {
    GlyphInfo info = glyphInfos.get(md5);
    if (info == null)
      glyphInfos.put(md5, info = new GlyphInfo);
    ret info;
  }
  
  void saveMeanings(L<S> md5s, L<S> characters) {
    if (l(md5s) != l(characters)) { print("huh?"); ret; }
    for i over md5s:
      saveMeaning(md5s.get(i), characters.get(i));
  }
  
  // lookup stored recognition of whole image
  Scored<S> recognizeCheat(BWImage img) {
    lock lock;
    Scored<GlyphInfo> scored = recognizeGlyph(img, false);
    GlyphInfo info = getVar(scored);
    if (info != null && info.meaning != null) ret scored(info.meaning, scored);
    null;
  }
  
  S recognize(BWImage img) {
    ret ocr_joinGroups(recognizeGrouped(img));
  }
  
  Scored<S> recognizeScored(BWImage img) {
    Scored<L<S>> s = recognizeGrouped(img, null);
    ret scored(ocr_joinGroups(s!), s);
  }
  
  L<S> recognizeGrouped(BWImage img) {
    ret getVar(recognizeGrouped(img, null));
  }
  
  // md5 -> recognition result
  Map<S, Scored<L<S>>> recognizeGrouped_cache = synchroMap();
  int cantCache, cacheHits, cacheMisses;

  Scored<L<S>> recognizeGrouped(BWImage img, L<Rect> clips_out) {
    S md5 = null;
    if (clips_out != null || !useCache1) ++cantCache;
    else {
      md5 = md5OfBWImage(img);
      Scored<L<S>> result = recognizeGrouped_cache.get(md5);
      if (result != null) {
        ++cacheHits;
        ret result;
      } else ++cacheMisses;
    }
    
    Scored<L<S>> result = recognizeGrouped_uncached(img, clips_out);
    if (md5 != null) recognizeGrouped_cache.put(md5, result);
    ret result;
  }
    
  Scored<L<S>> recognizeGrouped_uncached(BWImage img, L<Rect> clips_out) {
    lock lock;
    // TODO: auto-crop here?
    Scored<S> s = recognizeCheat(img);
    if (s != null) {
      if (clips_out != null) clips_out.add(new Rect(0, 0, img.w(), img.h()));
      ret scored(ll(ocr_escapeMeaning(s!)), s); // hmm... how to group? does it matter?
    }
    
    new L<S> buf;
    L<Rect> rects = horizontalAutoSplit2ThenAutoCrop(img);
    if (empty(rects)) ret scored(ll(unknownCharacter), 0.5);
    new L<Scored> scores;
    iLoop: for (int i = 0; i < l(rects); i++) {
      Rect r = null;
      for (int j = i; j < l(rects); j++) {
        r = rectUnion(r, rects.get(j));
        BWImage cImg = img.clip(r);
        Scored<GlyphInfo> scored = recognizeGlyph(cImg, false);
        GlyphInfo info = getVar(scored);
        if (info != null && info.meaning != null) {
          buf.add(info.meaning);
          buf.addAll(rep("_", j-i));
          if (clips_out != null) clips_out.addAll(rep(r, j-i+1));
          scores.add(scored);
          i = j;
          continue iLoop;
        }
      }
      r = rects.get(i);
      Scored<GlyphInfo> scored = recognizeGlyph(img.clip(r), true);
      GlyphInfo info = getVar(scored);
      if (info != null && info.meaning != null)
        buf.add(info.meaning);
      else
        buf.add(unknownCharacter);
      if (clips_out != null) clips_out.add(r);
      scores.add(scored);
    }
    ret scored(buf, averageScore(scores));
  }
  
  // md5 -> recognition result
  Map<S, Scored<GlyphInfo>> recognizeGlyph_cache = synchroMap();
  static int cacheHits2, cacheMisses2;
  
  Scored<GlyphInfo> recognizeGlyph(BWImage img, bool fullSearch) {
    S md5 = md5OfBWImage(img);
    GlyphInfo info = glyphInfos.get(md5);
    if (info != null || !fullSearch || fullSearchMap == null) ret fullScored(info);
    if (useCache2) {
      Scored<GlyphInfo> result = recognizeGlyph_cache.get(md5);
      if (result != null) { ++cacheHits2; ret result; }
      cacheMisses2++;
    }
    
    new Best<S> best;
    for (BWImage cImg : keys(fullSearchMap)) {
      float sim = bwImageSimilarityResized(img, cImg, (float) best.bestScore());
      best.put(fullSearchMap.get(cImg), sim);
    }
    Scored<GlyphInfo> result = !best.has() ? null : scored(glyphInfos.get(best!), best.score());
    if (useCache2)
      recognizeGlyph_cache.put(md5, result);
    ret result;
  }
  
  S cacheStats() {
    //ret "Cache size: " + l(recognizeGrouped_cache) + ", hits: " + cacheHits + ", misses: " + cacheMisses + ", uncachable: " + cantCache;
    ret "Cache size: " + l(recognizeGlyph_cache) + ", hits: " + cacheHits2 + ", misses: " + cacheMisses2 + ", full search map: " + l(fullSearchMap);
  }
}

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: 289 / 328
Referenced in: [show references]