环境准备

windows电脑,ubtuntu虚拟机,imx6ull开发板,虚拟机安装cmake,yum install cmake

版本信息

opencv编译

由于yolo一般习惯依赖opencv,所以先安装opencv,注意,因为虚拟机的cmake版本是3.10的,所以推荐opencv选择4.2版本,请自行下载

下载后复制到虚拟机root目录,注意不要使用虚拟机共享文件夹

执行命令

mkdir build-arm & cd build-arm
cmake \-DCMAKE_SYSTEM_NAME=Linux \-DCMAKE_SYSTEM_PROCESSOR=arm \-DCMAKE_C_COMPILER=arm-buildroot-linux-gnueabihf-gcc \-DCMAKE_CXX_COMPILER=arm-buildroot-linux-gnueabihf-g++ \-DCMAKE_INSTALL_PREFIX=/opt/opencv-arm \-DBUILD_LIST=core,highgui,imgcodecs,imgproc \-DWITH_GTK=OFF \-DWITH_JPEG=ON \-DWITH_PNG=ON \-DCMAKE_FIND_ROOT_PATH_MODE_PROGRAM=NEVER \-DCMAKE_FIND_ROOT_PATH_MODE_LIBRARY=ONLY \-DCMAKE_FIND_ROOT_PATH_MODE_INCLUDE=ONLY \..
make -j1
sudo make install 

安装成功后,目录/opt/opencv-arm/就出现了

yolo资源下载

# 预训练权重 下载

https://pjreddie.com/media/files/yolov3-tiny.weight

或者  wget https://pjreddie.com/media/files/yolov3-tiny.weights

cfg下载

https://gitee.com/mirrors/darknet/raw/master/cfg/yolov3-tiny.cfg

ncnn框架编译

​
Git clone https://gitee.com/Tencent/ncnn.git
Cd ncnn
Mkdir build-arm &&cd build-arm

​

新建toolchains.cmake

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)
set(TOOLCHAIN_PREFIX arm-buildroot-linux-gnueabihf-)
set(CMAKE_C_COMPILER ${TOOLCHAIN_PREFIX}gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_PREFIX}g++)

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

执行命令

cmake \
-DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \
-DCMAKE_INSTALL PREFIX=/opt/ncnn-arm \
-DNCNN BUILD EXAMPLES=OFF \
-DNCNN BUILD TOOLS=ON \
-DNCNN BUILD_BENCHMARK=OFF \
..

make -j1
sudo make install

成功后。在/opt/下就有了ncnn-arm/文件夹

将darknet转为ncnn

在上一步ncnn编译完成后,编译目录下tools/darknet下有一个现成的工具darknet2ncnn,极大的方便了我们,一步到位,不需要python,不需要ONNX

将darknet2ncnn拷贝到开发板,当然,也可以直接挂载放到虚拟机的nfs目录,韦东山老师一般是/home/book/nfs_rootfs/,在开发板的/mnt/直接可以访问

在开发板执行命令

./darknet2ncnn yolov3-tiny.cfg yolov3-tiny.weights yolov3-tiny.param yolov3-tiny.bin

成功得到param和bin文件

demo文件编写与编译

yolo_ncnn.cpp

#include <ncnn/net.h>
#include <opencv2/opencv.hpp>
#include <vector>
#include <string>

// ==================== 请根据自己模型修改以下参数 ====================
const int INPUT_W = 416;       // 模型输入宽度(和cfg一致)
const int INPUT_H = 416;       // 模型输入高度(和cfg一致)
const float CONF_THRESH = 0.25f; // 置信度阈值
const float NMS_THRESH = 0.45f;  // NMS阈值
// 类别名称,按训练数据集填写
const std::vector<std::string> CLASS_NAMES = {
    "class1", "class2", "class3"
};
// 模型文件路径
const std::string PARAM_PATH = "yolo.param";
const std::string BIN_PATH   = "yolo.bin";
// ===================================================================

struct Object
{
    cv::Rect rect;
    int class_id;
    float score;
};

static void nms(std::vector<Object>& objects)
{
    std::sort(objects.begin(), objects.end(), [](const Object& a, const Object& b) {
        return a.score > b.score;
    });

    std::vector<float> area(objects.size());
    for (int i = 0; i < objects.size(); i++)
    {
        area[i] = (float)objects[i].rect.area();
    }

    std::vector<bool> remove_flag(objects.size(), false);
    for (int i = 0; i < objects.size(); i++)
    {
        if (remove_flag[i]) continue;
        for (int j = i + 1; j < objects.size(); j++)
        {
            if (remove_flag[j]) continue;

            cv::Rect intersect = objects[i].rect & objects[j].rect;
            float inter_area = (float)intersect.area();
            float iou = inter_area / (area[i] + area[j] - inter_area);

            if (iou > NMS_THRESH)
                remove_flag[j] = true;
        }
    }

    std::vector<Object> res;
    for (int i = 0; i < objects.size(); i++)
    {
        if (!remove_flag[i])
            res.push_back(objects[i]);
    }
    objects.swap(res);
}

int main(int argc, char** argv)
{
    // 1. 初始化ncnn网络
    ncnn::Net net;
    // 开启多线程、优化(ARM平台自动启用NEON)
    net.opt.num_threads = 1;
    // net.opt.use_winograd = true;

    // 加载模型
    int ret = net.load_param(PARAM_PATH.c_str());
    ret |= net.load_model(BIN_PATH.c_str());
    if (ret != 0)
    {
        fprintf(stderr, "加载模型失败!\n");
        return -1;
    }

    // 2. 读取测试图片
    cv::Mat img = cv::imread("test.jpg");
    if (img.empty())
    {
        fprintf(stderr, "读取图片失败!\n");
        return -1;
    }
    int img_w = img.cols;
    int img_h = img.rows;

    // 3. 图像预处理 BGR -> RGB + resize + 归一化
    ncnn::Mat in = ncnn::Mat::from_pixels_resize(
        img.data, ncnn::Mat::PIXEL_BGR2RGB,
        img_w, img_h, INPUT_W, INPUT_H
    );
    // Darknet 常用归一化: /255.0
    const float mean_vals[3] = {0.f, 0.f, 0.f};
    const float norm_vals[3] = {1/255.f, 1/255.f, 1/255.f};
    in.substract_mean_normalize(mean_vals, norm_vals);

    // 4. 推理
    ncnn::Extractor ex = net.create_extractor();
    ex.input("data", in); // 输入节点名,默认darknet转出来是 data
    ncnn::Mat out;
    ex.extract("output", out); // 输出节点名,大部分yolo为 output

    // 5. 解析输出 (ncnn yolo输出格式: 每行 [cx,cy,w,h,score0,score1,...])
    std::vector<Object> objects;
    int rows = out.h;
    int cols = out.w;
    for (int y = 0; y < rows; y++)
    {
        const float* row = out.row(y);
        float cx = row[0] * img_w;
        float cy = row[1] * img_h;
        float w  = row[2] * img_w;
        float h  = row[3] * img_h;

        float max_score = 0.f;
        int max_cls = 0;
        for (int c = 4; c < cols; c++)
        {
            if (row[c] > max_score)
            {
                max_score = row[c];
                max_cls = c - 4;
            }
        }

        if (max_score < CONF_THRESH)
            continue;

        Object obj;
        obj.class_id = max_cls;
        obj.score = max_score;
        obj.rect.x = cv::saturate_cast<int>(cx - w/2);
        obj.rect.y = cv::saturate_cast<int>(cy - h/2);
        obj.rect.width = cv::saturate_cast<int>(w);
        obj.rect.height = cv::saturate_cast<int>(h);
        objects.push_back(obj);
    }

    // 6. NMS 非极大值抑制
    nms(objects);

    // 7. 绘图显示
    for (auto& obj : objects)
    {
        cv::rectangle(img, obj.rect, cv::Scalar(0, 0, 255), 2);
        std::string text = CLASS_NAMES[obj.class_id] + " " + std::to_string(obj.score).substr(0, 4);
        cv::putText(img, text, cv::Point(obj.rect.x, obj.rect.y-5),
                    cv::FONT_HERSHEY_SIMPLEX, 0.6, cv::Scalar(0, 255, 0), 2);
    }

    // cv::imshow("yolo-ncnn", img);
    // cv::waitKey(0);
    cv::imwrite("result.jpg", img);

    return 0;
}

CMakeLists.txt

# ========== 交叉编译工具链 (根据你的工具链修改) ==========
set(CMAKE_C_COMPILER   arm-buildroot-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER arm-buildroot-linux-gnueabihf-g++)

cmake_minimum_required(VERSION 3.10)
project(yolo_ncnn)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

# ========== 交叉编译版 ncnn / OpenCV 路径(你交叉编译出来的库) ==========
set(NCNN_ROOT /opt/ncnn-arm)
set(OPENCV_ROOT /opt/opencv-arm)

include_directories(
    ${NCNN_ROOT}/include
    ${OPENCV_ROOT}/include/opencv4
)

link_directories(
    ${NCNN_ROOT}/lib
    ${OPENCV_ROOT}/lib
)

# 编译目标
add_executable(demo yolo_ncnn.cpp)

target_link_libraries(demo
    ncnn
    opencv_core
    opencv_imgproc
    opencv_highgui
    opencv_imgcodecs
    pthread
    gomp
)

将这两个文件放到虚拟机上,执行

mkdir build-arm &&cd build-arm
cmake ..
make -j1

执行成功,至此所有编译工作已完成

开始移植测试

现在所有东西都齐全了,开始移植测试

确保test.jpg,demo文件,param,bin文件都在/home/book/root_fs。可以直接在开发板通过/mnt/访问

将虚拟机的/opt/opencv-arm复制到开发板的/opt/下

开发板执行

cd /mnt/
export LD_LIBRARY_PATH=/opt/opencv-arm/lib:$LD_LIBRARY_PATH
./demo

执行后,成功生成了result.jpg,虽然不太准确,但是先这样吧,嘿嘿。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐