😍

【やってみた】YOLOv8の機能試す&Webカメラでリアルタイム推論

2023/01/13に公開
12

宣伝

こんなコミュニティもやっているので良ければご参加ください!

https://kobe-sannomiya-ai-tech.vercel.app/

connpassはこちらから!

https://ai-tech.connpass.com/

はじめに

今回は最近登場した話題のYOLOv8をわかる範囲でしゃぶりつくします。

ところでYOLOv8ってすごい数まで来ましたね。つい1年前くらいはv5だとか言ってたはずなんですが。
そろそろYOLOって名前じゃなくて、別のアーキテクチャ名つけたほうが良いのでは・・・?🤔

実験環境

今回の実験環境は次の通りです。

  • Windows11(12th Gen Intel(R) Core(TM) i5-12400F 2.50 GHz)
  • conda

もしconda環境がまだない方は

https://zenn.dev/opamp/articles/b2d673bbccb9d4

を参考に導入お願いします。

まずはYOLOv8の準備

何はともあれYOLOv8の準備をしましょう。

git clone https://github.com/ultralytics/ultralytics

そして必要そうなものをインストール

pip install -r requirements.txt

公式ではpipでのインストールを勧められていますが、今回はお試しなのでリポジトリをクローンします。

YOLOv8を呼び出して推論する

公式のチュートリアルに従って呼び出しましょう。

from ultralytics import YOLO
model = YOLO("yolov8n.pt")

続いて推論してみましょう。

results = model("https://ultralytics.com/images/bus.jpg") 
results

#<generator object BasePredictor.__call__ at 0x00000266C4DA3EB0>

・・・。これは・・・、なんだ・・・?

詳しく扱えるようにいじってみましょう。

modelから手がかりを得る

まずはmodelが何で構成されているか確認します。

type(model)

#ultralytics.yolo.engine.model.YOLO

なるほど、modelultralytics.yolo.engine.model.YOLOで構成されているようです。

該当のスクリプトを確認してみましょう。

ultralytics/yolo/engine/model.py
class YOLO
    """
    YOLO

    A python interface which emulates a model-like behaviour by wrapping trainers.
    """

    def __init__(self, model='yolov8n.yaml', type="v8") -> None:
        """
        > Initializes the YOLO object.

        Args:
            model (str, Path): model to load or create
            type (str): Type/version of models to use. Defaults to "v8".
        """
        self.type = type
        self.ModelClass = None  # model class
        self.TrainerClass = None  # trainer class
        self.ValidatorClass = None  # validator class
        self.PredictorClass = None  # predictor class
        self.model = None  # model object
        self.trainer = None  # trainer object
        self.task = None  # task type
        self.ckpt = None  # if loaded from *.pt
        self.cfg = None  # if loaded from *.yaml
        self.ckpt_path = None
        self.overrides = {}  # overrides for trainer object

        # Load or create new YOLO model
        {'.pt': self._load, '.yaml': self._new}[Path(model).suffix](model)

    def __call__(self, source, **kwargs):
        return self.predict(source, **kwargs)

    def _new(self, cfg: str, verbose=True):
        """
        > Initializes a new model and infers the task type from the model definitions.

        Args:
            cfg (str): model configuration file
            verbose (bool): display model info on load
        """
        cfg = check_yaml(cfg)  # check YAML
        cfg_dict = yaml_load(cfg, append_filename=True)  # model dict
        self.task = guess_task_from_head(cfg_dict["head"][-1][-2])
        self.ModelClass, self.TrainerClass, self.ValidatorClass, self.PredictorClass = \
            self._guess_ops_from_task(self.task)
        self.model = self.ModelClass(cfg_dict, verbose=verbose)  # initialize
        self.cfg = cfg

    def _load(self, weights: str):
        """
        > Initializes a new model and infers the task type from the model head.

        Args:
            weights (str): model checkpoint to be loaded
        """
        self.model, self.ckpt = attempt_load_one_weight(weights)
        self.ckpt_path = weights
        self.task = self.model.args["task"]
        self.overrides = self.model.args
        self._reset_ckpt_args(self.overrides)
        self.ModelClass, self.TrainerClass, self.ValidatorClass, self.PredictorClass = \
            self._guess_ops_from_task(self.task)

    def reset(self):
        """
        > Resets the model modules.
        """
        for m in self.model.modules():
            if hasattr(m, 'reset_parameters'):
                m.reset_parameters()
        for p in self.model.parameters():
            p.requires_grad = True

    def info(self, verbose=False):
        """
        > Logs model info.

        Args:
            verbose (bool): Controls verbosity.
        """
        self.model.info(verbose=verbose)

    def fuse(self):
        self.model.fuse()

    @smart_inference_mode()
    def predict(self, source, return_outputs=True, **kwargs):
        """
        Visualize prediction.

        Args:
            source (str): Accepts all source types accepted by yolo
            **kwargs : Any other args accepted by the predictors. To see all args check 'configuration' section in docs
        """
        overrides = self.overrides.copy()
        overrides["conf"] = 0.25
        overrides.update(kwargs)
        overrides["mode"] = "predict"
        overrides["save"] = kwargs.get("save", False)  # not save files by default
        predictor = self.PredictorClass(overrides=overrides)

        predictor.args.imgsz = check_imgsz(predictor.args.imgsz, min_dim=2)  # check image size
        predictor.setup(model=self.model, source=source, return_outputs=return_outputs)
        return predictor() if return_outputs else predictor.predict_cli()

    @smart_inference_mode()
    def val(self, data=None, **kwargs):
        """
        > Validate a model on a given dataset .

        Args:
            data (str): The dataset to validate on. Accepts all formats accepted by yolo
            **kwargs : Any other args accepted by the validators. To see all args check 'configuration' section in docs
        """
        overrides = self.overrides.copy()
        overrides.update(kwargs)
        overrides["mode"] = "val"
        args = get_config(config=DEFAULT_CONFIG, overrides=overrides)
        args.data = data or args.data
        args.task = self.task

        validator = self.ValidatorClass(args=args)
        validator(model=self.model)

    @smart_inference_mode()
    def export(self, **kwargs):
        """
        > Export model.

        Args:
            **kwargs : Any other args accepted by the predictors. To see all args check 'configuration' section in docs
        """

        overrides = self.overrides.copy()
        overrides.update(kwargs)
        args = get_config(config=DEFAULT_CONFIG, overrides=overrides)
        args.task = self.task

        exporter = Exporter(overrides=args)
        exporter(model=self.model)

    def train(self, **kwargs):
        """
        > Trains the model on a given dataset.

        Args:
            **kwargs (Any): Any number of arguments representing the training configuration. List of all args can be found in 'config' section.
                            You can pass all arguments as a yaml file in `cfg`. Other args are ignored if `cfg` file is passed
        """
        overrides = self.overrides.copy()
        overrides.update(kwargs)
        if kwargs.get("cfg"):
            LOGGER.info(f"cfg file passed. Overriding default params with {kwargs['cfg']}.")
            overrides = yaml_load(check_yaml(kwargs["cfg"]), append_filename=True)
        overrides["task"] = self.task
        overrides["mode"] = "train"
        if not overrides.get("data"):
            raise AttributeError("dataset not provided! Please define `data` in config.yaml or pass as an argument.")
        if overrides.get("resume"):
            overrides["resume"] = self.ckpt_path

        self.trainer = self.TrainerClass(overrides=overrides)
        if not overrides.get("resume"):  # manually set model only if not resuming
            self.trainer.model = self.trainer.get_model(weights=self.model if self.ckpt else None, cfg=self.model.yaml)
            self.model = self.trainer.model
        self.trainer.train()

    def to(self, device):
        """
        > Sends the model to the given device.

        Args:
            device (str): device
        """
        self.model.to(device)

    def _guess_ops_from_task(self, task):
        model_class, train_lit, val_lit, pred_lit = MODEL_MAP[task]
        # warning: eval is unsafe. Use with caution
        trainer_class = eval(train_lit.replace("TYPE", f"{self.type}"))
        validator_class = eval(val_lit.replace("TYPE", f"{self.type}"))
        predictor_class = eval(pred_lit.replace("TYPE", f"{self.type}"))

        return model_class, trainer_class, validator_class, predictor_class

    @staticmethod
    def _reset_ckpt_args(args):
        args.pop("project", None)
        args.pop("name", None)
        args.pop("batch", None)
        args.pop("epochs", None)
        args.pop("cache", None)
        args.pop("save_json", None)

        # set device to '' to prevent from auto DDP usage
        args["device"] = ''

この中で推論時に使っていそうな関数は、predict関数ですね。正しいかどうか確認するために以下のようにして実験してみました。

 def predict(self, source, return_outputs=True, **kwargs):
        """
        Visualize prediction.

        Args:
            source (str): Accepts all source types accepted by yolo
            **kwargs : Any other args accepted by the predictors. To see all args check 'configuration' section in docs
        """
        print("これで推論しているよ")
        overrides = self.overrides.copy()
        overrides["conf"] = 0.25
        overrides.update(kwargs)
        overrides["mode"] = "predict"
        overrides["save"] = kwargs.get("save", False)  # not save files by default
        predictor = self.PredictorClass(overrides=overrides)

        predictor.args.imgsz = check_imgsz(predictor.args.imgsz, min_dim=2)  # check image size
        predictor.setup(model=self.model, source=source, return_outputs=return_outputs)
        return predictor() if return_outputs else predictor.predict_cli()

以下を実行してみます。

from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model("https://ultralytics.com/images/bus.jpg") 

#Found https://ultralytics.com/images/bus.jpg locally at bus.jpg
#Ultralytics YOLOv8.0.4  Python-3.9.15 torch-1.13.1+cpu CPU
#Fusing layers... 
#YOLOv8n summary: 168 layers, 3151904 parameters, 0 gradients, 8.7 GFLOPs
#これで推論しているよ

あたりですね!ではここを深堀してみましょう。

predict関数を深堀る

   def predict(self, source, return_outputs=True, **kwargs):
        """
        Visualize prediction.

        Args:
            source (str): Accepts all source types accepted by yolo
            **kwargs : Any other args accepted by the predictors. To see all args check 'configuration' section in docs
        """
        overrides = self.overrides.copy()
        overrides["conf"] = 0.25
        overrides.update(kwargs)
        overrides["mode"] = "predict"
        overrides["save"] = kwargs.get("save", False)  # not save files by default
        predictor = self.PredictorClass(overrides=overrides)

        predictor.args.imgsz = check_imgsz(predictor.args.imgsz, min_dim=2)  # check image size
        predictor.setup(model=self.model, source=source, return_outputs=return_outputs)
        return predictor() if return_outputs else predictor.predict_cli()

まずここから分かることは

  • 強制的にconf=0.25にされている
  • 予測にはself.PredictorClassを利用している
  • 詳しい引数はドキュメントに書いてある
  • 返り値はpredictor()である

です。意外と面倒ですね。

まずは楽そうなドキュメントを確認してみましょう。

ドキュメントの確認

コメントによると

Any other args accepted by the predictors. To see all args check 'configuration' section in docs

とあります。とりあえずdocs/config.mdを見てみましょう。

うーん英語、でもだいたいわかりそうです。

Key Value Description
source ultralytics/assets 入力には画像、フォルダ、ビデオ、URLが利用可能
show False 画像を表示する
save_txt False 結果をtxtに保存
save_conf False 信頼度を保存
save_crop Fasle 検出結果をクロップして保存
hide_labels False ラベルを消す
hide_conf False 信頼度を消す
vid_stride False フレームを飛ばす?(要調査)
line_thickness 3 bbox描画の太さを制御
visualize False モデルの特徴を表現
augment False 拡張推論を実行
agnostic_nms False クラスに依存しないNMSを利用する
retina_masks False 超解像でマスクを生成(Segmentationのみ)

引数で渡すとうまく動きそうですね。少し推論を試してみましょう。

soureceの扱い

sourceは次のが扱えるようです。

  • 画像までのパス
  • フォルダ
  • URL
  • Webカメラ
  • スクリーンショット

まずはフォルダのultralytics/assetsを利用してみます。

from ultralytics import YOLO
model = YOLO("yolov8n.pt")
for i in results:
    print(i)
# image 1/2 C:\Users\OPamp\Desktop\Python_experiment\ultralytics\ultralytics\assets\bus.jpg: 640x480 4 persons, 1 bus, 1 stop sign, 71.6ms
# image 2/2 C:\Users\OPamp\Desktop\Python_experiment\ultralytics\ultralytics\assets\zidane.jpg: 384x640 2 persons, 1 tie, 53.2ms
# Speed: 0.5ms pre-process, 62.4ms inference, 1.4ms postprocess per image at shape (1, 3, 640, 640)
# {'det': array([[         17,         231,         802,         768,     0.87055,           5],
#        [         49,         399,         245,         903,     0.86898,           0],
#        [        670,         380,         810,         876,      0.8536,           0],
#        [        221,         406,         345,         857,     0.81931,           0],
#        [          0,         255,          32,         325,     0.34607,          11],
#        [          0,         551,          67,         874,     0.30129,           0]], dtype=float32)}
# {'det': array([[        123,         197,        1111,         711,     0.80557,           0],
#        [        747,          41,        1142,         712,     0.79367,           0],
#        [        437,         437,         524,         714,     0.37022,          27]], dtype=float32)}

検出結果をこんな感じで受け取れるようです。

また、引数にsave=Trueを追記すると、runs/detect/predictに結果が保存されます。

showの扱い

続いてshowの扱いについて。こちらは

ultralytics/yolo/engine/predictor.py
def show(self, p):
        im0 = self.annotator.result()
        if platform.system() == 'Linux' and p not in self.windows:
            self.windows.append(p)
            cv2.namedWindow(str(p), cv2.WINDOW_NORMAL | cv2.WINDOW_KEEPRATIO)  # allow window resize (Linux)
            cv2.resizeWindow(str(p), im0.shape[1], im0.shape[0])
        cv2.imshow(str(p), im0)
        cv2.waitKey(1)  # 1 millisecond

が利用されているようです。ここが分かった理由としては、self.PredictorClassultralytics.yolo.v8.detect.predict.DetectionPredictorであり

ultralytics/yolo/v8/detect/predict.py
class DetectionPredictor(BasePredictor):

    def get_annotator(self, img):
        return Annotator(img, line_width=self.args.line_thickness, example=str(self.model.names))

となっており、BasePredictorを継承しているところから確認しました。
このshowは動画・Webカメラ・スクリーンショットを利用した際に利用できるようです。

スクリーンショットの場合は

from ultralytics import YOLO

model = YOLO("yolov8n.pt")
results = model("screen" , show=True) 
for i in enumerate(results):
    print(i)

Webカメラの場合はカメラ番号を入れれば実行可能です。

from ultralytics import YOLO

model = YOLO("yolov8n.pt")
results = model(0 , show=True) 
for i in enumerate(results):
    print(i)
    
# 0: 480x640 1 carrot, 1 cake, 1 book, 74.1ms
# runs\detect\predict5\0
# {'det': array([[          6,         334,         228,         374,     0.58858,          51],
#        [          0,          62,         304,         251,     0.39537,          55],
#        [        354,         111,         640,         274,     0.32949,          73]], dtype=float32)}
# 0: 480x640 1 carrot, 1 cake, 1 book, 70.5ms
# runs\detect\predict5\0
# {'det': array([[          6,         334,         231,         375,     0.53592,          51],
#        [          0,          62,         304,         251,     0.41555,          55],
#        [        354,         110,         640,         277,     0.36091,          73]], dtype=float32)}
# 0: 480x640 1 carrot, 1 cake, 1 book, 72.5ms

https://youtu.be/L2KMvuYG4oc

save_txtの扱い

続いてsave_txtの扱いについて見ていきましょう。こちらは実行時に予測結果を保存してくれる関数です。

from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model("ultralytics/assets",save_txt=True)
for i in results:
    print(i)

bus.txt
0 0.041358 0.659722 0.082716 0.299074
11 0.0197531 0.268519 0.0395062 0.0648148
0 0.349383 0.584722 0.153086 0.417593
0 0.91358 0.581481 0.17284 0.459259
0 0.181481 0.602778 0.241975 0.466667
5 0.505556 0.4625 0.969136 0.497222

save_confの扱い

続いてsave_confの扱いを見ていきましょう。こちらは信頼度を保存してくれる引数です。こちらを利用する際は上のsave_txt=Trueを同時に利用してください。上で生成された.txtにconfを追加するようです。

from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model("ultralytics/assets",save_txt=True,save_conf=True)
for i in results:
    print(i)
bus.txt
0 0.041358 0.659722 0.082716 0.299074 0.301294
11 0.0197531 0.268519 0.0395062 0.0648148 0.346069
0 0.349383 0.584722 0.153086 0.417593 0.819305
0 0.91358 0.581481 0.17284 0.459259 0.853604
0 0.181481 0.602778 0.241975 0.466667 0.86898
5 0.505556 0.4625 0.969136 0.497222 0.870545

save_cropの扱い

続いてsave_cropの扱いを見ていきましょう。こちらを実行すると検出された対象を切り出して保存してくれます。

from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model("ultralytics/assets",save_crop=True)
for i in results:
    print(i)

busの切り抜き

hide_labelsの扱い

続いてhide_labelsの扱いを見ていきましょう。こちらを実行すると、検出結果からラベルを消し去ります。

from ultralytics import YOLO

model = YOLO("yolov8n.pt")
results = model("ultralytics/assets",save=True, hide_labels=True) 
for i in results:
    print(i)

hide_confの扱い

続いてhide_confの扱いを見ていきましょう。こちらを実行すると、信頼度のみを消し去ります。

from ultralytics import YOLO

model = YOLO("yolov8n.pt")
results = model("ultralytics/assets",save=True, hide_conf=True) 
for i in results:
    print(i)

line_thicknessの扱い

続いてline_thicknessの扱いを見ていきましょう。こちらを変更すると、描画時のラインの太さが変化します。

from ultralytics import YOLO

model = YOLO("yolov8n.pt")
results = model("ultralytics/assets",save=True, line_thickness=1) 
for i in results:
    print(i)

そのほかの変数

そのほかに指定できる(効くかわからないが)は次の通りです。

{'task': 'detect', 'mode': 'train', 'model': 'yolov8n.yaml', 'data': 'coco.yaml', 'patience': 50, 'imgsz': 640, 'save': True, 'device': '', 'workers': 8, 'exist_ok': False, 'pretrained': False, 'optimizer': 'SGD', 'verbose': False, 'seed': 0, 'deterministic': True, 'single_cls': False, 'image_weights': False, 'rect': False, 'cos_lr': False, 'close_mosaic': 10, 'resume': False, 'overlap_mask': True, 'mask_ratio': 4, 'dropout': False, 'val': True, 'save_hybrid': False, 'conf': 0.25, 'iou': 0.7, 'max_det': 300, 'half': True, 'dnn': False, 'plots': True, 'source': 'ultralytics/assets/', 'show': False, 'save_txt': False, 'save_conf': False, 'save_crop': False, 'hide_labels': False, 'hide_conf': False, 'vid_stride': 1, 'line_thickness': 3, 'visualize': True, 'augment': False, 'agnostic_nms': False, 'retina_masks': False, 'format': 'torchscript', 'keras': False, 'optimize': False, 'int8': False, 'dynamic': False, 'simplify': False, 'opset': 17, 'workspace': 4, 'nms': False, 'lr0': 0.01, 'lrf': 0.01, 'momentum': 0.937, 'weight_decay': 0.001, 'warmup_epochs': 3.0, 'warmup_momentum': 0.8, 'warmup_bias_lr': 0.1, 'box': 7.5, 'cls': 0.5, 'dfl': 1.5, 'fl_gamma': 0.0, 'label_smoothing': 0.0, 'nbs': 64, 'hsv_h': 0.015, 'hsv_s': 0.7, 'hsv_v': 0.4, 'degrees': 0.0, 'translate': 0.1, 'scale': 0.5, 'shear': 0.0, 'perspective': 0.0, 'flipud': 0.0, 'fliplr': 0.5, 'mosaic': 1.0, 'mixup': 0.0, 'copy_paste': 0.0, 'cfg': None, 'hydra': {'output_subdir': None, 'run': {'dir': '.'}}, 'v5loader': True}

特に

imgsz': 640, 'conf': 0.25, 'iou': 0.7, 'max_det': 300, 'half': True,

は推論時に利用されているので変更すると精度が変わります。

例えば、conf=0.001にした場合は

from ultralytics import YOLO

model = YOLO("yolov8n.pt")
results = model("ultralytics/assets",save=True, conf=0.001) 
for i in results:
    print(i)

となります。(めちゃくちゃだ・・・)

もっと最小限でWebカメラリアルタイム推論がしたい

色々な関数を引っ張り出してきて、以下のようになりました。これでとりあえずコンペなどで利用できるようになりそうですね。

from ultralytics import YOLO
import torch
import cv2
from ultralytics.yolo.data.augment import LetterBox
from ultralytics.yolo.utils.plotting import Annotator, colors
from ultralytics.yolo.utils import ops
from copy import deepcopy
import numpy as np
import matplotlib.pyplot as plt

model = YOLO("yolov8n.pt")
cap = cv2.VideoCapture(0)


def preprocess(img, size=640):
        img = LetterBox(size, True)(image=img)
        img = img.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
        img = np.ascontiguousarray(img)  # contiguous
        img = torch.from_numpy(img)
        img = img.float()  # uint8 to fp16/32
        img /= 255  # 0 - 255 to 0.0 - 1.0
        return img.unsqueeze(0)

def postprocess(preds, img, orig_img):
    preds = ops.non_max_suppression(preds,
                                    0.25,
                                    0.8,
                                    agnostic=False,
                                    max_det=100)

    for i, pred in enumerate(preds):
        shape = orig_img.shape
        pred[:, :4] = ops.scale_boxes(img.shape[2:], pred[:, :4], shape).round()

    return preds

def drow_bbox(pred, names, annotator):
    for *xyxy, conf, cls in reversed(pred):
        c = int(cls)  # integer class
        label =  f'{names[c]} {conf:.2f}'
        annotator.box_label(xyxy, label, color=colors(c, True))


while True:
    ret, img = cap.read()
    origin = deepcopy(img)
    annotator = Annotator(origin,line_width=1,example=str(model.model.names))
    img = preprocess(img)
    preds = model.model(img, augment=False)
    preds = postprocess(preds,img,origin)
    drow_bbox(preds[0], model.model.names, annotator)
    cv2.imshow("test",origin)
    cv2.waitKey(1)
    

https://youtu.be/BzBzmJjK_3U

まとめ

今回は今はやりのYOLOv8の使い方について調査しました。YOLOv5から大きく進化を遂げており、学習・推論が簡単になっていそうな雰囲気を感じ取りました。

ただ、精度がどうかと言わるとまだコンペで使ってないから・・・。と答えるしか・・・。
とにもかくにも、最新モデルを触れたのでいい経験になりました!

次はYOLOv8の構造の調査とかしたいなぁ。

Discussion

手羽先手羽先

とても参考になりました!ありがとうございます。
質問なのですが、読み込む画像を、ファイルやデスクトップ全体、カメラではなく、mssライブラリを使ってデスクトップの一部分のみの画像を推論させることは可能でしょうか?
ご返信お待ちしております。

OPampOPamp

コメントありがとうございます。
mssライブラリを使っての推論ですが、これは如何でしょうか?

from mss import mss
from ultralytics import YOLO
output_filename = "tmp.png"
with mss() as mss_instance:
    mss_instance.shot(output=output_filename)
model = YOLO("yolov8n.pt")
results = model("tmp.png",save=True) 
for i in results:
    print(i)

手羽先手羽先

ありがとうございます。記載ミスしていました。正確には、mssライブラリを使ってリアルタイムでデスクトップ上の画面をキャプチャし、そのデータをyolov8に渡してリアルタイム推論できるようにしたいのです。
以前、自分はYOLOv5を使ってこのように書くことができました。

main.py
#スクリーンショットの範囲
monitor = {"top": 220, "left": 940, "width": 600, "height":600}
with mss() as sct:

    #以下無限ループ
    while True:
        
        #スタート時間計測
        start_time = time.perf_counter()

        #モニタ範囲でスクショ
        screenshot = np.array(sct.grab(monitor))
        #YOLOv5に入力&結果取得
        results = model(screenshot, size=600)

        #バウンディングボックス表示
        results.render()
        #opencvでウィンドウ作成
        cv2.imshow("frame", results.imgs[0])

        if(cv2.waitKey(1) == ord('q')):
            cv2.destroyAllWindows()
            break

yolov8で同じようなことをしてもうまくいかなかったので、質問させていただきました。素人質問で申し訳ないです💦

OPampOPamp

なるほどです!
YOLOv5のような手軽にレンダリングできる感じは今のところは見つけられていません・・・。

代替案として以下はどうでしょうか?

from ultralytics import YOLO
import torch
import cv2
from ultralytics.yolo.data.augment import LetterBox
from ultralytics.yolo.utils.plotting import Annotator, colors
from ultralytics.yolo.utils import ops
from copy import deepcopy
import numpy as np
import matplotlib.pyplot as plt

from mss import mss

model = YOLO("yolov8n.pt")


def preprocess(img, size=640):
        img = LetterBox(size, True)(image=img)
        img = img.transpose((2, 0, 1))[::-1]  # HWC to CHW, BGR to RGB
        img = np.ascontiguousarray(img)  # contiguous
        img = torch.from_numpy(img)
        img = img.float()  # uint8 to fp16/32
        img /= 255  # 0 - 255 to 0.0 - 1.0
        return img.unsqueeze(0)

def postprocess(preds, img, orig_img):
    preds = ops.non_max_suppression(preds,
                                    0.25,
                                    0.8,
                                    agnostic=False,
                                    max_det=100)

    for i, pred in enumerate(preds):
        shape = orig_img.shape
        pred[:, :4] = ops.scale_boxes(img.shape[2:], pred[:, :4], shape).round()

    return preds

def drow_bbox(pred, names, annotator):
    for *xyxy, conf, cls in reversed(pred):
        c = int(cls)  # integer class
        label =  f'{names[c]} {conf:.2f}'
        annotator.box_label(xyxy, label, color=colors(c, True))

monitor = {"top": 220, "left": 940, "width": 600, "height":600}
with mss() as sct:
    while True:
        screenshot = np.array(sct.grab(monitor))[:, :, :3]
        origin = deepcopy(screenshot)
        annotator = Annotator(origin,line_width=1,example=str(model.model.names))
        img = preprocess(screenshot)
        preds = model.model(img, augment=False)
        preds = postprocess(preds,img,origin)
        drow_bbox(preds[0], model.model.names, annotator)
        cv2.imshow("test",origin)
        cv2.waitKey(1)

リアルタイムに画面の一部を推論できると思います!

手羽先手羽先

ありがとうございます!感動しました!自分では到底作ることが不可能でした😓 まだコードの意味がわからないので、解読してみます。余談ですが、どのように勉強すればこのようなコードが書けるか、おすすめの勉強法などあったら初心者の自分にアドバイスいただけると嬉しいです🙏

OPampOPamp

勉強法・・・というほどのことでもないですが、とにかく作りたいと思ったものを頑張って実装するのが近道かと思います。今回の記事も最後には「もっと最小限の機能でリアルタイム推論がしたい」というところを実現するために、ライブラリのあらゆるコードを読んで必要そうな関数を引っ張り出してきていたりします。
こういう、泥臭ーいことをしている時が一番身になったりするんじゃないかなと考えます。

あんまり具体的な方法でなくて申し訳ありません・・・。

手羽先手羽先

とにかく作りたいと思ったものを頑張って実装するのが近道
ライブラリのあらゆるコードを読んで必要そうな関数を引っ張り出してきていたり

こちら、とても参考になりました。ありがとうございます!今後も記事を楽しみにしております!

TakumiTakumi

初めまして!とても参考になりました!質問なのですが、上記のスクリーンショットのリアルタイム推論の例はデフォルトでGPUが選択されているのでしょうか?よろしくお願いします。

OPampOPamp

たぶんですが、CPUで動いていると思います。ので、.to("cuda")でモデルとデータをGPUメモリに乗せてもらうのが確実かと思います。

TakumiTakumi

返信ありがとうございます!具体的にどの行を変更すればよいのでしょうか?素人で申し訳ありません。よろしくお願いします。

foncefonce

model = YOLO("yolov8n.pt")
のあとに
model.to("cuda")を追加して、preprocessのimg = torch.from_numpy(img)をimg = torch.from_numpy(img).to("cuda")に変えたら出来ました。

shimizu.phdshimizu.phd

はじめまして。この記事を見て勉強させていただいています。
どうしてもわからないことがあるので質問させてください。
この記事のコードのように
from ultralytics import YOLO
model = YOLO("yolov8n.pt")
results = model(0 , show=True)
for i in enumerate(results):
print(i)
として解析を一旦始めてしまうとどうやっても止めることができなくなります。検索するとqをおすなどが出てくるのですが、止めることができません。止め方をご存知でしたら教えていただけると助かります。