FC2ブログ

カラーボールのトラッキング


カラーボールのトラッキング。大昔からある技術だし、ライブラリが揃っている今であればやればいつでもできるだろう…
と思いつつもなかなか手がまわらないまま平成が終了してしまいました。
ので、理解のためにまずは原始的なところからはじめます。
シンプルなものであればラズパイでも十分なスピードが確保でき、すぐ遊べそうです。



プログラムの方針は、

1)カメラで画像を捉える。
2)その画像から赤色の濃い部分だけ抽出する。(つまりマスク画像を作る。)
3)いくつかの赤色のカタマリに分ける。
4)中でも、一番面積の大きいカタマリを選ぶ。
5)その中心座標を求める。

とします。



いろいろ調べつつなるべくコンパクトにpythonを書いてみました。
便利なライブラリを製作してくださった方々や記事を残してくださった皆様には頭が上がりません!
(参考記事:
 https://algorithm.joho.info/programming/python/opencv-color-tracking-py/
 http://opencv.jp/opencv-2svn/py/core_operations_on_arrays.html 等)

import numpy as np
import cv2

cap = cv2.VideoCapture(0) #カメラを定義

def red_range(img): #赤色の領域をマスクする関数
    hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) #BGRをHSV色空間に変換

#赤色の領域を閾値でフィルタリング
#OpenCVのHSV色空間では赤は0~30付近と150~180付近に分かれるが、
#181~255は0からの循環なので180を中心に範囲を取れば赤の閾値を一回で指定できる。たぶん。
    hsv_min = np.array([170,170,60]) #色相(Hue)、彩度(Saturation)、明度(Value)
    hsv_max = np.array([190,255,255])
    mask = cv2.inRange(hsv, hsv_min, hsv_max) #hsvの各ドットについてhsv_minからhsv_maxの範囲内ならtrue

    return mask

while( cap.isOpened() ): #カメラが使える限りループ

    ret, frame = cap.read() #カメラの情報を取得。frameに640x480x3の配列データが入る。
    frame_np = red_range(np.array(frame)) #frameデータをnp配列に変換。

#領域のカタマリである「ブロブ」を識別し、データを格納する。すごくありがたい機能。
    nLabels, labelimages, data, center = cv2.connectedComponentsWithStats(frame_np)

    blob_count = nLabels - 1 #ブロブの数。画面領域全体を1つのブロブとしてカウントするので、-1する。

    if blob_count >= 1: #ブロブが1つ以上存在すれば、画面全体を示すブロブデータを削除。
        data = np.delete(data, 0, 0)
        center = np.delete(center, 0, 0)

#認識したブロブの中で最大領域を持つもののインデックスを取得
    tbi = np.argmax(data[:, 4]) #target_blob_index

#最大ブロブの中心に青の円マークを直径10pix、太さ3pixで描く
    cv2.circle(frame,(int(center[tbi][0]),int(center[tbi][1])),10,(255,0,0),3)

#画像を表示する
    cv2.imshow('RaspiCam_Live',frame)

#キーが押されたら終了する
    if cv2.waitKey(1) != -1:
        break

#終了処理。カメラを解放し、表示ウィンドウを破棄。
cap.release()
cv2.destroyAllWindows()






動作しているところの様子。ポンコツデジカメなので画面が暗いですが、画面の中で一番大きい赤色の塊の中心を捉え緑の円でマーキングしているのが確認できるかと思います。


▼ cv2.connectedComponentsWithStats() について

nLabels, labelImages, data, center = cv2.connectedComponentsWithStats(image_src)
[0] nLabelsは、ブロブのラベル数(ただし、背景のラベル番号0も含むためオブジェクトの数は nLabels - 1 となる)
[1] labelImagesは、ラベル番号が入った配列データ(座標 (x, y) のラベル番号は、labelImages[x, y] で参照できる)
[2] dataは、オブジェク詳細の配列データ(x, y, w, h, size = data[ラベル番号] で参照できる)
※x, y, w, h は、オブジェクトの外接矩形の左上のx座標、y座標、高さ、幅 ※size は、面積(pixcel)
[3] centerは、オブジェクの中心点(オブジェクトの重心座標 (x, y) を浮動小数点数で出力。描画に使うときはintにする)
※ image_srcは、二値画像の入力配列


▼おまけ
もしこの記事を読んでいてロボットは作れるが画像処理はさっぱりわからない!という人がいたら、
「画像は配列として扱える」
という概念だけ合点がいけばあとはなんでもできるようになると思います。

たとえば640x480の画像は、それぞれのドットについて赤緑青の3原色の強さのデータになりますが、
それは「横ドット数640]x[縦ドット数480]x[赤0〜255、緑0〜255、青0〜255]といった感じで表されます。
画像としてではなく、そのデータを配列として扱うことで、いろんな画像操作ができるようになるのです。

赤のデータが閾値以上のところで切れば赤っぽい画素をマスクできます。
上下左右に隣接するドット同士の関係性を重視するアルゴリズムを用いてフィルタリングすることで画像の特徴としてデータ化することもできます。
難しいアルゴリズムについてもすでにかなりの量のライブラリが準備されていますので、
基本原理さえわかれば細かい計算式まで書かずともどんどん実装が可能になっています。
スポンサーサイト



テーマ : 自然科学
ジャンル : 学問・文化・芸術

分類器② YOLOv3


分類器としてYOLOv3も試してみました。環境はgoogle colab(GPUあり)です。
(Twitterでとり天さんとにゅくすさんにアドバイスいただきました。ありがとうございました!)



↓のQiitaを参考にコンパクトに。ただそのままだとGPUを使用しない状態なので少しだけアレンジ必要。
https://eng-memo.info/blog/deep_learning_yolo_detect/


from IPython.display import Image,display_jpeg
!git clone https://github.com/pjreddie/darknet

#ここで一旦Darknetの中のMakefileを開き、1行目のGPU=0をGPU=1に書き換える。
#自分はcat Makefileで内容表示させてからローカルのエディタ上で書き換えてcolabにアップロードしました。
#linuxコマンドでファイル内容を書き換えるのに慣れてないので・・・

%%bash #中身を書き換えたMakefileでコンパイルする。
cd darknet
make
wget https://pjreddie.com/media/files/yolov3.weights

%%bash #画像の読み込み
cd ./darknet/
./darknet detect cfg/yolov3.cfg yolov3.weights data/horses.jpg

#画像の表示
display_jpeg(Image('darknet/predictions.jpg'))



%%bashで複数行のシェルをかけるんですね。
google colabでは!cdで行ける範囲が限られているので、cdを伴うシェル作業の時に%%bashがすごく助かります。

colab環境での推論時間はGPUなしだと22秒ぐらい、GPUありだと0.7秒ぐらいでした。ものによっては0.2秒とかのスピードも。
SS 111



SS 113
ずうとるびは4名と認識。

SS 112
ぱくたそのガールもしっかり認識。スマホも認識。

th_yorov3.jpg

YOLOv3 からはロボットは何者にも分類されない。おもちゃとしても認識されない。テディとお人形タイプはOK。

テーマ : 自然科学
ジャンル : 学問・文化・芸術

分類器①


量的になるべくコンパクトなプログラムで書くことで理解を深めるシリーズ。
OpenCVによる画像分類器のおさらい。
ラズパイの前にまずはgoogle Colaboratory上で動くやつを検証。



(※ファイルのアップロードかファイルの入ったgoogleドライブのマウントは必要だが割愛。)

from matplotlib import pyplot as plt
import numpy as np
import cv2

img = cv2.imread('/content/drive/My Drive/<ディレクトリ名>/<画像ファイル名>.jpg') #画像の読み込み
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) #RGBの順番を変換

cascade = cv2.CascadeClassifier('/content/drive/My Drive/<ディレクトリ名>/haarcascade_frontalface_alt2.xml')
#↑公式サイトからのDLしたのじゃないとダメ。GITだとだめ。
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) #グレーに変換

face = cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=2, minSize=(30, 30))

for (x, y, w, h) in face:
cv2.rectangle(img, (x, y), (x + w, y + h), (200,0,0), 3) #顔に矩形を描写

plt.imshow(img) #画像の描写

よし、まあまあ短い。



結果。


SS 101
ビートルズはOK!

SS 102
ずうとるびもOK!

SS 100
SS 103
SS 105
SS 106
ぱくたそのガールはほぼ全滅。眉毛がかくれたり角度が斜めだとだめなのかな??

SS 107
おなじぱくたそでもこの人は大丈夫。



cascadeをいろいろ試したり性能のよいcascadeをどこかから借りてきたりはしてみたい。

テーマ : 自然科学
ジャンル : 学問・文化・芸術

ラズパイにキーボードから日本語をしゃべらせる


デモ用として、日本語テキストを入力してエンターを押すたびに発話するシェルスクリプト を作ってみました。
OpenJtalkの導入が済んでいることが前提。
ssh から使えるので、少し離れたところに置いたラズパイから発話させることができます。
また、アドホック接続にすれば、インターネットに繋がっていない環境でも、遠隔発話させることができます。
つまりこれをラズパイゼロなどに仕込んでロボッツに搭載してスピーカーをつければ、ロボッツから簡易的に遠隔発話できるやったね。



ファイル名 inlinetalk.sh

以下シェルスクリプト
#!/usr/bin/bash
while :
do
echo -n 発話テキスト: && read talk && talk_temp=$(mktemp talk_tempXXX ) && chmod 666 $talk_temp && echo $talk | open_jtalk -m /usr/share/hts-voice/mei/mei_happy.htsvoice -x /var/lib/mecab/dic/open-jtalk/naist-jdic -ow $talk_temp && aplay -q $talk_temp && rm $talk_temp
done

実行コマンド: sh inlinetalk.sh (inlinetalk.sh を置いたディレクトリで実行する。)



OpenJtalkのパラメータ調整は下記がすごく参考になりました。
http://moblog.absgexp.net/openjtalk/

-a 0.4 とかするとゼムネスのボイスイメージにけっこう近くなる。



SS 98

テーマ : 自然科学
ジャンル : 学問・文化・芸術

ラズパイに日本語をしゃべらせる


今回もほぼコピペ。
http://jellyware.jp/kurage/raspi/raspi_speech_synthesis.html
他のサイトもちょっと参考にしたかも。

Open JTalkと音声フォントはmeiを利用。
なかなかロボットらしくよい感じにしゃべってくれます。



うごいてるところ。
https://twitter.com/Ninagawa123/status/1194999936095375361



ゼムネスにベラベラしゃべらせたいのですが、音声フォントの自作も暇ができたらやってみたいです。
あと、ROSに飛んで来たメッセージを発話させるプログラムもたぶん簡単にかけるので暇ができたらやってみます。

テーマ : 自然科学
ジャンル : 学問・文化・芸術

ラズパイへのSSHログイン方法とアドホック接続


簡単なのに時間が経ったらすぐ忘れるマン。
ラズパイへのSSHログイン方法。

有線LANを繋いだら、
MACのターミナルから
ssh pi@raspberrypi.local
で入れます。



Macとラズパイをアドホック接続する方法。
これができるとインターネットのアクセスポイントがない環境でも、Macとラズパイをwifi通信できる。

https://research.itplants.com/?p=2113
を試す。書いてある通り、ラズパイ側でad-hocネットというものを立ち上げて、そこにMacからアクセスする手法。おもしろい^^

auto lo
iface lo inet loopback
iface etc0 inet manual
allow-hotplug wlan1
iface wlan1 inet manual
wpa-conf /etc/wpa_supplicant/wpa_supplicant.conf
iface wlan0 inet manual
auto wlan0
iface wlan0 inet static
address 192.168.X.XX
netmask 255.255.255.0
wireless-channel 1
wireless-mode ad-hoc
wireless-essid ほにゃらら-AP
wireless-key はぱらきぴりあらら

この後で、カメラに繋いでMac側で表示させたりとか、ubuntu側のROSと連携できるかとかいろいろ挑戦予定。



あと、競合するIPアドレスが周囲にあるかどうか調べる方法。
$arp -a

https://milestone-of-se.nesuke.com/knowhow/verify-deny-ping-host/

テーマ : 自然科学
ジャンル : 学問・文化・芸術

ROSの自動起動化

pytank.jpg



ROSでいろいろ試せるのはいいですが、ラズパイで作っている場合はラズパイの電源を入れたらPCに接続することなく動き出して欲しいものです。
けど開発やメンテの時にはちゃんとPCにも繋げたいし、なんならGUIも出して欲しいのです。

ということで、デスクトップにつながるROSラズパイだけど、電源を入れたらROSが自動起動するようにも仕込みます。
その自分向けメモです。




https://qiita.com/strv/items/535a370842ae60af9b64
を参考にしました。

自分的なコケポイントは.bash_profileの扱い。
あらかじめ.bash_profileを開いたら.bashを読み込むように設定はしていましたが、それではなぜかうまくいかず、
結局、.bash_profileに
source &HOME/catkin_ws/devel/setup.bash
を追記しました。
(上記記事の中では-ADmというコマンドが意味わからずでした。)




systemdの書き方については以下が大変参照になりました。
https://tex2e.github.io/blog/linux/create-my-systemd-service




また、その他の自動起動についてもいろいろ試しました。下記が参考になりました。
https://qiita.com/karaage0703/items/ed18f318a1775b28eab4
ここに載っていないやり方としては、
デスクトップ環境の立ち上がり時のautostartに実行ファイルを入れるというやり方も試しました。(pythonは実行できました。)




自分向けファイル構造です。ここだけはメモしておかないと絶対わすれます。
ディレクトリの移動が面倒なので、なるべくワークスペース内のファイルを書き換えるだけで済むように設定。
とりあえずwiimoteとpythonだけは動く状態になっています。

・メインとなる自動起動用ファイルの置き場所
/etc/systemd/system
├rescore.service #roscore を立ち上げるだけ。
├ralf_ros_launcher.service #次にralf_rosstart.launch (ほにゃ/ros_start(ROSパッケ名)/launch/の中)を立ち上げる。
└ralf_sh_laucher.service #次にralf_autostart.sh (ほにゃ/ros_start(ROSパッケ名)/scripts/の中)を立ち上げる。

・サブとなる実行内容書き換え用のファイルの置き場所
/ros_start(ROSパッケ名)/launch/ralf_rosstart.launch
→自動起動したいnodeはこのlaunchファイルに書く。

/ros_start(ROSパッケ名)/scripts/ralf_autostart.sh
→ROSと併用するpython ファイルなどの実行はここに書く。



wiimoteによるwiiリモコンの再ペアリングについて

wiimoteはnodeの起動時のみにペアリングを行ってくれるので、
任意のタイミングでリペアリングしたい時には、node自体を再起動させる必要がありそうです。
試したところ、wiimoteはnodeをkillしなくても、wiimoteを再起動することでリペリングが可能のようでした。
GPIOのハードウェアボタンの入力を待ちつつ、入力があったら
subprocess.Popen(["rosrun","wiimote","wiimote_node"])
を実行するようにすれば、再ペアリングがはじまります。

>自分へ
とりあえず今日動いているファイルを.zipにして自分宛に送っておきました。

テーマ : 自然科学
ジャンル : 学問・文化・芸術

プロフィール

二名川(ニナガワ)

Author:二名川(ニナガワ)
ホビーロボットをレトロゲームが発展したものと捉えて楽しく遊び倒します。
子供が夢を見ている時間帯に稼働します。

電子出版
「コンソロイド ガイドブック」
46107_CONSOLOID_GUID_FACE_200.jpg





■作成中の機体
汎用ヒト型決戦遊具 ~RX計画~
RX-7.5 ゼロタンク
RRf-0.6 ゼニィ
RXM-7.9 ゼムネス
RX-7.5R 量産型ゼロタンク
RX-7.5Fp ファミタンク仮設1号
RX-7.7 ゼロキャノン
RX-7.8 ゼログレイ
SMS-0.1 ゼロライナー
以下続く

RX整備計画(仮)

ブログ内検索
最近の記事
最近のコメント
カテゴリ
月別アーカイブ
リンク