M5Stick-CからMacにBluetoothで文字列を送信する
M5Stick-Cについて、一年ほど前に以下の記事を書いていました。
plant-raspberrypi3.hatenablog.com
今回、M5Stick-CのデータをMacに送信し、Macで(Pythonコードで)受け取る方法がわかったので、備忘録です。
Bluetooth経由でシリアル通信で送ります。
参考にさせていただいたのは以下のページです。
1. M5Stick-CからBluetoothで文字列を送信
Arduino IDEでM5Stick-Cに以下のコードを書き込みます。
#include "BluetoothSerial.h" #include <M5StickC.h> BluetoothSerial bts; void setup() { M5.begin(); M5.Lcd.setRotation(3); M5.Lcd.fillScreen(BLACK); M5.Lcd.setCursor(0, 0, 2); M5.Lcd.println("Bluetooth TEST"); bts.begin("M5StickC"); } void loop() { M5.Lcd.setCursor(0, 20, 2); M5.Lcd.printf("Start Bluetooth"); bts.println("Success"); delay(1000); }
bts.beginの引数はMac側でBluetoothを認識するときに表示される名前です。
bts.printlnの引数がMacに送信したい文字列です。
2. Mac側で文字列を受信(PythonのPySerial使用)
pyserialをインストール。
$ pip install pyserial
Macのシステム環境設定のBluetoothで「M5StickC」に接続する。
ターミナルで下記のコードを打ち込み、該当のBluetooth接続デバイスのパスを取得。
ls -l /dev/tty.*
今回は/dev/tty.M5StickC-ESP32SPP
でした。
Pythonで以下のコードを動かすと、文字列が取得できるはずです。
import serial m5data = serial.Serial('/dev/tty.M5StickC-ESP32SPP',9600, timeout=3) line = m5data.readline() print(line) m5data.close()
b'Success\r\n'
これと同じ手順で、M5Stick-Cでセンサから取得した数値データなども送信できるはずです。
M5StickC ENV HatをArduino IDEで使ってみた
Raspberry Piと連携させて使いたい!ということでM5StickCを買ってみました。
温度と湿度をモニターしてみよう、ということで、ENV Hatも。
主に、Macでの設定方法の備忘録です。
この辺りを参考に。
1. Arduino IDEのダウンロード
こちらのページから。
Macの場合は、ダウンロード・解凍したら、Aruduino appをアプリケーションフォルダに移して完了。
2. ドライバのインストール
こちらのページから
CP210X Driverをダウンロードし、インストールします。
3. ボードマネージャとライブラリの追加
「Arduino」->「Preferences」で環境設定画面を開く。
「追加のボードマネージャのURL」の右側のボタンをクリック。
https://dl.espressif.com/dl/package_esp32_index.json
を追加してOK。
「ツール」->「ボード」->「ボードマネージャ」
「esp32」で検索し、ESP32用のライブラリをインストール。
「ツール」 -> 「ボード」 -> 「M5Stick-C」
でM5Stick-Cを選択。
次に、
「スケッチ」->「ライブラリをインクルード」->「ライブラリを管理」
「M5StickC」で検索し、M5StickCのライブラリをインストール。
試しにHello Worldをしてみます。
M5Stick-CをUSBでMacに繋いで、
「ツール」 -> 「シリアルポート」
で新たに現れたシリアルポートを選択。
「ファイル」->「スケッチ例」->「M5StickC」 ->「Basics」 ->「HelloWorld」
でHelloWorldのコードを開き、「検証」と「マイコンボードに書き込む」を実行。
4. ENV Hatを動かしてみる
「ファイル」->「スケッチ例」->「M5StickC」->「Hat」->「ENV」
コードに
/* note: need add library Adafruit_BMP280 from library manage Github: https://github.com/adafruit/Adafruit_BMP280_Library */
とあるので、
「スケッチ」->「ライブラリをインクルード」->「ライブラリを管理」
BMP280で検索して、by Adafruitのライブラリをインストール。
「検証」と「マイコンボードに書き込む」を実行。
これで実行されるはずです。
Raspberry Pi 4&Raspbian BusterでOpenCV-Pythonをインストール
Raspberry Pi 4の4GBモデルがとうとう日本でも発売開始になりましたね!
これまでのモデルとの違いなどはいろんなところで比較されているので、割愛します。
Macを使ったOSのインストール方法は以下のページを参考にしました。
このブログでは、Raspbian Busterを入れたRaspberry Pi 4でのOpenCV-Pythonのインストール方法をメモしていきます。
参考にしたのは以下のページ。
こちらのページではOpenCV-Python 4のインストール方法が紹介されていますが、今回は諸事情でversion 3をインストールしました。手順は全く同じです。
1. 依存パッケージのインストール
LXTerminalで下記のコードを順次実行。
sudo apt-get update && sudo apt-get upgrade sudo apt-get install build-essential cmake pkg-config sudo apt-get install libjpeg-dev libtiff5-dev libjasper-dev libpng-dev sudo apt-get install libavcodec-dev libvformat-dev libswscale-dev libv4l-dev sudo apt-get install libxvidcore-dev libx264-dev sudo apt-get install libfontconfig1-dev libcairo2-dev sudo apt-get install libgdk-pixbuf2.0-dev libpango1.0-dev sudo apt-get install libgtk2.0-dev libgtk-3-dev sudo apt-get install libatlas-base-dev gfortran sudo apt-get install libhdf5-dev libhdf5-serial-dev libhdf5-103 sudo apt-get install libqtgui4 libqtwebkit4 libqt4-test python3-pyqt5 sudo apt-get install python3-dev
実際の作業では、上記を1つの.shファイルにして一括で実行しました。時々yes/no?と聞かれるので、その都度、yを押します。
2. pipとvirtualenvのインストール
次に、pipとvirtualenvをインストールしていきますが、楽するために、作業に入る前にパスの設定ファイルを準備しておきました。
nano opencvInstallpath
export WORKON_HOME=$HOME/.virtualenvs export VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3 source /usr/local/bin/virtualenvwrapper.sh
次に、以下を順次実行。
wget https://bootstrap.pypa.io/get-pip.py sudo python get-pip.py sudo python3 get-pip.py sudo rm -rf ~/.cache/pip sudo pip install virtualenv virtualenvwrapper cat opencvInstallpath >> ~/.bashrc pip install "picamera[array]" #if you use picamera
最後の行は、picameraを使うときだけ必要。
終わったら、
source ~/.bashrc
でパスを有効にします。
次に、virtualenv環境を構築します。
mkvirtualenv cv -p python3
cv環境が作られ、その中に移動します。
(cv) pi@raspberrypi:~ $
cv環境から抜けるには
deactivate
を実行します。
3. OpenCV-Pythonのインストール
cv環境中で、opencv-pythonをインストールします。
pip install opencv-contrib-python==3.4.4.19
これでうまくいきました。
ちなみに、いくつかのバージョンはエラーが出てしまってインストールできませんでした。(今回は試したのは3.3.0.10と3.4.7.28)
$ pip install opencv-contrib-python==3.4.7.28 Looking in indexes: https://pypi.org/simple, https://www.piwheels.org/simple Collecting opencv-contrib-python==3.4.7.28 Downloading https://www.piwheels.org/simple/opencv-contrib-python/opencv_contrib_python-3.4.7.28-cp37-cp37m-linux_armv7l.whl (15.3MB) |████████████▎ | 5.9MB 42kB/s eta 0:03:43 ERROR: THESE PACKAGES DO NOT MATCH THE HASHES FROM THE REQUIREMENTS FILE. If you have updated the package versions, please update the hashes. Otherwise, examine the package contents carefully; someone may have tampered with them.
このあたり、詳しい情報をお持ちの方がいらっしゃいましたら、コメントいただけると嬉しいです。
最後に、opencv-pythonが正しくインストールされたか確認します。
$ python3 Python 3.7.3 (default, Apr 3 2019, 05:39:12) [GCC 8.2.0] on linux Type "help", "copyright", "credits" or "license" for more information. >>> import cv2 >>>
エラーが出なければOKです。
以上です。
[2021/4/15追記] virtualenv環境をアクチベートするには
$ workon [env-name]
とします。
Raspberry Pi 7インチ タッチ・スクリーン ディスプレイ 設定覚書
公式のタッチ・スクリーン ディスプレイを購入したのですが、設定に苦戦したので覚書として。
Rasbianのバージョン:Buster
主な問題点は、フレームを装着した状態だと画面が逆向きになる、太い黒縁が出てタッチした場所とカーソルが反応する場所が違う、など。
LXTerminalでconfig.txtの設定を変更します。
sudo nano /boot/config.txt
色々なページを参考に変えていって、最終的にうまくいったバージョンのメモ。
#hdmi_safe=1 disable_overscan=1 #次は全てコメントアウト #overscan_left=32 #overscan_right=32 #overscan_top=16 #overscan_bottom=16 #framebuffer_width=1280 #framebuffer_height=960 #hdmi_force_hotplug=1 hdmi_group=2 hdmi_mode=87 hdmi_cvt=480 320 60 1 0 0 0 hdmi_drive=2 config_hdmi_boost=4 #中略 #最後に追記 lcd_rotate=2
以上です。
Raspberry Piストアができたらしい
TechCrunchの記事より。
店構えがステキ。
記事によるとイギリスのケンブリッジだけらしいですが、日本にもできるといいなあ、、、
The Raspberry Pi Store is a place where you can experience and buy Raspberry Pi products. Explore some of the things you can do with a Pi, discover our accessories and books, and get your hands on store-only exclusives.
Raspberry Pi Storeは、Raspberry Pi製品を体験して購入できる場所です。 Piを使ってできることのいくつかを探り、私たちのアクセサリーや本を見つけて、店内の限定品を手に入れよう。
Pythonで任意の四角形を正方形に変換する方法(射影変換・ホモグラフィ)
以前、台形補正についての記事を書きました。
plant-raspberrypi3.hatenablog.com
今回は、台形だけではなく、任意の四角形を正方形(または別の任意の四角形)に変換しよう、という試み。
射影変換の原理を勉強して、Pythonで実装してみました。
PythonライブラリのSympyとScikit-imageを使う方法です。
1. 射影変換の原理
射影変換(ホモグラフィ)の原理にはついて、たくさんの解説記事が見つかります。 この記事では、私が自分なりに理解したことについて書いていきます。 ベクトルをベクトルにうつす線形写像を求める問題を考えます。
これは とおくと が成り立つを求める問題と置き換えることができます。 次に、四隅の座標を基準にして変形する場合を考えます。変換前の座標を 変換後の座標を とします。ただし、 です。 具体的には、以下の図のような変換を考えます。
ここで、四隅の座標が与えられている場合、各について以下の式が成り立ちます。 これは、を計算することで得られます。 また、同様に も得られます。これを上の2つの式に代入して左辺を右辺に移動すると となるので、これをについて解くことで〜が得られ、が求まります。 求めたを使い、 の関係から画像の変換を行います。クリックすると展開されます
・ステップ1
・ステップ2
2. Sympyで連立方程式を解きHを求める
前述の通り、を求めるためには8元連立方程式を解く必要があります。 しかし、手計算で解くのはとても面倒なので、Sympyで解いていきます。 Sympyの使い方についてはこの辺りを参照。 3.2. Sympy : Python での代数計算 — Scipy lecture notes 環境は、MacOS Sierra, Python3.6です。
まずは必要なパッケージをインポートします。 〜を変数として設定します。 次に、のセットをnumpyアレイ を解くための連立方程式を以下のように記述します。 各ごとに方程式を求め、リスト 全ての方程式の入ったリストexpを引数としてSympyのsolve関数に渡し、連立方程式を解き、解をHに代入します。 仮に以下のような図の変換の場合
方程式は となり、Sympyによってを求めると となることがわかりました。 このが正しいかは、の値を用いてを計算して確かめることができます。クリックすると展開されます
from sympy import solve,symbols
import numpy as np
str_list = [f"h{i}" for i in [11,12,13,21,22,23,31,32]]
str_symbol = " ".join(str_list)
h11, h12, h13, h21, h22, h23, h31, h32 = symbols(str_symbol)
xy
に、のセットをnumpyアレイuv
に代入します。exp
に加えます。exp = []
for i in range(4):
exp_x = h11*xy[i,0]+h12*xy[i,1]+h13-(h31*xy[i,0]+h32*xy[i,1]+1)*uv[i,0]
exp_y = h21*xy[i,0]+h22*xy[i,1]+h23-(h31*xy[i,0]+h32*xy[i,1]+1)*uv[i,1]
exp.append(exp_x)
exp.append(exp_y)
result = solve(exp,dict=True)
r = result[0]
H = np.array([[r[h11],r[h12],r[h13]],
[r[h21],r[h22],r[h23]],
[r[h31],r[h32],1]],dtype=np.float64)
1: 10.0*h11 + 20.0*h12 + h13
2: 10.0*h21 + 20.0*h22 + h23
3: 20.0*h11 + 70.0*h12 + h13
4: 20.0*h21 + 70.0*h22 + h23 - 2000.0*h31 - 7000.0*h32 - 100.0
5: 90.0*h11 + 90.0*h12 + h13 - 9000.0*h31 - 9000.0*h32 - 100.0
6: 90.0*h21 + 90.0*h22 + h23 - 9000.0*h31 - 9000.0*h32 - 100.0
7: 90.0*h11 + 10.0*h12 + h13 - 9000.0*h31 - 1000.0*h32 - 100.0
8: 90.0*h21 + 10.0*h22 + h23
array([[ 2.06775593e+00, -4.13551186e-01, -1.24065356e+01],
[ 2.13237330e-01, 1.70589864e+00, -3.62503462e+01],
[ 8.18794424e-03, -4.13551186e-03, 1.00000000e+00]])
for i in range(4):
xy1 = np.append(xy[i],1)
uv1 = np.dot(H,xy1)
uv1 = np.round(uv1/uv1[-1]).astype(int)
uv1 = uv1[:-1]
print(f"{xy[i]} -> {uv1}")
#出力
[20 10] -> [0 0]
[70 20] -> [100 0]
[90 90] -> [100 100]
[10 90] -> [ 0 100]
3. 求めたHとScikit-imageを使って図形を変換する
を図形に適用して、実際に変換していきます。 変換がわかりやすいように多角形を描いてみました。
これを変換していきます。 前の項目で述べた、を求める過程を関数にまとめます。 この関数を使って解きます。四隅の変換前と変換後の座標を設定します。 早速、を解いていきます。 が、そのままでやると何故かわからないですがうまくいかないことがわかったので、xとyを入れ替えたりゴソゴソやっています。numpyのxyと画像にした時のxyの方向が異なるせいかなぁ、と思っていますが、もし詳しい方がいらっしゃったらコメントいただけるとありがたいです。。。 最後に求めたを、skimageのtransformモジュールの機能を使って、画像に適用します。 変換によって伸展した場所がモヤっとしてしまうので、最後に四捨五入によってバイナリ画像に修正してから表示します。 変換前
変換後
うまくいきました! 原理的には正方形だけでなく、別の任意の四角形にも変換できるので、色々と面白いことができそうです。 以上です。クリックすると展開されます
polygon = np.array([[20,10],[50,40],[70,20],[90,90],[50,60],[10,90]])
img = np.zeros((100,100),dtype=np.float64)
rr,cc = draw.polygon(polygon.T[0],polygon.T[1])
img[rr,cc] = 1
plt.imshow(img)
plt.show()
from sympy import solve,symbols
import numpy as np
from skimage import draw,transform
import matplotlib.pyplot as plt
from pprint import pprint
def solve_H(xy,uv):
print("XY to UV")
for i in range(4):
print(f"{xy[i]} -> {uv[i]}")
str_list = [f"h{i}" for i in [11,12,13,21,22,23,31,32]]
str_symbol = " ".join(str_list)
h11, h12, h13, h21, h22, h23, h31, h32 = symbols(str_symbol)
exp = []
for i in range(4):
exp_x = h11*xy[i,0]+h12*xy[i,1]+h13-(h31*xy[i,0]+h32*xy[i,1]+1)*uv[i,0]
exp_y = h21*xy[i,0]+h22*xy[i,1]+h23-(h31*xy[i,0]+h32*xy[i,1]+1)*uv[i,1]
exp.append(exp_x)
exp.append(exp_y)
print("")
print("Equations")
for i,e in enumerate(exp):
print(f"{i+1}: {e}")
result = solve(exp,dict=True)
r = result[0]
H = np.array([[r[h11],r[h12],r[h13]],
[r[h21],r[h22],r[h23]],
[r[h31],r[h32],1]],dtype=np.float64)
print("")
print("Result_array")
pprint(H)
return H
xy = np.array([[20,10],[70,20],[90,90],[10,90]])
uv = np.array([[0,0],[100,0],[100,100],[0,100]])
xy_t = xy[:,::-1]
uv_t = uv[:,::-1]
H = solve_H(xy_t,uv_t)
#出力
XY to UV
[10 20] -> [0 0]
[20 70] -> [ 0 100]
[90 90] -> [100 100]
[90 10] -> [100 0]
Equations
1: 10.0*h11 + 20.0*h12 + h13
2: 10.0*h21 + 20.0*h22 + h23
3: 20.0*h11 + 70.0*h12 + h13
4: 20.0*h21 + 70.0*h22 + h23 - 2000.0*h31 - 7000.0*h32 - 100.0
5: 90.0*h11 + 90.0*h12 + h13 - 9000.0*h31 - 9000.0*h32 - 100.0
6: 90.0*h21 + 90.0*h22 + h23 - 9000.0*h31 - 9000.0*h32 - 100.0
7: 90.0*h11 + 10.0*h12 + h13 - 9000.0*h31 - 1000.0*h32 - 100.0
8: 90.0*h21 + 10.0*h22 + h23
Result_array
array([[ 2.06775593e+00, -4.13551186e-01, -1.24065356e+01],
[ 2.13237330e-01, 1.70589864e+00, -3.62503462e+01],
[ 8.18794424e-03, -4.13551186e-03, 1.00000000e+00]])
使う機能はProjectiveTransform
とwarp
です。ProjectiveTransform
はinverse
にすると想定通りの変換となります。tform = transform.ProjectiveTransform(matrix=H).inverse
warped = transform.warp(img,tform,order=3,mode="edge")
plt.imshow(img)
plt.show()
plt.imshow(np.round(warped).astype(int))
plt.show()
画像のモーメントについての備忘録
最近、画像のモーメントについて(やっと)理解したので備忘録として。
「そもそもなんで画像のモーメントから重心座標が求められるの?」というところが主です。
長文です。
目次
- 前置き
- 二値画像で考えてみる
- さらに単純化してみる
- 「なぜ、が領域の面積になるのか」の答え
- 「なぜ、が重心の座標になるのか」の答え
- numpyとscikit-imageで試す
1. 前置き
OpenCV-Python Tutorialsでは、画像のモーメントは領域の面積や重心の求め方の項で紹介されています。
領域(輪郭)の特徴 — OpenCV-Python Tutorials 1 documentation
重心は と の関係から求められます。
モーメントについての詳細はWikipedia(英語版)に、、、ということなので、Wikipediaを見にいってみる。。。
In image processing, computer vision and related fields, an image moment is a certain particular weighted average (moment) of the image pixels' intensities, or a function of such moments, usually chosen to have some attractive property or interpretation.
Image moments are useful to describe objects after segmentation. Simple properties of the image which are found via image moments include area (or total intensity), its centroid, and information about its orientation.
画像処理、コンピュータビジョンおよび関連分野では、画像モーメントは、画像ピクセルの強度の特定の加重平均(モーメント)、または通常はある魅力的な特性または解釈を有するように選択されたそのようなモーメントの関数である。
画像モーメントは、分割後のオブジェクトを記述するのに便利です。 画像モーメントによって見出される画像の単純な特性には、面積(または全強度)、重心、およびその向きに関する情報が含まれる。
なんのこっちゃΣ( ̄ロ ̄lll)
下の方の項目を見ても、積分記号がいっぱい出てきて、ますますわからない、、、orz
という経緯があり、原理については謎のまま天下り的に使っていました。
2. 二値画像で考えてみる
単純化のために二値画像の場合に絞って考えることにします。
画像のモーメントと添字(など)をWikipediaのような積分記号ではなく、総和(シグマ)で表すと次のようになります。
シグマの添字の付け方がイレギュラーな感じですが、画像中の全ピクセルについての総和と考えてください。
具体的な作業としては、次のような二値画像について
1ピクセルずつ(x座標、y座標、画素値)を調べます。
次に、全てのピクセルについて
- x座標のn乗
- y座標のm乗
を求めます。
さらに、上記の2つと画素値(0または1)を掛け合わせます。
例えば、(15,17,1)のピクセルについて、n=2、m=0の場合は
となり、このピクセルでの計算結果は225となります。
全てのピクセルでの計算結果を足し合わせたものが画像のモーメントです。
3. さらに単純化してみる
二値画像の場合、画素値が0のピクセルの値は
となって必ず0となります。一方、画素値が1のピクセルは
となります。 「画素値1の領域」をと名付けることにします。
画素値が0のピクセルは計算結果も0となって消え去るので、最終的に
が画像のモーメントということになります。 添字の意味は、領域A内の全ピクセルについての総和と考えてください。
ちょっとだけ単純になりました(^ ^)ノ
4. 「なぜ、が領域の面積になるのか」の答え
、 のときを考えます。その場合、各ピクセルの計算結果は
なので、画像のモーメントは
となります。すなわち、は領域A内のピクセルの総数であることがわかります。
領域A内のピクセル数は、二値画像の世界では領域Aの面積と同値なので
となることがわかりました。
5. 「なぜ、が重心の座標になるのか」の答え
いよいよ、本丸の重心座標について。先ほどの領域Aの重心座標を仮にとおきます。
ここで領域の重心とは、重心を境に上側と下側、右側と左側のバランスがとれている点です。
つまり、重心を原点ととると、全てのx座標の合計、全てのy座標の合計のそれぞれが0となります。
具体的には、重心を原点とした時の各ピクセルの座標を(X, Y)とすると
と書けます。次に、Xの方だけ考えます。
Xは元座標を基準としたと重心座標で以下のように表せます。
この式を、前述のに代入して、
ゆえに
となります。
ここで、
また、
なので、最終的に
となりました。
Yの方も同じ手順で求められるので、OpenCV-Python Tutorialsで説明されていた
重心は と の関係から求められます。
が成り立つことがわかりました。
理解してみると意外と難しくなかった。。。
6. numpyとscikit-imageで試す
・numpyで計算してみる
手を動かすほうが実感できるかなー、ということで。
まず、二値の円を描きます。
from skimage import draw, measure import numpy as np import matplotlib.pyplot as plt img = np.zeros((10, 10), dtype=np.uint8) rr, cc = draw.circle(4, 4, 5) img[rr+1, cc] = 1 plt.imshow(img) plt.show()
次に、この円の画像のモーメントを求めてみます。
計算を簡単にするために、各座標のx、yの行列を作成しておきます。
y = np.zeros((10, 10), dtype=np.uint8) y += np.arange(0,10,1, dtype=np.uint8) x = y.T print("x =") print(x) print("y =") print(y) #出力 x = [[0 0 0 0 0 0 0 0 0 0] [1 1 1 1 1 1 1 1 1 1] [2 2 2 2 2 2 2 2 2 2] [3 3 3 3 3 3 3 3 3 3] [4 4 4 4 4 4 4 4 4 4] [5 5 5 5 5 5 5 5 5 5] [6 6 6 6 6 6 6 6 6 6] [7 7 7 7 7 7 7 7 7 7] [8 8 8 8 8 8 8 8 8 8] [9 9 9 9 9 9 9 9 9 9]] y = [[0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9] [0 1 2 3 4 5 6 7 8 9]]
それではいよいよ、、、の計算。
for n,m in [[0,0],[1,0],[0,1]]: M_nm = np.sum(np.power(x,n) * np.power(y,m) * img) print(f"M{n}{m}: {M_nm}") #出力 M00: 69 M10: 345 M01: 276
ここから、重心座標は
centroid = (345/69,276/69) print(f"モーメントから求めた重心座標 : {centroid}") #出力 モーメントから求めた重心座標 : (5.0, 4.0)
と計算できます。
重心を黒く塗りつぶして位置を確認してみます。
img[int(centroid[0]),int(centroid[1])] = 0 plt.imshow(img) plt.show()
大丈夫そうですね。
・scikit-imageでラクしてモーメントを求める
scikit-imageでは、画像のモーメントは
measureモジュールのmoments
という関数で求められます。
M = measure.moments(img) print(M) #出力 [[6.90000e+01 2.76000e+02 1.48000e+03 8.92800e+03] [3.45000e+02 1.38000e+03 7.40000e+03 4.46400e+04] [2.10100e+03 8.40400e+03 4.44400e+04 2.64352e+05] [1.42650e+04 5.70600e+04 2.96600e+05 1.73328e+06]]
画像のモーメントから面積と重心座標を計算してみます。
print(f"M00 (面積) : {M[0,0]}") print(f"M10 : {M[1,0]}") print(f"M01 : {M[0,1]}") print(f"モーメントから求めた重心座標:{(M[1,0]/M[0,0],M[0,1]/M[0,0])}") #出力 M00 (面積) : 69.0 M10 : 345.0 M01 : 276.0 モーメントから求めた重心座標:(5.0, 4.0)
numpyで求めた値と一致しています。よしよし。
・scikit-imageのさらに便利な関数たち
ちなみに、measureモジュールのregionprops
関数を使うと
もっと簡単に面積・重心が求められます。
result_props = measure.regionprops(img) print(f"面積: {result_props[0].area}") print(f"重心座標: {result_props[0].centroid}") #出力 面積: 69 重心座標: (5.0, 4.0)
measureモジュールにはmoments_central
という関数もあります。
これを使うと、重心を原点とした時の画像のモーメントが求められます。
M = measure.moments_central(img) print(M[0,0]) print(M[1,0]) print(M[0,1]) #出力 69.0 0.0 0.0
これはラクチンですね(^ ^)ノ