!7 cmodule ChessBoardRecognizer { S segmenterPreset; ParameterizedSegmenter segmenter; S status; S selectedSquare; S piece; LPair recognized; transient int recreatedSquareSize = 32; transient ReliableSingleThread rst = dm_rst(module(), r recognize); transient BufferedImage img, board, square, recreated; transient LL squareImages; transient ImageSurface isInput, isBoard, isSquare, isRecreated; transient LS pieces = ai_chessPieces(); transient Map> recreateStock; // false = dark, true = light visualize { isBoard = jImageSurface(); imageSurfaceOnHover(isBoard, voidfunc(Pt p) { if (board == null || p == null) ret with noToolTip(isBoard); setToolTip(isBoard, toolTipForSquare(boardPointToSquare(p)); }); imageSurfaceOnLeftClick(isBoard, voidfunc(Pt p) { selectSquare(boardPointToSquare(p)) }); ret withCalc(rst, jvsplit( jCenteredSection("Input image", jscroll_center(isInput = jImageSurface(img))), northCenterAndSouthWithMargins( centerAndEastWithMargin(withLabel("Segmenter preset: ", main.onChange(rst, dm_comboBox segmenterPreset(agiBlue_segmenterPresetNames()))), jbutton("Recognize", rst)), hgridWithSpacing( jCenteredSection("Recreated", jscroll_center(isRecreated = jImageSurface(recreated))), jCenteredSection("Chess board", jscroll_center(isBoard)), jCenteredLiveValueSection(dm_calculatedLiveValue(S, () -> "Selected square" + (empty(selectedSquare) ? "" : " (" + selectedSquare + ")")), centerAndSouthWithMargin( jscroll_center(isSquare = jImageSurface(square)), rightAlignedLine( withLabel("Piece: ", dm_comboBox('piece, pieces)), jbutton("Save & upload", rThread saveAndUploadPiece)) ))), dm_label('status) ))); } void recognize { segmenter = parameterizedSegmenterFromAGIBlue(segmenterPreset); if (segmenter == null) ret with infoBox(status("No segmenter")); //img = dm_shootScreenHidingOS(); if (isInput != null) img = isInput.getImage(); L rects = segmenter.get(img); if (empty(rects)) ret with status("No chess board found"); Rect r = first(rects); if (l(rects) > 1) status("More than one chess board found, choosing one"); else status("Chess board found at: " + r); board = clipBufferedImage(img, r); squareImages = bufferedImageMNGrid(board, 8, 8); if (isBoard != null) isBoard.setImageAndZoomToDisplay( mergeBufferedImagesVertically( map(bufferedImageNVerticalSlices(8, board), i -> mergeBufferedImagesHorizontally(bufferedImageNHorizontalSlices(8, i))))); new ChessPieceRecognizer pieceRecognizer; pieceRecognizer.load(); recognized = new L; for (BufferedImage img : concatLists(squareImages)) recognized.add(pieceRecognizer.recognize(img)); change(); print(recognized); recreate(); } S status(S status) { setField(+status); ret status; } O _getReloadData() { ret img; } void _setReloadData(BufferedImage img) { this.img = img; } void setImage(BufferedImage img) { this.img = img; if (isInput != null) isInput.setImageAndZoomToDisplay(img); rst.trigger(); } S boardPointToSquare(Pt p) { double space = imageMergeSpacing(); double squareW = board.getWidth()/8.0+space; double squareH = board.getHeight()/8.0+space; int x = clamp(iratio_floor(p.x+space/2, squareW), 0, 7); int y = clamp(iratio_floor(p.y+space/2, squareH), 0, 7); ret strCharPlus('A', x) + (8-y); } void saveAndUploadPiece enter { if (square == null || empty(piece)) ret; temp tempInfoBox_noHide("Uploading..."); S imageURL = uploadToImageServer("Chess square: " + piece, square); agiBlue_postInSlice(agiBlue_chessPieceImagesSlice(), imageURL, "is", piece); agiBlue_postInSlice(agiBlue_chessPieceImagesSlice(), imageURL, "was on square", selectedSquare); infoBox("Uploaded!"); if (mapGet(mapGet(recreateStock, chess_isLightSquare(selectedSquare)), piece) == null) recreateStock = null; rst.trigger(); } void recreate { if (recognized == null) ret; if (recreateStock == null) recreateStock = mapToValues(ll(false, true), light -> mapValues(pairsToMap(reversePairs(chessPieceImagesFromAGIBlue(light))), imageID -> resizeImage(loadImage2(imageID), recreatedSquareSize, recreatedSquareSize))); new L out; int i = 0; for (S piece : pairsA(recognized)) { bool light = even(i/8+i%8); out.add(or_func(mapGet(mapGet(recreateStock, light), piece), () -> whiteImage(recreatedSquareSize, recreatedSquareSize))); ++i; } new L rows; for (L row : listToChunks(out, 8)) rows.add(mergeBufferedImagesHorizontally(row, spacing := 0)); recreated = mergeBufferedImagesVertically(rows, spacing := 0); if (isRecreated != null) isRecreated.setImageAndZoomToDisplay(recreated); } Pair recognitionForSquare(S square) { int x, y = unpair chess_squareToPos(square); ret _get(recognized, y*8+x); } S toolTipForSquare(S square) { Pair rec = recognitionForSquare(square); ret square + " - " + (rec == null ? "?" : rec.a + " [" + iround(rec.b*100) + "%]"); } void selectSquare(S square) { setField(selectedSquare := square); S rec = pairA(recognitionForSquare(selectedSquare)); if (rec != null) setField(piece := rec); int x, y = unpair chess_squareToPos(selectedSquare); square = _get(_get(squareImages, y), x); //printVars_str("leftClick", +p, +selectedSquare, +x, +y, +square); isSquare.setImage(this.square); } }