今回は、Flask利用して、イメージサービスを実装します。
機能は以下の通りです
- イメージのアップロード
- イメージバーコード認識後のイメージ情報の保存
- バーコードでイメージ情報を照会
- イメージのプレビュー
開発環境
Mac OS X
Python 3.11.5
仮想環境のアクティベート
% python3 -m venv venv
% . venv/bin/activate
python lib インストール
バーコードの生成と認識については、こちらを参照してください。
pip install flask
pip install Flask-RESTful
pip install Werkzeug
pip install pyStrich
pip install pyzbar
% pip list
Package Version
------------- ------------
aniso8601 9.0.1
blinker 1.6.2
click 8.1.7
Flask 3.0.0
Flask-RESTful 0.3.10
itsdangerous 2.1.2
Jinja2 3.1.2
MarkupSafe 2.1.3
Pillow 10.0.1
pip 23.2.1
pyStrich 0.8
pytz 2023.3.post1
pyzbar 0.1.9
setuptools 68.1.2
six 1.16.0
Werkzeug 3.0.0
実装
Flask セットアップ
server.py
from flask import Flask
from flask_restful import Api
app = Flask('ImageLib')
if __name__ == '__main__':
api = Api(app)
app.run(host='0.0.0.0', port=5000, debug=True)
Sqlite3 セットアップ
server.py
from flask import Flask, render_template, request
from flask_restful import reqparse, Resource, Api
from werkzeug.datastructures import FileStorage
from pyzbar.pyzbar import decode
from PIL import Image
import sqlite3
from flask import g
import os
import uuid
import base64
DATABASE = 'db/images.db'
def get_db():
db = getattr(g, '_database', None)
if db is None:
db = g._database = sqlite3.connect(DATABASE)
return db
def init_db():
conn = get_db()
c = conn.cursor()
c.execute('''CREATE TABLE IF NOT EXISTS IMAGES
(ID INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
BARCODE TEXT NOT NULL,
TYPE TEXT NOT NULL,
LOCATION TEXT NOT NULL);''')
c.close()
app = Flask('ImageLib')
# dbを初期化
with app.app_context():
init_db()
if __name__ == '__main__':
api = Api(app)
app.run(host='0.0.0.0', port=5000, debug=True)
server.pyを実行して、table を作成します。
DBを接続
% sqlite3 ./db/images.db
SQLite version 3.39.5 2022-10-14 20:58:05
Enter ".help" for usage hints.
sqlite> .tables
IMAGES
sqlite>
イメージのアップロード
server.py
前略...
class UploadImg(Resource):
def __init__(self):
self.parser = reqparse.RequestParser()
self.parser.add_argument('imgFile', required=True,
type=FileStorage,
location='files')
def post(self):
# イメージファイルを取得する
img_file = self.parser.parse_args().get('imgFile')
name, ext = os.path.splitext(img_file.filename)
# 新しいファイル名を生成する
img_name = '%s%s' % (str(uuid.uuid1()), ext)
_path = os.path.join("files", img_name)
img_file.save(_path)
# バーコードを認識する
barcode = decode(Image.open(_path))[0].data.decode()
_barcode, _type = barcode.split('-')
_conn = get_db()
_c = _conn.cursor()
# イメージ情報をDBに保存する
_c.execute("insert into images (barcode, type, location) values (?, ?, ?)",
[_barcode, _type, _path])
_conn.commit()
_c.close()
_conn.close()
return barcode, 200
if __name__ == '__main__':
api = Api(app)
# 追加して
api.add_resource(UploadImg, '/img/upload')
app.run(host='0.0.0.0', port=5000, debug=True)
検証機能
イメージをアップロード
% curl -X POST -F 'imgFile=@1.png;filename=1.png' http://localhost:5000/img/upload
"000111222-1003"
% sqlite3 ./db/images.db
SQLite version 3.39.5 2022-10-14 20:58:05
Enter ".help" for usage hints.
sqlite> select * from images;
1|000111222|1003|files/76d9fc46-60e4-11ee-9482-8c85908f138e.png
2|000111222|1003|files/d81e082c-60fc-11ee-9131-8c85908f138e.png
3|000111222|1003|files/0ae6b254-60fd-11ee-9131-8c85908f138e.png
sqlite>
バーコードでイメージ情報を照会
server.py
前略...
class QueryByBarCode(Resource):
def get(self, barcode):
_conn = get_db()
_c = _conn.cursor()
lst = []
for item in _c.execute("select id, barcode, type from images where barcode=?", [barcode]):
lst.append({'id': item[0], 'barcode': item[1], 'type': item[2]})
_c.close()
return lst, 200
if __name__ == '__main__':
api = Api(app)
api.add_resource(UploadImg, '/img/upload')
# 追加して
api.add_resource(QueryByBarCode, '/img/info/<barcode>')
app.run(host='0.0.0.0', port=5000, debug=True)
検証機能
% curl http://127.0.0.1:5000/img/info/000111222
[
{
"id": 1,
"barcode": "000111222",
"type": "1003"
},
{
"id": 2,
"barcode": "000111222",
"type": "1003"
},
{
"id": 3,
"barcode": "000111222",
"type": "1003"
}
]
イメージのプレビュー
server.py
前略...
class GetImage(Resource):
def get(self, pid):
_conn = get_db()
_c = _conn.cursor()
location = _c.execute("select location from images where id=?", [pid]).fetchone()[0]
_c.close()
with open(location, "rb") as img_file:
base64_image = base64.b64encode(img_file.read())
return {'base64Image': base64_image.decode()}, 200
if __name__ == '__main__':
api = Api(app)
api.add_resource(UploadImg, '/img/upload')
api.add_resource(QueryByBarCode, '/img/info/<barcode>')
# 追加して
api.add_resource(GetImage, '/img/get/<pid>')
app.run(host='0.0.0.0', port=5000, debug=True)
検証機能
Base64エンコードされたイメージを返却
% curl http://127.0.0.1:5000/img/get/3
{
"base64Image": "iVBORw0KGgoAAAANSUhEUgAAA5IAAAMMCA
... AoDFYhMrEi/8f4aiHrYJj1CCAAAAAElFTkSuQmCC"
}
プレビューUIの作成
templates/index.html
<html>
<body>
<div>
<img src="data:image/png;base64, {{base64Img.base64Image}}"/>
</div>
</body>
</html>
テスト用のプレビュー機能をserver.pyに追加します
server.py
前略...
@app.route("/img")
def image_info():
_id = request.args.get('pid')
web = api.app.test_client()
base64_img = web.get('img/get/%s' % _id).json
return render_template('index.html', base64Img=base64_img)
プロジェクト全体の構成は以下の通り
% tree -L 3
.
├── 1.png
├── db
│ └── images.db
├── files
│ ├── 0ae6b254-60fd-11ee-9131-8c85908f138e.png
│ ├── 76d9fc46-60e4-11ee-9482-8c85908f138e.png
│ └── d81e082c-60fc-11ee-9131-8c85908f138e.png
├── server.py
├── templates
│ └── index.html
└── venv
├── bin
...
以上です。
何かご提案やご質問がありましたら、コメントをお待ちしています。