チュートリアル
このチュートリアルでは、Powder Level Monitor を使用して、カメラ接続、ROI 設定、Baseline 設定、粉面測定を行うまでの手順を説明します。
1. 基本構成
gRPC モードでは、サーバー側でカメラ取得と粉面認識を行います。
カメラ
↓
PowderLevelGrpcService
├── ImageStreamDeviceClass
└── PowderControlDeviceClass
↓
grpc_frame
↓
PowderLevelGrpcClient
├── CLI
└── GUI
測定処理はサーバープロセス内で完結します。クライアントへ送信する画像は監視表示用であり、認識処理の入力としてサーバーへ送り返すことはありません。
2. 接続設定
サーバーおよびクライアントのサンプルでは、次の設定を使用します。
SERVER_IP = "127.0.0.1"
SERVER_PORT = 50051
MAX_MESSAGE_MB = 64
別のコンピューターから接続する場合は、クライアント側の SERVER_IP をサーバーの IP アドレスへ変更します。
サーバー側は次の設定で全インターフェースに待ち受けます。
GRPC_HOST = "0.0.0.0"
GRPC_PORT = 50051
3. サーバーを起動する
run_server.py を実行します。
python examples/run_server.py
サーバーでは、次の順序で初期化されます。
PowderLevelGrpcServiceを生成EventBusを生成create_grpc_server()で gRPC サーバーを生成0.0.0.0:50051で待ち受けカメラ接続
画像取得ループと粉面処理ループを開始
カメラ接続に失敗した場合は、get_status() の内容を伴う RuntimeError で起動を中止します。
4. サーバー状態を確認する
status_client.py を実行します。
python examples/status_client.py
クライアントの基本形は次のとおりです。
from powder_level_monitor.server_grpc import PowderLevelGrpcClient
from x_logger import XLogger
logger = XLogger(
log_level="debug",
logger_name="status_client",
)
client = PowderLevelGrpcClient(
server_ip="127.0.0.1",
grpc_port=50051,
timeout_sec=5.0,
logger=logger,
max_message_mb=64,
)
try:
status = client.get_status()
print(status)
finally:
client.close()
get_status() の state は、おおむね次の状態を返します。
状態 |
意味 |
|---|---|
|
カメラに接続していない |
|
接続済みだが ROI が未設定 |
|
ROI 設定済みだが Baseline が未設定 |
|
測定開始可能 |
|
測定中 |
5. ROI を設定する
5.1 ROI ファイルを用意する
set_roi_client.py は、同じディレクトリの roi.json を読み込みます。
{
"points": [
[100, 80],
[540, 80],
[540, 430],
[100, 430]
]
}
4点は次の順序で指定します。
左上 → 右上 → 右下 → 左下
自己交差する四角形、画像範囲外の点、重複点は使用できません。
5.2 ROI を送信する
python examples/set_roi_client.py
実行される処理は次のとおりです。
client.set_roi_points(roi_data["points"])
ROI を変更すると、既存の Baseline は無効になります。ROI 変更後は Baseline を再設定してください。
5.3 ROI を確認する
python examples/get_roi_client.py
内部では次を呼び出します。
client.get_roi_points()
6. Baseline を設定する
ROI 設定後、現在検出されている粉面を Baseline として登録します。
python examples/set_baseline_client.py
内部では次を呼び出します。
result = client.set_baseline()
粉面を検出できていない場合、False が返ります。失敗理由は次で確認できます。
measurement = client.get_measurement()
print(measurement.get("last_error"))
Baseline の値を確認するには次を実行します。
python examples/get_baseline_client.py
7. 測定を開始する
Baseline 設定後に測定を開始します。
python examples/start_measurement_client.py
内部では次を呼び出します。
result = client.start_measurement()
開始できなかった場合は、次で理由を確認できます。
status = client.get_status()
print(status.get("last_error"))
測定開始には、少なくとも次の条件が必要です。
カメラに接続している
ROI が4点設定されている
Baseline が設定されている
8. 測定結果を取得する
python examples/measurement_client.py
内部では次を呼び出します。
measurement = client.get_measurement()
代表的な結果は次の形式です。
{
"measuring": true,
"current_detected": true,
"baseline_set": true,
"baseline_detected": true,
"level_y": 310.4,
"baseline_level_y": 280.2,
"decrease_px": 30.2,
"decrease_mm": 4.19,
"decrease_ratio": 0.0419,
"last_error": null
}
level_y と baseline_level_y は、透視変換後の縦方向座標です。画像へ線を描画するときは、元画像座標へ逆変換済みの次の値を使用します。
line_leftline_rightbaseline_line_leftbaseline_line_right
9. GUI で操作する
サーバーを起動した状態で、次を実行します。
python examples/monitor_gui.py
GUI 起動時には次の処理が行われます。
camera.frameの EventBus 購読を開始start_stream()を呼び出すJPEG 画像を受信して表示
get_status()とget_measurement()を定期取得
ROI 設定
「ROI設定開始」を押す
画像上で左上、右上、右下、左下の順に4点をクリック
「ROI送信」を押す
Baseline を再設定する
表示色は次のとおりです。
表示 |
色 |
|---|---|
サーバー登録済み ROI |
緑 |
編集中 ROI |
黄 |
Baseline |
赤 |
現在粉面 |
青 |
GUI 終了時
GUI は終了時に次を実行します。
stop_stream()を呼び出すEventBus 購読スレッドを停止
gRPC クライアントを閉じる
これにより、GUI を使用していない間は画像のネットワーク配信が停止します。
10. 測定を停止する
停止用クライアントから次を呼び出します。
client.stop_measurement()
python examples/stop_measurement_client.py
停止後も ROI と Baseline は保持されます。
11. Baseline と ROI を解除する
Baseline を解除する場合は次を呼び出します。
client.clear_baseline()
ROI を解除する場合は次を呼び出します。
client.clear_roi_points()
ROI を解除すると、測定は停止し、Baseline も無効になります。
12. 検出パラメーター
検出パラメーターは get_detector_params() で取得し、set_detector_params() で変更できます。
params = client.get_detector_params()
print(params)
client.set_detector_params(
{
"mode": 0,
"blur": 5,
"open_kernel": 3,
"close_kernel": 5,
"row_ratio_threshold": 0.2,
"stable_rows": 4,
"min_area": 500,
"margin_x_ratio": 0.05,
"margin_y_ratio": 0.05,
"measure_height_mm": 100.0,
}
)
主な項目は次のとおりです。
項目 |
内容 |
|---|---|
|
二値化方式。0~3 |
|
手動二値化しきい値。0~255 |
|
Gaussian Blur のカーネルサイズ |
|
Opening のカーネルサイズ |
|
Closing のカーネルサイズ |
|
行方向の粉体占有率しきい値 |
|
縦方向の連続画素確認数 |
|
マスクの最小面積 |
|
補正画像左右の除外率 |
|
補正画像上下の除外率 |
|
補正画像全高に対応する実長 |
偶数の blur、open_kernel、close_kernel は、内部で次の奇数へ補正されます。
13. ローカルで画像取得だけを使う
ImageStreamDeviceClass は、gRPC を使わず直接利用できます。
import time
from powder_level_monitor.image_stream_ctrl import ImageStreamDeviceClass
image_stream = ImageStreamDeviceClass(
camera_id=0,
)
if not image_stream.connect():
raise RuntimeError(str(image_stream.get_status()))
try:
while True:
frame = image_stream.get_latest_frame()
if frame is not None:
print(frame.shape)
break
time.sleep(0.01)
finally:
image_stream.disconnect()
14. ローカルで粉面測定を使う
PowderControlDeviceClass へ ImageStreamDeviceClass を渡すことで、gRPC を使わずに測定できます。
from powder_level_monitor.image_stream_ctrl import ImageStreamDeviceClass
from powder_level_monitor.powder_monitor_ctrl import PowderControlDeviceClass
image_stream = ImageStreamDeviceClass(
camera_id=0,
)
monitor = PowderControlDeviceClass(
image_source=image_stream,
measurement_interval_sec=0.1,
)
if not monitor.connect():
raise RuntimeError(str(monitor.get_status()))
try:
monitor.set_roi_points(
[
[100, 80],
[540, 80],
[540, 430],
[100, 430],
]
)
if not monitor.set_baseline():
raise RuntimeError(
str(monitor.get_measurement().get("last_error"))
)
if not monitor.start_measurement():
raise RuntimeError(
str(monitor.get_status().get("last_error"))
)
measurement = monitor.get_measurement()
print(measurement)
finally:
monitor.disconnect()
ローカル利用では PowderLevelGrpcService、PowderLevelGrpcClient、EventBus は不要です。
15. 推奨操作順序
1. サーバー起動
2. 状態確認
3. ROI設定
4. ROI確認
5. Baseline設定
6. Baseline確認
7. 測定開始
8. 測定結果取得
9. 測定停止
GUI を使用する場合は、ROI 設定から測定開始までを1つの画面で操作できます。
作者
Kengo NAKADA