サイトアイコン あきらちんの技術メモ

Pythonをはじめよう 第6回 cameraサンプルを改良して顔認識

はじめに

前回の第5回でWebカメラの映像をkivyにうまく表示できるようになりました。

今回はこれをさらに改良してOpenCVの顔検出器を使って顔認識を行います。

顔に赤枠を付けたりモザイクをかけたりしますので、画面も少々改良します。

 

顔検出カスケードファイルのダウンロード

OpenCVの顔検出器で利用するカスケードファイルをダウンロードしてきます。

いろいろなファイルがあるのですが、今回は顔正面のファイル「haarcascade_frontalface_alt.xml」を利用します。

次のサイト(GitHub)へアクセスします。

赤枠のhaarcascade_frontalface_alt.xmlをクリックすると次の画面が表示されます。

赤枠のRawを右クリックしてリンク先を保存します。

保存先は、D:\PyDev\GuiKivy としました。

改良したソースファイル

保存先は、D:\PyDev\GuiKivyです。

仮想環境のアクティブ化を忘れずに実行します。env\Scripts\activate

from kivy.app import App
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
import time
import cv2
from kivy.uix.image import Image
from kivy.graphics.texture import Texture
from kivy.properties import StringProperty, ObjectProperty
from kivy.clock import Clock

Builder.load_string('''
<CameraClick>:
    orientation: 'vertical'
    BoxLayout:
        orientation: 'horizontal'
        padding: 0
        spacing: 1
        size_hint: 1, 0.1
        Image:
            id: camera
            size_hint: None, None
            size: 640, 480

        BoxLayout:
            orientation: 'vertical'
            ToggleButton:
                text: 'Face'
                on_press: root.face()
                size_hint_y: None
                height: '48dp'
            ToggleButton:
                text: 'Mosaic'
                on_press: root.mosaic()
                size_hint_y: None
                height: '48dp'

    ToggleButton:
        text: 'Play'
        on_press: root.play()
        size_hint_y: None
        height: '48dp'
    Button:
        text: 'Capture'
        size_hint_y: None
        height: '48dp'
        on_press: root.capture()
''')


class CameraClick(BoxLayout):
    image_texture = ObjectProperty(None)
    image_capture = ObjectProperty(None)

    def face(self):
        global flgFace
        flgFace = not flgFace

    def mosaic(self):
        global flgMosaic
        flgMosaic = not flgMosaic

    def play(self):
        global flgPlay
        flgPlay = not flgPlay
        if flgPlay == True:
            self.image_capture = cv2.VideoCapture(0)
            Clock.schedule_interval(self.update, 1.0 / 30)
        else:
            Clock.unschedule(self.update)
            self.image_capture.release()

    def update(self, dt):
            ret, frame = self.image_capture.read()
            if ret:
                if flgFace == True or flgMosaic == True:
                    # カスケードファイルを指定して検出器を作成
                    cascade_file = "haarcascade_frontalface_alt.xml"
                    cascade = cv2.CascadeClassifier(cascade_file)
                    # カメラ映像をグレースケールに変換
                    frame_gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
                    # 顔検出
                    face_list = cascade.detectMultiScale(frame_gray, minSize=(30, 30))
                    if len(face_list) > 0:
                        red = (0, 0, 255) 
                        for (x, y, w, h) in face_list:
                            if flgFace == True:
                                frame = cv2.rectangle(frame, (x, y), (x+w, y+h), red, thickness = 2)
                            if flgMosaic == True:
                                frame = self.procMosaic(frame, (x, y, x+w, y+h), 5) 
                # カメラ映像を上下左右反転
                buf = cv2.flip(frame, -1)
                image_texture = Texture.create(size=(frame.shape[1], frame.shape[0]), colorfmt='bgr')
                image_texture.blit_buffer(buf.tostring(), colorfmt='bgr', bufferfmt='ubyte')
                camera = self.ids['camera']
                camera.texture = image_texture

    def capture(self):
        '''
        Function to capture the images and give them the names
        according to their captured time and date.
        '''
        camera = self.ids['camera']
        timestr = time.strftime("%Y%m%d_%H%M%S")
        camera.export_to_png("IMG_{}.png".format(timestr))
        print("Captured")

    def procMosaic(self, img, rect, size):
        # モザイクをかける領域を取得
        (x1, y1, x2, y2) = rect
        w = x2 - x1
        h = y2 -y1
        i_rect =img[y1:y2, x1:x2]
        # 一度縮小して拡大する
        i_small = cv2.resize(i_rect, (size, size))
        i_mos = cv2.resize(i_small, (w, h), interpolation=cv2.INTER_AREA)
        # 画像にモザイク画像を重ねる
        img2 = img.copy()
        img2[y1:y2, x1:x2] = i_mos
        return img2


class TestCamera(App):

    def build(self):
        return CameraClick()


flgPlay = False
flgFace = False
flgMosaic = False
TestCamera().run()

ちょこっと解説

kv言語

・今回、顔認識用とモザイク用の2つのトグルボタンを追加したのですが、カメラ映像の右側に表示させました。

・BoxLayoutを利用して、追加する2つのトグルボタンを縦並びにし(24行目から35行目)、カメラ映像のイメージコントロールと横並びにします。(14行目から35行目)

・追加する2つのトグルボタンは、Playと同じようにフラグのOnOffで利用します。

・トグルボタンはそれぞれ、FaceとMosaicとしました。

pythonコード

・FaceトグルボタンがクリックされたときにFaceフラグを変化させる処理を追加。(54行目から56行目)

・MosaicトグルボタンがクリックされたときにMosaicフラグを変化させる処理を追加。(58行目から60行目)

・FaceフラグかMosaicフラグがTrueの場合の処理を追加します。(75行目から89行目)

・ダウンロードしたカスケードファイルを指定して検出器を作成します。(77行目、78行目)

・カメラ映像をグレースケールに変換します。(80行目)

・顔検出させます。(82行目)

・FaceフラグがTrueの場合、赤枠で顔を囲みます。(86行目、87行目)

・MosaicフラグがTrueの場合、顔にモザイクをかけます。(88行目、89行目)

・モザイクをかける処理を追加します。(107行目から119行目)

・FaceフラグとMosaicフラグの初期化を行います。(129行目、130行目)

 

実行結果

起動すると次の画面が表示されます。

 

PlayしてFaceボタンとMosaicボタンを押してCaptureしたときの画像が次のものです。

証明写真と一緒に顔検出&モザイクがかかりました。赤枠だけやモザイクだけでも動作しますので是非お試しください。

 

最後に

上手く動作して少しニヤニヤしてしまい、その顔が画面に表示されてさらにニヤニヤ。あぶないおっさんの出来上がり。

どんどん面白くなってきました。

ボタンを配置するスペースもまだあるし色々な画像処理を加えてみるのも楽しいかもしれません。

また、多重処理になるのでどのくらいまでレスポンスに耐えられるものなのかも気になるところですね。

 

モバイルバージョンを終了