// thread-safe except for analysis output fields sclass ChessPieceRecognizer { MultiMap profiles; double magicCombineFactor = 0.0012589254117941495; int version = 1; // for analysis transient Pair, Double> best, runnerUp; void load { long time = sysNow(); profiles = agiBlue_chessPieceProfiles(agiBlue_chessPieceImages_loadedMultiMap(), +version); compact(); done2("Loading " + n2(l(profiles), "piece profile"), time); } void compact { int n = l(profiles); profiles = uniquifyMultiMapValues(profiles); int m = l(profiles); if (m < n) print("Compacted ChessPieceRecognizer " + n + " => " + m); internByteArrayField sideProfile(values(profiles)); } Pair recognize(BufferedImage img) { ChessPieceProfile1 profile = chessOCR_pieceProfileFromRawImage_1(img); new Lowest_withRunnerUp> lowest; for (unpair S piece, ChessPieceProfile1 p : multiMapToPairs(profiles)) { float sideProfileDiff = floatRatio(byteArraysTotalDiff(profile.sideProfile, p.sideProfile), l(p.sideProfile)); float fillGradeDiff = abs(profile.fillGrade-p.fillGrade); double score = magicCombine(sideProfileDiff, fillGradeDiff); lowest.put(pair(piece, p), score); } best = lowest.pair(); runnerUp = lowest.runnerUpPair(); ret !lowest.has() ? null : pair(pairA(lowest!), errorToPercent(lowest.score())); } // for humans double errorToPercent(double d) { ret 100-d; } double magicCombine(double sideProfileScore, double fillGradeScore) { double sum = sideProfileScore+fillGradeScore/magicCombineFactor; //print("magicCombine " + sideProfileScore + "*" + magicCombineFactor + "+" + fillGradeScore + " = " + sum); ret sum; } toString { ret shortClassName(this) + "(v" + version + ", " + nProfiles(l(profiles)) + ")"; } ChessOCR_RecognizedBoard recognizeBoard(BufferedImage board) { new ChessOCR_RecognizedBoard out; LL squareImages = bufferedImageMNGrid(board, 8, 8); out.pieces = new L; for (BufferedImage img : concatLists(squareImages)) out.pieces.add(recognize(img)); out.score = doubleAvg(pairsB(out.pieces)); out.fen = chess_makeFEN(listToChunks(8, pairsA(out.pieces))); ret out; } }