2019年10月18日金曜日

Tello Programming 011 -Pythonで離陸と着陸を制御する-

Telloをラップトップから制御して,離陸と着陸をさせるコードのメモです.

ラップトップとTello間の情報の送受信に必要なポート情報については,Tello SDK 2.0 User Guideによると,送受信は以下のポートから行えると書いてあります.
  • ラップトップのIPアドレス:192.168.10.2(ラップトップとTelloをWifiで接続すると自動的に割り振られる)
  • ラップトップからTelloへのUDPポート:8889
  • TelloのIPアドレス:192.168.10.1
  • Telloからラップトップへの命令に対するUDPポート:8889
  • Telloからラップトップへの情報(状況)送信UDPポート:8890
  • TelloからラップトップへのビデオをストリームするUDPポート:1111
ラップトップとTelloの接続

Tello SDK 2.0 User Guide, p.2

また,Tello SDK 2.0 User GuideにはTelloの様々なコマンドが記載されていますが,今回は離陸した後に着陸するだけのプログラムなので,使用するのは,以下の二つです.

離陸後に10秒待機して着陸するコードにしてみます.

import logging
import socket
import sys
import time


logging.basicConfig(level=logging.INFO, stream=sys.stdout)
logger = logging.getLogger(__name__)

class DroneManager(object): 
    def __init__(self, host_ip='192.168.10.2', host_port=8889
                 drone_ip='192.168.10.1', drone_port=8889):
        self.host_ip = host_ip
        self.host_port = host_port
        self.drone_ip = drone_ip
        self.drone_port = drone_port
        self.drone_address = (drone_ip, drone_port)
        self.socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        self.socket.bind((self.host_ip, self.host_port))
        self.socket.sendto(b'command', self.drone_address)
        self.socket.sendto(b'streamon', self.drone_address)
    
    def __dell__(self):
        self.stop()
    
    def stop(self):
        self.socket.close()
    
    def send_command(self, command):
        logger.info({'action': 'send_command', 'command': command})
        self.socket.sendto(command.encode('utf-8'), self.drone_address)
    
    def takeoff(self):
        self.send_command('takeoff')
    
    def land(self):
        self.send_command('land')

if __name__ == '__main__'
    drone_manager = DroneManager()
    drone_manager.takeoff()

    time.sleep(10)


    drone_manager.land()

実行に当たっては,ラップトップとTelloをWifi接続します.

そして,ターミナルから以下のように(この例では上記のコードに drone-manager.pyという名前をつけています)実行するだけです.
$ python3 drone_manager.py

実行した際に,ターミナルでは以下のように命令の送信をしている(プログラムは動いている)にも関わらず,Telloが反応しないというトラブルが生じました....
$ python3 drone_manager.py
INFO:__main__:{'action': 'send_command', 'command': 'takeoff'}
INFO:__main__:{'action': 'send_command', 'command': 'land'}

IPアドレスの割り振りの問題かと思って,Telloとラップトップを接続した状態でのラップトップに割り振られているIPアドレスをSystem Preference(システム環境設定)から調べてみると以下のように,192.168.10.2 が割り振られています.

その後,Telloのバッテリーがなくなるまで試行錯誤を繰り返したのですが,状況は変わらず,別のバッテリーに差し替えて試してみたところ,無事にTelloは離陸してホバリングした後に着陸しました.



結果としては離着陸に成功したのですが,何故,最初の時点で反応しなかったのかは,謎のままです.

2019年10月17日木曜日

[OpenCV] 動画の顔認識

OpenCVのライブラリを使用して,動画での顔認識を行ってみた際のメモです.なお,Python3とOpenCVがインストールされている前提です(Python3のインストールはこちら,OpenCVのインストールはこちら).

コードは以下のようになります.

import cv2 as cv

cap = cv.VideoCapture(0)

face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv.CascadeClassifier('haarcascade_eye.xml')

while True:
    ret, frame = cap.read()
    gray = cv.cvtColor(frame, cv.COLOR_BGR2GRAY)
    faces = face_cascade.detectMultiScale(gray, 1.3, 5) # default setting
    print(len(faces))
    
    for (x, y, w, h) in faces:
        cv.rectangle(frame, (x, y), (x+w, y+h), (255, 0, 0), 2)
        eye_gray = gray[y:y+h, x:x+w]
        eye_color = frame[y:y+h, x:x+w]
        eyes = eye_cascade.detectMultiScale(eye_gray)
        for (ex, ey, ew, eh) in eyes:
            cv.rectangle(eye_color, (ex, ey), (ex+ew, ey+eh), (0, 255, 0), 2)

    cv.imshow('frame', frame)
    if cv.waitKey(1) & 0xFF == ord('q'):
        break

cv.destroyAllWindows()

以下は,コードの解説です.
1行目
import cv2 as cv
OpenCVのパッケージを読み込む.

3行目
変数 cap にカメラの画像を読み込むように設定する.0 はラップトップに付属のカメラを使用する場合.上手く認識できない場合は,0を1, 2などと変えてみると上手く行く場合が多い.


2019年10月15日火曜日

[OpenCV] 静止画の顔認識 Object Detection (objdetect module) Cascade Classifier

前回投稿したPython3 + OpenCVによる顔認識では,Cascade Classifier を使用しています.
以下は,OpenCVのページに示されている説明に関してのメモです(ほぼ,直訳).

Goal 目標
In this tutorial,

  • We will learn how the Haar cascade object detection works.
  • We will see the basics of face detection and eye detection using the Haar Feature-based Cascade Classifiers.
  • We will use the cv::CascadeClassifier class to detect objects in a video stream. Particularly, we will use the functions:
  • cv::CascadeClassifier::load to load a .xml classifier file. It can be either a Haar or a LBP classifer
  • cv::CascadeClassifier::detectMultiScale to perform the detection.

このチュートリアルでは

  • Haar cascade 検出の仕組みを学習します.
  • Haar Feature-based Cascade 検出器を使用した顔検出と目の検出の基本について説明します.
  • cv::CascadeClassifierクラスを使用して、ビデオストリーム内のオブジェクトを検出します.特に,次の関数を使用します.
  • cv::CascadeClassifier::load 分類器ファイルを読み込む(HaarまたはLBP分類器のいずれか).
  • cv::CascadeClassifier::detectMultiScale 検出を実行する.


Theory 理論
Object Detection using Haar feature-based cascade classifiers is an effective object detection method proposed by Paul Viola and Michael Jones in their paper, "Rapid Object Detection using a Boosted Cascade of Simple Features" in 2001. It is a machine learning based approach where a cascade function is trained from a lot of positive and negative images. It is then used to detect objects in other images.
Haarの特徴に基づくカスケード分類器を使用したオブジェクト検出は,2001年にPaul ViolaとMichael Jonesの論文「単純な特徴のブーストカスケードを使用した迅速なオブジェクト検出」で提案された効果的なオブジェクト検出方法です.カスケード機能は,多くのポジティブ(正解)またはネガティブ(不正解)画像によってトレーニングされた機械学習です.その後,他の画像内のオブジェクトを検出するために使用されます.

Here we will work with face detection. Initially, the algorithm needs a lot of positive images (images of faces) and negative images (images without faces) to train the classifier. Then we need to extract features from it. For this, Haar features shown in the below image are used. They are just like our convolutional kernel. Each feature is a single value obtained by subtracting sum of pixels under the white rectangle from sum of pixels under the black rectangle.
ここでは,顔検出を行います.最初に,アルゴリズムは分類器を訓練するために多くのポジティブ(正解)画像(顔の画像)とネガティブ(不正解)画像(顔のない画像)を必要とします.次に,そこから(顔の)特徴を抽出する必要があります.このために,下図に示されているHaar機能を使用します.それらは畳み込みカーネルのようなものです.各特徴は,黒い長方形の下のピクセルの合計から白い長方形の下のピクセルの合計を引いて得られる一つの値となります.
image 画像

Now, all possible sizes and locations of each kernel are used to calculate lots of features. (Just imagine how much computation it needs? Even a 24x24 window results over 160000 features). For each feature calculation, we need to find the sum of the pixels under white and black rectangles. To solve this, they introduced the integral image. However large your image, it reduces the calculations for a given pixel to an operation involving just four pixels. Nice, isn't it? It makes things super-fast.
ここで,各カーネルのすべての可能なサイズと場所が多くの特徴を計算するために使用されます(必要な計算量を想像すると,24x24のウィンドウで160000以上の特徴が得られる).特徴の計算ごとに,白と黒の長方形の下にあるピクセルの合計を見つける必要があります.これを解決するために,彼ら(論文の著者達)は積分画像を導入しました.画像がどれほど大きくても,指定されたピクセルの計算は,4つのピクセルのみを含む演算に削減されます.素晴らしいでしょう.それによって超高速になります.

But among all these features we calculated, most of them are irrelevant. For example, consider the image below. The top row shows two good features. The first feature selected seems to focus on the property that the region of the eyes is often darker than the region of the nose and cheeks. The second feature selected relies on the property that the eyes are darker than the bridge of the nose. But the same windows applied to cheeks or any other place is irrelevant. So how do we select the best features out of 160000+ features? It is achieved by Adaboost.
しかし,計算した,全てのこれらの特徴のほとんどは(目とは)無関係です.たとえば,下の画像について考えてください.一番上の行は、2つのわかりやすい特徴を示しています.最初の特徴は,目の領域が鼻と頬よりも暗い色であるという特性にフォーカスしています.二番目の特徴は,しかし,目が鼻梁よりも暗い色である特徴です.しかし,ウィンドウが頬やその他の場所に適用される同じウィンドウは無関係です.それでは,160000以上の特徴の中から最適な特徴を選択するにはどうすればよいでしょうか? それは,Adaboostによって達成されます.
image 画像

For this, we apply each and every feature on all the training images. For each feature, it finds the best threshold which will classify the faces to positive and negative. Obviously, there will be errors or misclassifications. We select the features with minimum error rate, which means they are the features that most accurately classify the face and non-face images. (The process is not as simple as this. Each image is given an equal weight in the beginning. After each classification, weights of misclassified images are increased. Then the same process is done. New error rates are calculated. Also new weights. The process is continued until the required accuracy or error rate is achieved or the required number of features are found).
これによって,すべての特徴を,すべてのトレーニング画像に適用します.各々の特徴に対して,顔を正しいものを間違っているものに分類する,最適なしきい値を見つけますが,明らかに,エラーや誤分類が生じます.なので,エラー率が最小の特徴,すなわち顔と顔以外の画像を最も正確に分類する特徴を選択します(プロセスはこれほど単純ではありません.各々の画像には,最初に等しい重みが与えられます.各分類の後に,誤分類された画像の重みが増加します.その後,同じプロセスが実行されます.その後,新しいエラー率が計算されて,必要な精度またはエラー率が達成されるか,必要な機能の数が見つかるまでプロセスが実行されます).

The final classifier is a weighted sum of these weak classifiers. It is called weak because it alone can't classify the image, but together with others forms a strong classifier. The paper says even 200 features provide detection with 95% accuracy. Their final setup had around 6000 features. (Imagine a reduction from 160000+ features to 6000 features. That is a big gain).
最終的な分類器は,これらの弱い分類器の重みの和になります.それらは,単独では画像を分類できないために,弱いと呼ばれますが,他のものと一緒になって強力な分類器を形成します.この論文では,200の特徴ですら,95%の精度で検出できると述べています.最終的にセットアップされたものには約6000の特徴がありました (160000以上の特徴から6000の特徴への削減は,大きなメリットです).

So now you take an image. Take each 24x24 window. Apply 6000 features to it. Check if it is face or not. Wow.. Isn't it a little inefficient and time consuming? Yes, it is. The authors have a good solution for that.
撮影した画像に対して, 24 x 24の各ウィンドウを取得します.これに, 6000の特徴を適用して,顔かどうかを判断します.この方法は,非効率的で時間がかかると思いませんか?その通りです.著者はこの良い解決策を持っています.

In an image, most of the image is non-face region. So it is a better idea to have a simple method to check if a window is not a face region. If it is not, discard it in a single shot, and don't process it again. Instead, focus on regions where there can be a face. This way, we spend more time checking possible face regions.
画像では,画像のほとんどは顔以外の領域になるので,ウィンドウが顔領域でないかどうかを確認する簡単な方法を用意することをお勧めします.そうでない場合は,1回のショットで破棄し,再度処理はせずに,顔が存在する可能性のある領域にフォーカスします.このようにして,顔の可能性の高い領域を確認することに時間を費やします.

For this they introduced the concept of Cascade of Classifiers. Instead of applying all 6000 features on a window, the features are grouped into different stages of classifiers and applied one-by-one. (Normally the first few stages will contain very many fewer features). If a window fails the first stage, discard it. We don't consider the remaining features on it. If it passes, apply the second stage of features and continue the process. The window which passes all stages is a face region. How is that plan!
このように,彼ら(著者)は分類器のカスケードの概念を導入しました.ウィンドウに6000個のすべての(顔の)特徴判定機能を適用する代わりに,特徴は分類器のさまざまな段階にグループ化されて,1つずつ適用されます(通常,最初の数段階には非常に少ない機能が含まれます).ウィンドウが最初の段階で誤りである(顔ではない)と判断した場合には,そのウィンドウを破棄して,残りの特徴については考慮しません.正しい(顔である)と判断した場合は,次の特徴を判断するステージを適用して,プロセスを続行します.すべてのステージを通過する(顔だと判断される)ウィンドウは,顔の領域となります.この計画はどうでしょう!

The authors' detector had 6000+ features with 38 stages with 1, 10, 25, 25 and 50 features in the first five stages. (The two features in the above image are actually obtained as the best two features from Adaboost). According to the authors, on average 10 features out of 6000+ are evaluated per sub-window.
著者の検出器には、最初の5つの段階で1、10、25、25、および50の機能を備えた38の段階で6000以上の機能がありました(上の画像の2つの特徴は,Adaboostから最高の2つの特徴として実際に取得されています).著者によるとサブウィンドウごとに,平均して6000以上の10個の機能で評価されます.

So this is a simple intuitive explanation of how Viola-Jones face detection works. Read the paper for more details or check out the references in the Additional Resources section.
これは,Viola-Jonesの顔検出をどのように機能するかについての簡単かつ直感的な説明です.詳細については論文を読むか「その他のリソース」セクションを参照して確認してください.

Haar-cascade Detection in OpenCV 
OpenCV provides a training method (see Cascade Classifier Training) or pretrained models, that can be read using the cv::CascadeClassifier::loadmethod. The pretrained models are located in the data folder in the OpenCV installation or can be found here.
The following code example will use pretrained Haar cascade models to detect faces and eyes in an image. First, a cv::CascadeClassifier is created and the necessary XML file is loaded using the cv::CascadeClassifier::load method. Afterwards, the detection is done using the cv::CascadeClassifier::detectMultiScale method, which returns boundary rectangles for the detected faces or eyes.
This tutorial code's is shown lines below. You can also download it from here

objectDetection.cpp
#include "opencv2/objdetect.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include <iostream>

using namespace std;
using namespace cv;

/** Function Headers */
void detectAndDisplay( Mat frame );

/** Global variables */
CascadeClassifier face_cascade;
CascadeClassifier eyes_cascade;

/** @function main */
int main( int argc, const char** argv )
{
    CommandLineParser parser(argc, argv,
                             "{help h||}"
                             "{face_cascade|data/haarcascades/haarcascade_frontalface_alt.xml|Path to face cascade.}"
                             "{eyes_cascade|data/haarcascades/haarcascade_eye_tree_eyeglasses.xml|Path to eyes cascade.}"
                             "{camera|0|Camera device number.}");

    parser.about( "\nThis program demonstrates using the cv::CascadeClassifier class to detect objects (Face + eyes) in a video stream.\n"
                  "You can use Haar or LBP features.\n\n" );
    parser.printMessage();

    String face_cascade_name = samples::findFile( parser.get<String>("face_cascade") );
    String eyes_cascade_name = samples::findFile( parser.get<String>("eyes_cascade") );

    //-- 1. Load the cascades
    if( !face_cascade.load( face_cascade_name ) )
    {
        cout << "--(!)Error loading face cascade\n";
        return -1;
    };
    if( !eyes_cascade.load( eyes_cascade_name ) )
    {
        cout << "--(!)Error loading eyes cascade\n";
        return -1;
    };

    int camera_device = parser.get<int>("camera");
    VideoCapture capture;
    //-- 2. Read the video stream
    capture.open( camera_device );
    if ( ! capture.isOpened() )
    {
        cout << "--(!)Error opening video capture\n";
        return -1;
    }

    Mat frame;
    while ( capture.read(frame) )
    {
        if( frame.empty() )
        {
            cout << "--(!) No captured frame -- Break!\n";
            break;
        }

        //-- 3. Apply the classifier to the frame
        detectAndDisplay( frame );

        if( waitKey(10) == 27 )
        {
            break; // escape
        }
    }
    return 0;
}

/** @function detectAndDisplay */
void detectAndDisplay( Mat frame )
{
    Mat frame_gray;
    cvtColor( frame, frame_gray, COLOR_BGR2GRAY );
    equalizeHist( frame_gray, frame_gray );

    //-- Detect faces
    std::vector<Rect> faces;
    face_cascade.detectMultiScale( frame_gray, faces );

    for ( size_t i = 0; i < faces.size(); i++ )
    {
        Point center( faces[i].x + faces[i].width/2, faces[i].y + faces[i].height/2 );
        ellipse( frame, center, Size( faces[i].width/2, faces[i].height/2 ), 0, 0, 360, Scalar( 255, 0, 255 ), 4 );

        Mat faceROI = frame_gray( faces[i] );

        //-- In each face, detect eyes
        std::vector<Rect> eyes;
        eyes_cascade.detectMultiScale( faceROI, eyes );

        for ( size_t j = 0; j < eyes.size(); j++ )
        {
            Point eye_center( faces[i].x + eyes[j].x + eyes[j].width/2, faces[i].y + eyes[j].y + eyes[j].height/2 );
            int radius = cvRound( (eyes[j].width + eyes[j].height)*0.25 );
            circle( frame, eye_center, radius, Scalar( 255, 0, 0 ), 4 );
        }
    }

    //-- Show what you got
    imshow( "Capture - Face detection", frame );

}

Result
1. Here is the result of running the code above and using as input the video stream of a built-in webcam:
1. 以下は,上記のコードを実行して,入力として組み込みWebカメラのビデオストリームを使用した結果です.

Be sure the program will find the path of files haarcascade_frontalface_alt.xml and haarcascade_eye_tree_eyeglasses.xml. They are located in opencv/data/haarcascades
プログラムがhaarcascade_frontalface_alt.xmlファイルとhaarcascade_eye_tree_eyeglasses.xmlファイルのパスを見つけられるようにしてください.それらはopencv / data / haarcascadesに置いてあります.

2. This is the result of using the file lbpcascade_frontalface.xml (LBP trained) for the face detection. For the eyes we keep using the file used in the tutorial.
2. これは,顔検出にファイルlbpcascade_frontalface.xml(LBPトレーニング済み)を使用した結果です.目のために,チュートリアルで使用したファイルを使用し続けています.

Additional Resources

  1. Paul Viola and Michael J. Jones. Robust real-time face detection. International Journal of Computer Vision, 57(2):137–154, 2004. [239]
  2. Rainer Lienhart and Jochen Maydt. An extended set of haar-like features for rapid object detection. In Image Processing. 2002. Proceedings. 2002 International Conference on, volume 1, pages I–900. IEEE, 2002. [136]
  3. Video Lecture on Face Detection and Tracking
  4. An interesting interview regarding Face Detection by Adam Harvey
  5. OpenCV Face Detection: Visualized on Vimeo by Adam Harvey


2019年10月14日月曜日

[OpenCV] 静止画の顔認識

OpenCVのライブラリを使用して顔認識を行ってみた際のメモです.なお,Python3とOpenCVがインストールされている前提です(Python3のインストールはこちら,OpenCVのインストールはこちら).

コードは以下のようになります.

import cv2 as cv

face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml')
eye_cascade = cv.CascadeClassifier('haarcascade_eye.xml')

img = cv.imread('image.jpg')

gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, 1.3, 5) #default setting
print(len(faces))

for (x, y, w, h) in faces:
    cv.rectangle(img, (x, y), (x+w, y+h), (255, 0, 0), 2)
    eye_gray = gray[y:y+h, x:x+w]
    eye_color = img[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(eye_gray)
    for (ex, ey, ew, eh) in eyes:
        cv.rectangle(eye_color, (ex, ey), (ex+ew, ey+eh), (0, 255, 0), 2)

cv.imshow('img', img)
cv.waitKey(0)

cv.destroyAllWindows()

以下は,コードの解説です.
1行目
import cv2 as cv
OpenCVのパッケージを読み込む.

3行目
face_cascade = cv.CascadeClassifier('haarcascade_frontalface_default.xml')
face_cascade にClasfierメソッドとしてxmlファイルを読み込む.

4行目
eye_cascade = cv.CascadeClassifier('haarcascade_eye.xml')
eye_cascade にClasfierメソッドとしてxmlファイルを読み込む.

6行目
img = cv.imread('image.jpg')
imgに画像をimreadメソッドとして読み込む.

8行目
gray = cv.cvtColor(img, cv.COLOR_BGR2GRAY)
gray変数にimgを引数としてOpenCVのCOLOR_BGR2GRAYメソッドを使ってグレースケールの画像に変換する.

9行目
faces = face_cascade.detectMultiScale(gray, 1.35#default setting
face_cascadeのdetectMultiScaleメソッドを使って,グレースケールに変換した画像(gray)を渡す.セッテイングの引数としては,1.3, 5のデフォルトの値とする.
引数1.3を小さくして設定するとより細かく画像認識を行うようになる.
引数5は,顔が重複した時にどのように認識するかの値.
顔が認識されると,その数が変数facesに入る.

10行目
print(len(faces))
認識した顔の数を出力する.

12〜16行目
for (x, y, w, h) in faces:
    cv.rectangle(img, (x, y), (x+w, y+h), (25500), 2)
    eye_gray = gray[y:y+h, x:x+w]
    eye_color = img[y:y+h, x:x+w]
    eyes = eye_cascade.detectMultiScale(eye_gray)
認識した顔(faces)のx座標(x),y座標(y),横幅(w),高さ(h)の座標を取得する.
ループするので,結果として,認識したひとつひとつの顔に対して以下の処理を行うことになる.

cvのrectangleに引数としてimgを渡して四角で囲む.imgの中の顔として認識したエリア(座標(x, y)と(x+w, y+h))を指定して,その次に色((255, 0, 0) 青色)を指定する.さらにラインの太さ(2)を指定する.
なお,画像の座標は左上が (0, 0) となり,横軸(x座標)は左側が正で縦軸(y座標)は下側が正になる.よって,顔として認識するエリアの左上((x, y))から,右下((x+w, y+h))となる.

続いて,顔と認識した領域から目を検出する.
変数eye_grayに,グレースケールに変換した画像から顔と認識した領域を入れる.
同じようにして,元のカラー画像を変数eye_colorに入れる.
グレースケール画像に対して,eye_cascadeのdetectMultiScaleメソッドを使って目の検出を行う.

17〜18行目
    for (ex, ey, ew, eh) in eyes:
        cv.rectangle(eye_color, (ex, ey), (ex+ew, ey+eh), (02550), 2)
ネストのforループで,目が見つかった場合も,先と同様に検出領域を四角で囲む.
cvのrectangleに引数としてeye_colorを渡して四角で囲む.eye_colorの中の目として認識したエリア(座標(ex, ey)と(ex+ew, ey+eh))を指定して,その次に色((0, 255, 0) 緑色)を指定する.さらにラインの太さ(2)を指定する.

20行目
cv.imshow('img', img)
OpenCVのimgshowメソッドを使って,画像を表示させる.
表示する画像の名前をimgとして,四角の書いてあるimgを上書きする.

21行目
cv.waitKey(0)
OpenCVのwaitKeyメソッドを使って表示する.
0の単位はミリセカンド.

23行目
cv.destroyAllWindows()
OpenCVのdestroyAllWindowメソッドを使って,何らかの入力があった際にウィンドウを閉じることとする.

上記のコードを実行してみた結果は以下のようになりま.

Test1
使用する写真
実行結果
$ python3 image_face_detect.py
3
顔を3つと認識しています.
Daniel Craig(左側)の目は一つだけ,Eva Green(右側)の目は2つ認識しています.

Test2
写真を変えて実行してみます.

実行結果
$ python3 image_face_detect.py
2
顔を2つ認識していて,Daniel Craig(中央)とGemma Arterton(右側)の目は二つ認識していますが,Olga Kurylenko(左側)の顔は認識していません.

Test3
さらに,写真を変えて実行してみます.

実行結果
$ python3 image_face_detect.py
3

顔を3つ認識しています.
Daniel Craig(左)とJavier Bardem(右から2人目)の目は一つ認識しています.またNaomie Harris(右)の顔は認識していますが,目は認識していません.Bérénice Marlohe(左から2人目)の顔は認識していません.

また,写真を変えて実行してみます.
実行結果
$ python3 image_face_detect.py
6
顔を6つ認識しています.
上段では,Ben Whishaw(左)の顔は認識していません.Ralph Fiennes(中央)とAndrew Scott(右)の顔は認識していますが,目は認識していません.中段ではMonica Bellucci(左)とDaniel Craig(中央)の顔は認識していますが,Léa Seydoux(右)の顔は認識していません.下段ではErnst Blofeld(左)とDavid Bautista(右)の顔は認識していますが,Naomie Harris(中央)の顔は認識していません.

2019年10月12日土曜日

[macOS] OpenCVのインストール

macOSにOpenCVをインストールした際のメモです.HomeBrewがインストールされている前提です.

以下のように,ターミナルからHomeBrew経由でOpenCVをインストールします(インストールの際のコマンドは "brew install opencv").
コマンドを実行すると,以下のようにインストールがスタートします.
$ brew install opencv
Updating Homebrew...
==> Auto-updated Homebrew!
Updated 2 taps (homebrew/core and homebrew/cask).
... 途中省略
==> tesseract
This formula contains only the "eng", "osd", and "snum" language data files.
If you need all the other supported languages, `brew install tesseract-lang`.
上記のような感じでインストールが終了します(多少時間がかかります).

続いて,PythonでOpenCVを使うために,pip3を用いてインストールを行います(Anaconda環境などの場合はpip).
$ pip3 install opencv-python
Collecting opencv-python
  Downloading https://files.pythonhosted.org/packages/bf/d8/2422f90a596fc90d3027415121221f7cfb2513af8fd2a66203864941fabf/opencv_python-4.1.2.30-cp37-cp37m-macosx_10_9_x86_64.whl (45.2MB)
     |████████████████████████████████| 45.2MB 2.5MB/s 
Requirement already satisfied: numpy>=1.14.5 in /usr/local/lib/python3.7/site-packages (from opencv-python) (1.16.4)
Installing collected packages: opencv-python
Successfully installed opencv-python-4.1.2.30
上記のように,成功したと返ってくればO.K..

最後にインストールがうまくいっているかを確認しておきます.
Python3のインタプリタを起動して,"import cv2"というコマンドを入力してエラーメッセージが返ってこない(特に何も応答がない)ようであればインストールは成功しています.
$ python3
Python 3.7.5 (default, Nov  1 2019, 02:16:32) 
[Clang 11.0.0 (clang-1100.0.33.8)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> quit()

2019年10月9日水曜日

Tello Programming 008 -Scratchでプログラミング Test Flight 02-

これまでに数回に分けて,TelloScratchでプログラミングする準備やテストを行ってきました.これまでの投稿は以下のようになります.
以前の投稿の中の,Scratchによるプログラミング飛行では,一つのコマンドを送信すると,受信したTelloがコマンドにある動作を行うプログラム作成や,一連の動作を連続して行うプログラムを作成しています.

今回は,Telloの持っているフリップ飛行機能を使って,コマンド送信によって飛行する場合と,一連の流れによって飛行する場合を比較してみたいと思います.

以前の投稿の投稿にもあるように,ターミナルを立ち上げ,nodeを起動させます.
$ cd Tello.jsのあるディレクトリの場所 
$ node Tello.js
そして,Scratch2.0 Offline Editorを起動させ,Scratch2.0 Offline Editorの"File"メニューをShiftキーを押しながらクリックして,"Import Experimental HTTP Extension (実験的なHTTP拡張の読み込み)"をクリックして,Tello.s2eを読み込みます(手順の詳細は以前の投稿を参照して下さい).

ブロックパレットからブロックを移動させて,以下のように連続して動作を行うプログラムを作成します.ブロックパレットに,以下のような動作をプログラミングしました.

一つ目のプログラムの動作は,
  1. 離陸
  2. 100cm上昇
  3. 前フリップ
  4. 後フリップ
  5. フリップ
  6. 左フリップ
  7. 左フリップ
  8. 右フリップ
  9. フリップ
  10. 前フリップ
  11. 100cm下降
  12. 着陸
の1〜12を途中に5秒間の静止時間を挟みながら連続的に行うという流れです.一応,緊急時のために"space"キーを押したら緊急停止するコマンドも作成してあります(画面右上).

Telloがコマンドの通りに飛行すれば,元の場所に戻ってくるはずです.このプログラムを実行した際の動画は以下のようになります.

https://youtu.be/56_z0KeZtXA

離陸前と着陸後のTelloの場所は,動画のようになりました.離着陸の場所は,ほぼ同じになるはずだったのですが,V.P.S. が上手く動作していないと,動画のように,流されてしまい,離陸場所から相当離れた位置に着陸することになります.

2019年10月8日火曜日

Tello Programming 007 -TelloとGameSir T1d Controllerのリンク-

以前の投稿(Tello 本体の準備)でTello専用に使えるプロポとしてGameSir T1d ControllerGamevice iPhone用コントローラーの二種類を紹介しました.
GameSir T1d Controller

今回は,GameSir T1d ControllerTelloのリンクについてのメモです.

GameSir T1d Controllerは以下のような感じです.


GameSir T1d Controllerの前面には(コントローラーを握った時の左手側前面)上側にL1ボタン,下側にL2ボタンがついています.また右手上側にはR1ボタン,下側にはR2ボタンがついています.

また,スティックの上には左側に十字ボタンが,右側にはX, Y, A, Bボタンが配置されています.

GameSir T1d ControllerTelloの接続方法は以下のようになります.

 1.  Telloの電源を入れる.
    2.  GameSir T1dの電源を入れる
GameSir T1dの電源が入った状態
(X, Y, A, Bボタンや左上の電源インジケーターが点灯)

3.  Telloアプリを立ち上げてWifi接続する.
 4.  アプリの設定アイコン(ギアマーク)をタップして,Blootoothコントローラー設定をタップする.

GameSir T1dをタップする.
未接続状態(Disconnectedとなっています)
接続状態(Connectedとなっています)

GameSir T1d Controllerの操作方法は,以下のようになります(Mode 2).

左側スティック 上:上昇
        下:下降
        左:左旋回
        右:右旋回
右側スティック 上:前進
        下:後退
        左:左移動
        右:右移動
R1:写真モード切替,撮影(写真モードの時にR1ボタンを押すと撮影される)
写真モードの時は右上が白丸になります.

L1:動画モード切替,撮影(動画モードの時にL1ボタンを押すと撮影開始,終了)

R2 + Y:離陸,着陸
L2+十字キー上:前フリップ
L2+十字キー下:下フリップ
L2+十字キー左:左フリップ
L2+十字キー右:右フリップ

モードの変更は設定ボタンから行います.

プロポでの操作経験のある人は,GameSir T1d Controllerの方が操作がしやすいと思います(ただし,一般的なプロポに比べて若干反応が鈍い気がするので慣れが必要かもしれません).特に,フリップなどは非常にやりやすいです.