Markdownで表示

結果の操作

各推論は画像ごとにResultsオブジェクトを返します。ペイロードの種類ごとに名前付きスロットが1つあり、モデルが生成するもの以外は空です。エクスポートした成果物でも同じスロットを使います。

1オブジェクト、ペイロードごとに1スロット

1枚の画像に対する推論は1つのResultsを返します。18個のペイロードスロットがあり、モデルは自身のタスクが生成するものだけを埋めます。そのほかのスロットはすべてNoneなので、検出器でresult.masksを読み取るとエラーではなくNoneになります。

スロットクラス形状生成するタスク
boxesBoxes(N, 4)にスコアとクラスを追加検出、および最初に位置特定を行うタスク
masksMasks(N, H, W)インスタンスセグメンテーション
keypointsKeypoints(N, K, 2)または(N, K, 3)姿勢推定
probsProbs(C,)分類
obbOBB(N, 7)または(N, 8)回転バウンディングボックス
gazeGaze(N, 2)のピッチとヨー(ラジアン)視線推定
pointsPointsx、y、class、confidenceの(N, 4)点の位置特定
semantic_maskSemanticMask(H, W)のクラスIDセマンティックセグメンテーション
panopticPanopticSegmentation(H, W)のセグメントIDとsegments_infoパノプティックセグメンテーション
depth_mapDepthMap(H, W)の浮動小数点数深度推定
normal_mapNormalMap(H, W, 3)の単位ベクトル表面法線
edgesEdgeMap[0, 1]内の(H, W)浮動小数点数エッジ検出
restoredRestoredImage(H, W, 3)のuint8 RGB復元と超解像
matteMatte[0, 1]内の(H, W)浮動小数点数アルファマッティングと背景除去
ocrOCRRegions(N, 4, 2)のポリゴンと転記テキストテキスト検出と認識
embeddingsEmbeddings(N, D)のL2正規化済み行embedタスク
identitiesIdentitiesN個の名前とスコアギャラリーを伴うembedタスク
meshesMeshes身体パラメータと任意の頂点人体メッシュ復元

これらに加えて、すべての結果が持つフィールドがあります。orig_shape(height, width)pathはソースパス(メモリ内入力ではNone)、namesはクラスIDからクラス名へのマッピング、frame_idxは動画とライブフレーム用、track_idはトラッキング時に使われます。restore_scaleは復元結果の整数アップスケール係数です。

result.normalsresult.normal_mapの別名です。

result.speedはすべての結果に存在しますが、値が設定されるのはアンサンブルだけです。キーはmember_0member_1fusionで、単位はミリ秒です。単一モデルでは空の辞書のままです。

ボックス

ボックス
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) print(result.orig_shape)   # ソース画像の (height, width)print(result.path)         # ソースパス メモリ内入力では None for xyxy, conf, cls in zip(    result.boxes.xyxy.tolist(),    result.boxes.conf.tolist(),    result.boxes.cls.tolist(),):    print(result.names[int(cls)], round(float(conf), 3), xyxy)
正規化座標
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) print(result.boxes.xyxy[:1])    # ピクセル x1 y1 x2 y2print(result.boxes.xywh[:1])    # ピクセル 中心 x 中心 y w hprint(result.boxes.xyxyn[:1])   # 同じボックスを幅と高さで除算print(result.boxes.xywhn[:1])
NumPyとデバイス
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) # それぞれ新しい Results を返し元のオブジェクトは変更しないas_numpy = result.numpy()on_cpu = result.cpu() print(type(as_numpy.boxes.xyxy).__name__)print(type(on_cpu.boxes.xyxy).__name__)

Boxesは座標とスコアを1つのパック済みテンソルではなく、別々の配列として保持します。

属性内容
xyxy(N, 4)の絶対ピクセル座標、x1 y1 x2 y2
xywh(N, 4)の絶対ピクセル座標、中心x、中心y、幅、高さ
xyxyn, xywhn同じ値を画像の幅と高さで除算
conf(N,)の信頼度
cls(N,)のクラスID、浮動小数点数
id(N,)のトラックID、またはNone
is_trackidが設定されているか
dataすべてを連結した値。ボックス、任意のID、信頼度、クラス

clsは浮動小数点配列なので、result.names[int(cls)]として使ってください。

xyxynxywhnにはorig_shapeが必要で、Resultsが自動的に設定します。

密なペイロード

画像全体を覆うペイロードはインスタンスごとのペイロードとは異なる動作をし、スライス時に重要になります。

SemanticMaskは元のキャンバス上の(H, W)クラスIDを保持し、255をクラスとして数えない無視値として予約します。classesは存在するIDを列挙して無視値を除外し、class_mask(id)はブール値の(H, W)を返します。

PanopticSegmentation(H, W)のセグメントIDを保持し、0をvoid IDとして使います。segments_infoは少なくともidcategory_idを持つ辞書のリストです。segment_idsは存在するIDを列挙し、segment_mask(id)は1つを選択します。

DepthMap(H, W)の相対的な逆深度を保持します。値が大きいほど近く、メートル単位の距離ではありません。有限値に対するminmaxmeanを公開し、normalized()[0, 1]へ再スケーリングします。

NormalMapはOpenCVカメラ座標系の(H, W, 3)単位ベクトルを保持します。+xは右、+yは下、+zはシーンの奥を向くため、カメラに正対する面は(0, 0, -1)です。assert_normalized()は各ピクセルが有限で単位長であることを確認します。

EdgeMap[0, 1]内の(H, W)float32を保持します。連続マップはしきい値処理されずに維持されるため、binary(threshold=0.5)でカットオフを選択します。

Matte[0, 1]内の(H, W)float32を保持し、1が完全な前景です。arrayはfloat32としてクリップした値を返します。

RestoredImage(H, W, 3)のuint8 RGBを保持し、arrayで未加工のndarrayを取得し、save(path)で書き出せます。

Probsは画像の確率ベクトルを1つ保持します。top1top5はクラスインデックス、top1conftop5confは対応するスコアです。

EmbeddingsはL2正規化済みの(N, D)行を保持するため、コサイン類似度は内積になります。similarity(other)はギャラリーに対して(N, M)、単一ベクトルに対して(N,)を返し、verify(i, j, threshold=0.4)は2つの行を比較します。

OCRRegionsは読み取り順の(N, 4, 2)ポリゴンを保持し、コーナーの順序は左上、右上、右下、左下です。転記テキストはtexts、認識スコアはconf、検出スコアはdet_confにあります。これらは実際の回転ポリゴンなのでboxesには値を設定しません。長方形が必要な場合はocr.xyxyで軸平行の外接矩形を取得できます。

スライスと移動

result[i]は1つのインスタンスを保持する新しいResultsを返します。インスタンスごとのペイロードはスライスされ、画像全体のペイロードは変更せずに引き継がれます。そのため、分類結果をスライスしても確率ベクトルが単一クラスに切り詰められず、深度結果をスライスしても(H, W)の配置が壊れません。

len(result)はインスタンス数を数えます。ボックス、点、埋め込みベクトル、OCR領域、メッシュが対象です。密な画像全体のペイロードはどれも1として数えられ、何もない結果は0です。

to()cpu()cuda()numpy()はそれぞれ、値が設定された全スロットを変換した新しいResultsを返します。元のオブジェクトは変更しません。

update()だけがその場で変更するメソッドで、指定されたスロットを置き換え、同じオブジェクトを返します。

JSON

summaryとto_json
import json from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")result = model(SAMPLE_IMAGE) rows = result.summary()print(json.dumps(rows[:2], indent=2)) # 同じキーワード引数を使い同じ内容を文字列として返すprint(result.to_json(normalize=True, decimals=3)[:200])
CLI
libreyolo predict model=LibreYOLO9s.pt --json \  source=https://raw.githubusercontent.com/LibreYOLO/libreyolo/release/libreyolo/assets/parkour.jpg

summary()は通常の辞書のリストを返し、to_json()はそのリストをjson.dumpsへ渡します。どちらも同じ3つの引数を受け取ります。normalize=Falseは座標を[0, 1]へ切り替え、decimals=5は丸め桁数を設定し、embeddings=Falseは埋め込みベクトルを含めるかどうかを制御します。

行の形はペイロードに従います。検出行はnameclassconfidencebox辞書を持ちます。マスクが存在する場合はsegments、回転バウンディングボックスではobbcorners、視線ではラジアンと度の両方のgaze角度、トラッキング時はtrack_id、メッシュが存在する場合はmeshパラメータが追加されます。

ボックスがない場合、1つのペイロードが行を決定します。OCRは領域ごとにtextを持つ1行、点は点ごとに1行、パノプティックはセグメントごとにpixel_countpixel_fractionを持つ1行、セマンティックは存在するクラスごとに1行、分類は上位5クラスを生成します。深度、法線、エッジ、復元、マッティングはそれぞれ、ピクセルではなくマップを説明する要約行を1つ生成します。

2つのペイロードは意図的に省略されます。512個の浮動小数点数を持つ1行は顔ごとに約2 KBになるため、埋め込みベクトルはembedding_dimだけで報告されます。値を含めるにはembeddings=Trueを渡してください。メッシュ頂点は人物ごとに数万個の座標になるため、一切含まれません。形状データにはresult.meshes.verticesを読み取るか、result.meshes.save_obj(path)を呼び出してください。

描画と保存

アノテーション済み画像
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt") # save=True でペイロードを描画し runs/detect/predict* の下に書き込むresult = model(SAMPLE_IMAGE, save=True)print(result.saved_path)

predict(save=True)がアノテーションを付けて書き出す経路です。値が設定されたスロットに応じて描画ルーチンを選択します。そのため、セマンティック結果は色付きマスク、深度結果は深度表示、パノプティック結果はセグメント付き、matteは背景が透明なRGBA PNG、検出結果は下にマスクを重ねたボックスとして書き出されます。書き込まれたパスはresult.saved_pathとして結果に追加されます。

Results.plot()は名前から想像されるより対象が限定されています。法線マップとエッジマップだけで定義され、そのほかではNotImplementedErrorが発生します。ほかのタスクではsave=Trueを使ってください。

Results.save(path)も同様に対象が限定されています。matte結果を背景が透明なRGBA PNGの切り抜きとして書き出し、そのほかではNotImplementedErrorが発生します。Results.cutout()は書き込まずに同じRGBA配列を返します。どちらにもソース画像が必要で、result.pathから取得するかimage=で渡します。

2つのペイロードは固有の書き込み機能を持ちます。復元画像にはresult.restored.save(path)、メッシュにはresult.meshes.save_obj(path, index=0)を使います。

ファイルの保存先とoutput_pathおよびoutput_file_formatの動作については推論ソースを参照してください。

エクスポートした成果物も同じオブジェクトを返す

エクスポート用追加パッケージをインストール
pip install "libreyolo[onnx]"
エクスポート成果物から同じResultsを取得
from libreyolo import LibreYOLO, SAMPLE_IMAGE model = LibreYOLO("LibreYOLO9s.pt")path = model.export(format="onnx")   # 書き込んだパスを返す # LibreYOLO() はファイルサフィックスで振り分けexported = LibreYOLO(path)result = exported(SAMPLE_IMAGE) print(type(result).__name__, len(result.boxes))

LibreYOLO()はファイルサフィックスで振り分けるため、エクスポートした成果物は.ptチェックポイントと同じ呼び出しで読み込まれ、同じResultsを返します。.onnx.engine.pte.mnnファイルはサフィックスで認識され、OpenVINO、Paddle、ncnnのディレクトリとTritonモデルURLも認識されます。モデルをエクスポート済みビルドへ切り替えても、result.boxes.xyxyを読み取るコードは変わりません。すべての形式についてはエクスポートを参照してください。

代わりにランタイム固有のAPIを使う場合は、前処理、後処理、クラス名を自分で管理する必要があります。

ペイロードクラス、スロット、移動のセマンティクス、summary()、to_json()、plot()、save()、cutout()はlibreyolo/utils/results.pyで確認しました。アノテーションとディスク書き込みの動作はlibreyolo/models/base/inference.pyのInferenceRunner._save_annotated_imageとlibreyolo/utils/general.pyのresolve_save_pathで確認しました。サフィックスによる振り分けはlibreyolo/models/__init__.pyのLibreYOLO()で確認しました。