[GCP] VisonAPIを使いたい

tesseractよりvison APIの方が性能が良さそうなので、
vison APIを触ってみる

こちらの公式の
https://cloud.google.com/vision/docs/quickstart-client-libraries?hl=JA

1. GCPアカウントに行く
2. Vison APIを有効にする

3. サービスアカウントの作成

ロールを基本 -> オーナーで設定

4. 作成したサービスアカウントでキーを作成

鍵を追加でjsonタイプの新しい鍵を追加する

5. jsonをubuntuに配置
L vison.jsonとしますww

$ pip3 install –no-cache-dir –force-reinstall -Iv grpcio==1.43.0
$ pip3 install google-cloud-vision

image/license.jpeg

main.py

import os
os.environ["GOOGLE_APPLICATION_CREDENTIALS"] = "vision.json"

import io
from google.cloud import vision


client = vision.ImageAnnotatorClient()
with io.open("img/license.jpeg", 'rb') as image_file:
	content = image_file.read()
image = vision.Image(content=content)
response = client.document_text_detection(image=image)
print(response)

print(response.text_annotations[0].description)

$ python3 main.py
氏名


花子
昭和61年5月1日生)
住所 東京都千代田区霞加喂2-1-2
交付 令和01年05月07日 12345
12024年(今和06年)06月01日未有动
眼鏡等
免許の
条件等
見本
優良
番号| 第 012345678900 号
|– 平成15年04月01日

他平成17年06月01日
(三種平成29年08月01日
運転免許証
天特普二
大自天持
普皇引
種類
大型小特
中型原付
THI
141
00000
公安委員会
Em
KA | ||
Q00

うおおおおおおおおおおおおおおおおおおおおおおお
萎えたああああああああああああああああ

[tesseract] 免許証の文字を読み込みたい : tesseract_layoutの検証

免許証画像は以下を使用する

tesseract_layoutの値を1から10まで変更して検証する

### tesseract_layout=1
$ python3 main.py
(mm| 日 本 花 子 軌和61年 5月

]1 日生

人誠| 東京都生代田区霞が関2ー 1一2
け| 令和01年05807H 12345

### tesseract_layout=2
$ python3 main.py
Traceback (most recent call last):
File “main.py”, line 9, in
txt = tools[0].image_to_string(
File “/home/vagrant/.local/lib/python3.8/site-packages/pyocr/tesseract.py”, line 386, in image_to_string
raise TesseractError(
pyocr.error.TesseractError: (-1, “Unable to find output file (tested [‘/tmp/tmpuiijvfgx/output.txt’])”)

### tesseract_layout=3
$ python3 main.py
(mm| 日 本 花 子 軌和61年 5月

]1 日生

人誠| 東京都生代田区霞が関2ー 1一2
信和01年05』07H 12345

### tesseract_layout=4
$ python3 main.py
(名| 日 本 花 子 昭和61年 5月 1日生

人誠| 東京都生代田区霞が関2ー 1一2
交付| 令和O1年05』07H 12345

2024箇(06$06有O1Hまで勧

%の 眼鏡等
条件等

見 本
き引第 012345678900 号

0
を人 ト民捧人構誠還| 〇〇OOO
=層衣2908』018導笛にに回較固 ま中

### tesseract_layout=5
$ python3 main.py
| |
に 5
中 M
加 9
9
避 Ok
ら 呈較っ
守 CN OS
| 較 抽| |睦旧旧計員
ー|。居 則遇|
| mp
– 還 RE 逢遇
| | 国~園 必g式還
玲|上 O、 は半較|
陸中 8
| 錠 [壮 加|
| 略叶RS sss
是 陸中
| 還|二 地
| 層間 ら5 8さき
EE ーーベ
[es = 絆|還> 証庄計
首日 EHP

### tesseract_layout=6
$ python3 main.py
(信所| 東京都寺代田区霞が関2-1一2

$和01キ05』07B 12345

天の 眼鏡等 に

条件等

見本 *

*引012345678900 呈 叶

に天上1 5404』01H話司にに放還 ま
に17108801間 汗天市生天 )
にafW29*08801 只間四時間 Sgms 583

### tesseract_layout=7
$ python3 main.py
で 敵 ーー an

### tesseract_layout=8
$ python3 main.py
で 敵

### tesseract_layout=9
$ python3 main.py
|

### tesseract_layout=10
$ python3 main.py
er

### 結論
tesseract_layout=4, lang=’jpn’ が一番まともな気がするけど、
なんでだろう?
きちんと精度高く読み取るにはGoogleのVison APIで実装するっぽいな…

[Ubuntu] tesseractをinstallして使いたい

$ sudo apt -y install tesseract-ocr tesseract-ocr-jpn libtesseract-dev libleptonica-dev tesseract-ocr-script-jpan tesseract-ocr-script-jpan-vert
$ tesseract -v
tesseract 4.1.3
leptonica-1.79.0
libgif 5.1.4 : libjpeg 8d (libjpeg-turbo 2.0.3) : libpng 1.6.37 : libtiff 4.1.0 : zlib 1.2.11 : libwebp 0.6.1 : libopenjp2 2.3.1
Found AVX2
Found AVX
Found SSE
Found libarchive 3.4.0 zlib/1.2.11 liblzma/5.2.4 bz2lib/1.0.8 liblz4/1.9.2 libzstd/1.4.4
$ tesseract –list-langs
List of available languages (5):
Japanese
Japanese_vert
eng
jpn
osd
※osdとは、言語の判定、文字角度の識別を行う

$ tesseract test1.png output
->
$ tesseract test1.png stdout
Hello world
$ tesseract test2.png stdout -l jpn
こんにちは

### Pythonで使えるようにする
$ pip3 install pyocr

from PIL import Image
import sys
import pyocr

tools = pyocr.get_available_tools()
langs = tools[0].get_available_languages()

img = Image.open('test1.png')
txt = tools[0].image_to_string(
	img,
	lang=langs[0],
	builder=pyocr.builders.TextBuilder(tesseract_layout=6)
)
print(txt)

※tesseract_layout=6 が精度に重要

$ python3 main.py
Hello world

lang=’jpn’に変更すると、日本語も出力できる
$ python3 main.py
とこんにちは
|
tesseract_layout=3で実行すると…
$ python3 main.py
こんにちは

#### tesseract_layoutの意味
0 = Orientation and script detection (OSD) only.
1 = Automatic page segmentation with OSD.
2 = Automatic page segmentation, but no OSD, or OCR
3 = Fully automatic page segmentation, but no OSD. (Default)
4 = Assume a single column of text of variable sizes.
5 = Assume a single uniform block of vertically aligned text.
6 = Assume a single uniform block of text.
7 = Treat the image as a single text line.
8 = Treat the image as a single word.
9 = Treat the image as a single word in a circle.
10 = Treat the image as a single character.

0 =方向とスクリプトの検出(OSD)のみ。
1 = OSDによる自動ページセグメンテーション。
2 =自動ページセグメンテーション、ただしOSDまたはOCRなし
3 =完全自動のページセグメンテーション。ただし、OSDはありません。 (ディフォルト)
4 =可変サイズのテキストの単一列を想定します。
5 =垂直に配置されたテキストの単一の均一なブロックを想定します。
6 =単一の均一なテキストブロックを想定します。
7 =画像を単一のテキスト行として扱います。
8 =画像を1つの単語として扱います。
9 =画像を円の中の1つの単語として扱います。
10 =画像を1文字として扱います。

6で設定されているのが多いですね
文字角度を考慮しなければ3で、文字角度を考慮する場合は6でしょうか?
読み取りたい文字に併せて設定を変えながら試していくイメージか…

dockerでmysqlを操作2

$ sudo docker pull mysql
$ sudo docker run -it –name test-wolrd-mysql -e MYSQL_ROOT_PASSWORD=mysql -d mysql:latest
sudo docker exec -it test-wolrd-mysql bash -p
mysql -u root -p -h 127.0.0.1

$ tree
.
├── docker-compose.yml
└── mysql
├── DB
│   └── world.sql
├── Dockerfile
└── my.cnf

docker-compose.yml

version: "3"
services:
  mysql:
    build: ./mysql/
    volumes:
      - ./mysql/db:/docker-entrypoint-initdb.d
    image: original_mysql_world
    environment:
      - MYSQL_ROOT_PASSWORD=mysql

world.sql

-- MySQL dump 10.13  Distrib 5.1.51, for pc-linux-gnu (i686)
--
-- Host: 127.0.0.1    Database: world
-- ------------------------------------------------------
-- Server version       5.1.51-debug-log

Dockerfile

FROM mysql

EXPOSE 3306

ADD ./my.cnf /etc/mysql/conf.d/my.cnf

CMD ["mysqld"]

my.cnf

[mysqld]
character-set-server=utf8

[mysql]
default-character-set=utf8

[client]
default-character-set=utf8

$ sudo docker-compose build
$ sudo docker-compose up -d
$ sudo docker exec -it mysql_mysql_1 bash -p

flaskをmysqlに接続

from flask import Flask, render_template
import pymysql

app = Flask(__name__)

def getConnection():
	return pymysql.connect(
		host="localhost",
		db="hoge",
		user="fuga",
		password="hou",
		charset="utf8",
		cursorclass=pymysql.cursors.DictCursor
	)

@app.route("/")
def index():

	connection = getConnection()

	sql = "SELECT * FROM avalanche where id = 1"
	cursor = connection.cursor()
	cursor.execute(sql)
	results = cursor.fetchall()

	cursor.close()
	connection.close()
	return render_template('index.html', results=results)

if __name__ == "__main__":
	app.run(debug=True, host='0.0.0.0', port=5000)

なるほど
これをdockerでやりたい

flaskをDockerで起動したい

$ docker -v
Docker version 20.10.12, build e91ed57
$ pwd
/home/vagrant/dev/docker/flask

Dockerfile

FROM python:alpine

WORKDIR /app

COPY ./app /app

RUN pip install Flask

CMD ["python", "index.py"]

app/index.py

from flask import Flask
app = Flask(__name__)

@app.route("/")
def index():
	return "<h1>Hello, Flask</h1>"

if __name__ == "__main__":
	app.run(debug=True, host='0.0.0.0', port=80)

$ sudo docker image build -t flask .
$ sudo docker run -p 5000:80 -v /home/vagrant/dev/docker/flask/app:/app -d flask

from flask import Flask, render_template
app = Flask(__name__)

@app.route("/")
def index():
	values = {"name": "Taro"}
	return render_template('index.html', data=values)

@app.route("/test")
def test():
	values = {"message": "Hello! This is test page"}
	return render_template('test.html', data=values)



if __name__ == "__main__":
	app.run(debug=True, host='0.0.0.0', port=80)

$ sudo docker stop 0ef266c41263

### htmlの場合


$ docker build -t some-content-nginx .
$ sudo docker run –name some-nginx -d -p 8080:80 some-content-nginx

pythonでmysqlを操作2

mysql-connector-pythonでも良いが、pymysqlを使う場合

$ pip3 install pymysql

import pymysql

print("hello world")

connection = pymysql.connect(
	host="localhost",
	db="hoge",
	user="fuga",
	password="asdf",
	charset="utf8",
	cursorclass=pymysql.cursors.DictCursor
)

sql = "SELECT * FROM avalanche"
cursor = connection.cursor()
cursor.execute(sql)
posts = cursor.fetchall()

cursor.close()
connection.close()

for post in posts:
	print(post["title"])

ほう

flask入門3

sqlite3

create table results (
	id integer primary key autoincrement,
	title text,
	data text,
	img text,
	created datetime
);

models.py

def select(con, pk):
	cur = con.execute('select id, title, data, img, created from results where id=?', (pk,))
	return cur.fetchone()

def select_all(con):
	cur = con.execute('select id, title, data, img, created from results order by id desc')
	return cur.fetchall()

def delete(con, pk):
	cur = con.cursor()
	cur.execute('delete from results where id=?', (pk,))
	con.commit()

run.py

@app.route('/')
def index():
	con = get_db()
	results = models.select_all(con)
	return render_template('index.html', results=results)

@app.route('/delete/<pk>')
def delete(pk):
	con = get_db()
	models.delete(con, pk)
	return redirect(url_for('index'))

@app.route('/view/<pk>')
def view(pk):
	con = get_db()
	result = models.select(con, pk)
	return render_template("view.html", result=result)

index.html

    {% for result in results %}
    <tr>
        <td>{{result.id}}</td>
        <td>{{result.title|safe}}</td>
        <td>{{result.rcreated}}</td>
        <td>
            <a href="/view/{{result.id}}"><button class="btn btn-primary">参照</button></a>
            <form action="/delete/{{result.id}}" style="display:inline" method="post">
                <input class="btn btn-danger" type="submit" value="削除" onclick='return confirm("削除しますがよろしいですか")';>
            </form>
        </td>
    </tr>
    {% endfor %}

view.html

{% extends "base.html" %}
{% block body %}
<h1>結果参照</h1>

<h3>{{ result.id}}: {{ result.title|safe}}</h3>
<p>{{ result.created }}</p>
<div class="row">
	<img src="{{ url_for('static', filename=result.img)}}">
</div>

<div class="row">
	<textarea class="form-control" name="data" rows="5">{{result.data}}</textarea>
</div>
<br><br>
{% endblock %}

OK 一通り理解した
mysql & Dockerでやりたい

flask入門2

app.py

from flask import Flask
from flask import Flask, redirect, url_for, render_template

app = Flask(__name__)
app.config.update(dict(
	DATABASE = os.path.join(app.root_path, 'db.sqlite3'),
	SECRET_KEY='foo-baa',
))

@app.route('/')
def index():
	return render_template('index.html', result{})

@app.route('/create')
def create():
	return render_template('edit.html')

@app.route('/analysis', methods=['POST'])
def analysis():
	return redirect(url_for('view', pk=0))

@app.route('/delete/<pk>')
def delete(pk)
	return redirect(url_for('index'))

@app.route('/view/<pk>')
def view(pk):
	return render_template("index.html", values=values)

if __name__ == '__main__':
	app.run(debug=True, host='192.168.33.10', port=8000)

base.html

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<meta name="viewport" content="width=device-width, initial-scale=1.0">
	<title>簡易分析ツール</title>
	<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
            integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
            crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
            integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
            crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
            integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
            crossorigin="anonymous"></script>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <link rel="stylesheet" type=text/css href="{{ url_for('static', filename='style.css') }}">
</head>
<body>

<header>
	<div class="navbar navbar-dark bg-dark box-shadow">
        <div class="container d-flex justify-content-between">
            <a href="/" class="navbar-brand d-flex align-items-center">
                <strong>簡易分析ツール</strong>
            </a>
        </div>
    </div>
</header>

<div class="container">
	{% block body %}
	{% endblock %}
</div>
</body>
</html>

edit.html

{% extends "base.html" %}
{% block body %}
<h1>新規分析</h1>
<form action="{{ url_for('analysis') }}">
	<label>タイトル</label>
	<input class="form-control" type="text" size="30" name="title">
	<label>分析データ</label>
	<textarea class="form-control" name="data" rows="5"></textarea>
	<input class="btn btn-primary" type="submit" value="送信">
</form>
{% endblock %}

view.html

{% extends "base.html" %}
{% block body %}
<h1>結果参照</h1>

<h3>0:サンプルタイトル</h3>
<p>2022/01/01 12:00</p>
<div class="row">
	ここにグラフを配置
</div>

<div class="row">
	<textarea class="form-control" name="data" rows="5">テストデータ</textarea>
</div>
<br><br>
{% endblock %}

http://192.168.33.10:8000/

ほう、これは凄い
Djangoに似てる

ubuntu20.04にapache2とMod_wsgi

$ Mod_wsgi on
$ sudo apt install apache2 apache2-utils ssl-cert libapache2-mod-wsgi
$ sudo systemctl restart apache2
$ sudo mkdir -p /var/www/scripts
$ sudo vi /var/www/scripts/test_wsgi_script.py

def application(environ, start_response):
	status = '200 ok'
	html = '<html>\n' \
			'<body>\n' \
			'hello, mod_wsgi is working\n' \
			'</body>\n' \
			'</html>\n'
	response_header = [('Content-type', 'text/html')]
	start_response(status, response_header)
	return

$ sudo vi /etc/apache2/conf-available/mod-wsgi.conf

WSGIScriptAlias /test_wsgi /var/www/scripts/test_wsgi_script.py

$ sudo a2enconf mod-wsgi
$ sudo systemctl restart apache2
http://192.168.33.10/test_wsgi