国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

RK3588實(shí)戰(zhàn):調(diào)用npu加速,yolov5識(shí)別圖像、ffmpeg發(fā)送到rtmp服務(wù)器

這篇具有很好參考價(jià)值的文章主要介紹了RK3588實(shí)戰(zhàn):調(diào)用npu加速,yolov5識(shí)別圖像、ffmpeg發(fā)送到rtmp服務(wù)器。希望對(duì)大家有所幫助。如果存在錯(cuò)誤或未考慮完全的地方,請(qǐng)大家不吝賜教,您也可以點(diǎn)擊"舉報(bào)違法"按鈕提交疑問。

前言:最近在學(xué)習(xí)一些rk3588相關(guān)的東西,趁著這個(gè)項(xiàng)目,把學(xué)習(xí)的相關(guān)東西整合下,放到一個(gè)項(xiàng)目里面,鞏固學(xué)習(xí)的知識(shí)。

項(xiàng)目名稱:yolov5識(shí)別圖像、ffmpeg發(fā)送到rtmp服務(wù)器

功能:1、opencv讀取usb攝像頭,使用RK3588自帶的NPU推理yolov5s算法,識(shí)別圖像

? ? ? ? ? ?2、使用ffmpeg,將處理的圖像進(jìn)行壓縮成h264格式,發(fā)送到rtmp服務(wù)器上。?

2023.3.4補(bǔ)充:

? ? ? ? 這兩天搞了一下OpenCL相關(guān)的,順帶在rk3588上運(yùn)行了一下。對(duì)項(xiàng)目的圖像轉(zhuǎn)化部分做了一個(gè)調(diào)整。以前用的是OpenCV提供API,將BGR轉(zhuǎn)化為RBG格式,現(xiàn)在用OpenCL調(diào)用GPU轉(zhuǎn)化。自己寫的用CPU完成這個(gè)功能的代碼,運(yùn)行時(shí)間大概在11.09317ms,調(diào)用GPU運(yùn)行的時(shí)間平均在2.15199ms(均調(diào)用100次,取平均值),速度還是有比較大的提升,GPU在大規(guī)模數(shù)據(jù)運(yùn)算效率是高不少。這里放下GPU運(yùn)行的內(nèi)核函數(shù)代碼,寫的很簡單,供參考。

/*
   功能:使用GPU,見BGR像素轉(zhuǎn)化為RGB格式
   dst_img_buffer: 轉(zhuǎn)化好的圖像存放緩存區(qū),RGB格式
   src_img_buffer: 原始圖像,BGR格式
   img_w: 圖像寬
   img_h: 圖像高,代碼未用到
   無返回值 
*/
__kernel void bgr2rgb(
	__global unsigned char* dst_img_buffer,
	__global const unsigned char* src_img_buffer,
	const int img_w,
	const int img_h)
{
	int w = get_global_id(0);
	int h = get_global_id(1);
	dst_img_buffer[(h * img_w + w) * 3 + 0] = src_img_buffer[(h * img_w + w) * 3 + 2];
	dst_img_buffer[(h * img_w + w) * 3 + 1] = src_img_buffer[(h * img_w + w) * 3 + 1];
	dst_img_buffer[(h * img_w + w) * 3 + 2] = src_img_buffer[(h * img_w + w) * 3 + 0];
}

在makefile里面添加OpenCL相關(guān)的部分。

OPENCL_LDLIBS = -lmali
OPENCL_LDLIBS_PATH = -L/usr/lib/aarch64-linux-gnu

?以后有時(shí)間再更新OpenCL部分。

一、環(huán)境搭建

? ? ? ? 本次用到的組件有opencv、ffmpeg、npu相關(guān)的庫,因此,需要先安裝環(huán)境。

1、rk3588固件

? ? ? ? 筆者這里用的系統(tǒng)固件是RK官網(wǎng)的ubuntu固件,名字為:ROC-RK3588S-PC_Ubuntu20.04-Gnome-r2202_v1.0.4b_221118.7z。使用官方提供的下載工具?RKDevTool_Release_v2.84下載固件到板子里面。具體方式不說了,參考一下官方的資料下栽進(jìn)去即可。

2、opencv編譯

? ? ? ? opencv是圖像處理用到的比較多的一個(gè)開源庫。在官方的資料里面,可以通過交叉編譯,編譯出來opencv庫,筆者電腦實(shí)在拉跨,rk的sdk編譯不出來,因此,就直接在rk3588板子里面編譯opencv,不得不說,rk3588性能確實(shí)強(qiáng),編譯opencv這種庫,一會(huì)就好了,好像比我虛擬機(jī)的ubuntu系統(tǒng)編譯的還要快。筆者這里用的是opencv-4.5.4.zip這個(gè)版本的opencv。準(zhǔn)備好源碼之后,開始編譯。具體如下:

? ? ? ? 1)安裝必要的庫

sudo apt-get install build-essential? cmake ?cmake-gui g++ ?pkg-config libgtk2.0-dev?

? ? ? ? 2)解壓源碼、進(jìn)入對(duì)應(yīng)的目錄

mkdir build

cd build

cmake ?-D CMAKE_BUILD_TYPE=RELEASE ?-D WITH_TBB=ON ?-D WITH_V4L=ON -D WITH_GTK=ON -D WITH_OPENGL=ON? ..

make -j16 && make install

我這里安裝到默認(rèn)的目錄下,也可以使用-D CMAKE_INSTALL_PREFIX=../install安裝到指定的目錄下面。opencv參考網(wǎng)上的博客安裝下就行了,遇到的問題,網(wǎng)上基本都有解決方案。

3、ffmpeg安裝

? ? ? ? 本次使用的壓縮格式是h264,ffmpeg里面沒有帶相關(guān)的源碼,因此,在ffmpeg編譯之前,需要先編譯libx264庫。準(zhǔn)備好libx264源碼,筆者用到的是x264.tar.bz2。這里直接給出configure配置,同樣是直接安裝到默認(rèn)目錄,需要的話,可以使用--prefix=../install指定對(duì)應(yīng)的目錄。

./configure --enable-shared --enable-static --disable-cli --enable-pic
make -j16 &&? make install

編譯ffmpeg之前,還需要編譯openssl,本次用到的版本是openssl-3.1.0-alpha1.tar.gz。rk3588上,直接

?./configure?
?make -j16? &&? sudo make install

ffmpeg類似,本次用到的版本是ffmpeg-snapshot.tar.bz2。給出configure

./configure --target-os=linux --arch=arm64 ?--enable-shared --enable-ffmpeg --enable-pthreads --enable-libx264 --enable-libsrt --enable-gpl?
?make -j16? &&? sudo make install

????????編譯的時(shí)候會(huì)遇到一些問題,請(qǐng)百度,參考其他的博主的,寫這篇文章的時(shí)候,項(xiàng)目已經(jīng)做好了,具體有哪些問題也記不太清了,百度上都有解決方法。若是遇到有些庫找不到路徑,可以添加鏈接路徑,或者建立軟鏈接都可以。環(huán)境上的問題基本上都比較容易解決。若是rk3588里面缺少了其他的庫,請(qǐng)對(duì)照網(wǎng)上的教程安裝。

4、rtmp服務(wù)器安裝

? ? ? ? 網(wǎng)上很多rtmp服務(wù)器的安裝,這里給一個(gè)博客鏈接,供小伙伴參考。

nginx搭建rtmp服務(wù)器_普通網(wǎng)友的博客-CSDN博客

搭建好nginx(虛擬機(jī)ubuntu),編譯好ffmpeg(rk3588開發(fā)板)之后,可以使用如下指令測試在rk3588板子上是否能夠正常運(yùn)行。

ffmpeg -re -stream_loop -1 -i 1.mp4 -vcodec libx264 -acodec aac -f flv rtmp://192.168.1.102:1935/live/test

其中,1.mp4是測試視頻,使用h264編碼(用到了libx264),192.168.1.100是筆者局域網(wǎng)的服務(wù)器ip,1935是端口號(hào)。使用ffplay等帶拉流的軟件,輸入

ffplay rtmp://192.168.1.100:1935/live/test,正常情況下可以看到音視頻流。

rk3588 yolov5,邊緣計(jì)算,linux驅(qū)動(dòng)學(xué)習(xí),Linux雜談,ffmpeg,YOLO,音視頻,目標(biāo)檢測,opencv

ffmpeg指令推流界面

rk3588 yolov5,邊緣計(jì)算,linux驅(qū)動(dòng)學(xué)習(xí),Linux雜談,ffmpeg,YOLO,音視頻,目標(biāo)檢測,opencv?ffplay播放?

二、代碼流程

? ? ? ?這里,基本知識(shí)如h264編碼等不談,若是需要相關(guān)背景知識(shí),請(qǐng)參考網(wǎng)上其他博主的文章。本篇博文主要從代碼角度,談?wù)勗趺磳?shí)現(xiàn)功能并給出參考代碼。

????????代碼流程如圖。????????

rk3588 yolov5,邊緣計(jì)算,linux驅(qū)動(dòng)學(xué)習(xí),Linux雜談,ffmpeg,YOLO,音視頻,目標(biāo)檢測,opencv

?????????首先,需要初始化ffmpeg、opencv、npu相關(guān)部分。ffmpeg編程的時(shí)候,需要配置一些參數(shù),輸入流使用的是opencv打開的攝像頭。需要初始化、打開編碼器、初始化上下文。

    video->codec = avcodec_find_encoder(AV_CODEC_ID_H264);
    if (!video->codec) {
		printf("Codec '%s' not found\n", "h264");
		return -1;
	}
    video->codec_ctx = avcodec_alloc_context3(video->codec);
    if (!video->codec_ctx) {
		printf("codec_ctx alloc fail\n");
		return -1;
	}

    video->codec_ctx->width = width; // 設(shè)置編碼視頻寬度 
	video->codec_ctx->height = height; // 設(shè)置編碼視頻高度
    video->codec_ctx->bit_rate = 50 * 1024 * 8;   //50kb
    video->codec_ctx->codec_id = video->codec->id; 
    video->codec_ctx->thread_count = 8;
	video->codec_ctx->time_base.num = 1;
	video->codec_ctx->time_base.den = fps; // 設(shè)置幀率,num為分子,den為分母,如果是1/25則表示25幀/s
	video->codec_ctx->framerate.num = fps;
	video->codec_ctx->framerate.den = 1;
    video->codec_ctx->pix_fmt = AV_PIX_FMT_YUV420P; // 設(shè)置輸出像素格式
    //畫面組的大小,多少幀一個(gè)關(guān)鍵幀
    video->codec_ctx->gop_size = 50;
    video->codec_ctx->max_b_frames = 0;

    ret = avcodec_open2(video->codec_ctx, video->codec, NULL);
	if (ret < 0){
		printf("open codec fail\n");
		return -1;
	}

????????攝像頭輸入的是YUV格式,openCV打開的攝像頭,輸入的則是BGR格式,用ffmpeg轉(zhuǎn)碼的時(shí)候需要將BGR轉(zhuǎn)化為YUV420P格式。使用ffmpeg,定義一個(gè)轉(zhuǎn)化算法。

// 創(chuàng)建視頻重采樣上下文:指定源和目標(biāo)圖像分辨率、格式
video->swsCtx = sws_getContext(width, height, AV_PIX_FMT_BGR24,
							   width, height, AV_PIX_FMT_YUV420P,
							   SWS_BICUBIC,NULL, NULL, NULL);

????????代碼中,video是筆者自定義的一個(gè)結(jié)構(gòu)體,用于管理代碼。上面width、height是輸入圖像的格式,輸入格式為BGR,下面width、height是輸出圖像的格式,輸入格式為YUV420P。同時(shí),需要?jiǎng)?chuàng)建兩個(gè)視頻幀,用于保存視頻幀數(shù)據(jù),一個(gè)是BGR,一個(gè)YUV420P。video結(jié)構(gòu)體:

//定義輸入流,一般使用攝像頭
struct input_video_stream{
    //使用opencv打開輸入流
    VideoCapture                cap;                  // capture  
    Mat                         img;
    int                         width, height;        //寬高
    int                         fps;                  //幀率
    AVFormatContext             *fmt_ctx;
};

//定義輸出流
struct output_video_stream{
   const AVOutputFormat          *fmt;
   AVFormatContext               *fmt_ctx;
   AVStream                      *stream;
   AVPacket                      *packet; 
   const AVCodec                 *codec;  //編碼器
   
};


struct h_video
{
    struct input_video_stream          input_video_stream;
    struct output_video_stream         output_video_stream;

    const AVCodec                      *codec;      //編碼器
	AVCodecContext                     *codec_ctx;  // 給編碼器分配內(nèi)存,返回對(duì)應(yīng)編碼器上下文
    SwsContext                         *swsCtx;     //用于轉(zhuǎn)化視頻格式
    AVFrame                            *rgbFrame;   //存放RGB格式的數(shù)據(jù)幀    
    AVFrame                            *yuvFrame;   //存放YUV格式的數(shù)據(jù)幀     
    AVPacket                           *pkt;        //packet包,存放處理過的壓縮數(shù)據(jù) 


};

????????struct input_video_stream、struct output_video_stream、struct h_video筆者是定義的用于管理相關(guān)資源。

? ? ? ? BGR、YUV420P幀均需要地方存放,需要初始化兩個(gè)視頻幀。

//創(chuàng)建BGR視頻幀
video->rgbFrame = av_frame_alloc();
video->rgbFrame->format = AV_PIX_FMT_BGR24;
video->rgbFrame->width = width;
video->rgbFrame->height = height;
ret = av_frame_get_buffer(video->rgbFrame, 32);
 
//創(chuàng)建YUV視頻幀并配置
video->yuvFrame = av_frame_alloc();
video->yuvFrame->format = AV_PIX_FMT_YUV420P;
video->yuvFrame->width = width;
video->yuvFrame->height = height;
ret = av_frame_get_buffer(video->yuvFrame, 32);

packet用于存放轉(zhuǎn)碼之后的視頻數(shù)據(jù)。

video->pkt = av_packet_alloc();
if (!video->pkt){
	printf("pkt alloc fail\n");
	return -1;
}
av_init_packet(video->pkt);

ffmpeg處理的代碼基本上就這些。

初始化、配置npu部分,參考了rk的例程。

這里直接放我封裝的調(diào)用rknn識(shí)別圖像的類代碼吧,

頭文件

#ifndef __DETECET_H
#define __DETECET_H 


#include <dlfcn.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/time.h>
#include <iostream>
#include <sstream>

#define _BASETSD_H


#include "RgaUtils.h"
#include "im2d.h"
#include <opencv2/opencv.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/videoio.hpp>
#include "postprocess.h"
#include "rga.h"
#include "rknn_api.h"

#define PERF_WITH_POST 1

using namespace cv;	    //OpenCV標(biāo)準(zhǔn)庫



class detect{

public:
    rknn_context             ctx;
    rknn_sdk_version         version;
    rknn_input_output_num    io_num;
    struct timeval           start_time, stop_time;

    size_t                   actual_size = 0;
    int                      img_width;
    int                      img_height;
    int                      img_channel;
    const float              nms_threshold      = NMS_THRESH;
    const float              box_conf_threshold = BOX_THRESH;
    rga_buffer_t             src;
    rga_buffer_t             dst;
    im_rect                  src_rect;
    im_rect                  dst_rect;
    char                     *model_path;
    
                             detect(char* model_name);
                            ~detect();
    int                      rknn_envs_init(const char* model_path);
    int                      rknn_envs_free();
    int                      detect_image(Mat &orig_img, detect_result_group_t *detect_result_group);
    int                      draw_results(Mat &orig_img, detect_result_group_t *detect_result_group);


private:
    int                      model_data_size;
    unsigned char            *model_data;

    unsigned char*           load_data(FILE* fp, size_t ofst, size_t sz);
    unsigned char*           load_model(const char* filename, int* model_size);
    
};


#endif 

cpp文件

#include "./include/detect.h"


using namespace cv;	    //OpenCV標(biāo)準(zhǔn)庫
using namespace std;	//C++標(biāo)準(zhǔn)程序庫中的所有標(biāo)識(shí)符都被定義于一個(gè)名為std的namespace中

detect::detect(char* model_path){
    
    int ret = 0;
    memset(&src_rect, 0, sizeof(src_rect));
    memset(&dst_rect, 0, sizeof(dst_rect));
    memset(&src, 0, sizeof(src));
    memset(&dst, 0, sizeof(dst));
    this->model_path =  model_path;


}

detect::~detect(){


    
}

/**************************************************************************

    功能: 內(nèi)部使用,讀取rknn格式的模型文件數(shù)據(jù)
    參數(shù)說明  
      fp:文件句柄
      ofst: 偏移量
      sz: 模型大小
    返回值: 0表示成功

***************************************************************************/

unsigned char* detect::load_data(FILE* fp, size_t ofst, size_t sz)
{
  unsigned char* data;
  int            ret;

  data = NULL;

  if (NULL == fp) {
    return NULL;
  }

  ret = fseek(fp, ofst, SEEK_SET);
  if (ret != 0) {
    printf("blob seek failure.\n");
    return NULL;
  }

  data = (unsigned char*)malloc(sz);
  if (data == NULL) {
    printf("buffer malloc failure.\n");
    return NULL;
  }
  ret = fread(data, 1, sz, fp);
  return data;
}


/**************************************************************************

    功能: 內(nèi)部使用,加載rknn格式的模型文件
    參數(shù)說明  
      filename:模型路徑
      model_size: 模型大小
    返回值: 0表示成功

***************************************************************************/

unsigned char* detect::load_model(const char* filename, int* model_size)
{
  FILE*          fp;
  unsigned char* data;

  fp = fopen(filename, "rb");
  if (NULL == fp) {
    printf("Open file %s failed.\n", filename);
    return NULL;
  }

  fseek(fp, 0, SEEK_END);
  int size = ftell(fp);

  data = load_data(fp, 0, size);

  fclose(fp);

  *model_size = size;
  return data;
}


/**************************************************************************

    功能: 初始化rknn模型的運(yùn)行環(huán)境
    參數(shù)說明  
      model_path:rknn格式的模型路徑
    返回值: 0表示成功

***************************************************************************/

int detect::rknn_envs_init(const char* model_path)
{
    int ret = 0;    
    /* 加載rknn文件,創(chuàng)建網(wǎng)絡(luò) */
    model_data_size = 0;
    model_data      = load_model(model_path, &model_data_size);
    ret             = rknn_init(&ctx, model_data, model_data_size, 0, NULL);
    if (ret < 0) {
        printf("rknn_init error ret=%d\n", ret);
        return -1;
    }
    ret = rknn_query(ctx, RKNN_QUERY_SDK_VERSION, &version, sizeof(rknn_sdk_version));
    if (ret < 0) {
        printf("rknn_query RKNN_QUERY_SDK_VERSION error ret=%d\n", ret);
        return -1;
    }

    // printf("sdk version: %s driver version: %s\n", version.api_version, version.drv_version);

    ret = rknn_query(ctx, RKNN_QUERY_IN_OUT_NUM, &io_num, sizeof(io_num));
    if (ret < 0) {
        printf("rknn_init RKNN_QUERY_IN_OUT_NUM error ret=%d\n", ret);
        return -1;
    }
    return 0;
}

/**************************************************************************

    功能: 識(shí)別之后的圖像,在原始圖像上繪制結(jié)果方框
    參數(shù)說明  
      orig_img:待繪制圖像,原始圖像
      detect_result_group: 存放模型推理輸出的結(jié)果
    返回值: 0表示成功

***************************************************************************/

int detect::draw_results(Mat &orig_img, detect_result_group_t *detect_result_group){

    char text[256];
    //printf("count: %d\n", detect_result_group.count);
    for (int i = 0; i < detect_result_group->count; i++) {                     //處理推理結(jié)果
        detect_result_t* det_result = &(detect_result_group->results[i]);  
        sprintf(text, "%s %.1f%%", det_result->name, det_result->prop * 100);
        printf("name: %s @ size:(%d %d %d %d) %f\n", det_result->name, det_result->box.left, det_result->box.top,
                det_result->box.right, det_result->box.bottom, det_result->prop);
        int x1 = det_result->box.left;
        int y1 = det_result->box.top;
        int x2 = det_result->box.right;
        int y2 = det_result->box.bottom;

        rectangle(orig_img, cv::Point(x1, y1), cv::Point(x2, y2), cv::Scalar(255, 0, 0, 255), 3);
        putText(orig_img, text, cv::Point(x1, y1 + 12), cv::FONT_HERSHEY_SIMPLEX, 0.5, cv::Scalar(0, 0, 0));
    }

    imshow("窗口", orig_img);
    cv::waitKey(1); 

    return 0;

}

/**************************************************************************

    功能: 將傳入的圖像進(jìn)行識(shí)別,結(jié)果保存到detect_result_group
    參數(shù)說明  
      orig_img:待識(shí)別圖像,分辨率任意,最好大于 [640 × 640]
      detect_result_group: 存放模型推理輸出的結(jié)果
    返回值: 0表示成功

***************************************************************************/

int detect::detect_image(Mat &orig_img, detect_result_group_t *detect_result_group)
{
    int ret = 0;
    void* resize_buf = nullptr;
    rknn_tensor_attr input_attrs[io_num.n_input];  //存放輸入?yún)?shù)


    //先對(duì)傳來的圖像進(jìn)行處理
    Mat img;   //用于NPU推理的圖像
    Mat tImg;  //用于圖像的轉(zhuǎn)化
    cvtColor(orig_img, tImg, cv::COLOR_BGR2RGB);
    resize(tImg, img, Size(640, 640), 0, 0, cv::INTER_NEAREST);    //模型的輸入圖像分辨率為[640 × 640], 原始圖像需要縮放一次

    img_width  = img.cols;
    img_height = img.rows;


    memset(input_attrs, 0, sizeof(input_attrs));

    for (int i = 0; i < io_num.n_input; i++) {
        input_attrs[i].index = i;
        ret                  = rknn_query(ctx, RKNN_QUERY_INPUT_ATTR, &(input_attrs[i]), sizeof(rknn_tensor_attr));
        if (ret < 0) {
          printf("rknn_init error ret=%d\n", ret);
          return -1;
        }
    }

    rknn_tensor_attr output_attrs[io_num.n_output];  //存放輸出參數(shù)
    memset(output_attrs, 0, sizeof(output_attrs));
    for (int i = 0; i < io_num.n_output; i++) {
        output_attrs[i].index = i;
        ret                   = rknn_query(ctx, RKNN_QUERY_OUTPUT_ATTR, &(output_attrs[i]), sizeof(rknn_tensor_attr));
    }

    //模型的輸入通道數(shù)、寬、高  
    int channel = 3;
    int width   = 0;
    int height  = 0;

    if (input_attrs[0].fmt == RKNN_TENSOR_NCHW) {  //輸入的通道數(shù)、寬、高
            channel = input_attrs[0].dims[1];
            width   = input_attrs[0].dims[2];
            height  = input_attrs[0].dims[3];
    } else {
        width   = input_attrs[0].dims[1];
        height  = input_attrs[0].dims[2];
        channel = input_attrs[0].dims[3];
    }

    rknn_input inputs[1];  //存放輸入圖像相關(guān)參數(shù)
    memset(inputs, 0, sizeof(inputs)); 
    inputs[0].index        = 0;
    inputs[0].type         = RKNN_TENSOR_UINT8;
    inputs[0].size         = width * height * channel;
    inputs[0].fmt          = RKNN_TENSOR_NHWC;
    inputs[0].pass_through = 0;

    if (img_width != width || img_height != height) {  //長寬和模型輸入不一致,resize一次
        resize_buf = malloc(height * width * channel);
        memset(resize_buf, 0x00, height * width * channel);

        src = wrapbuffer_virtualaddr((void*)img.data, img_width, img_height, RK_FORMAT_RGB_888);
        dst = wrapbuffer_virtualaddr((void*)resize_buf, width, height, RK_FORMAT_RGB_888);
        ret = imcheck(src, dst, src_rect, dst_rect);
        if (IM_STATUS_NOERROR != ret) {
            printf("%d, check error! %s", __LINE__, imStrError((IM_STATUS)ret));
          return -1;
        }
        inputs[0].buf = resize_buf;   //
    } else {
        inputs[0].buf = (void*)img.data;  //存放圖像數(shù)據(jù)
    }



    gettimeofday(&start_time, NULL);      //開始時(shí)間
    rknn_inputs_set(ctx, io_num.n_input, inputs);  //設(shè)置NPU的輸入,

    rknn_output outputs[io_num.n_output];   //存放輸出結(jié)果
    memset(outputs, 0, sizeof(outputs));
    for (int i = 0; i < io_num.n_output; i++) {
      outputs[i].want_float = 0;
    }

    ret = rknn_run(ctx, NULL);  //使用模型推理
    ret = rknn_outputs_get(ctx, io_num.n_output, outputs, NULL);  //獲得模型結(jié)果
    gettimeofday(&stop_time, NULL);        //結(jié)束時(shí)間
  

     // 后處理
    float scale_w = (float)width / img_width;
    float scale_h = (float)height / img_height;
    scale_w = (float)width / orig_img.cols;
    scale_h = (float)height / orig_img.rows;


    std::vector<float>    out_scales;
    std::vector<int32_t>  out_zps;
    for (int i = 0; i < io_num.n_output; ++i) {
      out_scales.push_back(output_attrs[i].scale);
      out_zps.push_back(output_attrs[i].zp);
    }

    //將模型推理的結(jié)果存放到detect_result_group
    post_process((int8_t*)outputs[0].buf, (int8_t*)outputs[1].buf, (int8_t*)outputs[2].buf, height, width,
                box_conf_threshold, nms_threshold, scale_w, scale_h, out_zps, out_scales, detect_result_group);  

    ret = rknn_outputs_release(ctx, io_num.n_output, outputs);

    return 0;
}


/**************************************************************************

    功能: 釋放資源
    參數(shù)說明  無
    返回值: 0表示成功

***************************************************************************/

int detect::rknn_envs_free(){

    //釋放資源
       
    int ret = 0; 
    
    
    
    // release
    ret = rknn_destroy(ctx);

    if (model_data) {
      free(model_data);
    }
}













代碼都比較簡單,注釋也比較詳細(xì)。不懂的可以私聊博主。

detect類使用的時(shí)候,初始化一下環(huán)境,?

 detect.rknn_envs_init(detect.model_path);

然后就可以使用NPU推理圖像了,傳入?yún)?shù)為openCV的Mat格式圖像,結(jié)果保存在detect_result_group。

detect_result_group_t detect_result_group; 
detect.detect_image(orig_img, &detect_result_group);

最后,就是將圖像進(jìn)行編碼成h264格式、發(fā)送給rtmp服務(wù)器了,將關(guān)鍵代碼貼出。

uint8_t *src_data[4];
int src_linesize[4];

        //BGR24--->YUV420
av_image_fill_arrays(src_data, src_linesize, orig_img.data, AV_PIX_FMT_BGR24, orig_img.cols, orig_img.rows, 1);
cv::Size frameSize = orig_img.size();
int cvLinesizes[1];
cvLinesizes[0] = orig_img.step1();
        
av_image_copy(h_video.rgbFrame->data, h_video.rgbFrame->linesize, (const uint8_t **)src_data, src_linesize, AV_PIX_FMT_BGR24, orig_img.cols, orig_img.rows);
sws_scale(h_video.swsCtx, &orig_img.data, cvLinesizes, 0, height, h_video.yuvFrame->data, h_video.yuvFrame->linesize);
    
h_video.yuvFrame->pts = i++;

video_encode(&h_video.output_video_stream,h_video.codec_ctx, h_video.yuvFrame,h_video.pkt);

video_encode代碼在下面。

int video_encode(struct output_video_stream *out_stream,AVCodecContext *enc_ctx, AVFrame *frame, AVPacket *pkt)
{
	int ret;
 
	/* send the frame to the encoder */
	if (frame)
		printf("Send frame %lld\n", frame->pts);
 
	ret = avcodec_send_frame(enc_ctx, frame);
	if (ret < 0) {
		printf("Error sending a frame for encoding\n");
		return -1;
	}
	while (ret >= 0) {
        ret = avcodec_receive_packet(enc_ctx, pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
            return -1;
        } else if (ret < 0) {
            printf("Error during encoding\n");
            return -1;
        }

        //推流
        pkt->pts = av_rescale_q(pkt->pts,out_stream->stream->time_base,out_stream->stream->time_base);
        pkt->dts = av_rescale_q(pkt->dts,out_stream->stream->time_base,out_stream->stream->time_base);

        // 往輸出流寫入數(shù)據(jù)
        av_interleaved_write_frame(out_stream->fmt_ctx, pkt);
        av_packet_unref(pkt);
    }
}


????????整個(gè)項(xiàng)目做的都比較簡陋,供大家學(xué)習(xí)參考吧,路過的大佬請(qǐng)輕噴,筆者只是個(gè)初學(xué)者。

????????代碼部分參考了rk給的npu例子和網(wǎng)上各路大神的代碼,在此一并感謝。

????????筆者編譯用的是makefile進(jìn)行編譯程序,這里給出筆者的makefile部分,供小伙伴參考。

TARGET_NAME=app
CPPFLAGS = -g -fpermissive -std=c++11 -Wall -static 
CPP = g++
CPPFILES = main.cpp video.cpp detect.cpp postprocess.cpp

LDLIBS := 
LDLIBS_PATH:=
INCS_PATH:=

RKNN_LDLIBS = -ldl -lmpimmz -lrga -lrknn_api -lrknnrt
RKNN_LDLIBS_PATH = -L./lib
RKNN_INCS_PATH = -I./include

OPENCV_LDLIBS = -lopencv_calib3d -lopencv_core -lopencv_dnn -lopencv_features2d -lopencv_flann -lopencv_gapi -lopencv_highgui -lopencv_imgcodecs -lopencv_imgproc -lopencv_ml -lopencv_objdetect -lopencv_photo -lopencv_stitching -lopencv_video -lopencv_videoio
OPENCV_INCS_PATH = -I/usr/local/include/opencv4

FFMPEG_LDLIBS = -lavformat -lavdevice  -lavcodec  -lavutil  -lswresample  -lavfilter  -lpostproc  -lswscale  -lSDL2 

LDLIBS += $(FFMPEG_LDLIBS) $(OPENCV_LDLIBS) $(RKNN_LDLIBS) 
LDLIBS_PATH += -L/usr/local/lib $(RKNN_LDLIBS_PATH)
INCS_PATH += $(OPENCV_INCS_PATH) -I/usr/local/include $(RKNN_INCS_PATH)


all:$(TARGET_NAME)

$(TARGET_NAME):
	$(CPP) -o ${TARGET_NAME} ${CPPFILES}   ${INCS_PATH}  ${LDLIBS_PATH}  ${LDLIBS} 
	@echo "end"

clean:
	rm -rf *.o $(TARGET_NAME) 

三、結(jié)果

????????在rk3588上運(yùn)行程序,輸入./app "rtmp://192.168.1.100:1935/live/test"? 。在Ubuntu上輸入?ffplay rtmp://192.168.1.100:1935/live/test,測試效果。ffplay拉流界面

rk3588 yolov5,邊緣計(jì)算,linux驅(qū)動(dòng)學(xué)習(xí),Linux雜談,ffmpeg,YOLO,音視頻,目標(biāo)檢測,opencv

?這里是遠(yuǎn)程顯示的圖像。藍(lán)色的框框,是將識(shí)別到的人圈出來,用到的模型是之前訓(xùn)練出來的。

模型訓(xùn)練可以參考筆者之前的博文。

Yolo v5訓(xùn)練并移植到RK3588S平臺(tái)_rk3588 yolov5_紫川寧520的博客-CSDN博客

rk3588 yolov5,邊緣計(jì)算,linux驅(qū)動(dòng)學(xué)習(xí),Linux雜談,ffmpeg,YOLO,音視頻,目標(biāo)檢測,opencv

????????總結(jié):整個(gè)項(xiàng)目,從難度角度來看,其實(shí)都比較簡單,沒有用到特別復(fù)雜的東西,但牽扯到的東西比較多,有些很零碎,有些折騰起來很繁瑣。程序運(yùn)行的效果,只能說還行吧,有點(diǎn)卡頓,后面有時(shí)間再考慮優(yōu)化的問題了。

? ? ??

??文章來源地址http://www.zghlxwxcb.cn/news/detail-779721.html

到了這里,關(guān)于RK3588實(shí)戰(zhàn):調(diào)用npu加速,yolov5識(shí)別圖像、ffmpeg發(fā)送到rtmp服務(wù)器的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點(diǎn)僅代表作者本人,不代表本站立場。本站僅提供信息存儲(chǔ)空間服務(wù),不擁有所有權(quán),不承擔(dān)相關(guān)法律責(zé)任。如若轉(zhuǎn)載,請(qǐng)注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實(shí)不符,請(qǐng)點(diǎn)擊違法舉報(bào)進(jìn)行投訴反饋,一經(jīng)查實(shí),立即刪除!

領(lǐng)支付寶紅包贊助服務(wù)器費(fèi)用

相關(guān)文章

  • 香橙派5使用NPU加速yolov5的實(shí)時(shí)視頻推理(一)

    香橙派5使用NPU加速yolov5的實(shí)時(shí)視頻推理(一)

    ? ? ? ? 寒假里,博主完成了樹莓派4B搭載yolofastest-V2的ncnn加速,效果挺不錯(cuò)的,但總感覺還是稍微差點(diǎn)意思,于是就購買了一塊香橙派5,想要用RK3588芯片自帶的NPU來加速深度學(xué)習(xí)的部署,在2023年3月4日也是完成了香橙派5的NPU加速深度學(xué)習(xí)部分,其效果也確實(shí)非??捎^,在畫

    2024年02月02日
    瀏覽(20)
  • 香橙派5使用NPU加速yolov5的實(shí)時(shí)視頻推理(二)

    香橙派5使用NPU加速yolov5的實(shí)時(shí)視頻推理(二)

    ? ? ? ? 這一步就需要我們進(jìn)入到Ubuntu20.04系統(tǒng)中了,我的Ubuntu系統(tǒng)中已經(jīng)下載好了anaconda,使用anaconda的好處就是可以方便的安裝一些庫,而且還可以利用conda來配置虛擬環(huán)境,做到環(huán)境與環(huán)境之間相互獨(dú)立。 ??????? 對(duì)于我來說,使用了以下命令創(chuàng)建了一個(gè)名為rknn_cesh

    2024年02月02日
    瀏覽(15)
  • RK3588平臺(tái)開發(fā)系列講解(項(xiàng)目篇)YOLOv5部署測試

    RK3588平臺(tái)開發(fā)系列講解(項(xiàng)目篇)YOLOv5部署測試

    平臺(tái) 內(nèi)核版本 安卓版本 RK3588 Linux 5.10 Android 12 沉淀、分享、成長,讓自己和他人都能有所收獲!??

    2024年02月06日
    瀏覽(27)
  • 瑞芯微RK3568/RK3588平臺(tái)YOLOV5實(shí)時(shí)視頻算法的部署小白教程

    瑞芯微RK3568/RK3588平臺(tái)YOLOV5實(shí)時(shí)視頻算法的部署小白教程

    本文實(shí)現(xiàn)整體的部署流程比較小白,首先在PC上分別實(shí)現(xiàn)工程中的模型仿真推理、yolov5-pytorch仿真推理、自己訓(xùn)練yolov5模型仿真推理,完成仿真之后再在板端分別實(shí)現(xiàn)rk提供模型的板端推理、yolov5-pytorch板端推理、自己訓(xùn)練的yolov5模型板端推理,最后實(shí)現(xiàn)自己訓(xùn)練的yolov5模型實(shí)

    2024年02月06日
    瀏覽(221)
  • yolov5模型(.pt)在RK3588(S)上的部署(實(shí)時(shí)攝像頭檢測)

    github倉庫 所需: 安裝了Ubuntu20系統(tǒng)的RK3588 安裝了Ubuntu18的電腦或者虛擬機(jī) 一、yolov5 PT模型獲取 Anaconda教程 YOLOv5教程 經(jīng)過上面兩個(gè)教程之后,你應(yīng)該獲取了自己的 best.pt 文件 二、PT模型轉(zhuǎn)onnx模型 將 models/yolo.py 文件中的 class 類下的 forward 函數(shù)由: 改為: 將 export.py 文件中的

    2024年02月06日
    瀏覽(24)
  • 香橙派5 RK3588 yolov5模型轉(zhuǎn)換rknn及部署踩坑全記錄 orangepi 5

    香橙派5 RK3588 yolov5模型轉(zhuǎn)換rknn及部署踩坑全記錄 orangepi 5

    由于距離寫這篇文章過去很久,有的部分,官方已更新,請(qǐng)多結(jié)合其他人的看,并多琢磨、討論~ 另外打個(gè)小廣告: 博客 https://blog.vrxiaojie.top/ 歡迎大家前來做客玩耍,提出問題~~ 以后的文章都會(huì)在博客發(fā)布了,CSDN這邊可能這是最后一篇文章。 (1) 使用官方提供的Ubuntu鏡像:

    2024年02月05日
    瀏覽(28)
  • yolov5訓(xùn)練自己的pt文件,轉(zhuǎn)onnx,再轉(zhuǎn)成rknn,到RK3588開發(fā)板運(yùn)行測試

    yolov5訓(xùn)練好自己的模型,例如訓(xùn)練完后,名稱為best.pt,路徑為runs/exp/weights/best.pt。 采用detect.py文件驗(yàn)證best.pt可以正常檢測目標(biāo),再進(jìn)行下一步工作。 修改utils/yolo.py文件的后處理部分,將class Detect(nn.Module) 類的子函數(shù)forward由 修改為: 注意:訓(xùn)練和檢測的時(shí)候,yolo.py文件應(yīng)

    2024年02月01日
    瀏覽(20)
  • yolov5訓(xùn)練pt模型并轉(zhuǎn)換為rknn模型,部署在RK3588開發(fā)板上——從訓(xùn)練到部署全過程

    yolov5訓(xùn)練pt模型并轉(zhuǎn)換為rknn模型,部署在RK3588開發(fā)板上——從訓(xùn)練到部署全過程

    目錄 一、任務(wù)介紹 二、實(shí)驗(yàn)過程 2.1 使用正確版本的yolov5進(jìn)行訓(xùn)練(平臺(tái):x86機(jī)器windows系統(tǒng)) 2.2 best.pt轉(zhuǎn)換為best.onnx(平臺(tái):x86機(jī)器window系統(tǒng)) 2.3 best.onnx轉(zhuǎn)換為best.rknn(平臺(tái):x86機(jī)器Linux系統(tǒng)) 2.3.1 環(huán)境準(zhǔn)備和工具包安裝 2.3.2 onnx轉(zhuǎn)換為rknn 2.4 RK3588部署rknn實(shí)現(xiàn)NPU加速(平臺(tái):

    2024年02月03日
    瀏覽(23)
  • 36、RK3399Pro 環(huán)境搭建和Yolov5 c++調(diào)用opencv進(jìn)行RKNN模型部署和使用

    36、RK3399Pro 環(huán)境搭建和Yolov5 c++調(diào)用opencv進(jìn)行RKNN模型部署和使用

    基本思想:記錄rk3399 pro配置環(huán)境和c++ npu開發(fā)記錄,主要想搞一份c++代碼和其它圖像算法結(jié)合一下,好進(jìn)行部署,淘寶鏈接見附錄 ?需要的python3.7對(duì)應(yīng)的aarch64的whl包:包含opencv-whl 、h5py-whl包: 鏈接: https://pan.baidu.com/s/1cvCAmHBa_4KgEjrcFIYnig 提取碼: 5ui4 鏈接: https://pan.baidu.com/s/1hrc

    2024年02月07日
    瀏覽(28)
  • 使用yolov5實(shí)現(xiàn)圖像識(shí)別

    使用yolov5實(shí)現(xiàn)圖像識(shí)別

    開始之前 你應(yīng)當(dāng)先克隆這個(gè)倉庫 下載完畢后,進(jìn)入克隆的倉庫目錄 下載依賴 下載數(shù)據(jù)集 這里有個(gè)數(shù)據(jù)集,為了節(jié)省訓(xùn)練時(shí)間,文件不是很大。 結(jié)尾處我會(huì)推薦幾個(gè)大型數(shù)據(jù)集下載地址 。 數(shù)據(jù)集下載 提取碼:crnr 解壓后,你會(huì)看到這幾個(gè)文件夾: 標(biāo)記數(shù)據(jù)集 可以登錄 h

    2024年02月07日
    瀏覽(20)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請(qǐng)作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包