Raspberry Pi 400を使ってみた!〜キーボード一体型ラズパイ400にカメラを付けて画像解析! (ヨシケン)

Facebooktwittermail

こんにちは、Raspberry Piが大好きなヨシケンこと吉田顕一です!

キーボード一体型のRaspberry Pi 400(ラズパイ400)を使って、様々な電子工作をしています。今回はラズパイ400にカメラを付けて、画像解析を行い、仕事時間や笑顔判別により、働き方のチェックを行ってみたいと思います!

疲れた顔をしていると、スピーカーから休んで!の声が

今回の記事の流れ

  • Raspberry Pi 400にカメラをセット
  • Google Vision APIを使った画像解析
  • カメラで写真を撮って表情を読み取る
  • キーボードと連動させて時々働き方チェック!

必要な部品など

Raspberry Pi 400(ラズパイ400)

USBカメラ(なんでも構いませんが、今回はとても安い右側のUSBカメラを使用)

小型液晶ディスプレイ

USBマイク・アダプター
ミニスピーカー

Raspberry Pi 400にカメラをセット

今回は、Raspberry Pi 400(ラズパイ400)にカメラをセットして、写真撮影、画像解析、それに伴ったアクションなどをしてみたいと思います。

まずラズパイ400にカメラを追加するのですが、通常のラズパイにあるカメラコネクターがラズパイ400にはありません。その代わりラズパイ400にはUSBコネクターが3つあるので、ここにUSBカメラを接続します。

画像に alt 属性が指定されていません。ファイル名: vPn_XD3Kz37dqEIB0DgUnkSm6JLsS3INcQ4tLlLYXdnaLsTs702UxvDMqtWFScTQ_kTUKwCAhyKuwJXSyFZxBzERFyNv0AxzC4eyDn2O-LO-sGrlzMmRmB5AIpc68AQB1xgpjO3N
USBにカメラを差すだけで撮影が可能に

今回使ったUSBカメラはこのような小さいものです。また結果を画面で確認したいので、小型の液晶画面も追加します。ラズパイ400への接続は、カメラはUSBにつなぐだけ、小型液晶もマイクロHDMIに接続するだけですぐ使い始められます。

小型液晶もHDMIに接続するだけ

USBカメラから写真などを撮影するのには、fswebcamというアプリケーションを使います。aptコマンドでラズパイ400にfswebcamをインストールしましょう。fswebcamでの写真撮影方法はこちらのサイトに詳細のオプションがあります。画像解像度を指定したり、バナーを外したりして、サンプル撮影をしてみました。

pi@raspi400:~ $ sudo apt install fswebcam
pi@raspi400:~ $ fswebcam -r 640×480 –no-banner image.jpg

撮った写真を確認するためのアプリケーションも入れましょう。eogというGUI環境での画像ビューワで、こちらもatpコマンドでインストールできます。表示のためのオプションとして-fと付けると、全画面で表示してくれます。eogの説明はこちらが詳しいです。先ほど撮った写真を小型液晶に表示してみました。

pi@raspi400:~ $ sudo apt install eog
pi@raspi400:~ $ eog -f image.jpg

顔写真を撮ってeogで全画面表示

Google Vision APIを使って画像解析

写真が撮れたら、そこに何が写っているかをAIを使って判別させましょう。AI画像解析にはGoogleのVision APIを使用したいと思います。https://cloud.google.com/vision

Google Cloud Vision AIサイト [https://cloud.google.com/vision]

Google Visionを使い始めるには、APIライブラリのクイックスタートを参照します。ここにVision APIの設定ステップが詳しく載っているので、それに従ってセットアップします。

赤矢印の「APIを有効にする」でVision APIを有効化

このうち前回のGoogle Cloudの設定が終わっていたら、1、2までは完了していいると思います。まだの方はこのページに従って行っておいて下さい。3.も上記赤矢印のリンクからVison APIを有効化します。

4.の認証の設定では、以下画面に従って、サービスアカウントキーのダウンロードをして下さい。

サービスアカウントキーの作成画面で、「新しいサービスアカウント」を選んで、適当なアカウント名を入力します。「ロール」はプロジェクトオーナーを選びます。これでJson形式の認証ファイルを作成します。

上記で作成を押すと、認証ファイルがパソコンにダウンロードされます。これをscpコマンドなどを使って、認証ファイルをラズパイ400に転送します。

ダウンロードしたJSONファイルをscpコマンド(下記)でラズパイに転送

(パソコン側) user@local:~ $ scp my-key.json pi@raspi400.local:

認証ファイルのラズパイ400への転送が済んだら、ラズパイへ再度ログインします。ホームディレクトリにJsonファイルがあると思いますので、それを以下コマンドにより適用させます。

(ラズパイ側) pi@raspi400:~ $ export GOOGLE_APPLICATION_CREDENTIALS=”/home/pi/my-key.json”

最後にVision API自身をラズパイ400へインストールします。以下のようにpipを使って簡単にインストールする事ができます。

pi@raspi400:~ $ sudo pip3 install –upgrade google-cloud-vision

このVisionライブラリの詳細ドキュメントや使い方はちらにあります。更にVision APIのGithubにサンプルプログラムがありますので、これをラズパイにCloneしておきます。

pi@raspi400:~ $ git clone https://github.com/googleapis/python-vision.git
pi@raspi400:~ $ cd photo-vision/samples/snippets/detect/

サンプルプログラム中のdetect.pyというプログラムを流してみます。これはlabelsというパラメータを付けると写真の中に何が写っているかを解析してくれます。facesと入れると顔部分の座標を取るだけでなく、anger(怒り)やjoy(喜び)などの表情判断もしてくれます。以下がresourcesという所に入っているサンプル写真を使ったdetectプログラムの結果です。色々試してみて下さい。

$ python3 detect.py labels resources/wakeupcat.jpg

Labels:
Cat
Small to medium-sized cats
Mammal
Interior design
Window blindTongue





$ python3 detect.py faces resources/face_no_surprise.jpg
Faces:
anger: LIKELY
joy: VERY_UNLIKELY
surprise: LIKELY
face bounds: (93,425),(520,425),(520,922),(93,922)

画像に alt 属性が指定されていません。ファイル名: face_no_surprise_s.jpg
左がラベル(labels)判別、右が顔(faces)判別の結果

カメラで写真を撮って表情を読み取る

Vision APIが使えるようになったので、ラズパイ400にセットしたUSBカメラで写真を撮って、それと連動できるようにしましょう。先ほどダウンロードしたdetectサンプルプログラムを改造して、以下のようなdetect_cameraプログラムを作りました。

[ Program: detect_camera.py ]

#!/usr/bin/env python
import argparse
import os
from datetime import datetime
image_path = '/home/pi/Pictures/'
def camera(): #Add Camera function
    now     = datetime.now()
    dir_name= now.strftime('%Y%m%d')
    dir_path= image_path + dir_name + '/'
    fname   = dir_path + now.strftime('%H%M%S') + '.jpg'
    try:
        os.mkdir(dir_path)
    except OSError:
        print('Date dir already exists')
    c1 = 'fswebcam -r 640x480 --no-banner '
    os.system(c1 + fname)
    os.system('eog -f '+fname+' &')
    return fname
# [START vision_face_detection]
def detect_faces(path):
    """Detects faces in an image."""
    from google.cloud import vision
    import io
    client = vision.ImageAnnotatorClient()
    # [START vision_python_migration_face_detection]
    # [START vision_python_migration_image_file]
    with io.open(path, 'rb') as image_file:
        content = image_file.read()
    image = vision.Image(content=content)
    # [END vision_python_migration_image_file]
    response = client.face_detection(image=image)
    faces = response.face_annotations
    # Names of likelihood from google.cloud.vision.enums
    likelihood_name = ('UNKNOWN', 'VERY_UNLIKELY', 'UNLIKELY', 'POSSIBLE',
                       'LIKELY', 'VERY_LIKELY')
    print('Faces:')
    for face in faces:
        print('anger: {}'.format(likelihood_name[face.anger_likelihood]))
        print('joy: {}'.format(likelihood_name[face.joy_likelihood]))
        print('surprise: {}'.format(likelihood_name[face.surprise_likelihood]))
        if likelihood_name[face.joy_likelihood] in ('UNLIKELY', 'VERY_UNLIKELY'):
            os.system('aplay rest.wav')
        vertices = (['({},{})'.format(vertex.x, vertex.y)
                    for vertex in face.bounding_poly.vertices])
        print('face bounds: {}'.format(','.join(vertices)))
    if response.error.message:
        raise Exception(
            '{}\nFor more info on error messages, check: '
            'https://cloud.google.com/apis/design/errors'.format(
                response.error.message))
    # [END vision_python_migration_face_detection]
# [END vision_face_detection]
# [START vision_label_detection]
def detect_labels(path):
    """Detects labels in the file."""
    from google.cloud import vision
    import io
    client = vision.ImageAnnotatorClient()
    # [START vision_python_migration_label_detection]
    with io.open(path, 'rb') as image_file:
        content = image_file.read()
    image = vision.Image(content=content)
    response = client.label_detection(image=image)
    labels = response.label_annotations
    print('Labels:')
    for label in labels:
        print(label.description)
    if response.error.message:
        raise Exception(
            '{}\nFor more info on error messages, check: '
            'https://cloud.google.com/apis/design/errors'.format(
                response.error.message))
    # [END vision_python_migration_label_detection]
# [END vision_label_detection]
# [START vision_text_detection]
def detect_text(path):
    """Detects text in the file."""
    from google.cloud import vision
    import io
    client = vision.ImageAnnotatorClient()
    # [START vision_python_migration_text_detection]
    with io.open(path, 'rb') as image_file:
        content = image_file.read()
    image = vision.Image(content=content)
    response = client.text_detection(image=image)
    texts = response.text_annotations
    print('Texts:')
    for text in texts:
        print('\n"{}"'.format(text.description))
        vertices = (['({},{})'.format(vertex.x, vertex.y)
                    for vertex in text.bounding_poly.vertices])
        print('bounds: {}'.format(','.join(vertices)))
    if response.error.message:
        raise Exception(
            '{}\nFor more info on error messages, check: '
            'https://cloud.google.com/apis/design/errors'.format(
                response.error.message))
    # [END vision_python_migration_text_detection]
# [END vision_text_detection]
def run_local(args):
    if args.command == 'faces':
        detect_faces(args.path)
    elif args.command == 'labels':
        detect_labels(args.path)
    elif args.command == 'text':
        detect_text(args.path)
if __name__ == '__main__':
    parser = argparse.ArgumentParser(
        description=__doc__,
        formatter_class=argparse.RawDescriptionHelpFormatter)
    subparsers = parser.add_subparsers(dest='command')
    detect_faces_parser = subparsers.add_parser(
        'faces', help=detect_faces.__doc__)
    #detect_faces_parser.add_argument('path')
    detect_labels_parser = subparsers.add_parser(
        'labels', help=detect_labels.__doc__)
    #detect_labels_parser.add_argument('path')
    detect_text_parser = subparsers.add_parser(
        'text', help=detect_text.__doc__)
    #detect_text_parser.add_argument('path')
    args     = parser.parse_args()
    args.path= camera()
    if 'uri' in args.command:
        run_uri(args)
    else:
        run_local(args)

さあこのプログラムを流してみましょう。パラメータとして、labelsと入れるとそこに写っているものを解析、facesをセットすると顔解析、textと入れれば文字読み取りを行ってくれます。

pi@raspi400:~ $ python3 detect_camera.py faces

Faces:
anger: VERY_UNLIKELY
joy: LIKELY
surprise: VERY_UNLIKELY
face bounds: …

ここではfacesオプションを指定して、見事喜んでいるっぽい(Joy Likely)顔と判別してもらいました。

キーボードと連動させて、時々働き方チェック!

最後にせっかくキーボード一体型のラズパイ400なので、時々キーボードを押すと、顔写真を撮ってお仕事中などに疲れた顔をしていないかのチェックをしてくれるようにしましょう。

まずシェルスクリプトを作って、認証ファイルの適用やdetectプログラムを起動出来るようにしておきます。

[ Program: rp400vision.sh]

#!/bin/bash --rcfile
export GOOGLE_APPLICATION_CREDENTIALS=/home/pi/RaspberryAi-fe5c92410ba3.json
echo "Applied Vision certificate."
echo "Run Google Vision!"
cd /home/pi/Programs/
python3 detect_camera.py faces
echo "Completed Google Vision!"

そうしたら前々回作ったキーボード操作と連動させたプログラムを改良して、”V”と打ったら、シェルスクリプトを起動するようにします。その結果、もし楽しそうな顔をしていなかったら(joyがUNLIKELYかVERY_UNLIKELY)、「疲れてそうだから少し休んで!」という音声を流すようにします。

detect_camera.pyプログラムに、音声出力の記述を追加します。

[ Program: detect_camera.py ]

...
    for face in faces:
        print('anger: {}'.format(likelihood_name[face.anger_likelihood]))
        print('joy: {}'.format(likelihood_name[face.joy_likelihood]))
        print('surprise: {}'.format(likelihood_name[face.surprise_likelihood]))
 +       if likelihood_name[face.joy_likelihood] in ('UNLIKELY', 'VERY_UNLIKELY'):
 +           os.system('aplay rest.wav')
        vertices = (['({},{})'.format(vertex.x, vertex.y)
...

前々回のキーボード操作プログラムrp400_keys.pyをコピーして、vision_keys.pyとして、Vを押した時の記述として以下5行を追加します。これまでのようにプログラムは、Githubにも格納しています。https://github.com/ktrips/raspi400

[ Program: vision_keys.py ]

import sys,tty,termios
import os
...
while True:
    char = getch()
+    if char in ("V”):
+        print("Google Vision!")
+        pixels.fill((255, 0, 0))
+        pixels.show()
+        os.system("bash /home/pi/Programs/rp400vision.sh")
     if char in ("g",”G”): #green
...

それではこれまでのように、ラズパイ400上で流してみます。大文字のV(Shift+v)を押すと以下のように写真を撮って、音楽を再生してくれたでしょうか?

pi@raspi400:~ $ sudo python3 vision_keys.py

Faces:
anger: VERY_UNLIKELY
joy: UNLIKELY
surprise: VERY_UNLIKELY
face bounds: (22,0),(535,0),(535,319),(22,319)
Playing WAVE ‘rest.wav’ : Unsigned 8 bit, Rate 8000 Hz, Mono
Completed Google Vision!

Shift+Vで写真撮影して顔解析、音声出力

自宅作業が多くなる中、ついつい根を詰めて働きすぎてしまった時などに、Vボタンを押して、(無理矢理にでも)笑って息抜きしてみるのもいいのではないでしょうか!?

ラズパイ400を使って色々な電子工作にチャレンジしてみて下さい!

(吉田 顕一)

Facebooktwitterrssyoutubeinstagram