OiO.lk Community platform!

Oio.lk is an excellent forum for developers, providing a wide range of resources, discussions, and support for those in the developer community. Join oio.lk today to connect with like-minded professionals, share insights, and stay updated on the latest trends and technologies in the development field.
  You need to log in or register to access the solved answers to this problem.
  • You have reached the maximum number of guest views allowed
  • Please register below to remove this limitation

Problem when using YOLOv8 with NCNN on Raspberry Pi 4

  • Thread starter Thread starter Lorenzo Catarsi
  • Start date Start date
L

Lorenzo Catarsi

Guest


I'm using this c++ program on raspberry pi 4: https://github.com/Qengineering/YoloV8-ncnn-Raspberry-Pi-4 , when using the standard yolov8n.bin and yolov8.param weights that you can find in the repo, it works fine and swiftly, but when I try to change the code and use my personally trained model, it doesn't work and stops with a segmentation fault.

I've tried multiple methods to get the ncnn model:

  • The python yolov8 method:

Code:
from ultralytics import YOLO
import ncnn

model=YOLO('yolov8n.pt')

model.train(data="data.yaml", epochs=100, batch=8)  
path = model.export(format="ncnn")
  • Converting from onnx to ncnn

Code:
from ultralytics import YOLO

model=YOLO('yolov8n.pt')

model.train(data="data.yaml", epochs=100, batch=8)  
path = model.export(format="onxx")

After running this code, I've followed the steps in this guide: https://github.com/Tencent/ncnn/wiki/use-ncnn-with-pytorch-or-onnx

These two methods create two different model.param file, but none of them work. I've tried using these files to predict with YOLOv8 in Python, and the model works in Python, but stop working with the C++ code.

I've tried to locate the point where the segmentation fault happens, but it's too deep in the code for me to understand it.
If someone can help i'll be grateful because this is driving me crazy

The C++ code is running with OpenCV 4.9.0 compiled.

This is the modified c++ source code:

yolov8.cpp

Code:
#include "yoloV8.h"

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

const char* class_names[] = {
    "ambulance"
};

static float fast_exp(float x)
{
    union {
        uint32_t i;
        float f;
    } v{};
    v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);
    return v.f;
}

static float sigmoid(float x)
{
    return 1.0f / (1.0f + fast_exp(-x));
}
static float intersection_area(const Object& a, const Object& b)
{
    cv::Rect_<float> inter = a.rect & b.rect;
    return inter.area();
}

static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{
    int i = left;
    int j = right;
    float p = faceobjects[(left + right) / 2].prob;

    while (i <= j)
    {
        while (faceobjects[i].prob > p)
            i++;

        while (faceobjects[j].prob < p)
            j--;

        if (i <= j)
        {
            // swap
            std::swap(faceobjects[i], faceobjects[j]);

            i++;
            j--;
        }
    }

    //     #pragma omp parallel sections
    {
        //         #pragma omp section
        {
            if (left < j) qsort_descent_inplace(faceobjects, left, j);
        }
        //         #pragma omp section
        {
            if (i < right) qsort_descent_inplace(faceobjects, i, right);
        }
    }
}

static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{
    if (faceobjects.empty())
        return;

    qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}

static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{
    picked.clear();

    const int n = faceobjects.size();

    std::vector<float> areas(n);
    for (int i = 0; i < n; i++)
    {
        areas[i] = faceobjects[i].rect.width * faceobjects[i].rect.height;
    }

    for (int i = 0; i < n; i++)
    {
        const Object& a = faceobjects[i];

        int keep = 1;
        for (int j = 0; j < (int)picked.size(); j++)
        {
            const Object& b = faceobjects[picked[j]];

            // intersection over union
            float inter_area = intersection_area(a, b);
            float union_area = areas[i] + areas[picked[j]] - inter_area;
            // float IoU = inter_area / union_area
            if (inter_area / union_area > nms_threshold)
                keep = 0;
        }

        if (keep)
            picked.push_back(i);
    }
}
static void generate_grids_and_stride(const int target_w, const int target_h, std::vector<int>& strides, std::vector<GridAndStride>& grid_strides)
{
    for (int i = 0; i < (int)strides.size(); i++)
    {
        int stride = strides[i];
        int num_grid_w = target_w / stride;
        int num_grid_h = target_h / stride;
        for (int g1 = 0; g1 < num_grid_h; g1++)
        {
            for (int g0 = 0; g0 < num_grid_w; g0++)
            {
                GridAndStride gs;
                gs.grid0 = g0;
                gs.grid1 = g1;
                gs.stride = stride;
                grid_strides.push_back(gs);
            }
        }
    }
}
static void generate_proposals(std::vector<GridAndStride> grid_strides, const ncnn::Mat& pred, float prob_threshold, std::vector<Object>& objects)
{
    const int num_points = grid_strides.size();
    const int num_class = 1;
    const int reg_max_1 = 16;

    for (int i = 0; i < num_points; i++)
    {
        const float* scores = pred.row(i) + 4 * reg_max_1;

        // find label with max score
        int label = -1;
        float score = -FLT_MAX;
        for (int k = 0; k < num_class; k++)
        {
            float confidence = scores[k];
            if (confidence > score)
            {
                label = k;
                score = confidence;
            }
        }
        float box_prob = sigmoid(score);
        if (box_prob >= prob_threshold)
        {
            ncnn::Mat bbox_pred(reg_max_1, 4, (void*)pred.row(i));
            {
                ncnn::Layer* softmax = ncnn::create_layer("Softmax");

                ncnn::ParamDict pd;
                pd.set(0, 1); // axis
                pd.set(1, 1);
                softmax->load_param(pd);

                ncnn::Option opt;
                opt.num_threads = 1;
                opt.use_packing_layout = false;

                softmax->create_pipeline(opt);

                softmax->forward_inplace(bbox_pred, opt);

                softmax->destroy_pipeline(opt);

                delete softmax;
            }

            float pred_ltrb[4];
            for (int k = 0; k < 4; k++)
            {
                float dis = 0.f;
                const float* dis_after_sm = bbox_pred.row(k);
                for (int l = 0; l < reg_max_1; l++)
                {
                    dis += l * dis_after_sm[l];
                }

                pred_ltrb[k] = dis * grid_strides[i].stride;
            }

            float pb_cx = (grid_strides[i].grid0 + 0.5f) * grid_strides[i].stride;
            float pb_cy = (grid_strides[i].grid1 + 0.5f) * grid_strides[i].stride;

            float x0 = pb_cx - pred_ltrb[0];
            float y0 = pb_cy - pred_ltrb[1];
            float x1 = pb_cx + pred_ltrb[2];
            float y1 = pb_cy + pred_ltrb[3];

            Object obj;
            obj.rect.x = x0;
            obj.rect.y = y0;
            obj.rect.width = x1 - x0;
            obj.rect.height = y1 - y0;
            obj.label = label;
            obj.prob = box_prob;

            objects.push_back(obj);
        }
    }
}

YoloV8::YoloV8()
{
}


int YoloV8::load(int _target_size)
{
    yolo.clear();

    yolo.opt = ncnn::Option();

    yolo.opt.num_threads = 4;

    yolo.load_param("./best.param");
    yolo.load_model("./best.bin");

    target_size = _target_size;
    mean_vals[0] = 103.53f;
    mean_vals[1] = 116.28f;
    mean_vals[2] = 123.675f;
    norm_vals[0] = 1.0 / 255.0f;
    norm_vals[1] = 1.0 / 255.0f;
    norm_vals[2] = 1.0 / 255.0f;

    return 0;
}

int YoloV8::detect(const cv::Mat& rgb, std::vector<Object>& objects, float prob_threshold, float nms_threshold)
{
    int width = rgb.cols;
    int height = rgb.rows;

    // pad to multiple of 32
    int w = width;
    int h = height;
    float scale = 1.f;
    if (w > h)
    {
        scale = (float)target_size / w;
        w = target_size;
        h = h * scale;
    }
    else
    {
        scale = (float)target_size / h;
        h = target_size;
        w = w * scale;
    }

    ncnn::Mat in = ncnn::Mat::from_pixels_resize(rgb.data, ncnn::Mat::PIXEL_RGB2BGR, width, height, w, h);

    // pad to target_size rectangle
    int wpad = (w + 31) / 32 * 32 - w;
    int hpad = (h + 31) / 32 * 32 - h;
    ncnn::Mat in_pad;
    ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 0.f);

    in_pad.substract_mean_normalize(0, norm_vals);

    ncnn::Extractor ex = yolo.create_extractor();

    ex.input("images", in_pad);

    std::vector<Object> proposals;

    ncnn::Mat out;
    ex.extract("output0", out);

    std::vector<int> strides = {8, 16, 32}; // might have stride=64
    std::vector<GridAndStride> grid_strides;
    generate_grids_and_stride(in_pad.w, in_pad.h, strides, grid_strides);
    generate_proposals(grid_strides, out, prob_threshold, proposals);

    // sort all proposals by score from highest to lowest
    qsort_descent_inplace(proposals);

    // apply nms with nms_threshold
    std::vector<int> picked;
    nms_sorted_bboxes(proposals, picked, nms_threshold);

    int count = picked.size();

    objects.resize(count);
    for (int i = 0; i < count; i++)
    {
        objects[i] = proposals[picked[i]];

        // adjust offset to original unpadded
        float x0 = (objects[i].rect.x - (wpad / 2)) / scale;
        float y0 = (objects[i].rect.y - (hpad / 2)) / scale;
        float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;
        float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;

        // clip
        x0 = std::max(std::min(x0, (float)(width - 1)), 0.f);
        y0 = std::max(std::min(y0, (float)(height - 1)), 0.f);
        x1 = std::max(std::min(x1, (float)(width - 1)), 0.f);
        y1 = std::max(std::min(y1, (float)(height - 1)), 0.f);

        objects[i].rect.x = x0;
        objects[i].rect.y = y0;
        objects[i].rect.width = x1 - x0;
        objects[i].rect.height = y1 - y0;
    }

    // sort objects by area
    struct
    {
        bool operator()(const Object& a, const Object& b) const
        {
            return a.rect.area() > b.rect.area();
        }
    } objects_area_greater;
    std::sort(objects.begin(), objects.end(), objects_area_greater);

    return 0;
}

int YoloV8::draw(cv::Mat& rgb, const std::vector<Object>& objects)
{
    for (size_t i = 0; i < objects.size(); i++)
    {
        const Object& obj = objects[i];

//         fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,
//                 obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);

        cv::rectangle(rgb, obj.rect, cv::Scalar(255, 0, 0));

        char text[256];
        sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);

        int baseLine = 0;
        cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);

        int x = obj.rect.x;
        int y = obj.rect.y - label_size.height - baseLine;
        if (y < 0)
            y = 0;
        if (x + label_size.width > rgb.cols)
            x = rgb.cols - label_size.width;

        cv::rectangle(rgb, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
                      cv::Scalar(255, 255, 255), -1);

        cv::putText(rgb, text, cv::Point(x, y + label_size.height),
                    cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
    }

    return 0;
}

yolov8main.cpp

Code:
#include "yoloV8.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <stdio.h>
#include <vector>

YoloV8 yolov8;
int target_size = 640; //416; //320;  must be divisible by 32.

int main(int argc, char** argv)
{
    const char* imagepath = argv[1];

    if (argc != 2)
    {
        fprintf(stderr, "Usage: %s [imagepath]\n", argv[0]);
        return -1;
    }

    cv::Mat m = cv::imread(imagepath, 1);
    if (m.empty())
    {
        fprintf(stderr, "cv::imread %s failed\n", imagepath);
        return -1;
    }

    yolov8.load(target_size);       //load model (once) see yoloyV8.cpp line 246

    std::vector<Object> objects;
    yolov8.detect(m, objects);      //recognize the objects
    yolov8.draw(m, objects);        //show the outcome

    cv::imshow("RPi4 - 1.95 GHz - 2 GB ram",m);
//    cv::imwrite("out.jpg",m);
    cv::waitKey(0);

    return 0;
}

yolov8.h

Code:
#ifndef YOLOV8_H
#define YOLOV8_H

#include <opencv2/core/core.hpp>
#include <net.h>

struct Object
{
    cv::Rect_<float> rect;
    int label;
    float prob;
};

struct GridAndStride
{
    int grid0;
    int grid1;
    int stride;
};

class YoloV8
{
public:
    YoloV8();
    int load(int target_size);
    int detect(const cv::Mat& rgb, std::vector<Object>& objects, float prob_threshold = 0.4f, float nms_threshold = 0.5f);
    int draw(cv::Mat& rgb, const std::vector<Object>& objects);
private:
    ncnn::Net yolo;
    int target_size;
    float mean_vals[3];
    float norm_vals[3];
};

#endif // YOLOV8_H
<hr />
<p>I'm using this c++ program on raspberry pi 4: <a href="https://github.com/Qengineering/YoloV8-ncnn-Raspberry-Pi-4" rel="nofollow noreferrer">https://github.com/Qengineering/YoloV8-ncnn-Raspberry-Pi-4</a> , when using the standard yolov8n.bin and yolov8.param weights that you can find in the repo, it works fine and swiftly, but when I try to change the code and use my personally trained model, it doesn't work and stops with a segmentation fault.</p>
<p>I've tried multiple methods to get the ncnn model:</p>
<ul>
<li>The python yolov8 method:</li>
</ul>
<pre class="lang-py prettyprint-override"><code>from ultralytics import YOLO
import ncnn

model=YOLO('yolov8n.pt')

model.train(data="data.yaml", epochs=100, batch=8)
path = model.export(format="ncnn")
</code></pre>
<ul>
<li>Converting from onnx to ncnn</li>
</ul>
<pre class="lang-py prettyprint-override"><code>from ultralytics import YOLO

model=YOLO('yolov8n.pt')

model.train(data="data.yaml", epochs=100, batch=8)
path = model.export(format="onxx")
</code></pre>
<p>After running this code, I've followed the steps in this guide: <a href="https://github.com/Tencent/ncnn/wiki/use-ncnn-with-pytorch-or-onnx" rel="nofollow noreferrer">https://github.com/Tencent/ncnn/wiki/use-ncnn-with-pytorch-or-onnx</a></p>
<p>These two methods create two different model.param file, but none of them work. I've tried using these files to predict with YOLOv8 in Python, and the model works in Python, but stop working with the C++ code.</p>
<p>I've tried to locate the point where the segmentation fault happens, but it's too deep in the code for me to understand it.<br />
If someone can help i'll be grateful because this is driving me crazy</p>
<p>The C++ code is running with OpenCV 4.9.0 compiled.</p>
<p>This is the modified c++ source code:</p>
<p>yolov8.cpp</p>
<pre class="lang-cpp prettyprint-override"><code>#include "yoloV8.h"

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>

const char* class_names[] = {
"ambulance"
};

static float fast_exp(float x)
{
union {
uint32_t i;
float f;
} v{};
v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);
return v.f;
}

static float sigmoid(float x)
{
return 1.0f / (1.0f + fast_exp(-x));
}
static float intersection_area(const Object& a, const Object& b)
{
cv::Rect_<float> inter = a.rect & b.rect;
return inter.area();
}

static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{
int i = left;
int j = right;
float p = faceobjects[(left + right) / 2].prob;

while (i <= j)
{
while (faceobjects.prob > p)
i++;

while (faceobjects[j].prob < p)
j--;

if (i <= j)
{
// swap
std::swap(faceobjects, faceobjects[j]);

i++;
j--;
}
}

// #pragma omp parallel sections
{
// #pragma omp section
{
if (left < j) qsort_descent_inplace(faceobjects, left, j);
}
// #pragma omp section
{
if (i < right) qsort_descent_inplace(faceobjects, i, right);
}
}
}

static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{
if (faceobjects.empty())
return;

qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}

static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{
picked.clear();

const int n = faceobjects.size();

std::vector<float> areas(n);
for (int i = 0; i < n; i++)
{
areas = faceobjects.rect.width * faceobjects.rect.height;
}

for (int i = 0; i < n; i++)
{
const Object& a = faceobjects;

int keep = 1;
for (int j = 0; j < (int)picked.size(); j++)
{
const Object& b = faceobjects[picked[j]];

// intersection over union
float inter_area = intersection_area(a, b);
float union_area = areas + areas[picked[j]] - inter_area;
// float IoU = inter_area / union_area
if (inter_area / union_area > nms_threshold)
keep = 0;
}

if (keep)
picked.push_back(i);
}
}
static void generate_grids_and_stride(const int target_w, const int target_h, std::vector<int>& strides, std::vector<GridAndStride>& grid_strides)
{
for (int i = 0; i < (int)strides.size(); i++)
{
int stride = strides;
int num_grid_w = target_w / stride;
int num_grid_h = target_h / stride;
for (int g1 = 0; g1 < num_grid_h; g1++)
{
for (int g0 = 0; g0 < num_grid_w; g0++)
{
GridAndStride gs;
gs.grid0 = g0;
gs.grid1 = g1;
gs.stride = stride;
grid_strides.push_back(gs);
}
}
}
}
static void generate_proposals(std::vector<GridAndStride> grid_strides, const ncnn::Mat& pred, float prob_threshold, std::vector<Object>& objects)
{
const int num_points = grid_strides.size();
const int num_class = 1;
const int reg_max_1 = 16;

for (int i = 0; i < num_points; i++)
{
const float* scores = pred.row(i) + 4 * reg_max_1;

// find label with max score
int label = -1;
float score = -FLT_MAX;
for (int k = 0; k < num_class; k++)
{
float confidence = scores[k];
if (confidence > score)
{
label = k;
score = confidence;
}
}
float box_prob = sigmoid(score);
if (box_prob >= prob_threshold)
{
ncnn::Mat bbox_pred(reg_max_1, 4, (void*)pred.row(i));
{
ncnn::Layer* softmax = ncnn::create_layer("Softmax");

ncnn::ParamDict pd;
pd.set(0, 1); // axis
pd.set(1, 1);
softmax->load_param(pd);

ncnn::Option opt;
opt.num_threads = 1;
opt.use_packing_layout = false;

softmax->create_pipeline(opt);

softmax->forward_inplace(bbox_pred, opt);

softmax->destroy_pipeline(opt);

delete softmax;
}

float pred_ltrb[4];
for (int k = 0; k < 4; k++)
{
float dis = 0.f;
const float* dis_after_sm = bbox_pred.row(k);
for (int l = 0; l < reg_max_1; l++)
{
dis += l * dis_after_sm[l];
}

pred_ltrb[k] = dis * grid_strides.stride;
}

float pb_cx = (grid_strides.grid0 + 0.5f) * grid_strides.stride;
float pb_cy = (grid_strides.grid1 + 0.5f) * grid_strides.stride;

float x0 = pb_cx - pred_ltrb[0];
float y0 = pb_cy - pred_ltrb[1];
float x1 = pb_cx + pred_ltrb[2];
float y1 = pb_cy + pred_ltrb[3];

Object obj;
obj.rect.x = x0;
obj.rect.y = y0;
obj.rect.width = x1 - x0;
obj.rect.height = y1 - y0;
obj.label = label;
obj.prob = box_prob;

objects.push_back(obj);
}
}
}

YoloV8::YoloV8()
{
}


int YoloV8::load(int _target_size)
{
yolo.clear();

yolo.opt = ncnn::Option();

yolo.opt.num_threads = 4;

yolo.load_param("./best.param");
yolo.load_model("./best.bin");

target_size = _target_size;
mean_vals[0] = 103.53f;
mean_vals[1] = 116.28f;
mean_vals[2] = 123.675f;
norm_vals[0] = 1.0 / 255.0f;
norm_vals[1] = 1.0 / 255.0f;
norm_vals[2] = 1.0 / 255.0f;

return 0;
}

int YoloV8::detect(const cv::Mat& rgb, std::vector<Object>& objects, float prob_threshold, float nms_threshold)
{
int width = rgb.cols;
int height = rgb.rows;

// pad to multiple of 32
int w = width;
int h = height;
float scale = 1.f;
if (w > h)
{
scale = (float)target_size / w;
w = target_size;
h = h * scale;
}
else
{
scale = (float)target_size / h;
h = target_size;
w = w * scale;
}

ncnn::Mat in = ncnn::Mat::from_pixels_resize(rgb.data, ncnn::Mat::PIXEL_RGB2BGR, width, height, w, h);

// pad to target_size rectangle
int wpad = (w + 31) / 32 * 32 - w;
int hpad = (h + 31) / 32 * 32 - h;
ncnn::Mat in_pad;
ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 0.f);

in_pad.substract_mean_normalize(0, norm_vals);

ncnn::Extractor ex = yolo.create_extractor();

ex.input("images", in_pad);

std::vector<Object> proposals;

ncnn::Mat out;
ex.extract("output0", out);

std::vector<int> strides = {8, 16, 32}; // might have stride=64
std::vector<GridAndStride> grid_strides;
generate_grids_and_stride(in_pad.w, in_pad.h, strides, grid_strides);
generate_proposals(grid_strides, out, prob_threshold, proposals);

// sort all proposals by score from highest to lowest
qsort_descent_inplace(proposals);

// apply nms with nms_threshold
std::vector<int> picked;
nms_sorted_bboxes(proposals, picked, nms_threshold);

int count = picked.size();

objects.resize(count);
for (int i = 0; i < count; i++)
{
objects = proposals[picked];

// adjust offset to original unpadded
float x0 = (objects.rect.x - (wpad / 2)) / scale;
float y0 = (objects.rect.y - (hpad / 2)) / scale;
float x1 = (objects.rect.x + objects.rect.width - (wpad / 2)) / scale;
float y1 = (objects.rect.y + objects.rect.height - (hpad / 2)) / scale;

// clip
x0 = std::max(std::min(x0, (float)(width - 1)), 0.f);
y0 = std::max(std::min(y0, (float)(height - 1)), 0.f);
x1 = std::max(std::min(x1, (float)(width - 1)), 0.f);
y1 = std::max(std::min(y1, (float)(height - 1)), 0.f);

objects.rect.x = x0;
objects.rect.y = y0;
objects.rect.width = x1 - x0;
objects.rect.height = y1 - y0;
}

// sort objects by area
struct
{
bool operator()(const Object& a, const Object& b) const
{
return a.rect.area() > b.rect.area();
}
} objects_area_greater;
std::sort(objects.begin(), objects.end(), objects_area_greater);

return 0;
}

int YoloV8::draw(cv::Mat& rgb, const std::vector<Object>& objects)
{
for (size_t i = 0; i < objects.size(); i++)
{
const Object& obj = objects;

// fprintf(stderr, "%d = %.5f at %.2f %.2f %.2f x %.2f\n", obj.label, obj.prob,
// obj.rect.x, obj.rect.y, obj.rect.width, obj.rect.height);

cv::rectangle(rgb, obj.rect, cv::Scalar(255, 0, 0));

char text[256];
sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);

int baseLine = 0;
cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);

int x = obj.rect.x;
int y = obj.rect.y - label_size.height - baseLine;
if (y < 0)
y = 0;
if (x + label_size.width > rgb.cols)
x = rgb.cols - label_size.width;

cv::rectangle(rgb, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)),
cv::Scalar(255, 255, 255), -1);

cv::putText(rgb, text, cv::Point(x, y + label_size.height),
cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
}

return 0;
}
</code></pre>
<p>yolov8main.cpp</p>
<pre class="lang-cpp prettyprint-override"><code>#include "yoloV8.h"

#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <iostream>
#include <stdio.h>
#include <vector>

YoloV8 yolov8;
int target_size = 640; //416; //320; must be divisible by 32.

int main(int argc, char** argv)
{
const char* imagepath = argv[1];

if (argc != 2)
{
fprintf(stderr, "Usage: %s [imagepath]\n", argv[0]);
return -1;
}

cv::Mat m = cv::imread(imagepath, 1);
if (m.empty())
{
fprintf(stderr, "cv::imread %s failed\n", imagepath);
return -1;
}

yolov8.load(target_size); //load model (once) see yoloyV8.cpp line 246

std::vector<Object> objects;
yolov8.detect(m, objects); //recognize the objects
yolov8.draw(m, objects); //show the outcome

cv::imshow("RPi4 - 1.95 GHz - 2 GB ram",m);
// cv::imwrite("out.jpg",m);
cv::waitKey(0);

return 0;
}
</code></pre>
<p>yolov8.h</p>
<pre class="lang-cpp prettyprint-override"><code>
#ifndef YOLOV8_H
#define YOLOV8_H

#include <opencv2/core/core.hpp>
#include <net.h>

struct Object
{
cv::Rect_<float> rect;
int label;
float prob;
};

struct GridAndStride
{
int grid0;
int grid1;
int stride;
};

class YoloV8
{
public:
YoloV8();
int load(int target_size);
int detect(const cv::Mat& rgb, std::vector<Object>& objects, float prob_threshold = 0.4f, float nms_threshold = 0.5f);
int draw(cv::Mat& rgb, const std::vector<Object>& objects);
private:
ncnn::Net yolo;
int target_size;
float mean_vals[3];
float norm_vals[3];
};

#endif // YOLOV8_H
</code></pre>
 

Latest posts

Top