pythonで画像をDBに登録し、DBから取得する

# 目的
業務にて画像をSQLite3のDBへ登録し保存する機能を実装する必要があり、調査を兼ね実装を行いました。

HTTPサーバーを兼ねたRaspberry PiにUSB接続のWebカメラを接続し、このカメラを外部から制御(画像の撮影)を行い、サーバー内のDBへ保存します。
外部機器(PC、スマートフォン)からサーバーへリクエストをかけたら、任意の画像を取得するなどの機能を実装する必要がありました。

今回は、有線接続したWebカメラから画像を取得し、DBへ保存、保存完了後にDBから画像を取得し、ローカルディレクトリに任意のファイルネームで保存するまでを実装しました。


## 開発環境
#### IDE 
PyCharm Community Edition
#### ライブラリ
SQLite3
OpenCv
PyQt5
#### 環境構築
pythonはHomebrewでインストールします。
SQLite3はpythonに標準で入っているので、インストールは不要です。

OpneCvはPyCharmからインストールしました。
Preference->Project Interpreterを開き、+ボタンを押します。
Availabel Packagesが開くので、opencv-pythonを検索し、Install Packageにてインストールしました。

PyQt5はHomebrewでインストールします。

```
brew install pyqt5
```

## 実装
```python:save_db.py
import cv2
import sqlite3
import sys
from PyQt5.QtWidgets import QApplication, QWidget, QPushButton
from PyQt5.QtGui import QIcon
from PyQt5.QtCore import pyqtSlot

class App (QWidget):

    def __init__(self):
        super().__init__()
        self.title = 'Sample image to save to DB'
        self.left = 10
        self.top = 10
        self.width = 320
        self.height = 240
        self.initUI()

    def initUI(self):

        # ウィンドウのtitleを設定
        self.setWindowTitle(self.title)

        # ウィンドウサイズを設定
        self.setGeometry(self.left, self.top, self.width, self.height)

        # ボタンのtitleを設定
        buttton = QPushButton('Save Captcha to DB', self)
        # ボタンの意味を設定
        buttton.setToolTip('This is a button to save the camera Captcha image to DB')
        # ボタンの描画位置を設定
        buttton.move(80, 70)
        # ボタン押下処理を設定
        buttton.clicked.connect(self.on_click)

        self.show()

    def camera_capture(self):
        cap = cv2.VideoCapture(0)
        count = 0

        while count < 10:
            count+=1
            ret, frame = cap.read()
            # デバッグ用：画像表示
            #cv2.imshow('camera capture', frame)
            cv2.waitKey(1)

            if count == 10:
                # データベース作成・コネクション作成
                db = sqlite3.connect("my.db")
                # カーソルをコネクションから作成
                cur = db.cursor()
                # テーブル作成
                try:
                    cur.execute('''create table images (id string, img blob)''')
                except sqlite3.OperationalError:
                    print
                    "table images already exists!"

                # コネクションを保存(コミット)
                db.commit()

                # 画像エンコーディング
                _, enc = cv2.imencode(".png", frame)

                # データを追加
                sql = "insert into images values (?, ?)"
                cur.execute(sql, ("camera", memoryview(enc)))

                # コネクションを保存(コミット)
                db.commit()

                # データの読み出し
                # byteデータの取得
                row = cur.execute('SELECT * FROM images').fetchone()
                pic = row[1]

                # 画像保存
                f = open('load2DBforCameraImage.png', 'wb')
                f.write(pic)
                f.close()
                cur.close()
                # OpneCvでも画像保存
                cv2.imwrite("save.png", frame)

        cap.release()
        exit()


    @pyqtSlot()
    def on_click(self):
        print('button on click!')
        self.camera_capture()


if __name__ == '__main__':
    app = QApplication(sys.argv)
    ex = App()
    sys.exit(app.exec_())
```

## 結果
<img width="432" alt="スクリーンショット 2017-09-17 20.29.56.png" src="https://qiita-image-store.s3.amazonaws.com/0/50627/67317a40-f90e-156a-441e-9ac226714672.png">
中央のボタンを押下すると、画像をVideoCaptchaの画像をDBへ保存し、その後DBから取得しリネームしローカルディレクトに保存します。
ついでに、OpenCvのimwrite()メソッドでも保存を行っています。

現状では、VideoCaptcha()が起動し画像が取得できるまでに、すこし時間がかかるためカウンターを回し、10回カウントしたところで保存処理を行っています。
(C++書いていたときは、特にこんなコトはなかったんですけどね...)
<img width="489" alt="スクリーンショット 2017-09-17 20.38.43.png" src="https://qiita-image-store.s3.amazonaws.com/0/50627/3b5974a8-5505-ed7c-93f4-7b6ac20c53d5.png">
上記のとおり、DBから取得しリネームした画像ファイルと、imwrite()メソッドで保存した2つのファイルが出来上がっていることがわかります。

## おわりに
CやC++でゴリゴリコードを書いてきて、ここ最近MySQLやPHPなんかをやったりしていました。
今回取り組んだpythonによるSQLite画像の保存処理も、比較的簡単に記述できることがわかりました。
また、PyQt5によるGUIの作成も簡単に行えることがわかりました。
(ImGUIより簡単に書けますね！)

まだ、最終目的の機能は実装出来ていませんので、pythonによるGPIOの制御、javascriptとの連携処理などを実装していきたいと思います。

もっとスマートに記述できる箇所が多々あると思いますので、ツッコミ等頂けましたらと思います。
よろしくお願いします。