1
4

Pythonでイメージのアップロードとプレビューサービスを実装します

Last updated at Posted at 2023-10-02

今回は、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)

イメージidをパラメータとしてイメージにアクセスします。
00004.gif

プロジェクト全体の構成は以下の通り
 % 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
    ...

以上です。
何かご提案やご質問がありましたら、コメントをお待ちしています。

1
4
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
4