Monday, January 1, 2018

Pythonではじめる機械学習を読んで その1

最近、AIという言葉をどこでも耳にします。
私は、ここでいうAIは機械学習のことだと認識しています。
以前にも機械学習は勉強したことがあるのですが、すっかり忘れてしまったので再度学習してみようと思い立ちました。

色々情報を集めていると以下の本がなかなかよさそうです。
今は機械学習といえばPythonが流行りのようです。

Pythonではじめる機械学習 ―scikit-learnで学ぶ特徴量エンジニアリングと機械学習の基礎

私が購入したのは2017年11月6日発行の初版第3刷のモノです。
本通りにやって詰まった部分がちょこちょこありましたので、メモ代わりにブログに残しておきます。
同じように悩める方がスムーズに学習できることを期待しております。


それでは今回は1章 はじめについてです。
以下の章番号はPythonではじめる機械学習に準じています。
なお、私の環境はWindows10 Pro 64bitです。

1章 はじめに

1.3.1 scikit-learnのインストール

3つのやり方が示されていましたが、私は一番無難そうだったAnacondaでインストールしていました。
下記サイトにアクセスして各プラットフォームにあったインストーラをダウンロードしてください。
私が入手したのはAnaconda 5.0.1 For Windows InstallerのPython 3.6 version 64bitです。
https://www.anaconda.com/download/

後述しますが、書籍では別途mglearnをイ入手するよう指示しています。


1.4.1 Jupyter Notebook

Jupyter NotebookはWindowsのスタートメニューから起動します。
起動するとブラウザ上に下記のような画面が表示されます。
①のNewをクリックしてPython3を選択します。
下記の画面が表示されます。
②のセル内にコードを入力します。
 Ctrl+Enterを押すと選択したセル内のコードが実行され、その結果が表示されます。
Shift+Enterで新しいセルを追加できます。
③のテクストボックスに名前を入力し、④のボタンで保存できます。
保存すればまたいつでも途中から作業を続けることができます。

1.4.5 pandas

IPythonでDataFrameを表示しますが、display()を読んだところでエラーとなりました。
以下のようにimportするとうまくいきます。
from IPython.core.display import display

1.4.6 mglearn

Anacondaにはmglearnは含まれていないので別途インストールする必要があります。
(ちゃんと書籍には記してありましたがうっかり見落としていました。)
Anacondaのcoda installコマンドではインストールできないのでpipを使います。
pip install mglearn
特にパスを通すなど必要なくjupyter noteで動きました。


私がつまづいたところは以上です。





Python版OpenCVで平滑化を行う

今回は,Python版のOpenCVを使って取得した画像の平滑化を行う方法をメモしておきます.

環境は以下のとおりです.
Python 2.6
OpenCV 2.4.6


それでは早速スクリプトを見ていきます.

import cv2

if __name__=="__main__":

    capture = cv2.VideoCapture(0)
    
    if capture.isOpened() is False:

        raise("IO Error")

    cv2.namedWindow("Capture", cv2.WINDOW_AUTOSIZE)
    cv2.namedWindow("Red", cv2.WINDOW_AUTOSIZE)

    while True:

        ret, image = capture.read()
        if ret == False:
            continue

        gaussian_image = cv2.GaussianBlur(image, (5, 5),  0)
        
        red_image = []
        red_image.append(extract_color(image, 170, 5, 50, 200))
        red_image.append(extract_color(gaussian_image, 170, 5,  50, 200))
        
        gaussian_result = cv2.hconcat([image, gaussian_image])
        red_result = cv2.hconcat(red_image)
        
        cv2.imshow("Capture", gaussian_result)
        cv2.imshow("Red", red_result)
       
        if cv2.waitKey(33) >= 0:
            cv2.imwrite("capture.png", gaussian_result)
            cv2.imwrite("red_image.png", red_result)
            break

    cv2.destroyAllWindows()

Webカメラからの画像の取得と色抽出については,記事の終わりにあるリンクからそれぞれの説明をご覧ください.
なお,色抽出には以前に作成した以下のようなextract_color関数を使います.

出力画像 extract_color( 入力画像, 色相のしきい値(下), 色相のしきい値(上), 彩度のしきい値, 明度のしきい値 )



cv2.THRESH_BINARY: しきい値より大きい値は最大値,それ以外は0
cv2.THRESH_BINARY_INV: しきい値より大きい値は0,それ以外は最大値

その後,各画像のORを取ることで,2枚の画像を合わせます.
    if h_th_low > h_th_up:
        ret, h_dst_1 = cv2.threshold(h, h_th_low, 255, cv2.THRESH_BINARY) 
        ret, h_dst_2 = cv2.threshold(h, h_th_up,  255, cv2.THRESH_BINARY_INV)
        
        dst = cv2.bitwise_or(h_dst_1, h_dst_2)
次に,360°をまたがない場合を考えます.
ここでは,しきい値処理に以下のものを使用します.

cv2.THRESH_TOZERO: しきい値よりより大きい値はそのまま,それ以外は0
cv2.THRESH_TOZERO_INV: しきい値よりより大きい値は0,それ以外はそのまま

まず,しきい値(下)の値を0にし,その後,しきい値(上)より大きい値を0にすることで特定の範囲の値を抽出できます.
    else:
        ret, dst = cv2.threshold(h,   h_th_low, 255, cv2.THRESH_TOZERO) 
        ret, dst = cv2.threshold(dst, h_th_up,  255, cv2.THRESH_TOZERO_INV)

        ret, dst = cv2.threshold(dst, 0, 255, cv2.THRESH_BINARY)
色相については,ここまでで処理できたので,残りの明度と彩度についても処理を行います.
ここでは,黒に近い色を排除するだけでよいので,下限値のみのしきい値処理を行います.
    ret, s_dst = cv2.threshold(s, s_th, 255, cv2.THRESH_BINARY)
    ret, v_dst = cv2.threshold(v, v_th, 255, cv2.THRESH_BINARY)
そして最後に,各画像のANDをとり,各しきい値を満たす画素を抽出します.
    dst = cv2.bitwise_and(dst, s_dst)
    dst = cv2.bitwise_and(dst, v_dst)
これで,任意の色を抽出するextract_color関数が完成しました.

それでは,この関数を使って色抽出を行ってみます.
基本的なWebカメラの扱い使い方は,ここを見てください.
if __name__=="__main__":

    capture = cv2.VideoCapture(0)
    
    if capture.isOpened() is False:

        raise("IO Error")

    cv2.namedWindow("Capture", cv2.WINDOW_AUTOSIZE)
    cv2.namedWindow("Red",     cv2.WINDOW_AUTOSIZE)
    cv2.namedWindow("Yellow",  cv2.WINDOW_AUTOSIZE)

    while True:

        ret, image = capture.read()
        if ret == False:
            continue

        red_image    = extract_color(image, 170, 5,  190, 200)
        yellow_image = extract_color(image, 10,  25, 50,  50)

        cv2.imshow("Capture", image)
        cv2.imshow("Red",     red_image)
        cv2.imshow("Yellow",  yellow_image)
       
        if cv2.waitKey(33) >= 0:
            cv2.imwrite("image.png", image)
            cv2.imwrite("red_image.png", red_image)
            cv2.imwrite("yellow_image.png", yellow_image)
            break

    cv2.destroyAllWindows()
このスクリプトの実行結果は以下のようになります.
まずは,入力画像です.
image
次に,赤色の抽出結果です.
red_image
最後に,黄色の抽出結果です.
yellow_image

この例では,赤色と黄色をそれぞれ抽出して画像を表示しています.
各画像を見ると,それぞれの色で抽出出来ていることがわかります.
基本的な処理は既に説明していますが,OpenCVではHSVの各チャンネルは以下のような値域となっていることに注意してください.

H(色相):0~180
S(彩度):0~255
V(明度):0~255

以上でHSV色空間で色抽出をする方法の説明を終わります.  

Python版OpenCV関連の記事:
参考文献:
「Miscellaneous Image Transformations」『OpenCV 2.4.9.0 documentation』<http://docs.opencv.org/modules/imgproc/doc /miscellaneous_transformations.html> (2015/02/20アクセス)
「Basic Operations on Images」『OpenCV 3.0.0-dev documentation』<http://docs.opencv.org/trunk/doc/py_tutorials/py_core /py_basic_ops/py_basic_ops.html> (2015/02/20アクセス) 

Saturday, October 10, 2015

MOCO'Sキッチンのフォントに似せるβ版

お久しぶりです。
今回は昔に作ったまな板の話です。

2012年に料理好きの友人にMOCO'Sキッチン風のまな板を作成してプレゼントしました。
このときに使用したフォントはなんですか?という質問をもらいました。
検索してみると同じように悩んでいる人が多いようです。

結論からいうとキッチンは元ネタの画像を加工して使用していました。
英語のフォントは本当に適当です。

そのまま答えるのも申し訳ないので似たフォントを探してみました。
その結果が以下の画像です。


mocos

オリジナルの「キッチン」の部分はかなり特徴的なフォントです。
 ここには、以下のフォントを使用させていただきました。

マキナス: http://moji-waku.com/makinas/index.html

「MOCO S」の部分はSimplified Arabicで「'」はArial Unicodeを使用しました。

マジマジと見ると違う部分が目立ちます。

課題は、「チ」と「ン」のフラットさの再現でしょうか。

正直フォントで頑張って再現するより、自分でラインを引いたほうが早い気がしてきました。
今度はMOCO'Sキッチン風フォントの作成に挑戦しても面白いかもしれませんね。

それでは今回はここまでです。

Tuesday, March 10, 2015

RICHO THETAの簡単なAndroidアプリ

RICHO THETAを最初見たときは,あんまりピンと来なかったのですが使ってみると便利で面白いですね.
そんなTHETAをいじる機会があったので,簡単なAndroid用のプログラムを作ってみました.

SDKは下記のサイトからDLできます.

RICHO THETA Developpers
https://developers.theta360.com/ja/


SDKに付属しているサンプルプログラムは私にとって複雑で,何をしているのかわかりにくいものでした.
そこで,SDKのサンプルとリファレンスを頼りに,以下のようなシャッターを切るだけの簡単なサンプルを作りました.

SimpleTHETA_01

このサンプルはCONNECT Buttonを押してTHETAに接続し,SHOOT Buttonを押してシャッターを切るというプログラムです.
切断するときはDISCONNECT Buttonを押します.
THETAのステイタスからGUIを動かすサンプルとして,各操作の状況をTHETA statusというtextviewに描画しています.

それではこのプログラムの説明を行います.

まずは,以下のように,インターネットのpermissionを取ります.
この記述は,AndroidManifest.xmlに記述します.
<uses-permission android:name="android.permission.INTERNET"/>
つぎに,GUIのレイアウトを設定します.デフォルトではactivity_main.xmlに記述します.
    <LinearLayout
    android:id="@+id/linearLayout1"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_alignParentLeft="true"
    android:layout_alignParentTop="true"
    android:layout_marginLeft="28dp"
    android:layout_marginTop="64dp"
    android:orientation="vertical" >
        
        <LinearLayout
        android:id="@+id/linearLayout2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        
         <TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="THETA status:" />    
             
         <TextView
         android:id="@+id/status"
         android:layout_width="wrap_content"
         android:layout_height="match_parent"
         android:text="Status" />
        
        </LinearLayout>
        
        <LinearLayout
        android:id="@+id/linearLayout3"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:orientation="horizontal" >
        
         <Button
         android:id="@+id/connect"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="CONNECT" />
         
         <Button
         android:id="@+id/disconnect"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="DISCONNECT" />
         
         <Button
         android:id="@+id/shoot"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="SHOOT" />
        
        </LinearLayout>
 
    </LinearLayout>

これまでで,準備が整ったのでMainActivity.javaに処理を記述します.
この処理は基本的に各ボタンのListnerを設定して,そのボタンに合わせて処理を行います.
気を付ける点は以下の2つです.
  1. THETAとのやりとりはINTERNETを使うので,AsyncTaskなどを使って別スレッドで行う.
  2. GUIを別スレッドから操作するときは,Handlerを使う.
この点だけ気を付ければ特別難しい処理はないと思います.
各ボタンを押すとConnectTask,DisConnectTask,ShootTaskが実行されます.

ConnectTaskでは,指定したIPアドレスのTHETAに接続を行い,成功したらCONNECTED,失敗したらERRORと表示します.
DisConnectTaskでは,TETAとの接続を断ち,成功したらDISCONNECTED,失敗したらエラーログを表示します.
ShootTaskでは,シャッターを切り,成功したらSHOOT,失敗したら各エラーを表示します.

public class MainActivity extends Activity {
 
 PtpipInitiator camera;
 private static final String IP_ADDRESS = "192.168.1.1";
 private TextView txt_theta_status;
 private Button btn_theta_connect;
 private Button btn_theta_disconnect;
 private Button btn_theta_shoot;
 
 private Handler handler = null;

 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  
  txt_theta_status = (TextView) findViewById(R.id.status);
  btn_theta_connect = (Button) findViewById(R.id.connect);
  btn_theta_disconnect = (Button) findViewById(R.id.disconnect);
  btn_theta_shoot = (Button) findViewById(R.id.shoot);
        
  btn_theta_connect.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
        new ConnectTask().execute();
       }
  });
        
         btn_theta_disconnect.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
        new DisConnectTask().execute();
       }
         });
        
         btn_theta_shoot.setOnClickListener(new View.OnClickListener() {
       @Override
       public void onClick(View v) {
        new ShootTask().execute();
   }
         });
        
         handler = new Handler();
 }
 
 
 private class ConnectTask extends AsyncTask {
     @Override
     protected String doInBackground(String... params) {
         try {
             camera = new PtpipInitiator(IP_ADDRESS);
             
             handler.post(new Runnable() {
              @Override
              public void run() {
               txt_theta_status.setText("CONNECTED");
              }
             });
             
             
         } catch (Throwable e) {
             e.printStackTrace();
             
             handler.post(new Runnable() {
              @Override
              public void run() {
               txt_theta_status.setText("ERROR");
              }
             });
         }
         return null;
     }
 }
 
 private class DisConnectTask extends AsyncTask {
  @Override
  protected Boolean doInBackground(Void... params) {

   try {
    PtpipInitiator.close();
    
    handler.post(new Runnable() {
              @Override
              public void run() {
               txt_theta_status.setText("DISCONNECTED");
              }
             });
    
    return true;

   } catch (Throwable throwable) {
    final String errorLog = Log.getStackTraceString(throwable);
    handler.post(new Runnable() {
              @Override
              public void run() {
               txt_theta_status.setText(errorLog);
              }
             });

    
    return false;
   }
  }
 }

 private static enum ShootResult {
  SUCCESS, FAIL_CAMERA_DISCONNECTED, FAIL_STORE_FULL, FAIL_DEVICE_BUSY
 }
 
 private class ShootTask extends AsyncTask {

  @Override
  protected ShootResult doInBackground(Void... params) {
   try {
    PtpipInitiator camera = new PtpipInitiator(IP_ADDRESS);
    camera.initiateCapture();
    handler.post(new Runnable() {
              @Override
              public void run() {
               txt_theta_status.setText("SHOOT");
              }
             });
    return ShootResult.SUCCESS;

   } catch (IOException e) {
    handler.post(new Runnable() {
              @Override
              public void run() {
               txt_theta_status.setText("FAIL_CAMERA_DISCONNECTED");
              }
             });
    return ShootResult.FAIL_CAMERA_DISCONNECTED;
   } catch (ThetaException e) {
    if (Response.RESPONSE_CODE_STORE_FULL == e.getStatus()) {
     handler.post(new Runnable() {
               @Override
               public void run() {
                txt_theta_status.setText("FAIL_STORE_FULL");
               }
              });
     return ShootResult.FAIL_STORE_FULL;
    } else if (Response.RESPONSE_CODE_DEVICE_BUSY == e.getStatus()) {
     handler.post(new Runnable() {
               @Override
               public void run() {
                txt_theta_status.setText("FAIL_DEVICE_BUSY");
               }
              });
     return ShootResult.FAIL_DEVICE_BUSY;
    } else {
     handler.post(new Runnable() {
               @Override
               public void run() {
                txt_theta_status.setText("FAIL_CAMERA_DISCONNECTED");
               }
              });
     return ShootResult.FAIL_CAMERA_DISCONNECTED;
    }
   }
  }

 }

以上で,AndroidアプリからTHETAのシャッターを切る方法の説明を終わります.

Tuesday, March 3, 2015

Raspberry Pi 2のケースを自作する

Raspberry Pi 2を手に入れたのでケースを作ってみました.
市販のケースを買うのもいいですが,ケースを自作するのも楽しさの一つだと思います.

調べてみるとケースは3Dプリンタやレーザーカッターで作っるのがメジャーなようです.
3Dプリンタで作ると市販の射出成形のケースに見劣りしそうだったので,レーザーカッターをメインに使いました.
レーザーカッターは基本的に板を切るものなので,積層や箱組で立体的にケースを作るという手法が多く見られました.
今回,私は板を金属の支柱で支えることでケースを作ることにしました.
シンプルですが,各コネクタやIOへのアクセスを考えると悪くない選択だと思います.

Raspberry Pi2とケースの部品は以下のようになります.
各部品は,自作品や在庫品を使用しています.
ここを見てくれた方の参考になるよう,同等の市販品を参考に載せました.
もっと安いものもありますが,趣味に使うものなので良い物を選んでいます.

RaspberryPi2 Case veiw4

天板と下板のデータはここからDLできます.
DXF,ISG,STLで用意したので興味のある方は是非使ってみてください.
下板のパーツのRaspberry Pi2固定用の穴はM2.6のタップでメネジをたてて下さい.
それができない場合は,φ2.6の穴に拡張して,ナットで固定してください.
その場合は,ナットの厚み分,プラネジを長くしてください.
また,下板-天板接続用ネジの下面側のネジはナットよりも背の高いものを使用してください.

Drawing of Raspberry Pi2 Case


それでは,これらの部品を組み立てていきます.
まずは,下板にプラスティックの支柱とM2.6のプラネジを使って固定します.
さらに,アルミ製の支柱を下板に止めます.

RaspberryPi2 Case veiw2

最後に,天板を固定すれば完成です.
天板には,お決まりのロゴを彫刻しました.
すりガラス風のアクリルを使っているので,ロゴがはっきりと見えます.
すりガラス風のロゴを彫刻する場合は, すりガラスのような加工が入った面に彫刻するほうがきれいにロゴが見えると思います.

RaspberryPi2 Case veiw1

横から見ると以下のようになります.
各コネクタやIOは隠していないのでちゃんとアクセスできます.

RaspberryPi2 Case veiw3

簡単な作りですが,それなりに見栄えのするものになったと思います.

Thursday, February 19, 2015

Python版OpenCVで色抽出を行う

今回は,Python版のOpenCVを使ってHSV色空間で色抽出をする方法をメモしておきます.

環境は以下のとおりです.
Python 2.6
OpenCV 2.4.6

更新履歴
  • 2015/03/12
    しきい値処理におけるTHRESH_BINARYとTHRESH_BINARY_INVが逆になっているのを修正しました.

それでは早速スクリプトを見ていきます.

import cv2

def extract_color( src, h_th_low, h_th_up, s_th, v_th ):

    hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
    h, s, v = cv2.split(hsv)

    if h_th_low > h_th_up:
        ret, h_dst_1 = cv2.threshold(h, h_th_low, 255, cv2.THRESH_BINARY) 
        ret, h_dst_2 = cv2.threshold(h, h_th_up,  255, cv2.THRESH_BINARY_INV)
        
        dst = cv2.bitwise_or(h_dst_1, h_dst_2)

    else:
        ret, dst = cv2.threshold(h,   h_th_low, 255, cv2.THRESH_TOZERO) 
        ret, dst = cv2.threshold(dst, h_th_up,  255, cv2.THRESH_TOZERO_INV)

        ret, dst = cv2.threshold(dst, 0, 255, cv2.THRESH_BINARY)
        
    ret, s_dst = cv2.threshold(s, s_th, 255, cv2.THRESH_BINARY)
    ret, v_dst = cv2.threshold(v, v_th, 255, cv2.THRESH_BINARY)

    dst = cv2.bitwise_and(dst, s_dst)
    dst = cv2.bitwise_and(dst, v_dst)

    return dst

色抽出する処理を関数にまとめてみました.
この関数の戻り値と引数は以下のようになります.黒を除外して色相で抜きたい色を指定するイメージで処理を行います.
出力画像 extract_color( 入力画像, 色相のしきい値(下), 色相のしきい値(上), 彩度のしきい値, 明度のしきい値 )
関数の内部の処理を説明します.
入力画像はRGBカラーモデルであることを想定し,HSV色空間に変換します.
その後,HSVの各チャンネルを別々の画像にわけます.
hsv = cv2.cvtColor(src, cv2.COLOR_BGR2HSV)
h, s, v = cv2.split(hsv)
赤色を抽出する場合は,色相が360°をまたぐ可能性があるため,ここで処理を分けます.
色相のしきい値(下)がしきい値(上)より大きいというのは,赤のように350°から10°までというような場合を想定しています.
360°を境界として2回しきい値処理を行った後に各画像を合わせることで抽出します.
まず,以下のようなcv2.threshold関数を使って,しきい値処理を行います.
cv2.threshold(画像, しきい値, 最大値, しきい値処理のタイプ)
しきい値処理のタイプは以下のものを使用します.

cv2.THRESH_BINARY: しきい値より大きい値は最大値,それ以外は0
cv2.THRESH_BINARY_INV: しきい値より大きい値は0,それ以外は最大値

その後,各画像のORを取ることで,2枚の画像を合わせます.
    if h_th_low > h_th_up:
        ret, h_dst_1 = cv2.threshold(h, h_th_low, 255, cv2.THRESH_BINARY) 
        ret, h_dst_2 = cv2.threshold(h, h_th_up,  255, cv2.THRESH_BINARY_INV)
        
        dst = cv2.bitwise_or(h_dst_1, h_dst_2)
次に,360°をまたがない場合を考えます.
ここでは,しきい値処理に以下のものを使用します.

cv2.THRESH_TOZERO: しきい値よりより大きい値はそのまま,それ以外は0
cv2.THRESH_TOZERO_INV: しきい値よりより大きい値は0,それ以外はそのまま

まず,しきい値(下)の値を0にし,その後,しきい値(上)より大きい値を0にすることで特定の範囲の値を抽出できます.
    else:
        ret, dst = cv2.threshold(h,   h_th_low, 255, cv2.THRESH_TOZERO) 
        ret, dst = cv2.threshold(dst, h_th_up,  255, cv2.THRESH_TOZERO_INV)

        ret, dst = cv2.threshold(dst, 0, 255, cv2.THRESH_BINARY)
色相については,ここまでで処理できたので,残りの明度と彩度についても処理を行います.
ここでは,黒に近い色を排除するだけでよいので,下限値のみのしきい値処理を行います.
    ret, s_dst = cv2.threshold(s, s_th, 255, cv2.THRESH_BINARY)
    ret, v_dst = cv2.threshold(v, v_th, 255, cv2.THRESH_BINARY)
そして最後に,各画像のANDをとり,各しきい値を満たす画素を抽出します.
    dst = cv2.bitwise_and(dst, s_dst)
    dst = cv2.bitwise_and(dst, v_dst)
これで,任意の色を抽出するextract_color関数が完成しました.

それでは,この関数を使って色抽出を行ってみます.
基本的なWebカメラの扱い使い方は,ここを見てください.
if __name__=="__main__":

    capture = cv2.VideoCapture(0)
    
    if capture.isOpened() is False:

        raise("IO Error")

    cv2.namedWindow("Capture", cv2.WINDOW_AUTOSIZE)
    cv2.namedWindow("Red",     cv2.WINDOW_AUTOSIZE)
    cv2.namedWindow("Yellow",  cv2.WINDOW_AUTOSIZE)

    while True:

        ret, image = capture.read()
        if ret == False:
            continue

        red_image    = extract_color(image, 170, 5,  190, 200)
        yellow_image = extract_color(image, 10,  25, 50,  50)

        cv2.imshow("Capture", image)
        cv2.imshow("Red",     red_image)
        cv2.imshow("Yellow",  yellow_image)
       
        if cv2.waitKey(33) >= 0:
            cv2.imwrite("image.png", image)
            cv2.imwrite("red_image.png", red_image)
            cv2.imwrite("yellow_image.png", yellow_image)
            break

    cv2.destroyAllWindows()
このスクリプトの実行結果は以下のようになります.
まずは,入力画像です.
image
次に,赤色の抽出結果です.
red_image
最後に,黄色の抽出結果です.
yellow_image

この例では,赤色と黄色をそれぞれ抽出して画像を表示しています.
各画像を見ると,それぞれの色で抽出出来ていることがわかります.
基本的な処理は既に説明していますが,OpenCVではHSVの各チャンネルは以下のような値域となっていることに注意してください.

H(色相):0~180
S(彩度):0~255
V(明度):0~255

以上でHSV色空間で色抽出をする方法の説明を終わります.  

Python版OpenCV関連の記事:
Python版OpenCVでWebカメラの画像を取得する
Python版OpenCVで色抽出を行う

参考文献:
「Miscellaneous Image Transformations」『OpenCV 2.4.9.0 documentation』<http://docs.opencv.org/modules/imgproc/doc/miscellaneous_transformations.html> (2015/02/20アクセス)
「Basic Operations on Images」『OpenCV 3.0.0-dev documentation』<http://docs.opencv.org/trunk/doc/py_tutorials/py_core/py_basic_ops/py_basic_ops.html> (2015/02/20アクセス) 

Friday, February 13, 2015

Python版OpenCVでWebカメラの画像を取得する

OpenCVを使うときは主にC++を使っているのですが,Pythonでも使えると何かと便利なので使い方を調べてみました.

今回は,Webカメラから画像を取得する方法をメモしておきます.
ちなみにPython 2.6を使っている関係で,OpenCV 2.4.6を使っています.

それでは早速スクリプトを見ていきます.
import cv2

if __name__=="__main__":

    capture = cv2.VideoCapture(0)
    
    if capture.isOpened() is False:
        raise("IO Error")

    cv2.namedWindow("Capture", cv2.WINDOW_AUTOSIZE)

    while True:

        ret, image = capture.read()

        if ret == False:
            continue

        cv2.imshow("Capture", image)
       
        if cv2.waitKey(33) >= 0:
            cv2.imwrite("image.png", image)
            break

    cv2.destroyAllWindows()

まずは,0番のカメラを引数にしてVideoCaptureクラスをインスタンス化します.
capture = cv2.VideoCapture(0)
次に,isOpenedメソッドを使ってWebカメラに接続できているか確認します.
もしできていなければIOエラーをだします.
if capture.isOpened() is False:
        raise("IO Error")
Webカメラで取得した画像を表示するCaptureという名前のウィンドウを設定します.
cv2.WINDOW_AUTOSIZEを指定すると,表示する画像に応じてウィンドウの大きさが自動的に決まります.
cv2.namedWindow("Capture", cv2.WINDOW_AUTOSIZE)
この先の処理は後ほど説明するキー入力があるまで,処理をwhileループで繰り返します.
まずは,画像をWebカメラから取得します.
取得できればimageにnumpyのndarray形式で画像が,retにbool形式でTrueが格納されます.
ret, image = capture.read()
もし,retにFalseが入っていればこの後の処理を飛ばします.
if ret == False:
            continue
取得した画像を先ほど生成したウィンドウCaptureに表示します.
cv2.imshow("Capture", image)
最後に,cv2.waitKeyを呼び出します.引数はウェイト時間 msです.0を指定すると無限にキー入力を待ちます.
ちなみにcv2.watiKeyはイベント処理を扱うので定期的に呼び出す必要があることに注意してください.
これを書かないとキー入力を受け取れないだけでなく,画像もウィンドウに表示されません.
ここでは,何かキー入力があった場合,image.pngというPNGファイルに画像を保存します.
if cv2.waitKey(33) >= 0:
            cv2.imwrite("image.png", image)
            break
キーが押された場合に,全てのウィンドウを破壊して終わります.
cv2.destroyAllWindows()
以上でWebカメラの画像取得方法の説明を終わります.  

Python版OpenCV関連の記事:
Python版OpenCVでWebカメラの画像を取得する
Python版OpenCVで色抽出を行う

参考文献:
「User Interface」『OpenCV 2.4.9.0 documentation』<http://docs.opencv.org/modules/highgui/doc/user_interface.html> (2015/02/14アクセス)
「Reading and Writing Images and Video」『OpenCV 2.4.9.0 documentation』<http://docs.opencv.org/modules/highgui/doc/reading_and_writing_images_and_video.html> (2015/02/14アクセス)