From e86af3b1ce4ca08680cd90463bc55c6077df4907 Mon Sep 17 00:00:00 2001 From: "yi.cao2@mail.mcgill.ca" Date: Mon, 10 Feb 2020 15:26:11 -0500 Subject: [PATCH] add tesseract-ocr into req, test use canvas util img for pyterract img-to-string --- apps/dash-oss-canvas-text/.gitignore | 126 ++++++ apps/dash-oss-canvas-text/README.md | 2 + apps/dash-oss-canvas-text/app.py | 55 ++- apps/dash-oss-canvas-text/assets/base.css | 414 ++++++++++++++++++ .../assets/plotly_logo.png | Bin 0 -> 6820 bytes apps/dash-oss-canvas-text/assets/style.css | 0 apps/dash-oss-canvas-text/requirements.txt | 4 +- apps/dash-oss-canvas-text/test.py | 14 + 8 files changed, 596 insertions(+), 19 deletions(-) create mode 100644 apps/dash-oss-canvas-text/.gitignore create mode 100644 apps/dash-oss-canvas-text/README.md create mode 100644 apps/dash-oss-canvas-text/assets/base.css create mode 100644 apps/dash-oss-canvas-text/assets/plotly_logo.png create mode 100644 apps/dash-oss-canvas-text/assets/style.css create mode 100644 apps/dash-oss-canvas-text/test.py diff --git a/apps/dash-oss-canvas-text/.gitignore b/apps/dash-oss-canvas-text/.gitignore new file mode 100644 index 000000000..638d108ad --- /dev/null +++ b/apps/dash-oss-canvas-text/.gitignore @@ -0,0 +1,126 @@ +# Created by .ignore support plugin (hsz.mobi) +### Python template +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +pip-wheel-metadata/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +.hypothesis/ +.pytest_cache/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +.python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# celery beat schedule file +celerybeat-schedule + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + diff --git a/apps/dash-oss-canvas-text/README.md b/apps/dash-oss-canvas-text/README.md new file mode 100644 index 000000000..b88c4fd23 --- /dev/null +++ b/apps/dash-oss-canvas-text/README.md @@ -0,0 +1,2 @@ +# Dash Canvas OCR + diff --git a/apps/dash-oss-canvas-text/app.py b/apps/dash-oss-canvas-text/app.py index f73874d9e..98efcf0be 100644 --- a/apps/dash-oss-canvas-text/app.py +++ b/apps/dash-oss-canvas-text/app.py @@ -1,3 +1,7 @@ +from PIL import Image +import base64 +from io import BytesIO + import dash import numpy as np import dash_html_components as html @@ -5,45 +9,60 @@ from dash_canvas.utils import array_to_data_url, parse_jsonstring from dash.dependencies import Input, Output, State from dash.exceptions import PreventUpdate -import pytesseract -#todo: add tesseract-ocr and libtesseract-dev to venv +import pytesseract app = dash.Dash(__name__) server = app.server -canvas_width = 300 -canvas_height = 200 +canvas_width = 600 +canvas_height = 450 app.layout = html.Div([ + # Banner + html.Div( + [ + html.Img(src=app.get_asset_url("logo.png"), className="app__logo"), + html.H4("Dash OCR", className="header__text"), + ], + className="app__header", + ), html.H6('Draw on image and press Save to show annotations geometry'), html.Div([ - DashCanvas(id='canvas', - lineWidth=2, - width=canvas_width, - hide_buttons=["zoom", "pan", "line", "pencil", "rectangle", "undo", "select"], - lineColor='black', - goButtonTitle='Sign' - ), + DashCanvas(id='canvas', + lineWidth=2, + width=canvas_width, + hide_buttons=["zoom", "pan", "line", "pencil", "rectangle", "undo", "select"], + lineColor='black', + goButtonTitle='Sign' + ), ], className="five columns"), html.Div([ - # html.Img(id='my-image', width=300), + html.Img(id='my-image', width=300), + html.Div(id='text-output', children=''), html.P(id='my-output') ], className="five columns"), - ]) +]) -@app.callback(Output('my-output', 'children'), +@app.callback(Output('text-output', 'children'), [Input('canvas', 'json_data')]) def update_data(string): if string: - mask = parse_jsonstring(string, (canvas_height, canvas_width)) + mask = parse_jsonstring(string, shape=(canvas_height, canvas_width)) # np.savetxt('data.csv', mask) use this to save the canvas annotations as a numpy array + + image_string = array_to_data_url((255 * mask).astype(np.uint8)) # todo: include outputted image as well + + # this is from canvas.utils.image_string_to_PILImage(image_string) + img = Image.open(BytesIO(base64.b64decode(image_string[22:]))) #try save img to see what it looks like? + print('img', img) + text = pytesseract.image_to_string(img, lang='eng', config='--psm 7') + print('text', text) + return text #todo : handle condition which ocr cannot recognize: return message: "enpty, try again" + else: raise PreventUpdate - # array = array_to_data_url((255 * mask).astype(np.uint8)) #todo: include outputted image as well - text = pytesseract.image_to_string(mask) - return text if __name__ == '__main__': diff --git a/apps/dash-oss-canvas-text/assets/base.css b/apps/dash-oss-canvas-text/assets/base.css new file mode 100644 index 000000000..0a20b7243 --- /dev/null +++ b/apps/dash-oss-canvas-text/assets/base.css @@ -0,0 +1,414 @@ +/* Table of contents +–––––––––––––––––––––––––––––––––––––––––––––––––– +- Plotly.js +- Grid +- Base Styles +- Typography +- Links +- Buttons +- Forms +- Lists +- Code +- Tables +- Spacing +- Utilities +- Clearing +- Media Queries +*/ + +/* PLotly.js +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +/* plotly.js's modebar's z-index is 1001 by default + * https://github.com/plotly/plotly.js/blob/7e4d8ab164258f6bd48be56589dacd9bdd7fded2/src/css/_modebar.scss#L5 + * In case a dropdown is above the graph, the dropdown's options + * will be rendered below the modebar + * Increase the select option's z-index + */ + +/* This was actually not quite right - + dropdowns were overlapping each other (edited October 26) + +.Select { + z-index: 1002; +}*/ + +/* Grid +–––––––––––––––––––––––––––––––––––––––––––––––––– */ +.container { + position: relative; + width: 100%; + max-width: 960px; + margin: 0 auto; + padding: 0 20px; + box-sizing: border-box; } + .column, + .columns { + width: 100%; + float: left; + box-sizing: border-box; } + + /* For devices larger than 400px */ + @media (min-width: 400px) { + .container { + width: 85%; + padding: 0; } + } + + /* For devices larger than 550px */ + @media (min-width: 550px) { + .container { + width: 80%; } + .column, + .columns { + margin-left: 4%; } + .column:first-child, + .columns:first-child { + margin-left: 0; } + + .one.column, + .one.columns { width: 4.66666666667%; } + .two.columns { width: 13.3333333333%; } + .three.columns { width: 22%; } + .four.columns { width: 30.6666666667%; } + .five.columns { width: 39.3333333333%; } + .six.columns { width: 48%; } + .seven.columns { width: 56.6666666667%; } + .eight.columns { width: 65.3333333333%; } + .nine.columns { width: 74.0%; } + .ten.columns { width: 82.6666666667%; } + .eleven.columns { width: 91.3333333333%; } + .twelve.columns { width: 100%; margin-left: 0; } + + .one-third.column { width: 30.6666666667%; } + .two-thirds.column { width: 65.3333333333%; } + + .one-half.column { width: 48%; } + + /* Offsets */ + .offset-by-one.column, + .offset-by-one.columns { margin-left: 8.66666666667%; } + .offset-by-two.column, + .offset-by-two.columns { margin-left: 17.3333333333%; } + .offset-by-three.column, + .offset-by-three.columns { margin-left: 26%; } + .offset-by-four.column, + .offset-by-four.columns { margin-left: 34.6666666667%; } + .offset-by-five.column, + .offset-by-five.columns { margin-left: 43.3333333333%; } + .offset-by-six.column, + .offset-by-six.columns { margin-left: 52%; } + .offset-by-seven.column, + .offset-by-seven.columns { margin-left: 60.6666666667%; } + .offset-by-eight.column, + .offset-by-eight.columns { margin-left: 69.3333333333%; } + .offset-by-nine.column, + .offset-by-nine.columns { margin-left: 78.0%; } + .offset-by-ten.column, + .offset-by-ten.columns { margin-left: 86.6666666667%; } + .offset-by-eleven.column, + .offset-by-eleven.columns { margin-left: 95.3333333333%; } + + .offset-by-one-third.column, + .offset-by-one-third.columns { margin-left: 34.6666666667%; } + .offset-by-two-thirds.column, + .offset-by-two-thirds.columns { margin-left: 69.3333333333%; } + + .offset-by-one-half.column, + .offset-by-one-half.columns { margin-left: 52%; } + + } + + + /* Base Styles + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + /* NOTE + html is set to 62.5% so that all the REM measurements throughout Skeleton + are based on 10px sizing. So basically 1.5rem = 15px :) */ + html { + font-size: 62.5%; } + body { + font-size: 1.5em; /* currently ems cause chrome bug misinterpreting rems on body element */ + line-height: 1.6; + font-weight: 400; + font-family: "Open Sans", verdana, arial, sans-serif; + color: rgb(50, 50, 50); } + + + /* Typography + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + h1, h2, h3, h4, h5, h6 { + margin-top: 0; + margin-bottom: 0; + font-weight: 300; } + h1 { font-size: 4.5rem; line-height: 1.2; letter-spacing: -.1rem; margin-bottom: 2rem; } + h2 { font-size: 3.6rem; line-height: 1.25; letter-spacing: -.1rem; margin-bottom: 1.8rem; margin-top: 1.8rem;} + h3 { font-size: 3.0rem; line-height: 1.3; letter-spacing: -.1rem; margin-bottom: 1.5rem; margin-top: 1.5rem;} + h4 { font-size: 2.6rem; line-height: 1.35; letter-spacing: -.08rem; margin-bottom: 1.2rem; margin-top: 1.2rem;} + h5 { font-size: 2.2rem; line-height: 1.5; letter-spacing: -.05rem; margin-bottom: 0.6rem; margin-top: 0.6rem;} + h6 { font-size: 2.0rem; line-height: 1.6; letter-spacing: 0; margin-bottom: 0.75rem; margin-top: 0.75rem;} + + p { + margin-top: 0; } + + + /* Blockquotes + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + blockquote { + border-left: 4px lightgrey solid; + padding-left: 1rem; + margin-top: 2rem; + margin-bottom: 2rem; + margin-left: 0rem; + } + + + /* Links + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + a { + color: #1EAEDB; + text-decoration: underline; + cursor: pointer;} + a:hover { + color: #0FA0CE; } + + + /* Buttons + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + .button, + button, + input[type="submit"], + input[type="reset"], + input[type="button"] { + display: inline-block; + height: 38px; + padding: 0 30px; + color: #555; + text-align: center; + font-size: 11px; + font-weight: 600; + line-height: 38px; + letter-spacing: .1rem; + text-transform: uppercase; + text-decoration: none; + white-space: nowrap; + background-color: transparent; + border-radius: 4px; + border: 1px solid #bbb; + cursor: pointer; + box-sizing: border-box; } + .button:hover, + button:hover, + input[type="submit"]:hover, + input[type="reset"]:hover, + input[type="button"]:hover, + .button:focus, + button:focus, + input[type="submit"]:focus, + input[type="reset"]:focus, + input[type="button"]:focus { + color: #333; + border-color: #888; + outline: 0; } + .button.button-primary, + button.button-primary, + input[type="submit"].button-primary, + input[type="reset"].button-primary, + input[type="button"].button-primary { + color: #FFF; + background-color: #33C3F0; + border-color: #33C3F0; } + .button.button-primary:hover, + button.button-primary:hover, + input[type="submit"].button-primary:hover, + input[type="reset"].button-primary:hover, + input[type="button"].button-primary:hover, + .button.button-primary:focus, + button.button-primary:focus, + input[type="submit"].button-primary:focus, + input[type="reset"].button-primary:focus, + input[type="button"].button-primary:focus { + color: #FFF; + background-color: #1EAEDB; + border-color: #1EAEDB; } + + + /* Forms + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + input[type="email"], + input[type="number"], + input[type="search"], + input[type="text"], + input[type="tel"], + input[type="url"], + input[type="password"], + textarea, + select { + height: 38px; + padding: 6px 10px; /* The 6px vertically centers text on FF, ignored by Webkit */ + background-color: #fff; + border: 1px solid #D1D1D1; + border-radius: 4px; + box-shadow: none; + box-sizing: border-box; + font-family: inherit; + font-size: inherit; /*https://stackoverflow.com/questions/6080413/why-doesnt-input-inherit-the-font-from-body*/} + /* Removes awkward default styles on some inputs for iOS */ + input[type="email"], + input[type="number"], + input[type="search"], + input[type="text"], + input[type="tel"], + input[type="url"], + input[type="password"], + textarea { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; } + textarea { + min-height: 65px; + padding-top: 6px; + padding-bottom: 6px; } + input[type="email"]:focus, + input[type="number"]:focus, + input[type="search"]:focus, + input[type="text"]:focus, + input[type="tel"]:focus, + input[type="url"]:focus, + input[type="password"]:focus, + textarea:focus, + select:focus { + border: 1px solid #33C3F0; + outline: 0; } + label, + legend { + display: block; + margin-bottom: 0px; } + fieldset { + padding: 0; + border-width: 0; } + input[type="checkbox"], + input[type="radio"] { + display: inline; } + label > .label-body { + display: inline-block; + margin-left: .5rem; + font-weight: normal; } + + + /* Lists + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + ul { + list-style: circle inside; } + ol { + list-style: decimal inside; } + ol, ul { + padding-left: 0; + margin-top: 0; } + ul ul, + ul ol, + ol ol, + ol ul { + margin: 1.5rem 0 1.5rem 3rem; + font-size: 90%; } + li { + margin-bottom: 1rem; } + + + /* Tables + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + table { + border-collapse: collapse; + } + th, + td { + padding: 12px 15px; + text-align: left; + border-bottom: 1px solid #E1E1E1; } + th:first-child, + td:first-child { + padding-left: 0; } + th:last-child, + td:last-child { + padding-right: 0; } + + + /* Spacing + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + button, + .button { + margin-bottom: 0rem; } + input, + textarea, + select, + fieldset { + margin-bottom: 0rem; } + pre, + dl, + figure, + table, + form { + margin-bottom: 0rem; } + p, + ul, + ol { + margin-bottom: 0.75rem; } + + /* Utilities + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + .u-full-width { + width: 100%; + box-sizing: border-box; } + .u-max-full-width { + max-width: 100%; + box-sizing: border-box; } + .u-pull-right { + float: right; } + .u-pull-left { + float: left; } + + + /* Misc + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + hr { + margin-top: 3rem; + margin-bottom: 3.5rem; + border-width: 0; + border-top: 1px solid #E1E1E1; } + + + /* Clearing + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + + /* Self Clearing Goodness */ + .container:after, + .row:after, + .u-cf { + content: ""; + display: table; + clear: both; } + + + /* Media Queries + –––––––––––––––––––––––––––––––––––––––––––––––––– */ + /* + Note: The best way to structure the use of media queries is to create the queries + near the relevant code. For example, if you wanted to change the styles for buttons + on small devices, paste the mobile query code up in the buttons section and style it + there. + */ + + + /* Larger than mobile */ + @media (min-width: 400px) {} + + /* Larger than phablet (also point when grid becomes active) */ + @media (min-width: 550px) {} + + /* Larger than tablet */ + @media (min-width: 750px) {} + + /* Larger than desktop */ + @media (min-width: 1000px) {} + + /* Larger than Desktop HD */ + @media (min-width: 1200px) {} diff --git a/apps/dash-oss-canvas-text/assets/plotly_logo.png b/apps/dash-oss-canvas-text/assets/plotly_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..5a4dcb93e847eb0f4adb2b4b90ad3693f5b68571 GIT binary patch literal 6820 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 ZPPOfa;A_FoPW0FbP*Ko;SHoTg{Re31s#^d6 literal 0 HcmV?d00001 diff --git a/apps/dash-oss-canvas-text/assets/style.css b/apps/dash-oss-canvas-text/assets/style.css new file mode 100644 index 000000000..e69de29bb diff --git a/apps/dash-oss-canvas-text/requirements.txt b/apps/dash-oss-canvas-text/requirements.txt index 6b2ac270b..a841cc4f8 100644 --- a/apps/dash-oss-canvas-text/requirements.txt +++ b/apps/dash-oss-canvas-text/requirements.txt @@ -1,4 +1,6 @@ dash==1.8.0 dash_canvas==0.1.0 Pillow==7.0.0 -pytesseract==0.3.2 \ No newline at end of file +pytesseract==0.3.2 +tesseract-ocr +libtesseract-dev diff --git a/apps/dash-oss-canvas-text/test.py b/apps/dash-oss-canvas-text/test.py new file mode 100644 index 000000000..f295eb8e8 --- /dev/null +++ b/apps/dash-oss-canvas-text/test.py @@ -0,0 +1,14 @@ +from PIL import Image +import pytesseract + + +def ocr_core(filename): + """ + This function will handle the core OCR processing of images. + """ + text = pytesseract.image_to_string(Image.open( + filename)) # We'll use Pillow's Image class to open the image and pytesseract to detect the string in the image + return text + + +print(ocr_core('assets/sample_image.png'))