推論ソース
source引数は何かを開く前に分類されるため、1つの呼び出しでJPEG、フォルダー、MP4、Webカメラ番号、RTSP URL、画面領域、カメラのリストを処理できます。
ソースの分類方法
classify_sourceは何かを開く、またはダウンロードする前に、次の順序で値を調べます。最初に一致した規則が適用されます。
| ソース | 読み取り方法 |
|---|---|
"screen"、"screen 1"、"screen 1 100 200 512 256" | 画面キャプチャ |
0以上のint、または同名のファイルがない数字だけの文字列 | Webカメラ |
rtsp://、rtmp://、tcp://、udp://のURL | ネットワークストリーム |
パスが.m3u8で終わるhttp(s):// URL | ネットワークストリーム |
| YouTubeページのURL | ネットワークストリーム |
| 全項目がライブソースまたは動画であるリストかタプル | 複数のライブストリーム |
| そのほかのリストまたはタプル | 画像のバッチ |
.streamsで終わるパス | 複数のライブストリーム |
| 動画拡張子を持つパス | 動画ファイル |
| 既存のディレクトリ | 画像フォルダー |
| そのほか | 単一画像 |
ライブソースと画像が混在するリストではTypeErrorが発生します。負のWebカメラ番号ではValueErrorが発生します。
分類器はネットワークに一切アクセスしないため、入力ミスのあるURLはpredictの呼び出し時ではなく、キャプチャを開くときに明らかになります。
画像
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") # 単一画像ソースはリストではなく1つの Results を返すresult = model(SAMPLE_IMAGE)print(len(result.boxes), "detections")import numpy as npfrom PIL import Image from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") pil_image = Image.open(SAMPLE_IMAGE)array = np.asarray(pil_image)raw_bytes = open(SAMPLE_IMAGE, "rb").read() # NumPy配列は指定がない限りBGRとして読み取られるが、この配列はRGBresult = model(array, color_format="rgb")print(type(array).__name__, len(result.boxes)) for source in (pil_image, raw_bytes): result = model(source) print(type(source).__name__, len(result.boxes))from pathlib import Pathfrom PIL import Image from libreyolo import LibreYOLO, SAMPLE_IMAGE folder = Path("sample_folder")folder.mkdir(exist_ok=True)image = Image.open(SAMPLE_IMAGE)for index in range(3): image.save(folder / f"frame_{index}.jpg") model = LibreYOLO("LibreYOLO9s.pt") # フォルダーはパス順に画像ごと1つの Results を含むリストを返すresults = model(str(folder))print(len(results), "images")libreyolo predict model=LibreYOLO9s.pt \ source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg単一画像ソースは7種類の型を受け付けます。
| 型 | 読み取り方法 |
|---|---|
strまたはpathlib.Path | ローカルファイル、http(s)://、s3://、gs:// |
PIL.Image.Image | RGBへ変換 |
numpy.ndarray | 2Dグレースケール、またはBGR順の3DのHWCかCHW。4D配列はバッチとして扱う |
torch.Tensor | CHWまたはNCHWをRGBとして読み取り。4Dテンソルはバッチとして扱う |
bytes | エンコードされた画像データ |
io.BytesIO | エンコードされた画像データ |
4Dの配列またはテンソルは、画像ごとに1つのResultsを含むリストを返します。
前処理の前にすべてRGBへ変換されます。チャンネル順序が曖昧なのはNumPy配列だけなので、color_formatで制御します。デフォルトの"auto"と"bgr"は配列をOpenCVが返す順序であるBGRとして読み取り、"rgb"は配列をそのまま維持します。PIL画像から作った配列には後者が必要です。
浮動小数点配列は自身の範囲に基づいて再スケーリングされます。1.0以下の値は255倍され、それより大きい値は[0, 255]内にクリップされます。RGBA配列ではアルファチャンネルを削除します。
リモートパスにはそれぞれ1つのパッケージが必要です。http(s)://用のrequestsは基本インストールに含まれますが、s3://用のboto3とgs://用のgcsfsは含まれません。
追跡は、画像、ファイル名で並べたフォルダー、リスト、タプル、遅延評価する画像イテレーターを連続フレームとして受け付けます。fps=30.0で画像列の時刻情報を定義し、color_format="auto"で入力の解釈を選択してください。追跡を参照してください。
フォルダー
ディレクトリは再帰的に走査して並べ替えられ、次のいずれかのサフィックスを持つすべてのファイルが画像になります。.jpg、.jpeg、.png、.gif、.webp、.bmp、.tiff、.tifです。フォルダー内のそのほかのものはスキップされます。空のフォルダーでは例外を発生させず、空のリストを返します。
フォルダーとリストはbatchを受け付ける2種類のソースです。対応するファミリーでは、チャンクごとに積み重ねた順伝播を1回実行します。推論性能を参照してください。
動画ファイル
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # clip.mp4 をディスク上の動画ファイルに置き換えるfor result in model("clip.mp4", stream=True): print(result.frame_idx, len(result.boxes))from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") for result in model("clip.mp4", stream=True, vid_stride=3, save=True): passパスのサフィックスが.3g2、.3gp、.asf、.avi、.dav、.f4v、.flv、.gif、.h264、.h265、.hevc、.m2ts、.m4v、.mkv、.mov、.mp4、.mpeg、.mpg、.mts、.mxf、.ogv、.ts、.vob、.wmv、.webmのいずれかなら、動画として扱われます。
.gifは両方の一覧に含まれます。動画の確認が先に行われるため、.gifパスを直接predictに渡すと動画として開かれます。走査対象フォルダー内の.gifは静止画像として読み込まれます。
vid_strideはN番目ごとのフレームを処理し、デフォルトは1です。stream=Trueを指定しないと動画全体が1つのリストへデコードされ、間引き後に500フレームを超える場合はstream=Trueを推奨する警告が表示されます。
動画から得られる各Resultsはframe_idxを持ちます。
動画エンコードでは、H.264を開けない場合に利用可能なコーデックへフォールバックします。別のコーデックが成功した場合にだけ、フォールバックをINFOで記録し、コーデックとキャンバスごとにキャッシュします。
Webカメラ、ネットワークストリーム、YouTube
import itertools from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # Webカメラ番号 0 ライブソースは終了しないためループを制限for result in itertools.islice(model(0, stream=True), 100): print(result.frame_idx, len(result.boxes))import itertools from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt")source = "rtsp://user:password@192.168.1.64:554/Streaming/Channels/101" for result in itertools.islice(model(source, stream=True), 100): print(result.frame_idx, len(result.boxes))ライブソースには終端がないため、stream=Trueが必要です。指定しない場合、predictは無限のリストを収集しようとせずValueErrorを発生させます。
フレームはキャプチャごとに1つのバックグラウンドスレッドで読み取られます。デフォルトではキューに最新フレームだけを保持するため、モデルがカメラより遅い場合は遅延が積み上がらずにフレームをスキップします。stream_buffer=Trueではキャプチャしたすべてのフレームを保持し、フレームを維持する代わりにレイテンシが増大します。
Webカメラ番号はintまたは数字の文字列です。Windowsでは最初にDirectShowバックエンドでキャプチャを開き、失敗した場合はデフォルトのバックエンドにフォールバックします。
YouTubeページのURLは動画をダウンロードせずに直接メディアURLへ解決され、そのためにyt-dlpが必要です。
pip install "libreyolo[stream]"ストリームのラベルは、ログへ記録する、またはファイル名に使う前に機密部分が伏せられます。認証情報を含むURLはuser:***@hostとして表示され、署名付きURLとbearer tokenが含まれるため、直接ストリームのラベルからクエリ文字列が削除されます。YouTubeの動画IDは認証情報ではないため維持されます。
複数のカメラを同時に処理
import itertoolsfrom pathlib import Path from libreyolo import LibreYOLO Path("cameras.streams").write_text( "# one source per line, blank lines and comments are skipped\n" "rtsp://192.168.1.64:554/Streaming/Channels/101\n" "rtsp://192.168.1.65:554/Streaming/Channels/101\n", encoding="utf-8",) model = LibreYOLO("LibreYOLO9s.pt")for result in itertools.islice(model("cameras.streams", stream=True), 100): print(result.frame_idx, len(result.boxes))import itertools from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt")cameras = [0, "rtsp://192.168.1.64:554/Streaming/Channels/101"] for result in itertools.islice(model(cameras, stream=True), 100): print(result.frame_idx, len(result.boxes)).streamsファイルでは1行に1つのソースを記述します。空行と#で始まる行は無視されます。残る各行はWebカメラ番号、ネットワークストリーム、YouTube URL、動画ファイルのパスのいずれかでなければなりません。それ以外の場合は行番号を示してValueErrorが発生します。空のファイルでは、カメラなしで開始せず例外が発生します。
ライブソースのリストまたはタプルを使うと、ファイルなしで同じ処理を行えます。
各キャプチャに固有のスレッドが割り当てられ、すべてのフレームが1つのジェネレーターへ多重化されます。各処理ではアクティブなストリームをポーリングし、準備ができたものを生成します。そのため、遅いカメラが速いカメラを待たせず、異なるカメラのフレームが交互に現れます。終了したストリームはローテーションから外れ、そのほかは続行します。
画面キャプチャ
from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # stream=True を指定しない場合は1フレームだけ取得result = model("screen")print(len(result.boxes), "detections")import itertools from libreyolo import LibreYOLO model = LibreYOLO("LibreYOLO9s.pt") # "screen <monitor> <left> <top> <width> <height>"for result in itertools.islice(model("screen 1 100 200 512 256", stream=True), 50): print(len(result.boxes))画面ソースはscreenという単語の後に0個、1個、4個、5個の整数を続けます。それ以外の個数ではValueErrorが発生します。
| 形式 | キャプチャ対象 |
|---|---|
"screen" | すべてのモニターを統合 |
"screen 1" | モニター1 |
"screen 100 200 512 256" | 統合デスクトップ上のボックス |
"screen 1 100 200 512 256" | モニター1上のボックス |
ボックス座標はleft top width heightで、選択したモニターの左上隅を基準とします。画面ソースはFPSを30÷vid_strideとして報告し、これは保存する動画のFPSになります。キャプチャにはmssパッケージが必要です。
pip install mssstream=Trueを指定しない場合、画面ソースは1フレームを取得して1つのResultsを返します。これは画像ファイルで推論する場合のスクリーンショット版です。stream=Trueを指定すると、ループが中断されるまでキャプチャします。
predictの返り値
返り値の形はソースとstreamによって異なります。
| ソース | stream=False | stream=True |
|---|---|---|
| 単一画像 | 1つのResults | 1つのResultsを生成するジェネレーター |
| 画像のリスト | Resultsのリスト | ジェネレーター |
| フォルダー | Resultsのリスト | ジェネレーター |
| 動画ファイル | Resultsのリスト | ジェネレーター |
| 画面 | 1つのResults | 終端のないジェネレーター |
Webカメラ、ネットワークストリーム、.streams | ValueError | 終端のないジェネレーター |
単一画像はResultsオブジェクト自体を返します。インデックス付けを行うと画像ではなく検出を選択するため、単一画像の推論結果に対するresult[0]は最初の画像ではなく最初のボックスです。これらのオブジェクトに含まれる内容については結果の操作を参照してください。
saveの書き込み先
save=Trueはアノテーション済み出力を返さず、runディレクトリの下に書き込みます。
画像は自動的に番号が増えるruns/detect/predict、runs/detect/predict2などへ、ソースのファイル名を維持して保存されます。1つのプロセス内の画像はすべて同じディレクトリに入るため、2つの入力フォルダーに同じファイル名があると互いに上書きします。メモリ内画像には再利用できるファイル名がありません。1枚だけの場合はinferenceとして保存されるため、呼び出しを繰り返すと上書きされます。リストやバッチの場合はimage0、image1のように番号が付きます。2つのプロセスが同時に保存する場合、各実行がディレクトリを作成することで確保するため、別々の番号付きディレクトリになります。
動画とライブソースは、ソースに基づく名前を持つ1つの.mp4として書き込まれます。
output_pathはディレクトリを上書きします。サフィックスを持つパスはファイル、持たないパスはディレクトリとして扱われます。output_file_formatは静止画像のエンコードを選択し、jpg、png、webpを受け付けます。
画像を保存した後、書き込まれたパスはresult.saved_pathとして結果にも追加されます。