2014年2月21日金曜日

Raspberry Pi + OpenCV 日経Linux の顔認識が動かない

日経Linux の 2013/8 や「電子工作入門」に掲載されている「専用カメラモジュールを活用“美女Linux”を顔認識」をやってみた。
その結果は、動かなかった。
まず、この記事で使用している「まなみん」だか「てるみん」という女性の顔写真が入手できない。しかたがないので、IMDb で適当な番組を検索して、その出演者の顔写真をダウンロードして使用した。
3/4 くらいは認識できなかった。
Wil Wheaton、Patrick Stewart、Naveen Andrews、Michael Emerson、Matthew Fox、LeVar Burton、Ken Leung、Josh Holloway、Jorge Garcia、Jonathan Frakes、Grace Park、Brent Spiner、Mayim Bialik、Simon Helberg、Johnny Galecki、Kunal Nayyar は認識できる顔写真だった。さらに探して20枚でやってみた。
で、実行すると・・・。
pi@raspberrypi /opt/vc/camcv $ ./camcv face.cfg 1 5000
start
(init) People initialized
(OK) csv=face.cfg
(init) 10 pictures read to train
(init) 10 pictures of Pierre (0) read to train
(init) 0 pictures of Natacha (1) read to train
(init) 0 pictures of Mona Lisa (2) read to train
(init) 0 pictures of Lisa (3) read to train
(OK) read CSV ok
(init) taille images ok
(init) start train images
(init) train images : ok
(init) Load modele : ok
Xlib:  extension "RANDR" missing on display ":1.0".
mmal: main: Failed to create preview component
mmal: Failed to run camera app. Please check for firmware updates

2668812741 seconds for 0 frames : FPS = 0.000000
pi@raspberrypi /opt/vc/camcv $ 

「mmal: main: Failed to create preview component」と言われてしまう。
このメッセージの出所を調べると、camcv_vid1.cpp の 764 行めあたりにあるこれ「else if (!raspipreview_create(&state.preview_parameters))」が、エラーの条件を判断しているところであることがわかった。
次に、「raspipreview_create()」が何を返しているかを調べてみた。
RaspiPreview.h で宣言されていて、MMAL_STATUS_T を返している。
それは、mmal_types.h に定義されている enum の値であった。
見ると、「MMAL_SUCCESS = 0」となっているではないか。
さきほどの「else if (!raspipreview_create()」は0でなければエラーとしている。
でも0なら MMAL_SUCCESS ということで、エラーの無い状態です。
逆なんです。というわけで 764 行めを次のように変更した。
else if (raspipreview_create(&state.preview_parameters) != MMAL_SUCCESS)
0でエラー、ではなく、0でなければエラー、にした。
これで一応なんとか動いているが、しばらくするとやっぱりエラーがでる。
pi@raspberrypi / $ /opt/vc/camcv/camcv /opt/vc/camcv/face.cfg 1 5000
start
(init) People initialized
(OK) csv=/opt/vc/camcv/face.cfg
(init) 10 pictures read to train
(init) 10 pictures of Pierre (0) read to train
(init) 0 pictures of Natacha (1) read to train
(init) 0 pictures of Mona Lisa (2) read to train
(init) 0 pictures of Lisa (3) read to train
(OK) read CSV ok
(init) taille images ok
(init) start train images
(init) train images : ok
(init) Load modele : ok
Xlib:  extension "RANDR" missing on display ":1.0".
mmal: Failed to run camera app. Please check for firmware updates

65 seconds for 208 frames : FPS = 3.200000
pi@raspberrypi / $ 

これって、そうしているようです。
同じソースファイル camcv_vid1.cpp の 233 行めにこれがありました。
state->timeout = 65000; // capture time : here 65 s
65秒で止まるようにしてありました。でも「Please check for firmware updates」は無いと思います。

2014年1月24日金曜日

SDメモリの丸ごとバックアップ

SDメモリを丸ごとバックアップする方法について書いておく。
こうして作成したバックアップファイルをリストアする方法についても書いておく。

まず、バックアップ。(disk3の場合。これの確認は $ df -h)
$ sudo diskutil unmount /dev/disk3s1
$ sudo diskutil unmount /dev/disk3s5
$ sudo dd if=/dev/disk3 of=~/Desktop/RaspberryPi_20130620.img bs=8m
$ sudo diskutil eject /dev/rdisk3

あるいは?
$ sudo diskutil unmount /dev/disk3s1
$ sudo diskutil unmount /dev/disk3s5
$ sudo dd if=/dev/rdisk3 of=~/Desktop/RaspberryPi_20130620.img bs=8m
$ sudo diskutil eject /dev/rdisk3

次にリストアする。(disk4の場合)
$ sudo diskutil unmount /dev/disk4s1
$ sudo dd bs=8m if=~/Desktop/RaspberryPi_20130620.img of=/dev/rdisk4
$ sudo diskutil eject /dev/rdisk4

bs=8m にしているけれど、てきとうです。

2014年1月8日水曜日

OpenCV で、C のソースを Python にする

OpenCV の C のソースを Python に変換するときのメモ。
C にしておくと、ソースとオブジェクトの二つのファイルの管理が必要となったり、ライブラリの置く場所など、いろいろめんどう。
というわけで、Python にしてしまおうという計画。

言語文法上で注意するのはこれくらい?
  1. C の文は「;」で終了するが、Python にはない。
  2. C の変数宣言では型が必要だが、Python は変数名だけ。
  3. C では「&」をつけて、その変数のポインタを求めたりするが、Python では不要。
  4. True/False や Not の表記
  5. nil や NULL や None の表記
  6. Python のインデント。
「import cv」とすると、メソッド名などに「cv.」をつけることになる。
しかし、cv. の後のメソッド名には「cv」がつかない。
ただし、定数名の頭についている「CV」はそのまま残る。
こんな感じ。

C:
frame = cvQueryFrame(capture);
cvSmooth(frame, frame, CV_GAUSSIAN, 3, 3);

Python:
frame = cv.QueryFrame(capture)
cv.Smooth(frame, frame, cv.CV_GAUSSIAN, 3, 3)

C の cvQueryFrame は Python では cv.QueryFrame となる。
ただし定数は、C で CV_GAUSSIAN、Python で cv.CV_GAUSSIAN となる。

厄介なのがマクロ(?)。Python にはない。ふつーにタプルで表現したりする。

C:
const int w = frame->width;
const int h = frame->height;
imgR = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
cvCircle(frame, cvPoint(gX, gY), 80, CV_RGB(0,0,255), 6, 8, 0);

Python:
the_size = cv.GetSize(frame)
imgR = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
cv.Circle(frame, (gX, gY), 80, (0, 0, 255), 6, 8, 0)

そのほか、調べないとわからないもの。

C:
cvMoments(imgResult, &moments, 0);
double m00 = cvGetSpatialMoment(&moments, 0, 0);

Python:
moments = cv.Moments(cv.GetMat(imgResult, 1), 0)
m00 = cv.GetSpatialMoment(moments, 0, 0)


サンプルとして、次のページにあった赤い色のエリアを抽出し、重心を求めるソースの C 版と Python 版を示す。
http://d.hatena.ne.jp/shokai/20090203/1233609981
http://opencv-srf.blogspot.ro/2010/09/object-detection-using-color-seperation.html

これは Mac (10.9.1 Mavericks) と Raspberry Pi で動作確認した。

C:
#include <opencv/cv.h>
#include <opencv/highgui.h>

// This function threshold the HSV image and create a binary image
IplImage* GetThresholdedImage(IplImage* imgHSV) {
    IplImage* imgResult = cvCreateImage(cvGetSize(imgHSV), IPL_DEPTH_8U, 1);
    cvInRangeS(imgHSV, cvScalar(150, 160, 60), cvScalar(200, 256, 256), imgResult);
    return imgResult;
}

int main() {
    CvCapture* capture = NULL;
    IplImage *frame = NULL;
    IplImage *imgR = NULL, *imgG = NULL, *imgB = NULL;
    IplImage *imgThreshold_R = NULL, *imgThreshold_G = NULL, *imgThreshold_B = NULL;
    IplImage *imgTmp = NULL;
    IplImage *imgHSV = NULL;
    IplImage *imgResult = NULL;
    CvMoments moments;
    
    capture = cvCaptureFromCAM(0);
    if (!capture) {
        printf("Capture failure\n");
        return -1;
    }
    
    cvNamedWindow("Video");
    cvNamedWindow("Results");
    
    
    // iterate through each frames of the video
    while (true) {
        
        frame = cvQueryFrame(capture);
        if (!frame) {
            break;
        }
        
        frame = cvCloneImage(frame);
        cvSmooth(frame, frame, CV_GAUSSIAN, 3, 3); // smooth the original image using Gaussian kernel
                
        if (1) {
            const int w = frame->width;
            const int h = frame->height;
            
            imgR = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgG = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgB = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgThreshold_R = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgThreshold_G = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgThreshold_B = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgResult = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);
            imgTmp = cvCreateImage(cvSize(w, h), IPL_DEPTH_8U, 1);

            cvSplit(frame, imgB, imgG, imgR, NULL); // BGRを分解
            
            // 赤の要素が100以上で、緑と青より1.5倍以上あるピクセルを抽出
            cvThreshold(imgR, imgThreshold_R, 100, 255, CV_THRESH_BINARY);
            cvDiv(imgR, imgG, imgTmp, 10); // 10倍
            cvThreshold(imgTmp, imgThreshold_G, 15, 255, CV_THRESH_BINARY);
            cvDiv(imgR, imgB, imgTmp, 10);
            cvThreshold(imgTmp, imgThreshold_B, 15, 255, CV_THRESH_BINARY);
            cvAnd(imgThreshold_G, imgThreshold_B, imgTmp, NULL);
            cvAnd(imgTmp, imgThreshold_R, imgResult, NULL);
            
            cvMoments(imgResult, &moments, 0);
            double m00 = cvGetSpatialMoment(&moments, 0, 0);
            double m10 = cvGetSpatialMoment(&moments, 1, 0);
            double m01 = cvGetSpatialMoment(&moments, 0, 1);
            int gX = m10 / m00;
            int gY = m01 / m00;
            cvCircle(frame, cvPoint(gX, gY), 80, CV_RGB(0,0,255), 6, 8, 0);
        } else {
            imgHSV = cvCreateImage(cvGetSize(frame), IPL_DEPTH_8U, 3);
            cvCvtColor(frame, imgHSV, CV_BGR2HSV); // Change the color format from BGR to HSV
            imgResult = GetThresholdedImage(imgHSV);
            cvSmooth(imgResult, imgResult, CV_GAUSSIAN, 3, 3); // smooth the binary image using Gaussian kernel
        }
        cvShowImage("Results", imgResult);
        cvShowImage("Video", frame);
        
        
        // Clean up used images
        cvReleaseImage(&imgHSV);
        cvReleaseImage(&imgResult);
        cvReleaseImage(&frame);
        
        // Wait 50mS
        int c = cvWaitKey(10);
        // If 'ESC' is pressed, break the loop
        if ((char)c == 0x1b ) {
            break;
        }
    }
    
    cvDestroyAllWindows() ;
    cvReleaseCapture(&capture);
    
    return 0;
}

Python:
#!/usr/bin/env python
# coding: UTF-8
"""
DetectingRedObjects.py

"""
import cv

def GetThresholdedImage(imgHSV):
    imgResult = cv.CreateImage(cv.GetSize(imgHSV), cv.IPL_DEPTH_8U, 1)
    cv.InRangeS(imgHSV, cv.Scalar(150, 160, 60), cv.Scalar(200, 256, 256), imgResult)
    return imgResult

def main():
    capture = None
    frame = None
    imgR = imgG = imgB = None
    imgThreshold_R = imgThreshold_G = imgThreshold_B = None
    imgTmp = imgHSV = imgResult = None
    moments = None
    
    capture = cv.CaptureFromCAM(0)
    if (not capture):
        print("Capture failure")
        return -1
    
    cv.NamedWindow("Video")
    cv.NamedWindow("Results")
    
    
    # iterate through each frames of the video
    while (True):
        frame = cv.QueryFrame(capture)
        if (not frame):
            break;
        
        frame = cv.CloneImage(frame)
        cv.Smooth(frame, frame, cv.CV_GAUSSIAN, 3, 3) # smooth the original image using Gaussian kernel
        
        if (True):
            # 赤色領域を検出(3) 重心も計算
            # http://d.hatena.ne.jp/shokai/20090203/1233609981
            the_size = cv.GetSize(frame)
            # w = frame.width
            # h = frame.height
            
            imgR = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgG = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgB = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgThreshold_R = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgThreshold_G = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgThreshold_B = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgResult = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            imgTmp = cv.CreateImage(the_size, cv.IPL_DEPTH_8U, 1)
            
            cv.Split(frame, imgB, imgG, imgR, None) # BGRを分解
            
            # 赤の要素が100以上で、緑と青より1.5倍以上あるピクセルを抽出
            cv.Threshold(imgR, imgThreshold_R, 100, 255, cv.CV_THRESH_BINARY)
            cv.Div(imgR, imgG, imgTmp, 10) # 10倍
            cv.Threshold(imgTmp, imgThreshold_G, 15, 255, cv.CV_THRESH_BINARY)
            cv.Div(imgR, imgB, imgTmp, 10)
            cv.Threshold(imgTmp, imgThreshold_B, 15, 255, cv.CV_THRESH_BINARY)
            cv.And(imgThreshold_G, imgThreshold_B, imgTmp, None)
            cv.And(imgTmp, imgThreshold_R, imgResult, None)
            
            # cv.Moments(imgResult, moments, 0)
            moments = cv.Moments(cv.GetMat(imgResult, 1), 0)
            m00 = cv.GetSpatialMoment(moments, 0, 0)
            m10 = cv.GetSpatialMoment(moments, 1, 0)
            m01 = cv.GetSpatialMoment(moments, 0, 1)
            if (m00 != 0):
                gX = int(m10 // m00)
                gY = int(m01 // m00)
                # cvCircle(frame, cvPoint(gX, gY), 80, CV_RGB(0,0,255), 6, 8, 0);
                cv.Circle(frame, (gX, gY), 80, (0, 0, 255), 6, 8, 0)
        else:
            # Color Detection & Object Tracking
            # http://opencv-srf.blogspot.ro/2010/09/object-detection-using-color-seperation.html
            imgHSV = cv.CreateImage(cv.GetSize(frame), cv.IPL_DEPTH_8U, 3)
            cv.CvtColor(frame, imgHSV, cv.CV_BGR2HSV) # Change the color format from BGR to HSV
            imgResult = GetThresholdedImage(imgHSV)
            cv.Smooth(imgResult, imgResult, cv.CV_GAUSSIAN, 3, 3) # smooth the binary image using Gaussian kernel

        cv.ShowImage("Results", imgResult)
        cv.ShowImage("Video", frame)
        
        
        # Clean up used images
        # cv.ReleaseImage(imgHSV)
        # cv.ReleaseImage(imgResult)
        # cv.ReleaseImage(frame)
        
        # Wait 50mS
        c = cv.WaitKey(10)
        # If 'ESC' is pressed, break the loop
        if (c == 0x1b):
            break

    cv.DestroyAllWindows()
    # cv.ReleaseCapture(capture)
    return 0


if __name__ == '__main__':
    main()