์ฝ˜ํ…์ธ ๋กœ ๊ฑด๋„ˆ๋›ฐ๊ธฐ

YOLO11 ๋ชจ๋ธ ๋ฐ ๋ฐฐํฌ๋ฅผ ์œ„ํ•œ MNN ๋‚ด๋ณด๋‚ด๊ธฐ

MNN

MNN ์•„ํ‚คํ…์ฒ˜

MNN์€ ๋งค์šฐ ํšจ์œจ์ ์ด๊ณ  ๊ฐ€๋ฒผ์šด ๋”ฅ ๋Ÿฌ๋‹ ํ”„๋ ˆ์ž„์›Œํฌ์ž…๋‹ˆ๋‹ค. ๋”ฅ๋Ÿฌ๋‹ ๋ชจ๋ธ์˜ ์ถ”๋ก ๊ณผ ํ•™์Šต์„ ์ง€์›ํ•˜๋ฉฐ, ์—…๊ณ„ ์ตœ๊ณ  ์ˆ˜์ค€์˜ ์˜จ๋””๋ฐ”์ด์Šค ์ถ”๋ก  ๋ฐ ํ•™์Šต ์„ฑ๋Šฅ์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ํ˜„์žฌ MNN์€ ํƒ€์˜ค๋ฐ”์˜ค, ํ‹ฐ๋ชฐ, ์œ ์ฟ , ๋”ฉํ†ก, ์…ด์œ„ ๋“ฑ ์•Œ๋ฆฌ๋ฐ”๋ฐ”์˜ 30๊ฐœ ์ด์ƒ์˜ ์•ฑ์— ํ†ตํ•ฉ๋˜์–ด ๋ผ์ด๋ธŒ ๋ฐฉ์†ก, ์งง์€ ๋™์˜์ƒ ์บก์ฒ˜, ๊ฒ€์ƒ‰ ์ถ”์ฒœ, ์ด๋ฏธ์ง€๋กœ ์ƒํ’ˆ ๊ฒ€์ƒ‰, ๋Œ€ํ™”ํ˜• ๋งˆ์ผ€ํŒ…, ์ฃผ์‹ ๋ถ„๋ฐฐ, ๋ณด์•ˆ ์œ„ํ—˜ ์ œ์–ด ๋“ฑ 70๊ฐœ ์ด์ƒ์˜ ์‚ฌ์šฉ ์‹œ๋‚˜๋ฆฌ์˜ค์— ์ ์šฉ๋˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ๋˜ํ•œ MNN์€ IoT์™€ ๊ฐ™์€ ์ž„๋ฒ ๋””๋“œ ๋””๋ฐ”์ด์Šค์—์„œ๋„ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

MNN์œผ๋กœ ๋‚ด๋ณด๋‚ด๊ธฐ: YOLO11 ๋ชจ๋ธ ๋ณ€ํ™˜ํ•˜๊ธฐ

YOLO11 ๋ชจ๋ธ์„ MNN ํ˜•์‹์œผ๋กœ ๋ณ€ํ™˜ํ•˜์—ฌ ๋ชจ๋ธ ํ˜ธํ™˜์„ฑ ๋ฐ ๋ฐฐํฌ ์œ ์—ฐ์„ฑ์„ ํ™•์žฅํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์„ค์น˜

ํ•„์š”ํ•œ ํŒจํ‚ค์ง€๋ฅผ ์„ค์น˜ํ•˜๋ ค๋ฉด ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค:

์„ค์น˜

# Install the required package for YOLO11 and MNN
pip install ultralytics
pip install MNN

์‚ฌ์šฉ๋ฒ•

์‚ฌ์šฉ ์ง€์นจ์„ ์‚ดํŽด๋ณด๊ธฐ ์ „์— ๋ชจ๋“  Ultralytics YOLO11 ๋ชจ๋ธ์—์„œ ๋‚ด๋ณด๋‚ด๊ธฐ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์ง€๋งŒ, ์„ ํƒํ•œ ๋ชจ๋ธ์ด ๋‚ด๋ณด๋‚ด๊ธฐ ๊ธฐ๋Šฅ์„ ์ง€์›ํ•˜๋Š”์ง€ ์—ฌ๊ธฐ์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ์ ์— ์œ ์˜ํ•˜์„ธ์š”.

์‚ฌ์šฉ๋ฒ•

from ultralytics import YOLO

# Load the YOLO11 model
model = YOLO("yolo11n.pt")

# Export the model to MNN format
model.export(format="mnn")  # creates 'yolo11n.mnn'

# Load the exported MNN model
mnn_model = YOLO("yolo11n.mnn")

# Run inference
results = mnn_model("https://ultralytics.com/images/bus.jpg")
# Export a YOLO11n PyTorch model to MNN format
yolo export model=yolo11n.pt format=mnn  # creates 'yolo11n.mnn'

# Run inference with the exported model
yolo predict model='yolo11n.mnn' source='https://ultralytics.com/images/bus.jpg'

์ง€์›๋˜๋Š” ๋‚ด๋ณด๋‚ด๊ธฐ ์˜ต์…˜์— ๋Œ€ํ•œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋ฐฐํฌ ์˜ต์…˜์— ๋Œ€ํ•œUltralytics ๋ฌธ์„œ ํŽ˜์ด์ง€๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

MNN ์ „์šฉ ์ถ”๋ก 

YOLO11 ์ถ”๋ก  ๋ฐ ์ „์ฒ˜๋ฆฌ๋ฅผ ์œ„ํ•ด MNN์—๋งŒ ์˜์กดํ•˜๋Š” ํ•จ์ˆ˜๊ฐ€ ๊ตฌํ˜„๋˜์–ด ์žˆ์œผ๋ฉฐ, Python ๋ฐ C++ ๋ฒ„์ „์„ ๋ชจ๋‘ ์ œ๊ณตํ•˜์—ฌ ์–ด๋–ค ์‹œ๋‚˜๋ฆฌ์˜ค์—์„œ๋„ ์‰ฝ๊ฒŒ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MNN

import argparse

import MNN
import MNN.cv as cv2
import MNN.numpy as np


def inference(model, img, precision, backend, thread):
    config = {}
    config["precision"] = precision
    config["backend"] = backend
    config["numThread"] = thread
    rt = MNN.nn.create_runtime_manager((config,))
    # net = MNN.nn.load_module_from_file(model, ['images'], ['output0'], runtime_manager=rt)
    net = MNN.nn.load_module_from_file(model, [], [], runtime_manager=rt)
    original_image = cv2.imread(img)
    ih, iw, _ = original_image.shape
    length = max((ih, iw))
    scale = length / 640
    image = np.pad(original_image, [[0, length - ih], [0, length - iw], [0, 0]], "constant")
    image = cv2.resize(
        image, (640, 640), 0.0, 0.0, cv2.INTER_LINEAR, -1, [0.0, 0.0, 0.0], [1.0 / 255.0, 1.0 / 255.0, 1.0 / 255.0]
    )
    input_var = np.expand_dims(image, 0)
    input_var = MNN.expr.convert(input_var, MNN.expr.NC4HW4)
    output_var = net.forward(input_var)
    output_var = MNN.expr.convert(output_var, MNN.expr.NCHW)
    output_var = output_var.squeeze()
    # output_var shape: [84, 8400]; 84 means: [cx, cy, w, h, prob * 80]
    cx = output_var[0]
    cy = output_var[1]
    w = output_var[2]
    h = output_var[3]
    probs = output_var[4:]
    # [cx, cy, w, h] -> [y0, x0, y1, x1]
    x0 = cx - w * 0.5
    y0 = cy - h * 0.5
    x1 = cx + w * 0.5
    y1 = cy + h * 0.5
    boxes = np.stack([x0, y0, x1, y1], axis=1)
    # get max prob and idx
    scores = np.max(probs, 0)
    class_ids = np.argmax(probs, 0)
    result_ids = MNN.expr.nms(boxes, scores, 100, 0.45, 0.25)
    print(result_ids.shape)
    # nms result box, score, ids
    result_boxes = boxes[result_ids]
    result_scores = scores[result_ids]
    result_class_ids = class_ids[result_ids]
    for i in range(len(result_boxes)):
        x0, y0, x1, y1 = result_boxes[i].read_as_tuple()
        y0 = int(y0 * scale)
        y1 = int(y1 * scale)
        x0 = int(x0 * scale)
        x1 = int(x1 * scale)
        print(result_class_ids[i])
        cv2.rectangle(original_image, (x0, y0), (x1, y1), (0, 0, 255), 2)
    cv2.imwrite("res.jpg", original_image)


if __name__ == "__main__":
    parser = argparse.ArgumentParser()
    parser.add_argument("--model", type=str, required=True, help="the yolo11 model path")
    parser.add_argument("--img", type=str, required=True, help="the input image path")
    parser.add_argument("--precision", type=str, default="normal", help="inference precision: normal, low, high, lowBF")
    parser.add_argument(
        "--backend",
        type=str,
        default="CPU",
        help="inference backend: CPU, OPENCL, OPENGL, NN, VULKAN, METAL, TRT, CUDA, HIAI",
    )
    parser.add_argument("--thread", type=int, default=4, help="inference using thread: int")
    args = parser.parse_args()
    inference(args.model, args.img, args.precision, args.backend, args.thread)
#include <stdio.h>
#include <MNN/ImageProcess.hpp>
#include <MNN/expr/Module.hpp>
#include <MNN/expr/Executor.hpp>
#include <MNN/expr/ExprCreator.hpp>
#include <MNN/expr/Executor.hpp>

#include <cv/cv.hpp>

using namespace MNN;
using namespace MNN::Express;
using namespace MNN::CV;

int main(int argc, const char* argv[]) {
    if (argc < 3) {
        MNN_PRINT("Usage: ./yolo11_demo.out model.mnn input.jpg [forwardType] [precision] [thread]\n");
        return 0;
    }
    int thread = 4;
    int precision = 0;
    int forwardType = MNN_FORWARD_CPU;
    if (argc >= 4) {
        forwardType = atoi(argv[3]);
    }
    if (argc >= 5) {
        precision = atoi(argv[4]);
    }
    if (argc >= 6) {
        thread = atoi(argv[5]);
    }
    MNN::ScheduleConfig sConfig;
    sConfig.type = static_cast<MNNForwardType>(forwardType);
    sConfig.numThread = thread;
    BackendConfig bConfig;
    bConfig.precision = static_cast<BackendConfig::PrecisionMode>(precision);
    sConfig.backendConfig = &bConfig;
    std::shared_ptr<Executor::RuntimeManager> rtmgr = std::shared_ptr<Executor::RuntimeManager>(Executor::RuntimeManager::createRuntimeManager(sConfig));
    if(rtmgr == nullptr) {
        MNN_ERROR("Empty RuntimeManger\n");
        return 0;
    }
    rtmgr->setCache(".cachefile");

    std::shared_ptr<Module> net(Module::load(std::vector<std::string>{}, std::vector<std::string>{}, argv[1], rtmgr));
    auto original_image = imread(argv[2]);
    auto dims = original_image->getInfo()->dim;
    int ih = dims[0];
    int iw = dims[1];
    int len = ih > iw ? ih : iw;
    float scale = len / 640.0;
    std::vector<int> padvals { 0, len - ih, 0, len - iw, 0, 0 };
    auto pads = _Const(static_cast<void*>(padvals.data()), {3, 2}, NCHW, halide_type_of<int>());
    auto image = _Pad(original_image, pads, CONSTANT);
    image = resize(image, Size(640, 640), 0, 0, INTER_LINEAR, -1, {0., 0., 0.}, {1./255., 1./255., 1./255.});
    auto input = _Unsqueeze(image, {0});
    input = _Convert(input, NC4HW4);
    auto outputs = net->onForward({input});
    auto output = _Convert(outputs[0], NCHW);
    output = _Squeeze(output);
    // output shape: [84, 8400]; 84 means: [cx, cy, w, h, prob * 80]
    auto cx = _Gather(output, _Scalar<int>(0));
    auto cy = _Gather(output, _Scalar<int>(1));
    auto w = _Gather(output, _Scalar<int>(2));
    auto h = _Gather(output, _Scalar<int>(3));
    std::vector<int> startvals { 4, 0 };
    auto start = _Const(static_cast<void*>(startvals.data()), {2}, NCHW, halide_type_of<int>());
    std::vector<int> sizevals { -1, -1 };
    auto size = _Const(static_cast<void*>(sizevals.data()), {2}, NCHW, halide_type_of<int>());
    auto probs = _Slice(output, start, size);
    // [cx, cy, w, h] -> [y0, x0, y1, x1]
    auto x0 = cx - w * _Const(0.5);
    auto y0 = cy - h * _Const(0.5);
    auto x1 = cx + w * _Const(0.5);
    auto y1 = cy + h * _Const(0.5);
    auto boxes = _Stack({x0, y0, x1, y1}, 1);
    auto scores = _ReduceMax(probs, {0});
    auto ids = _ArgMax(probs, 0);
    auto result_ids = _Nms(boxes, scores, 100, 0.45, 0.25);
    auto result_ptr = result_ids->readMap<int>();
    auto box_ptr = boxes->readMap<float>();
    auto ids_ptr = ids->readMap<int>();
    auto score_ptr = scores->readMap<float>();
    for (int i = 0; i < 100; i++) {
        auto idx = result_ptr[i];
        if (idx < 0) break;
        auto x0 = box_ptr[idx * 4 + 0] * scale;
        auto y0 = box_ptr[idx * 4 + 1] * scale;
        auto x1 = box_ptr[idx * 4 + 2] * scale;
        auto y1 = box_ptr[idx * 4 + 3] * scale;
        auto class_idx = ids_ptr[idx];
        auto score = score_ptr[idx];
        rectangle(original_image, {x0, y0}, {x1, y1}, {0, 0, 255}, 2);
    }
    if (imwrite("res.jpg", original_image)) {
        MNN_PRINT("result image write to `res.jpg`.\n");
    }
    rtmgr->updateCache();
    return 0;
}

์š”์•ฝ

์ด ๊ฐ€์ด๋“œ์—์„œ๋Š” Ultralytics YOLO11 ๋ชจ๋ธ์„ MNN์œผ๋กœ ๋‚ด๋ณด๋‚ด๊ณ  ์ถ”๋ก ์— MNN์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์„ ์†Œ๊ฐœํ•ฉ๋‹ˆ๋‹ค.

์ž์„ธํ•œ ์‚ฌ์šฉ๋ฒ•์€ MNN ๋ฌธ์„œ๋ฅผ ์ฐธ์กฐํ•˜์„ธ์š”.

์ž์ฃผ ๋ฌป๋Š” ์งˆ๋ฌธ

Ultralytics YOLO11 ๋ชจ๋ธ์„ MNN ํ˜•์‹์œผ๋กœ ๋‚ด๋ณด๋‚ด๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•˜๋‚˜์š”?

Ultralytics YOLO11 ๋ชจ๋ธ์„ MNN ํ˜•์‹์œผ๋กœ ๋‚ด๋ณด๋‚ด๋ ค๋ฉด ๋‹ค์Œ ๋‹จ๊ณ„๋ฅผ ๋”ฐ๋ฅด์„ธ์š”:

๋‚ด๋ณด๋‚ด๊ธฐ

from ultralytics import YOLO

# Load the YOLO11 model
model = YOLO("yolo11n.pt")

# Export to MNN format
model.export(format="mnn")  # creates 'yolo11n.mnn' with fp32 weight
model.export(format="mnn", half=True)  # creates 'yolo11n.mnn' with fp16 weight
model.export(format="mnn", int8=True)  # creates 'yolo11n.mnn' with int8 weight
yolo export model=yolo11n.pt format=mnn            # creates 'yolo11n.mnn' with fp32 weight
yolo export model=yolo11n.pt format=mnn half=True  # creates 'yolo11n.mnn' with fp16 weight
yolo export model=yolo11n.pt format=mnn int8=True  # creates 'yolo11n.mnn' with int8 weight

์ž์„ธํ•œ ๋‚ด๋ณด๋‚ด๊ธฐ ์˜ต์…˜์€ ๋ฌธ์„œ์—์„œ ๋‚ด๋ณด๋‚ด๊ธฐ ํŽ˜์ด์ง€๋ฅผ ํ™•์ธํ•˜์„ธ์š”.

๋‚ด๋ณด๋‚ธ YOLO11 MNN ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ˆ์ธกํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?

๋‚ด๋ณด๋‚ธ YOLO11 MNN ๋ชจ๋ธ์„ ์‚ฌ์šฉํ•˜์—ฌ ์˜ˆ์ธกํ•˜๋ ค๋ฉด ๋‹ค์Œ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. predict ํ•จ์ˆ˜์—์„œ YOLO ํด๋ž˜์Šค์˜

์˜ˆ์ธก

from ultralytics import YOLO

# Load the YOLO11 MNN model
model = YOLO("yolo11n.mnn")

# Export to MNN format
results = mnn_model("https://ultralytics.com/images/bus.jpg")  # predict with `fp32`
results = mnn_model("https://ultralytics.com/images/bus.jpg", half=True)  # predict with `fp16` if device support

for result in results:
    result.show()  # display to screen
    result.save(filename="result.jpg")  # save to disk
yolo predict model='yolo11n.mnn' source='https://ultralytics.com/images/bus.jpg'              # predict with `fp32`
yolo predict model='yolo11n.mnn' source='https://ultralytics.com/images/bus.jpg' --half=True  # predict with `fp16` if device support

MNN์€ ์–ด๋–ค ํ”Œ๋žซํผ์„ ์ง€์›ํ•˜๋‚˜์š”?

MNN์€ ๋‹ค๋ชฉ์ ์ด๋ฉฐ ๋‹ค์–‘ํ•œ ํ”Œ๋žซํผ์„ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค:

  • Mobile: Android, iOS, ํ•˜๋ชจ๋‹ˆ.
  • ์ž„๋ฒ ๋””๋“œ ์‹œ์Šคํ…œ ๋ฐ IoT ์žฅ์น˜: ๋ผ์ฆˆ๋ฒ ๋ฆฌ ํŒŒ์ด ๋ฐ NVIDIA Jetson๊ณผ ๊ฐ™์€ ์žฅ์น˜.
  • ๋ฐ์Šคํฌํ†ฑ ๋ฐ ์„œ๋ฒ„: Linux, Windows ๋ฐ macOS.

๋ชจ๋ฐ”์ผ ๋””๋ฐ”์ด์Šค์— Ultralytics YOLO11 MNN ๋ชจ๋ธ์„ ๋ฐฐํฌํ•˜๋ ค๋ฉด ์–ด๋–ป๊ฒŒ ํ•ด์•ผ ํ•˜๋‚˜์š”?

๋ชจ๋ฐ”์ผ ์žฅ์น˜์— YOLO11 ๋ชจ๋ธ์„ ๋ฐฐํฌํ•˜๋ ค๋ฉด:

  1. ๋นŒ๋“œ Android: MNN์„ ํŒ”๋กœ์šฐํ•˜์„ธ์š” Android.
  2. ๋นŒ๋“œ iOS: MNN์„ ํŒ”๋กœ์šฐํ•˜์„ธ์š” iOS.
  3. ํ•˜๋ชจ๋‹ˆ๋ฅผ ์œ„ํ•œ ๋นŒ๋“œ: MNN ํ•˜๋ชจ๋‹ˆ๋ฅผ ๋”ฐ๋ฅด์„ธ์š”.
1๊ฐœ์›” ์ „ ์ƒ์„ฑ๋จ โœ๏ธ 1๊ฐœ์›” ์ „ ์—…๋ฐ์ดํŠธ๋จ

๋Œ“๊ธ€