@ -0,0 +1,73 @@
|
|||||||
|
# OCR Diagram Tools
|
||||||
|
|
||||||
|
This suite of tools is to help create a simpler way to get braille diagrams made.
|
||||||
|
This works upon the *original* image and does not create a new diagram from scratch.
|
||||||
|
I believe this is optimal for most cases.
|
||||||
|
|
||||||
|
You can run each tool individually with `cargo run --bin <binary_name_here> [arg1] [arg2]`.
|
||||||
|
|
||||||
|
Go to `braille-diagram-tools` to use the binaries.
|
||||||
|
|
||||||
|
## btt-get-ocr
|
||||||
|
|
||||||
|
`btt-get-ocr [diagram.png] [out.json]`
|
||||||
|
|
||||||
|
This program take the image at `diagram.png` (this is the default value) and output OCR data in JSON format into `out.json` (default).
|
||||||
|
|
||||||
|
## btt-label-ocr
|
||||||
|
|
||||||
|
`btt-label-ocr [diagram.png] [out.json]`
|
||||||
|
|
||||||
|
This program takes the OCR data and lays it overtop the diagram.
|
||||||
|
How so?
|
||||||
|
It adds a rectangular box around each OCRed piece of text, along with a label (usually a number starting from 0) to the left of the box.
|
||||||
|
This allows you to see how acurate the OCR was and to change what is broken with this next tool:
|
||||||
|
|
||||||
|
## btt-edit-tools
|
||||||
|
|
||||||
|
`btt-edit-tools [out.json]`
|
||||||
|
|
||||||
|
The program edits the json (NOT in place) with advanced manipulation functions to help with OCR-related tasks.
|
||||||
|
The id to the left of the box is very useful right about now.
|
||||||
|
|
||||||
|
Here is a list of all the commands you can use while running `btt-edit-tools`:
|
||||||
|
|
||||||
|
```
|
||||||
|
merge|id1|id2 # merges two OCRed sections together, least of x and y to greatest of x+width and y+height.
|
||||||
|
vsplit|id # split OCRed section into top and bottom pieces.
|
||||||
|
hsplit|id # split OCRed section into left and right pieces.
|
||||||
|
add|x|y|w|h # add a new OCR section with x, y, width and height.
|
||||||
|
rem|id # remove an OCR section
|
||||||
|
triml|id|dw # change width by dw (delta width)
|
||||||
|
trimr|id|dw # change width by dw (delta width) and move x the same amount (plus)
|
||||||
|
trimt|id|dh # change height by dh (delta height) and move y the same amount (minus)
|
||||||
|
trimb|id|dh # change height by dh (delta height)
|
||||||
|
movel|id|dx # change x by dx (positive = left move)
|
||||||
|
mover|id|dx # change x by dx (positive = right move)
|
||||||
|
moveu|id|dy # change y by dy (positive = upward move)
|
||||||
|
moved|id|dy # change y by dy (positive = downward move)
|
||||||
|
paddl|id|dx # change x by dx and add to width same amount
|
||||||
|
paddr|id|dx # add dx to width
|
||||||
|
paddt|id|dy # negate dy from y
|
||||||
|
paddb|id|dy # add dy to height
|
||||||
|
text|id|some string # make the text of some string (used for braille printing later) associated with id
|
||||||
|
save|filename # save current json data to filename
|
||||||
|
```
|
||||||
|
|
||||||
|
**NOTE: currently, any command error will crash the program;
|
||||||
|
this will be fixed eventually.**
|
||||||
|
|
||||||
|
## btt-whiteout-labels
|
||||||
|
|
||||||
|
`btt-whiteout-labels [out.img] [out.json]`
|
||||||
|
|
||||||
|
This program takes an image and a json file and places a filled white box overtop all OCR sections.
|
||||||
|
This is useful before running the next tool.
|
||||||
|
|
||||||
|
## btt-add-braille
|
||||||
|
|
||||||
|
`btt-add-braille [out.img] [out.json] [font_size]`
|
||||||
|
|
||||||
|
This program takes an image and a json file and based upon the position (x,y) adds text of `font_size` (default `20.0`) where each OCR section is in the JSON.
|
||||||
|
This often does not work the first time due to the braille using more characters than standard print.
|
||||||
|
You'll generally need to go back to editing the json, running `whiteout` and running this tool a few times to get it just right.
|
@ -0,0 +1,16 @@
|
|||||||
|
[package]
|
||||||
|
name = "braille-diagram-tools"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
leptess = "^0.13.1"
|
||||||
|
ocr-json-common = { path = "../ocr-json-common" }
|
||||||
|
serde_json = "^1.0"
|
||||||
|
imageproc = "^0.22.0"
|
||||||
|
image = "^0.23.14"
|
||||||
|
louis = { git = "https://github.com/TTWNO/liblouis-rust" }
|
||||||
|
rusttype = "^0.9.2"
|
||||||
|
text_io = "^0.1.9"
|
After Width: | Height: | Size: 57 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"0","hint":"A","confidence":0,"width":89,"height":94,"x":212,"y":105},{"id":"1","hint":"AND","confidence":0,"width":333,"height":112,"x":512,"y":500},{"id":"3","hint":"AB","confidence":0,"width":180,"height":130,"x":1080,"y":170},{"id":"2","hint":"B","confidence":0,"width":140,"height":95,"x":162,"y":257}]
|
@ -0,0 +1 @@
|
|||||||
|
[{"id":"0","hint":"A\n","confidence":96,"width":89,"height":94,"x":212,"y":105},{"id":"1","hint":"AND","confidence":0,"width":333,"height":112,"x":512,"y":500},{"id":"3","hint":"AB","confidence":0,"width":200,"height":100,"x":1075,"y":187},{"id":"2","hint":"B","confidence":0,"width":89,"height":98,"x":212,"y":254}]
|
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 16 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"0","hint":"A","confidence":0,"width":89,"height":94,"x":212,"y":105},{"id":"1","hint":"AND","confidence":0,"width":333,"height":112,"x":512,"y":500},{"id":"3","hint":"AB","confidence":0,"width":180,"height":130,"x":1080,"y":170},{"id":"2","hint":"B","confidence":0,"width":140,"height":95,"x":162,"y":257}]
|
@ -0,0 +1 @@
|
|||||||
|
[{"id":"0","hint":"1\n","confidence":90,"width":14,"height":23,"x":322,"y":4},{"id":"1","hint":"1\n","confidence":80,"width":14,"height":23,"x":571,"y":4},{"id":"2","hint":"1\n","confidence":90,"width":14,"height":23,"x":821,"y":4},{"id":"3","hint":"1\n","confidence":90,"width":14,"height":23,"x":1070,"y":4},{"id":"4","hint":"Clock","confidence":0,"width":130,"height":55,"x":6,"y":28},{"id":"5","hint":"0","confidence":0,"width":48,"height":28,"x":418,"y":38},{"id":"6","hint":"0","confidence":0,"width":62,"height":28,"x":663,"y":38},{"id":"7","hint":"0","confidence":0,"width":78,"height":28,"x":912,"y":38},{"id":"8","hint":"Load","confidence":0,"width":134,"height":54,"x":5,"y":91},{"id":"9","hint":"D","confidence":0,"width":130,"height":53,"x":4,"y":167},{"id":"10","hint":"Q","confidence":0,"width":133,"height":35,"x":6,"y":273},{"id":"11","hint":"0","confidence":0,"width":63,"height":28,"x":168,"y":38}]
|
After Width: | Height: | Size: 26 KiB |
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 8.0 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"0","hint":"1\n","confidence":90,"width":14,"height":23,"x":322,"y":4},{"id":"1","hint":"1\n","confidence":80,"width":14,"height":23,"x":571,"y":4},{"id":"2","hint":"1\n","confidence":90,"width":14,"height":23,"x":821,"y":4},{"id":"3","hint":"1\n","confidence":90,"width":14,"height":23,"x":1070,"y":4},{"id":"11","hint":"","confidence":0,"width":63,"height":28,"x":168,"y":38},{"id":"4","hint":"Clock","confidence":0,"width":130,"height":55,"x":6,"y":28},{"id":"5","hint":"0","confidence":0,"width":48,"height":28,"x":418,"y":38},{"id":"6","hint":"0","confidence":0,"width":62,"height":28,"x":663,"y":38},{"id":"7","hint":"0","confidence":0,"width":78,"height":28,"x":912,"y":38},{"id":"8","hint":"Load","confidence":0,"width":134,"height":54,"x":5,"y":91},{"id":"9","hint":"D","confidence":0,"width":130,"height":53,"x":4,"y":167},{"id":"10","hint":"Q","confidence":0,"width":133,"height":35,"x":6,"y":273}]
|
@ -0,0 +1 @@
|
|||||||
|
[{"id":"0","hint":"branch","confidence":0,"width":78,"height":19,"x":1015,"y":15},{"id":"1","hint":"ALU operation","confidence":0,"width":162,"height":25,"x":792,"y":431},{"id":"2","hint":"add","confidence":0,"width":47,"height":23,"x":567,"y":317},{"id":"3","hint":"pc","confidence":0,"width":39,"height":42,"x":97,"y":557},{"id":"6","hint":"add","confidence":0,"width":47,"height":21,"x":362,"y":329},{"id":"12","hint":"MemRead","confidence":0,"width":125,"height":33,"x":1143,"y":735},{"id":"21","hint":"m","confidence":0,"width":25,"height":27,"x":385,"y":90},{"id":"22","hint":"m","confidence":0,"width":33,"height":28,"x":840,"y":589},{"id":"24","hint":"u","confidence":0,"width":25,"height":25,"x":385,"y":125},{"id":"26","hint":"x","confidence":0,"width":25,"height":25,"x":385,"y":155},{"id":"28","hint":"x","confidence":0,"width":30,"height":30,"x":840,"y":655},{"id":"27","hint":"u","confidence":0,"width":30,"height":30,"x":840,"y":620},{"id":"29","hint":"m","confidence":0,"width":30,"height":30,"x":844,"y":310},{"id":"30","hint":"u","confidence":0,"width":30,"height":30,"x":845,"y":340},{"id":"31","hint":"x","confidence":0,"width":30,"height":30,"x":845,"y":375},{"id":"33","hint":"0","confidence":0,"width":57,"height":25,"x":1000,"y":598},{"id":"4","hint":"Data","confidence":0,"width":56,"height":24,"x":522,"y":463},{"id":"11","hint":"Registers","confidence":0,"width":118,"height":28,"x":624,"y":564},{"id":"14","hint":"Register #","confidence":0,"width":120,"height":29,"x":519,"y":599},{"id":"5","hint":"Register #","confidence":0,"width":119,"height":28,"x":522,"y":532},{"id":"19","hint":"Register #","confidence":0,"width":119,"height":28,"x":522,"y":663},{"id":"25","hint":"control","confidence":0,"width":134,"height":45,"x":527,"y":877},{"id":"13","hint":"Address","confidence":0,"width":98,"height":24,"x":1068,"y":560},{"id":"17","hint":"Data memory","confidence":0,"width":151,"height":76,"x":1094,"y":612},{"id":"23","hint":"Data","confidence":0,"width":56,"height":24,"x":1072,"y":718},{"id":"9","hint":"Instruction","confidence":0,"width":120,"height":23,"x":312,"y":565},{"id":"8","hint":"Address","confidence":0,"width":98,"height":23,"x":192,"y":565},{"id":"16","hint":"Insturction","confidence":0,"width":135,"height":23,"x":199,"y":628},{"id":"18","hint":"memory","confidence":0,"width":104,"height":24,"x":215,"y":665},{"id":"7","hint":"MemWrite","confidence":0,"width":139,"height":24,"x":1128,"y":516},{"id":"32","hint":"ALU","confidence":0,"width":52,"height":25,"x":913,"y":520},{"id":"20","hint":"RegWrite","confidence":0,"width":118,"height":28,"x":642,"y":681},{"id":"15","hint":"4","confidence":0,"width":25,"height":25,"x":278,"y":237}]
|
After Width: | Height: | Size: 171 KiB |
After Width: | Height: | Size: 107 KiB |
After Width: | Height: | Size: 46 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"15","hint":"","confidence":0,"width":25,"height":25,"x":278,"y":237},{"id":"0","hint":"branch","confidence":0,"width":78,"height":19,"x":1015,"y":15},{"id":"1","hint":"ALU operation","confidence":0,"width":162,"height":25,"x":792,"y":431},{"id":"2","hint":"add","confidence":0,"width":47,"height":23,"x":567,"y":317},{"id":"3","hint":"pc","confidence":0,"width":39,"height":42,"x":97,"y":557},{"id":"6","hint":"add","confidence":0,"width":47,"height":21,"x":362,"y":329},{"id":"12","hint":"MemRead","confidence":0,"width":125,"height":33,"x":1143,"y":735},{"id":"21","hint":"m","confidence":0,"width":25,"height":27,"x":385,"y":90},{"id":"22","hint":"m","confidence":0,"width":33,"height":28,"x":840,"y":589},{"id":"24","hint":"u","confidence":0,"width":25,"height":25,"x":385,"y":125},{"id":"26","hint":"x","confidence":0,"width":25,"height":25,"x":385,"y":155},{"id":"28","hint":"x","confidence":0,"width":30,"height":30,"x":840,"y":655},{"id":"27","hint":"u","confidence":0,"width":30,"height":30,"x":840,"y":620},{"id":"29","hint":"m","confidence":0,"width":30,"height":30,"x":844,"y":310},{"id":"30","hint":"u","confidence":0,"width":30,"height":30,"x":845,"y":340},{"id":"31","hint":"x","confidence":0,"width":30,"height":30,"x":845,"y":375},{"id":"33","hint":"0","confidence":0,"width":57,"height":25,"x":1000,"y":598},{"id":"4","hint":"Data","confidence":0,"width":56,"height":24,"x":522,"y":463},{"id":"11","hint":"Registers","confidence":0,"width":118,"height":28,"x":624,"y":564},{"id":"14","hint":"Register #","confidence":0,"width":120,"height":29,"x":519,"y":599},{"id":"5","hint":"Register #","confidence":0,"width":119,"height":28,"x":522,"y":532},{"id":"19","hint":"Register #","confidence":0,"width":119,"height":28,"x":522,"y":663},{"id":"20","hint":"RegWrite","confidence":0,"width":108,"height":28,"x":662,"y":671},{"id":"25","hint":"control","confidence":0,"width":134,"height":45,"x":527,"y":877},{"id":"13","hint":"Address","confidence":0,"width":98,"height":24,"x":1068,"y":560},{"id":"17","hint":"Data memory","confidence":0,"width":151,"height":76,"x":1094,"y":612},{"id":"23","hint":"Data","confidence":0,"width":56,"height":24,"x":1072,"y":718},{"id":"32","hint":"ALU","confidence":0,"width":52,"height":25,"x":933,"y":560},{"id":"9","hint":"Instruction","confidence":0,"width":120,"height":23,"x":312,"y":565},{"id":"8","hint":"Address","confidence":0,"width":98,"height":23,"x":192,"y":565},{"id":"16","hint":"Insturction","confidence":0,"width":135,"height":23,"x":199,"y":628},{"id":"18","hint":"memory","confidence":0,"width":104,"height":24,"x":215,"y":665},{"id":"7","hint":"MemWrite","confidence":0,"width":139,"height":24,"x":1128,"y":516}]
|
After Width: | Height: | Size: 53 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"9","hint":"S3","confidence":0,"width":61,"height":60,"x":300,"y":517},{"id":"10","hint":"S2","confidence":0,"width":60,"height":59,"x":543,"y":517},{"id":"11","hint":"S1","confidence":0,"width":60,"height":59,"x":786,"y":517},{"id":"12","hint":"S0","confidence":0,"width":60,"height":60,"x":1029,"y":517},{"id":"6","hint":"FA","confidence":0,"width":133,"height":61,"x":244,"y":260},{"id":"4","hint":"FA","confidence":0,"width":112,"height":68,"x":491,"y":260},{"id":"7","hint":"FA","confidence":0,"width":124,"height":81,"x":737,"y":260},{"id":"13","hint":"FA","confidence":0,"width":89,"height":76,"x":991,"y":260},{"id":"14","hint":"B3","confidence":0,"width":66,"height":59,"x":321,"y":8},{"id":"0","hint":"A3","confidence":0,"width":66,"height":59,"x":255,"y":8},{"id":"1","hint":"A2","confidence":0,"width":66,"height":58,"x":498,"y":8},{"id":"15","hint":"B2","confidence":0,"width":66,"height":58,"x":564,"y":8},{"id":"2","hint":"A1","confidence":0,"width":66,"height":58,"x":741,"y":8},{"id":"16","hint":"B1","confidence":0,"width":66,"height":58,"x":807,"y":8},{"id":"8","hint":"C0","confidence":0,"width":77,"height":61,"x":1267,"y":270},{"id":"5","hint":"C4","confidence":0,"width":87,"height":60,"x":3,"y":268},{"id":"18","hint":"C3","confidence":0,"width":80,"height":42,"x":390,"y":225},{"id":"19","hint":"C3","confidence":0,"width":75,"height":45,"x":635,"y":225},{"id":"20","hint":"C1","confidence":0,"width":72,"height":45,"x":877,"y":225},{"id":"17","hint":"B0","confidence":0,"width":66,"height":59,"x":1050,"y":8},{"id":"3","hint":"A0","confidence":0,"width":66,"height":59,"x":984,"y":8}]
|
After Width: | Height: | Size: 32 KiB |
After Width: | Height: | Size: 13 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"5","hint":"C4","confidence":0,"width":62,"height":60,"x":28,"y":268},{"id":"9","hint":"S3","confidence":0,"width":61,"height":60,"x":300,"y":517},{"id":"10","hint":"S2","confidence":0,"width":60,"height":59,"x":543,"y":517},{"id":"11","hint":"S1","confidence":0,"width":60,"height":59,"x":786,"y":517},{"id":"12","hint":"S0","confidence":0,"width":60,"height":60,"x":1029,"y":517},{"id":"6","hint":"FA","confidence":0,"width":133,"height":61,"x":244,"y":260},{"id":"14","hint":"","confidence":0,"width":66,"height":59,"x":321,"y":8},{"id":"1","hint":"AsB5","confidence":0,"width":66,"height":58,"x":498,"y":8},{"id":"15","hint":"","confidence":0,"width":66,"height":58,"x":564,"y":8},{"id":"2","hint":"A,B4","confidence":0,"width":66,"height":58,"x":741,"y":8},{"id":"16","hint":"","confidence":0,"width":66,"height":58,"x":807,"y":8},{"id":"3","hint":"AnBg","confidence":0,"width":66,"height":59,"x":984,"y":8},{"id":"17","hint":"","confidence":0,"width":66,"height":59,"x":1050,"y":8},{"id":"0","hint":"ALB4","confidence":0,"width":66,"height":59,"x":255,"y":8},{"id":"18","hint":"","confidence":0,"width":70,"height":67,"x":405,"y":200},{"id":"19","hint":"C3","confidence":0,"width":75,"height":70,"x":645,"y":200},{"id":"20","hint":"C1","confidence":0,"width":72,"height":70,"x":885,"y":200},{"id":"8","hint":"C0","confidence":0,"width":62,"height":61,"x":1282,"y":270},{"id":"4","hint":"FA","confidence":0,"width":112,"height":68,"x":491,"y":260},{"id":"7","hint":"FA","confidence":0,"width":124,"height":81,"x":737,"y":260},{"id":"13","hint":"FA","confidence":0,"width":89,"height":76,"x":991,"y":260}]
|
@ -0,0 +1 @@
|
|||||||
|
[{"id":"0","hint":"Half Adder","confidence":0,"width":154,"height":24,"x":189,"y":38},{"id":"2","hint":"A","confidence":0,"width":21,"height":26,"x":28,"y":107},{"id":"1","hint":"B","confidence":0,"width":21,"height":26,"x":28,"y":133},{"id":"3","hint":"S (sum)","confidence":0,"width":122,"height":25,"x":437,"y":124},{"id":"5","hint":"C (carry out)","confidence":0,"width":230,"height":28,"x":436,"y":232}]
|
After Width: | Height: | Size: 15 KiB |
After Width: | Height: | Size: 7.5 KiB |
After Width: | Height: | Size: 68 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"4","hint":"OR","confidence":0,"width":212,"height":118,"x":439,"y":477},{"id":"2","hint":"A+B","confidence":0,"width":319,"height":109,"x":996,"y":171},{"id":"0","hint":"A","confidence":0,"width":189,"height":93,"x":102,"y":83},{"id":"1","hint":"B","confidence":0,"width":194,"height":130,"x":100,"y":227}]
|
After Width: | Height: | Size: 48 KiB |
After Width: | Height: | Size: 23 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"4","hint":"OR","confidence":0,"width":212,"height":118,"x":439,"y":477},{"id":"2","hint":"A+B","confidence":0,"width":319,"height":109,"x":996,"y":171},{"id":"0","hint":"A","confidence":0,"width":189,"height":93,"x":102,"y":83},{"id":"1","hint":"B","confidence":0,"width":194,"height":130,"x":100,"y":227}]
|
After Width: | Height: | Size: 87 KiB |
@ -0,0 +1 @@
|
|||||||
|
[{"id":"0","hint":"A\n","confidence":96,"width":89,"height":93,"x":171,"y":46},{"id":"2","hint":"XOR\n","confidence":77,"width":310,"height":118,"x":440,"y":435},{"id":"1","hint":"B","confidence":0,"width":162,"height":116,"x":89,"y":185},{"id":"3","hint":"A$=[\"6B","confidence":0,"width":350,"height":125,"x":1025,"y":125}]
|
After Width: | Height: | Size: 54 KiB |
After Width: | Height: | Size: 26 KiB |
@ -0,0 +1,22 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
prefix="btt-"
|
||||||
|
cmd=$1
|
||||||
|
extra=$2
|
||||||
|
|
||||||
|
|
||||||
|
if [[ "$cmd" == "edit-json" ]]; then
|
||||||
|
# TODO: find easier way
|
||||||
|
json=$(test -z "$extra" && echo "./out.json" || echo "$extra")
|
||||||
|
cargo run --bin "${prefix}edit-tools" "$json"
|
||||||
|
elif [[ "$cmd" == "whiteout" ]]; then
|
||||||
|
json=$(test -z "$extra" && echo "./out.json" || echo "$extra")
|
||||||
|
cargo run --bin "${prefix}whiteout-labels" ./diagram.png "$json"
|
||||||
|
elif [[ "$cmd" == "gen-ocr" ]]; then
|
||||||
|
cargo run --bin "${prefix}get-ocr" ./diagram.png > out.json
|
||||||
|
elif [[ "$cmd" == "show-labels" ]]; then
|
||||||
|
cargo run --bin "${prefix}label-ocr" ./diagram.png ./out.json
|
||||||
|
elif [[ "$cmd" == "add-braille" ]]; then
|
||||||
|
json=$(test -z "$extra" && echo "./out.json" || echo "$extra")
|
||||||
|
cargo run --bin "${prefix}add-braille" ./out.png "$json" $3
|
||||||
|
fi
|
@ -0,0 +1,99 @@
|
|||||||
|
DejaVu Fonts License
|
||||||
|
|
||||||
|
Fonts are (c) Bitstream (see below). DejaVu changes are in public domain.
|
||||||
|
Glyphs imported from Arev fonts are (c) Tavmjong Bah (see below)
|
||||||
|
|
||||||
|
Bitstream Vera Fonts Copyright
|
||||||
|
———————————————
|
||||||
|
|
||||||
|
Copyright (c) 2003 by Bitstream, Inc. All Rights Reserved. Bitstream Vera is
|
||||||
|
a trademark of Bitstream, Inc.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of the fonts accompanying this license (“Fonts”) and associated
|
||||||
|
documentation files (the “Font Software”), to reproduce and distribute the
|
||||||
|
Font Software, including without limitation the rights to use, copy, merge,
|
||||||
|
publish, distribute, and/or sell copies of the Font Software, and to permit
|
||||||
|
persons to whom the Font Software is furnished to do so, subject to the
|
||||||
|
following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice shall
|
||||||
|
be included in all copies of one or more of the Font Software typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in particular
|
||||||
|
the designs of glyphs or characters in the Fonts may be modified and
|
||||||
|
additional glyphs or characters may be added to the Fonts, only if the fonts
|
||||||
|
are renamed to names not containing either the words “Bitstream” or the word
|
||||||
|
“Vera”.
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts or Font
|
||||||
|
Software that has been modified and is distributed under the “Bitstream
|
||||||
|
Vera” names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but no
|
||||||
|
copy of one or more of the Font Software typefaces may be sold by itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS
|
||||||
|
OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF COPYRIGHT, PATENT,
|
||||||
|
TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL BITSTREAM OR THE GNOME
|
||||||
|
FOUNDATION BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, INCLUDING
|
||||||
|
ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL DAMAGES,
|
||||||
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
|
||||||
|
THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM OTHER DEALINGS IN THE
|
||||||
|
FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the names of Gnome, the Gnome
|
||||||
|
Foundation, and Bitstream Inc., shall not be used in advertising or
|
||||||
|
otherwise to promote the sale, use or other dealings in this Font Software
|
||||||
|
without prior written authorization from the Gnome Foundation or Bitstream
|
||||||
|
Inc., respectively. For further information, contact: fonts at gnome dot
|
||||||
|
org.
|
||||||
|
|
||||||
|
Arev Fonts Copyright
|
||||||
|
———————————————
|
||||||
|
|
||||||
|
Copyright (c) 2006 by Tavmjong Bah. All Rights Reserved.
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the fonts accompanying this license (“Fonts”) and
|
||||||
|
associated documentation files (the “Font Software”), to reproduce
|
||||||
|
and distribute the modifications to the Bitstream Vera Font Software,
|
||||||
|
including without limitation the rights to use, copy, merge, publish,
|
||||||
|
distribute, and/or sell copies of the Font Software, and to permit
|
||||||
|
persons to whom the Font Software is furnished to do so, subject to
|
||||||
|
the following conditions:
|
||||||
|
|
||||||
|
The above copyright and trademark notices and this permission notice
|
||||||
|
shall be included in all copies of one or more of the Font Software
|
||||||
|
typefaces.
|
||||||
|
|
||||||
|
The Font Software may be modified, altered, or added to, and in
|
||||||
|
particular the designs of glyphs or characters in the Fonts may be
|
||||||
|
modified and additional glyphs or characters may be added to the
|
||||||
|
Fonts, only if the fonts are renamed to names not containing either
|
||||||
|
the words “Tavmjong Bah” or the word “Arev”.
|
||||||
|
|
||||||
|
This License becomes null and void to the extent applicable to Fonts
|
||||||
|
or Font Software that has been modified and is distributed under the
|
||||||
|
“Tavmjong Bah Arev” names.
|
||||||
|
|
||||||
|
The Font Software may be sold as part of a larger software package but
|
||||||
|
no copy of one or more of the Font Software typefaces may be sold by
|
||||||
|
itself.
|
||||||
|
|
||||||
|
THE FONT SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL
|
||||||
|
TAVMJONG BAH BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of Tavmjong Bah shall not
|
||||||
|
be used in advertising or otherwise to promote the sale, use or other
|
||||||
|
dealings in this Font Software without prior written authorization
|
||||||
|
from Tavmjong Bah. For further information, contact: tavmjong @ free
|
||||||
|
. fr.
|
@ -0,0 +1,51 @@
|
|||||||
|
use std::fs;
|
||||||
|
use ocr_json_common::TextBox;
|
||||||
|
use image::{Rgba};
|
||||||
|
use imageproc::drawing::{
|
||||||
|
draw_text_mut,
|
||||||
|
};
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
use rusttype::{Font, Scale};
|
||||||
|
use louis::Louis;
|
||||||
|
use louis::modes::DOTS_UNICODE;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let img_file_name = if env::args().count() >= 2 {
|
||||||
|
env::args().nth(1).unwrap()
|
||||||
|
} else {
|
||||||
|
panic!("Please enter a target file path for image")
|
||||||
|
};
|
||||||
|
let json_file_name = if env::args().count() >= 3 {
|
||||||
|
env::args().nth(2).unwrap()
|
||||||
|
} else{
|
||||||
|
panic!("Please enter a target file path for json")
|
||||||
|
};
|
||||||
|
let font_size_str = if env::args().count() >= 4 {
|
||||||
|
env::args().nth(3).unwrap()
|
||||||
|
} else {
|
||||||
|
"20.0".to_string()
|
||||||
|
};
|
||||||
|
let font_size = font_size_str.parse().unwrap();
|
||||||
|
let json = fs::read_to_string(json_file_name).expect("There was an error reading the file.");
|
||||||
|
let ocr_rects: Vec<TextBox> = serde_json::from_str(&json).unwrap();
|
||||||
|
let image_path = Path::new(&img_file_name);
|
||||||
|
let black = Rgba([0u8, 0u8, 0u8, 255u8]);
|
||||||
|
|
||||||
|
let font_data: &[u8] = include_bytes!("../../fonts/UBraille.ttf");
|
||||||
|
let font: Font<'static> = Font::try_from_bytes(font_data).expect("Error loading font.");
|
||||||
|
|
||||||
|
let mut img = image::open(image_path).unwrap();
|
||||||
|
|
||||||
|
let brl = Louis::new().unwrap();
|
||||||
|
|
||||||
|
// run OCR on each word bounding box
|
||||||
|
for rect in &ocr_rects {
|
||||||
|
let text = rect.hint.clone();
|
||||||
|
let brl_text = brl.translate_simple("en_US.tbl", &text, false, DOTS_UNICODE);
|
||||||
|
println!("[{}]: {}", rect.id, brl_text);
|
||||||
|
draw_text_mut(&mut img, black, rect.x.try_into().unwrap(), rect.y.try_into().unwrap(), Scale::uniform(font_size), &font, &brl_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
img.save("out.png").unwrap();
|
||||||
|
}
|
@ -0,0 +1,473 @@
|
|||||||
|
use ocr_json_common::TextBox;
|
||||||
|
use serde_json;
|
||||||
|
use std::{
|
||||||
|
env,
|
||||||
|
cmp::min,
|
||||||
|
process::Command,
|
||||||
|
io::Write,
|
||||||
|
fs::OpenOptions,
|
||||||
|
fs,
|
||||||
|
};
|
||||||
|
use text_io::read;
|
||||||
|
|
||||||
|
// TODO: make more extensible
|
||||||
|
fn save_json(json: String, fname: &String) {
|
||||||
|
let mut file = OpenOptions::new().write(true).truncate(true).create(true).open(fname).expect("Unable to open file");
|
||||||
|
file.write_all(json.as_bytes()).expect("unable to write to file");
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: make more exensible!!!
|
||||||
|
fn reload_json(fname: &String) {
|
||||||
|
Command::new("cargo")
|
||||||
|
.arg("run")
|
||||||
|
.arg("--bin")
|
||||||
|
.arg("btt-label-ocr")
|
||||||
|
.arg("./diagram.png")
|
||||||
|
.arg(fname)
|
||||||
|
.output()
|
||||||
|
.expect("Failed to execute command");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_id(ids: &Vec<String>) -> String {
|
||||||
|
let mut new_id = 0;
|
||||||
|
let mut new_sid = format!("{}", new_id);
|
||||||
|
while ids.contains(&new_sid) {
|
||||||
|
new_sid = format!("{}", new_id);
|
||||||
|
new_id+=1;
|
||||||
|
}
|
||||||
|
new_sid
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rem_rect(boxes: &mut Vec<TextBox>, id: String) {
|
||||||
|
boxes.retain(|b| b.id != id);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn new_rect(boxes: &mut Vec<TextBox>, id: String, x: String, y: String, w: String, h: String) {
|
||||||
|
// TODO: unsafe
|
||||||
|
boxes.push(TextBox {
|
||||||
|
id,
|
||||||
|
x: x.parse().unwrap(),
|
||||||
|
y: y.parse().unwrap(),
|
||||||
|
width: w.parse().unwrap(),
|
||||||
|
height: h.parse().unwrap(),
|
||||||
|
hint: String::new(),
|
||||||
|
confidence: 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_text(boxes: &mut Vec<TextBox>, xid: String, new_text: String) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == xid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != xid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
confidence: 0,
|
||||||
|
hint: new_text.clone(),
|
||||||
|
x: bx.x,
|
||||||
|
y: bx.y,
|
||||||
|
width: bx.width,
|
||||||
|
height: bx.height,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn merge(boxes: &mut Vec<TextBox>, xid: String, yid: String) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == xid).unwrap();
|
||||||
|
// TODO: unsafe
|
||||||
|
let by = boxes.iter().find(|b| b.id == yid).unwrap();
|
||||||
|
let y = min(bx.y, by.y);
|
||||||
|
let x = min(bx.x, by.x);
|
||||||
|
let w = (bx.x - by.x).abs() + (if x == bx.x {by.width as i32} else {bx.width as i32});
|
||||||
|
let h = (bx.y - by.y).abs() + (if y == by.y {by.height as i32} else {bx.height as i32});
|
||||||
|
let text = format!("{} {}", bx.hint, by.hint);
|
||||||
|
let confi = 0;
|
||||||
|
let id = bx.id.clone();
|
||||||
|
boxes.retain(|b| b.id != xid && b.id != yid);
|
||||||
|
boxes.push(TextBox {
|
||||||
|
id,
|
||||||
|
confidence: confi,
|
||||||
|
hint: text,
|
||||||
|
x,
|
||||||
|
y,
|
||||||
|
width: w as u32,
|
||||||
|
height: h as u32,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn vsplit(boxes: &mut Vec<TextBox>, sid: String) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap();
|
||||||
|
|
||||||
|
let w = bx.width;
|
||||||
|
let x = bx.x;
|
||||||
|
let h = bx.height/2;
|
||||||
|
let y1 = bx.y;
|
||||||
|
let y2 = bx.y + (bx.height as i32)/2;
|
||||||
|
let hint = bx.hint.clone();
|
||||||
|
let mut tsplit = hint.split("\n"); // tesseract likes to use newlines for some reason... use to our advantage
|
||||||
|
let t1 = tsplit.next().unwrap_or("");
|
||||||
|
let t2 = tsplit.next().unwrap_or("");
|
||||||
|
let id1 = bx.id.clone();
|
||||||
|
let ids: Vec<String> = boxes.iter().map(|b| b.id.clone()).collect();
|
||||||
|
let id2 = new_id(&ids);
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox {
|
||||||
|
x,
|
||||||
|
y: y1,
|
||||||
|
hint: t1.to_string().clone(),
|
||||||
|
confidence: 0,
|
||||||
|
id: id1,
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
});
|
||||||
|
boxes.push(TextBox {
|
||||||
|
x,
|
||||||
|
y: y2,
|
||||||
|
hint: t2.to_string().clone(),
|
||||||
|
confidence: 0,
|
||||||
|
id: id2,
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn hsplit(boxes: &mut Vec<TextBox>, sid: String) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap();
|
||||||
|
|
||||||
|
let w = bx.width/2;
|
||||||
|
let y = bx.y;
|
||||||
|
let h = bx.height;
|
||||||
|
let x1 = bx.x;
|
||||||
|
let x2 = bx.x + (bx.width as i32)/2;
|
||||||
|
let hint = bx.hint.clone();
|
||||||
|
let mut tsplit = hint.split("\n"); // tesseract likes to use newlines for some reason... use to our advantage
|
||||||
|
let t1 = tsplit.next().unwrap_or("");
|
||||||
|
let t2 = tsplit.next().unwrap_or("");
|
||||||
|
let id1 = bx.id.clone();
|
||||||
|
let ids: Vec<String> = boxes.iter().map(|b| b.id.clone()).collect();
|
||||||
|
let id2 = new_id(&ids);
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox {
|
||||||
|
x: x1,
|
||||||
|
y,
|
||||||
|
hint: t1.to_string().clone(),
|
||||||
|
confidence: 0,
|
||||||
|
id: id1,
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
});
|
||||||
|
boxes.push(TextBox {
|
||||||
|
x: x2,
|
||||||
|
y,
|
||||||
|
hint: t2.to_string().clone(),
|
||||||
|
confidence: 0,
|
||||||
|
id: id2,
|
||||||
|
width: w,
|
||||||
|
height: h,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn triml(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x + mw,
|
||||||
|
width: bx.width - (mw as u32),
|
||||||
|
y: bx.y,
|
||||||
|
height: bx.height,
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn trimr(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x,
|
||||||
|
width: bx.width - (mw as u32),
|
||||||
|
y: bx.y,
|
||||||
|
height: bx.height,
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn trimt(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x,
|
||||||
|
width: bx.width,
|
||||||
|
y: bx.y + mw,
|
||||||
|
height: bx.height - (mw as u32),
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn trimb(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x,
|
||||||
|
width: bx.width,
|
||||||
|
y: bx.y,
|
||||||
|
height: bx.height - (mw as u32),
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn movel(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x - mw,
|
||||||
|
width: bx.width,
|
||||||
|
y: bx.y,
|
||||||
|
height: bx.height,
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn mover(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x + mw,
|
||||||
|
width: bx.width,
|
||||||
|
y: bx.y,
|
||||||
|
height: bx.height,
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn moveu(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x,
|
||||||
|
width: bx.width,
|
||||||
|
y: bx.y - mw,
|
||||||
|
height: bx.height,
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn moved(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x,
|
||||||
|
width: bx.width,
|
||||||
|
y: bx.y + mw,
|
||||||
|
height: bx.height,
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paddl(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x - mw,
|
||||||
|
width: bx.width + (mw as u32),
|
||||||
|
y: bx.y,
|
||||||
|
height: bx.height,
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn paddr(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x,
|
||||||
|
width: bx.width + (mw as u32),
|
||||||
|
y: bx.y,
|
||||||
|
height: bx.height,
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn paddt(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x,
|
||||||
|
width: bx.width,
|
||||||
|
y: bx.y - mw,
|
||||||
|
height: bx.height + (mw as u32),
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
fn paddb(boxes: &mut Vec<TextBox>, sid: String, mw: i32) {
|
||||||
|
// TODO: unsafe
|
||||||
|
let bx = boxes.iter().find(|b| b.id == sid).unwrap().clone();
|
||||||
|
boxes.retain(|b| b.id != sid);
|
||||||
|
boxes.push(TextBox{
|
||||||
|
id: bx.id,
|
||||||
|
hint: bx.hint,
|
||||||
|
x: bx.x,
|
||||||
|
width: bx.width,
|
||||||
|
y: bx.y,
|
||||||
|
height: bx.height + (mw as u32),
|
||||||
|
confidence: 0,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let json_fname = if env::args().count() == 2 {
|
||||||
|
env::args().nth(1).unwrap()
|
||||||
|
} else {
|
||||||
|
panic!("Please enter a file path");
|
||||||
|
};
|
||||||
|
let json_str = fs::read_to_string(json_fname.clone()).expect("There was an error reading the provided file.");
|
||||||
|
let mut boxes: Vec<TextBox> = serde_json::from_str(&json_str).unwrap();
|
||||||
|
let mut line: String = String::new();
|
||||||
|
println!("Type 'exit' to quit!");
|
||||||
|
while line != "exit" {
|
||||||
|
line = read!("{}\n");
|
||||||
|
let mut split = line.split("|");
|
||||||
|
let command = split.next();
|
||||||
|
if command == Some("merge") {
|
||||||
|
// TODO: not safe
|
||||||
|
let one_id = split.next().unwrap();
|
||||||
|
let two_id = split.next().unwrap();
|
||||||
|
merge(&mut boxes, one_id.to_string(), two_id.to_string());
|
||||||
|
} else if command == Some("vsplit") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
vsplit(&mut boxes, id.to_string());
|
||||||
|
} else if command == Some("hsplit") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
hsplit(&mut boxes, id.to_string());
|
||||||
|
} else if command == Some("triml") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
let px = split.next().unwrap();
|
||||||
|
triml(&mut boxes, id.to_string(), px.parse().unwrap());
|
||||||
|
} else if command == Some("trimr") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
let px = split.next().unwrap();
|
||||||
|
trimr(&mut boxes, id.to_string(), px.parse().unwrap());
|
||||||
|
} else if command == Some("trimt") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
let px = split.next().unwrap();
|
||||||
|
trimt(&mut boxes, id.to_string(), px.parse().unwrap());
|
||||||
|
} else if command == Some("trimb") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
let px = split.next().unwrap();
|
||||||
|
trimb(&mut boxes, id.to_string(), px.parse().unwrap());
|
||||||
|
} else if command == Some("text") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
let new_text = split.next().unwrap();
|
||||||
|
set_text(&mut boxes, id.to_string(), new_text.to_string());
|
||||||
|
} else if command == Some("moveu") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap().to_string();
|
||||||
|
let diff = split.next().unwrap().parse().unwrap();
|
||||||
|
moveu(&mut boxes, id, diff);
|
||||||
|
} else if command == Some("moved") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap().to_string();
|
||||||
|
let diff = split.next().unwrap().parse().unwrap();
|
||||||
|
moved(&mut boxes, id, diff);
|
||||||
|
} else if command == Some("mover") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap().to_string();
|
||||||
|
let diff = split.next().unwrap().parse().unwrap();
|
||||||
|
mover(&mut boxes, id, diff);
|
||||||
|
} else if command == Some("movel") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap().to_string();
|
||||||
|
let diff = split.next().unwrap().parse().unwrap();
|
||||||
|
movel(&mut boxes, id, diff);
|
||||||
|
} else if command == Some("paddl") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap().to_string();
|
||||||
|
let diff = split.next().unwrap().parse().unwrap();
|
||||||
|
paddl(&mut boxes, id, diff);
|
||||||
|
} else if command == Some("paddr") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap().to_string();
|
||||||
|
let diff = split.next().unwrap().parse().unwrap();
|
||||||
|
paddr(&mut boxes, id, diff);
|
||||||
|
} else if command == Some("paddt") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap().to_string();
|
||||||
|
let diff = split.next().unwrap().parse().unwrap();
|
||||||
|
paddt(&mut boxes, id, diff);
|
||||||
|
} else if command == Some("paddb") {
|
||||||
|
// TODO: not safe
|
||||||
|
let id = split.next().unwrap().to_string();
|
||||||
|
let diff = split.next().unwrap().parse().unwrap();
|
||||||
|
paddb(&mut boxes, id, diff);
|
||||||
|
} else if command == Some("add") {
|
||||||
|
// TODO: not safe
|
||||||
|
let ids: Vec<String> = boxes.iter().map(|b| b.id.clone()).collect();
|
||||||
|
let id = new_id(&ids);
|
||||||
|
let x = split.next().unwrap();
|
||||||
|
let y = split.next().unwrap();
|
||||||
|
let w = split.next().unwrap();
|
||||||
|
let h = split.next().unwrap();
|
||||||
|
new_rect(&mut boxes,
|
||||||
|
id.to_string(),
|
||||||
|
x.to_string(),
|
||||||
|
y.to_string(),
|
||||||
|
w.to_string(),
|
||||||
|
h.to_string());
|
||||||
|
} else if command == Some("save") {
|
||||||
|
// TODO: unsafe
|
||||||
|
let fname = split.next().unwrap();
|
||||||
|
let json_out = serde_json::to_string(&boxes).unwrap();
|
||||||
|
save_json(json_out, &fname.to_string());
|
||||||
|
println!("Saved as {}", fname);
|
||||||
|
} else if command == Some("show") {
|
||||||
|
// TODO: VERY unsafe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
let bx = boxes.iter().find(|b| b.id == id).unwrap();
|
||||||
|
let json = serde_json::to_string(bx).unwrap();
|
||||||
|
println!("JSON: {}", json);
|
||||||
|
} else if command == Some("rem") {
|
||||||
|
// TODO: unsafe
|
||||||
|
let id = split.next().unwrap();
|
||||||
|
rem_rect(&mut boxes, id.to_string());
|
||||||
|
} else if command == Some("exit") {
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
println!("Invalid command.");
|
||||||
|
}
|
||||||
|
let json_out = serde_json::to_string(&boxes).unwrap();
|
||||||
|
save_json(json_out, &json_fname);
|
||||||
|
reload_json(&json_fname);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,53 @@
|
|||||||
|
extern crate leptess;
|
||||||
|
|
||||||
|
use ocr_json_common::TextBox;
|
||||||
|
use leptess::{leptonica, tesseract};
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
/* TODO: preprox here */
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mut ocr_rects = Vec::new();
|
||||||
|
let file_name = if env::args().count() == 2 {
|
||||||
|
env::args().nth(1).unwrap()
|
||||||
|
} else {
|
||||||
|
panic!("Please enter a target file path")
|
||||||
|
};
|
||||||
|
let image_path = Path::new(&file_name);
|
||||||
|
let mut api = tesseract::TessApi::new(Some("/usr/share/tessdata/"), "eng").unwrap();
|
||||||
|
let pix = leptonica::pix_read(image_path).unwrap();
|
||||||
|
api.set_image(&pix);
|
||||||
|
|
||||||
|
// detect bounding boxes for words
|
||||||
|
let boxes = api
|
||||||
|
.get_component_images(leptess::capi::TessPageIteratorLevel_RIL_WORD, true)
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let mut boxid = 0;
|
||||||
|
// run OCR on each word bounding box
|
||||||
|
for b in &boxes {
|
||||||
|
api.set_rectangle(&b);
|
||||||
|
let text = api.get_utf8_text().unwrap();
|
||||||
|
let confi = api.mean_text_conf();
|
||||||
|
let bref = b.as_ref();
|
||||||
|
/*
|
||||||
|
println!(
|
||||||
|
"[X: {}, Y: {}, W: {}, H: {}]: confidence: {}, text: {}",
|
||||||
|
bref.x, bref.y, bref.w, bref.h, confi, text
|
||||||
|
);*/
|
||||||
|
ocr_rects.push(TextBox {
|
||||||
|
id: format!("{}", boxid),
|
||||||
|
hint: text,
|
||||||
|
confidence: confi as u32,
|
||||||
|
x: bref.x,
|
||||||
|
y: bref.y,
|
||||||
|
height: bref.h as u32,
|
||||||
|
width: bref.w as u32,
|
||||||
|
});
|
||||||
|
boxid += 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
let json = serde_json::to_string(&ocr_rects).unwrap();
|
||||||
|
println!("{}", json);
|
||||||
|
}
|
@ -0,0 +1,45 @@
|
|||||||
|
use std::fs;
|
||||||
|
use ocr_json_common::TextBox;
|
||||||
|
use image::{Rgba};
|
||||||
|
use imageproc::drawing::{
|
||||||
|
draw_hollow_rect_mut,
|
||||||
|
draw_text_mut,
|
||||||
|
};
|
||||||
|
use imageproc::rect::Rect;
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
use rusttype::{Font, Scale};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let img_file_name = if env::args().count() >= 2 {
|
||||||
|
env::args().nth(1).unwrap()
|
||||||
|
} else {
|
||||||
|
panic!("Please enter a target file path for image")
|
||||||
|
};
|
||||||
|
let json_file_name = if env::args().count() >= 3 {
|
||||||
|
env::args().nth(2).unwrap()
|
||||||
|
} else{
|
||||||
|
panic!("Please enter a target file path for json")
|
||||||
|
};
|
||||||
|
let json = fs::read_to_string(json_file_name).expect("There was an error reading the file.");
|
||||||
|
let ocr_rects: Vec<TextBox> = serde_json::from_str(&json).unwrap();
|
||||||
|
let image_path = Path::new(&img_file_name);
|
||||||
|
let red = Rgba([255u8, 0u8, 0u8, 255u8]);
|
||||||
|
|
||||||
|
let font_data: &[u8] = include_bytes!("../../fonts/DejaVuSansMono.ttf");
|
||||||
|
let font: Font<'static> = Font::try_from_bytes(font_data).expect("Error loading font.");
|
||||||
|
|
||||||
|
let mut img = image::open(image_path).unwrap();
|
||||||
|
|
||||||
|
// run OCR on each word bounding box
|
||||||
|
for rect in &ocr_rects {
|
||||||
|
draw_hollow_rect_mut(&mut img, Rect::at(rect.x, rect.y).of_size(rect.width, rect.height), red);
|
||||||
|
let y: u32 = rect.y as u32;
|
||||||
|
let x: u32 = (rect.x-25) as u32;
|
||||||
|
let text = String::from(rect.id.clone());
|
||||||
|
//let text = "⠨⠙⠕⠃⠗⠕⠙⠕⠱⠇⠊";
|
||||||
|
draw_text_mut(&mut img, red, x, y, Scale::uniform(20.0), &font, &text);
|
||||||
|
}
|
||||||
|
|
||||||
|
img.save("out.png").unwrap();
|
||||||
|
}
|
@ -0,0 +1,35 @@
|
|||||||
|
use std::fs;
|
||||||
|
use ocr_json_common::TextBox;
|
||||||
|
use image::{Rgba};
|
||||||
|
use imageproc::drawing::{
|
||||||
|
draw_filled_rect_mut
|
||||||
|
};
|
||||||
|
use imageproc::rect::Rect;
|
||||||
|
use std::env;
|
||||||
|
use std::path::Path;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let img_file_name = if env::args().count() >= 2 {
|
||||||
|
env::args().nth(1).unwrap()
|
||||||
|
} else {
|
||||||
|
panic!("Please enter a target file path for image")
|
||||||
|
};
|
||||||
|
let json_file_name = if env::args().count() >= 3 {
|
||||||
|
env::args().nth(2).unwrap()
|
||||||
|
} else{
|
||||||
|
panic!("Please enter a target file path for json")
|
||||||
|
};
|
||||||
|
let json = fs::read_to_string(json_file_name).expect("There was an error reading the file.");
|
||||||
|
let ocr_rects: Vec<TextBox> = serde_json::from_str(&json).unwrap();
|
||||||
|
let image_path = Path::new(&img_file_name);
|
||||||
|
let white = Rgba([255u8, 255u8, 255u8, 255u8]);
|
||||||
|
|
||||||
|
let mut img = image::open(image_path).unwrap();
|
||||||
|
|
||||||
|
// run OCR on each word bounding box
|
||||||
|
for rect in &ocr_rects {
|
||||||
|
draw_filled_rect_mut(&mut img, Rect::at(rect.x, rect.y).of_size(rect.width, rect.height), white);
|
||||||
|
}
|
||||||
|
|
||||||
|
img.save("out.png").unwrap();
|
||||||
|
}
|
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
println!("Hello, world!");
|
||||||
|
}
|
@ -0,0 +1 @@
|
|||||||
|
Subproject commit 954e7a20b3fbe2bc613d1ec072aa8e4704f2c691
|