成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

把檢測器加進來,YOLOv8部署實戰!

人工智能 智能汽車
本文是我在學習韓博《CUDA與TensorRT部署實戰課程》第六章的課程部分輸出的個人學習筆記。

本文經自動駕駛之心公眾號授權轉載,轉載請聯系出處。

0 把檢測器加進來

本文是我在學習韓博《CUDA與TensorRT部署實戰課程》第六章的課程部分輸出的個人學習筆記,歡迎大家一起討論學習!

1 導出onnx需要注意的地方

不要pip install ultralytics 而是選擇git clone 的方式安裝yolov8

# Clone the ultralytics repository
git clone https://github.com/ultralytics/ultralytics

# Navigate to the cloned directory
cd ultralytics

# Install the package in editable mode for development
pip install -e .

然后導出onnx看一下

model = YOLO('yolov8n.pt')  # load an official model
model = YOLO('path/to/best.pt')  # load a custom trained model

# Export the model
model.export(format='onnx')

圖片圖片

這個onnx長這樣,但是為了速度能夠更好一些,因為內存是連續的,為了提升速度我們需要把這個輸出變成1x8400x84, 因為這樣我們的數據才是8400 個bbox信息(cx, cy, w, h)連在一起, 不然根據官方的信息這里是每8400個cx, 然后是每8400個cy這樣的,所以這里需要對他進行一個轉置,簡單的代碼如下:

# ultralytics/ultralytics/nn/modules/head.py
class Detect(nn.Module):
    # ...
    def forward(self, x):
        # ...
        y = torch.cat((dbox, cls.sigmoid()), 1)
        y = y.transpose(1, 2)
        return y if self.export else (y, x)

這里加了一個轉置之后,他就會變成

圖片圖片

這樣我們的yolov8的onnx就成功的導出了

2 看一下在分類器的框架上這里修改了哪些地方

這里再復習一下這個推理框架的操作

首先, Worker 是一個類,它負責管理模型的生命周期,包括模型的初始化、加載圖像、進行推理等。它根據任務類型(分類或檢測)來創建相應的模型實例。

然后,我們有兩種類型的模型:Classifier 和 Detector 。這兩種模型都繼承自 Model 基類。

Classifier 是用于圖像分類任務的模型。它包含了一些特定于分類任務的方法,如預處理和后處理。

Detector 是用于目標檢測任務的模型。它也包含了一些特定于檢測任務的方法,如預處理和后處理。

這兩種模型都有自己的 setup 方法來初始化模型,包括創建推理引擎、設置輸入/輸出綁定、分配內存等。此外,它們還有自己的 preprocess 和 postprocess 方法來處理輸入圖像和輸出結果。

圖片圖片

主要更改下面幾個文件

  • src/cpp/trt_detector.cpp
  • src/cpp/trt_worker.cpp
  • src/cpp/trt_model.cpp
  • include/trt_detector.hpp
  • inlcude/trt_worker.hpp

下面是整個框架的一個流程圖

圖片圖片

其實通過上面的流程圖可以簡單的看出來除了前后處理其他的都是差不多的, 所以也就是前面worker加了一個m_detector, 然后加了一些bbox的數據結構,

所以這里重點看這個前后處理

3 前處理

讀取圖片, 然后調用一下前處理函數, 這里的以GPU版本的前處理為例子, 下面是在Detector類實現的, 先讀取圖片, 然后從param讀取信息, 然后這里調用

bool Detector::preprocess_gpu() {
    /*Preprocess -- yolo的預處理并沒有mean和std,所以可以直接skip掉mean和std的計算 */

    /*Preprocess -- 讀取數據*/
    m_inputImage = cv::imread(m_imagePath);
    if (m_inputImage.data == nullptr) {
        LOGE("ERROR: file not founded! Program terminated"); return false;
    }
    
    /*Preprocess -- 測速*/
    m_timer->start_gpu();

    /*Preprocess -- 使用GPU進行warpAffine, 并將結果返回到m_inputMemory中*/
    preprocess::preprocess_resize_gpu(m_inputImage, m_inputMemory[1],
                                   m_params->img.h, m_params->img.w, 
                                   preprocess::tactics::GPU_WARP_AFFINE);

    m_timer->stop_gpu();
    m_timer->duration_gpu("preprocess(GPU)");
    return true;
}

這里就是正常的核函數的一個步驟, 通過分配內存然后把數據從Host搬到Device然后開始執行cu里面的函數, 再借由執行cu文件里面的函數調用kernel函數

void preprocess_resize_gpu(
    cv::Mat &h_src, float* d_tar, 
    const int& tar_h, const int& tar_w, 
    tactics tac) 
{
    uint8_t* d_src  = nullptr;

    int height   = h_src.rows;
    int width    = h_src.cols;
    int chan     = 3;

    int src_size  = height * width * chan * sizeof(uint8_t);
    int norm_size = 3 * sizeof(float);


    // 分配device上的src的內存
    CUDA_CHECK(cudaMalloc(&d_src, src_size));

    // 將數據拷貝到device上
    CUDA_CHECK(cudaMemcpy(d_src, h_src.data, src_size, cudaMemcpyHostToDevice));

    // device上處理resize, BGR2RGB的核函數
    resize_bilinear_gpu(d_tar, d_src, tar_w, tar_h, width, height, tac);

    // host和device進行同步處理
    CUDA_CHECK(cudaDeviceSynchronize());
    CUDA_CHECK(cudaFree(d_src));

    // 因為接下來會繼續在gpu上進行處理,所以這里不用把結果返回到host
}

這個是cu里面的核函數, 這里主要是用來調用核函數,這里有很多種, 通過選擇的方法來進行不同的核函數:

warpaffine_init(srcH, srcW, tarH, tarW);
        warpaffine_BGR2RGB_kernel <<<dimGrid, dimBlock>>> 
                (d_tar, d_src, trans, affine_matrix);
void resize_bilinear_gpu(
    float* d_tar, uint8_t* d_src, 
    int tarW, int tarH, 
    int srcW, int srcH, 
    tactics tac) 
{
    dim3 dimBlock(32, 32, 1);
    dim3 dimGrid(tarW / 32 + 1, tarH / 32 + 1, 1);
   
    //scaled resize
    float scaled_h = (float)srcH / tarH;
    float scaled_w = (float)srcW / tarW;
    float scale = (scaled_h > scaled_w ? scaled_h : scaled_w);

    switch (tac) {
    case tactics::GPU_NEAREST:
        nearest_BGR2RGB_nhwc2nchw_kernel <<<dimGrid, dimBlock>>>
                (d_tar, d_src, tarW, tarH, srcW, srcH, scaled_w, scaled_h);
        break;
    case tactics::GPU_NEAREST_CENTER:
        nearest_BGR2RGB_nhwc2nchw_kernel <<<dimGrid, dimBlock>>>
                (d_tar, d_src, tarW, tarH, srcW, srcH, scale, scale);
        break;
    case tactics::GPU_BILINEAR:
        bilinear_BGR2RGB_nhwc2nchw_kernel <<<dimGrid, dimBlock>>> 
                (d_tar, d_src, tarW, tarH, srcW, srcH, scaled_w, scaled_h);
        break;
    case tactics::GPU_BILINEAR_CENTER:
        bilinear_BGR2RGB_nhwc2nchw_shift_kernel <<<dimGrid, dimBlock>>> 
                (d_tar, d_src, tarW, tarH, srcW, srcH, scale, scale);
        break;
    case tactics::GPU_WARP_AFFINE:
        warpaffine_init(srcH, srcW, tarH, tarW);
        warpaffine_BGR2RGB_kernel <<<dimGrid, dimBlock>>> 
                (d_tar, d_src, trans, affine_matrix);
        break;
    default:
        LOGE("ERROR: Wrong GPU resize tactics selected. Program terminated");
        exit(1);
    }
}

這里是對這些函數的解釋:

1.warpaffine_init:

void warpaffine_init(int srcH, int srcW, int tarH, int tarW){
 trans.src_h = srcH;
 trans.src_w = srcW;
 trans.tar_h = tarH;
 trans.tar_w = tarW;
 affine_matrix.init(trans);
}

初始化仿射變換所需的參數。它設置了源圖像和目標圖像的高度和寬度,并初始化一個AffineMatrix對象。

2.affine_transformation:

這是一個在CPU和GPU上都可以調用的函數,用于執行仿射變換。它根據傳入的變換矩陣和源坐標計算目標坐標。

__host__ __device__ void affine_transformation(
    float trans_matrix[6], 
    int src_x, int src_y, 
    float* tar_x, float* tar_y)
{
    *tar_x = trans_matrix[0] * src_x + trans_matrix[1] * src_y + trans_matrix[2];
    *tar_y = trans_matrix[3] * src_x + trans_matrix[4] * src_y + trans_matrix[5];
}

3.nearest_BGR2RGB_nhwc2nchw_norm_kernel:

這是一個CUDA核函數,用于將圖像從BGR格式轉換為RGB格式,同時將圖像從NHWC格式轉換為NCHW格式,并應用最鄰近插值方法進行縮放。它還包括對像素值進行歸一化處理(減去均值,除以標準差)。

__global__ void nearest_BGR2RGB_nhwc2nchw_norm_kernel(
    float* tar, uint8_t* src, 
    int tarW, int tarH, 
    int srcW, int srcH,
    float scaled_w, float scaled_h,
    float* d_mean, float* d_std) 
{
    // nearest neighbour -- resized之后的圖tar上的坐標
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    // nearest neighbour -- 計算最近坐標
    int src_y = floor((float)y * scaled_h);
    int src_x = floor((float)x * scaled_w);

    if (src_x < 0 || src_y < 0 || src_x > srcW || src_y > srcH) {
        // nearest neighbour -- 對于越界的部分,不進行計算
    } else {
        // nearest neighbour -- 計算tar中對應坐標的索引
        int tarIdx  = y * tarW + x;
        int tarArea = tarW * tarH;

        // nearest neighbour -- 計算src中最近鄰坐標的索引
        int srcIdx = (src_y * srcW + src_x) * 3;

        // nearest neighbour -- 實現nearest beighbour的resize + BGR2RGB + nhwc2nchw + norm
        tar[tarIdx + tarArea * 0] = (src[srcIdx + 2] / 255.0f - d_mean[2]) / d_std[2];
        tar[tarIdx + tarArea * 1] = (src[srcIdx + 1] / 255.0f - d_mean[1]) / d_std[1];
        tar[tarIdx + tarArea * 2] = (src[srcIdx + 0] / 255.0f - d_mean[0]) / d_std[0];
    }
}

4.bilinear_BGR2RGB_nhwc2nchw_norm_kernel:

類似于上一個函數,但使用雙線性插值方法進行縮放。它也執行BGR到RGB的顏色轉換、NHWC到NCHW的格式轉換,并對像素值進行歸一化。

__global__ void bilinear_BGR2RGB_nhwc2nchw_norm_kernel(
    float* tar, uint8_t* src, 
    int tarW, int tarH, 
    int srcW, int srcH, 
    float scaled_w, float scaled_h,
    float* d_mean, float* d_std) 
{

    // bilinear interpolation -- resized之后的圖tar上的坐標
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    // // bilinear interpolation -- 計算x,y映射到原圖時最近的4個坐標
    int src_y1 = floor((y + 0.5) * scaled_h - 0.5);
    int src_x1 = floor((x + 0.5) * scaled_w - 0.5);
    int src_y2 = src_y1 + 1;
    int src_x2 = src_x1 + 1;

    if (src_y1 < 0 || src_x1 < 0 || src_y2 > srcH || src_x2 > srcW) {
        // bilinear interpolation -- 對于越界的坐標不進行計算
    } else {
        // bilinear interpolation -- 計算原圖上的坐標(浮點類型)在0~1之間的值
        float th   = ((y + 0.5) * scaled_h - 0.5) - src_y1;
        float tw   = ((x + 0.5) * scaled_w - 0.5) - src_x1;

        // bilinear interpolation -- 計算面積(這里建議自己手畫一張圖來理解一下)
        float a1_1 = (1.0 - tw) * (1.0 - th);  //右下
        float a1_2 = tw * (1.0 - th);          //左下
        float a2_1 = (1.0 - tw) * th;          //右上
        float a2_2 = tw * th;                  //左上

        // bilinear interpolation -- 計算4個坐標所對應的索引
        int srcIdx1_1 = (src_y1 * srcW + src_x1) * 3;  //左上
        int srcIdx1_2 = (src_y1 * srcW + src_x2) * 3;  //右上
        int srcIdx2_1 = (src_y2 * srcW + src_x1) * 3;  //左下
        int srcIdx2_2 = (src_y2 * srcW + src_x2) * 3;  //右下

        // bilinear interpolation -- 計算resized之后的圖的索引
        int tarIdx    = y * tarW  + x;
        int tarArea   = tarW * tarH;

        // bilinear interpolation -- 實現bilinear interpolation的resize + BGR2RGB + NHWC2NCHW normalization
        // 注意,這里tar和src進行遍歷的方式是不一樣的
        tar[tarIdx + tarArea * 0] = 
            (round((a1_1 * src[srcIdx1_1 + 2] + 
                   a1_2 * src[srcIdx1_2 + 2] +
                   a2_1 * src[srcIdx2_1 + 2] +
                   a2_2 * src[srcIdx2_2 + 2])) / 255.0f - d_mean[2]) / d_std[2];

        tar[tarIdx + tarArea * 1] = 
            (round((a1_1 * src[srcIdx1_1 + 1] + 
                   a1_2 * src[srcIdx1_2 + 1] +
                   a2_1 * src[srcIdx2_1 + 1] +
                   a2_2 * src[srcIdx2_2 + 1])) / 255.0f - d_mean[1]) / d_std[1];

        tar[tarIdx + tarArea * 2] = 
            (round((a1_1 * src[srcIdx1_1 + 0] + 
                   a1_2 * src[srcIdx1_2 + 0] +
                   a2_1 * src[srcIdx2_1 + 0] +
                   a2_2 * src[srcIdx2_2 + 0])) / 255.0f - d_mean[0]) / d_std[0];

    }
}

5.bilinear_BGR2RGB_nhwc2nchw_shift_norm_kernel:

這個函數也執行雙線性插值縮放、BGR到RGB顏色轉換、格式轉換,但在縮放過程中還考慮了坐標的平移(shift),并進行了像素值歸一化。

__global__ void bilinear_BGR2RGB_nhwc2nchw_shift_norm_kernel(
    float* tar, uint8_t* src, 
    int tarW, int tarH, 
    int srcW, int srcH, 
    float scaled_w, float scaled_h,
    float* d_mean, float* d_std) 
{
    // resized之后的圖tar上的坐標
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    // bilinear interpolation -- 計算x,y映射到原圖時最近的4個坐標
    int src_y1 = floor((y + 0.5) * scaled_h - 0.5);
    int src_x1 = floor((x + 0.5) * scaled_w - 0.5);
    int src_y2 = src_y1 + 1;
    int src_x2 = src_x1 + 1;

    if (src_y1 < 0 || src_x1 < 0 || src_y2 > srcH || src_x2 > srcW) {
        // bilinear interpolation -- 對于越界的坐標不進行計算
    } else {
        // bilinear interpolation -- 計算原圖上的坐標(浮點類型)在0~1之間的值
        float th   = (float)y * scaled_h - src_y1;
        float tw   = (float)x * scaled_w - src_x1;

        // bilinear interpolation -- 計算面積(這里建議自己手畫一張圖來理解一下)
        float a1_1 = (1.0 - tw) * (1.0 - th);  // 右下
        float a1_2 = tw * (1.0 - th);          // 左下
        float a2_1 = (1.0 - tw) * th;          // 右上
        float a2_2 = tw * th;                  // 左上

        // bilinear interpolation -- 計算4個坐標所對應的索引
        int srcIdx1_1 = (src_y1 * srcW + src_x1) * 3;  // 左上
        int srcIdx1_2 = (src_y1 * srcW + src_x2) * 3;  // 右上
        int srcIdx2_1 = (src_y2 * srcW + src_x1) * 3;  // 左下
        int srcIdx2_2 = (src_y2 * srcW + src_x2) * 3;  // 右下

        // bilinear interpolation -- 計算原圖在目標圖中的x, y方向上的偏移量
        y = y - int(srcH / (scaled_h * 2)) + int(tarH / 2);
        x = x - int(srcW / (scaled_w * 2)) + int(tarW / 2);

        // bilinear interpolation -- 計算resized之后的圖的索引
        int tarIdx    = (y * tarW  + x) * 3;
        int tarArea   = tarW * tarH;

        // bilinear interpolation -- 實現bilinear interpolation + BGR2RGB + shift + nhwc2nchw
        tar[tarIdx + tarArea * 0] = 
            (round((a1_1 * src[srcIdx1_1 + 2] + 
                   a1_2 * src[srcIdx1_2 + 2] +
                   a2_1 * src[srcIdx2_1 + 2] +
                   a2_2 * src[srcIdx2_2 + 2])) / 255.0f - d_mean[2]) / d_std[2];

        tar[tarIdx + tarArea * 1] = 
            (round((a1_1 * src[srcIdx1_1 + 1] + 
                   a1_2 * src[srcIdx1_2 + 1] +
                   a2_1 * src[srcIdx2_1 + 1] +
                   a2_2 * src[srcIdx2_2 + 1])) / 255.0f - d_mean[1]) / d_std[1];

        tar[tarIdx + tarArea * 2] = 
            (round((a1_1 * src[srcIdx1_1 + 0] + 
                   a1_2 * src[srcIdx1_2 + 0] +
                   a2_1 * src[srcIdx2_1 + 0] +
                   a2_2 * src[srcIdx2_2 + 0])) / 255.0f - d_mean[0]) / d_std[0];
    }
}

6.nearest_BGR2RGB_nhwc2nchw_kernel 和 bilinear_BGR2RGB_nhwc2nchw_kernel:

這兩個函數類似于之前描述的_norm版本的函數,但它們不進行像素值的歸一化處理。

__global__ void nearest_BGR2RGB_nhwc2nchw_kernel(
    float* tar, uint8_t* src, 
    int tarW, int tarH, 
    int srcW, int srcH,
    float scaled_w, float scaled_h)
{
    // nearest neighbour -- resized之后的圖tar上的坐標
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    // nearest neighbour -- 計算最近坐標
    int src_y = floor((float)y * scaled_h);
    int src_x = floor((float)x * scaled_w);

    if (src_x < 0 || src_y < 0 || src_x > srcW || src_y > srcH) {
        // nearest neighbour -- 對于越界的部分,不進行計算
    } else {
        // nearest neighbour -- 計算tar中對應坐標的索引
        int tarIdx  = y * tarW + x;
        int tarArea = tarW * tarH;

        // nearest neighbour -- 計算src中最近鄰坐標的索引
        int srcIdx = (src_y * srcW + src_x) * 3;

        // nearest neighbour -- 實現nearest beighbour的resize + BGR2RGB + nhwc2nchw + norm
        tar[tarIdx + tarArea * 0] = src[srcIdx + 2] / 255.0f;
        tar[tarIdx + tarArea * 1] = src[srcIdx + 1] / 255.0f;
        tar[tarIdx + tarArea * 2] = src[srcIdx + 0] / 255.0f;
    }
}

7.bilinear_BGR2RGB_nhwc2nchw_shift_kernel:

類似于bilinear_BGR2RGB_nhwc2nchw_kernel,但考慮了坐標的平移(shift)。

__global__ void bilinear_BGR2RGB_nhwc2nchw_shift_kernel(
    float* tar, uint8_t* src, 
    int tarW, int tarH, 
    int srcW, int srcH, 
    float scaled_w, float scaled_h)
{
    // resized之后的圖tar上的坐標
    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;


    // bilinear interpolation -- 計算x,y映射到原圖時最近的4個坐標
    int src_y1 = floor((y + 0.5) * scaled_h - 0.5);
    int src_x1 = floor((x + 0.5) * scaled_w - 0.5);
    int src_y2 = src_y1 + 1;
    int src_x2 = src_x1 + 1;

    if (src_y1 < 0 || src_x1 < 0 || src_y2 > srcH || src_x2 > srcW) {
        // bilinear interpolation -- 對于越界的坐標不進行計算
    } else {
        // bilinear interpolation -- 計算原圖上的坐標(浮點類型)在0~1之間的值
        float th   = (float)y * scaled_h - src_y1;
        float tw   = (float)x * scaled_w - src_x1;

        // bilinear interpolation -- 計算面積(這里建議自己手畫一張圖來理解一下)
        float a1_1 = (1.0 - tw) * (1.0 - th);  // 右下
        float a1_2 = tw * (1.0 - th);          // 左下
        float a2_1 = (1.0 - tw) * th;          // 右上
        float a2_2 = tw * th;                  // 左上

        // bilinear interpolation -- 計算4個坐標所對應的索引
        int srcIdx1_1 = (src_y1 * srcW + src_x1) * 3;  // 左上
        int srcIdx1_2 = (src_y1 * srcW + src_x2) * 3;  // 右上
        int srcIdx2_1 = (src_y2 * srcW + src_x1) * 3;  // 左下
        int srcIdx2_2 = (src_y2 * srcW + src_x2) * 3;  // 右下

        // bilinear interpolation -- 計算原圖在目標圖中的x, y方向上的偏移量
        y = y - int(srcH / (scaled_h * 2)) + int(tarH / 2);
        x = x - int(srcW / (scaled_w * 2)) + int(tarW / 2);

        // bilinear interpolation -- 計算resized之后的圖的索引
        int tarIdx    = y * tarW  + x;
        int tarArea   = tarW * tarH;

        // bilinear interpolation -- 實現bilinear interpolation + BGR2RGB + shift + nhwc2nchw
        tar[tarIdx + tarArea * 0] = 
            round((a1_1 * src[srcIdx1_1 + 2] + 
                   a1_2 * src[srcIdx1_2 + 2] +
                   a2_1 * src[srcIdx2_1 + 2] +
                   a2_2 * src[srcIdx2_2 + 2])) / 255.0f;

        tar[tarIdx + tarArea * 1] = 
            round((a1_1 * src[srcIdx1_1 + 1] + 
                   a1_2 * src[srcIdx1_2 + 1] +
                   a2_1 * src[srcIdx2_1 + 1] +
                   a2_2 * src[srcIdx2_2 + 1])) / 255.0f;

        tar[tarIdx + tarArea * 2] = 
            round((a1_1 * src[srcIdx1_1 + 0] + 
                   a1_2 * src[srcIdx1_2 + 0] +
                   a2_1 * src[srcIdx2_1 + 0] +
                   a2_2 * src[srcIdx2_2 + 0])) / 255.0f;
    }
}

8.warpaffine_BGR2RGB_kernel:

執行仿射變換,并將圖像從BGR格式轉換為RGB格式。

__global__ void warpaffine_BGR2RGB_kernel(
    float* tar, uint8_t* src, 
    TransInfo trans,
    AffineMatrix affine_matrix)
{
    float src_x, src_y;

    int x = blockIdx.x * blockDim.x + threadIdx.x;
    int y = blockIdx.y * blockDim.y + threadIdx.y;

    affine_transformation(affine_matrix.reverse, x + 0.5, y + 0.5, &src_x, &src_y);

    int src_x1 = floor(src_x - 0.5);
    int src_y1 = floor(src_y - 0.5);
    int src_x2 = src_x1 + 1;
    int src_y2 = src_y1 + 1;

    if (src_y1 < 0 || src_x1 < 0 || src_y1 > trans.src_h || src_x1 > trans.src_w) {
    } else {
        float tw   = src_x - src_x1;
        float th   = src_y - src_y1;

        float a1_1 = (1.0 - tw) * (1.0 - th);
        float a1_2 = tw * (1.0 - th);
        float a2_1 = (1.0 - tw) * th;
        float a2_2 = tw * th;

        int srcIdx1_1 = (src_y1 * trans.src_w + src_x1) * 3;
        int srcIdx1_2 = (src_y1 * trans.src_w + src_x2) * 3;
        int srcIdx2_1 = (src_y2 * trans.src_w + src_x1) * 3;
        int srcIdx2_2 = (src_y2 * trans.src_w + src_x2) * 3;

        int tarIdx    = y * trans.tar_w  + x;
        int tarArea   = trans.tar_w * trans.tar_h;

        tar[tarIdx + tarArea * 0] = 
            round((a1_1 * src[srcIdx1_1 + 2] + 
                   a1_2 * src[srcIdx1_2 + 2] +
                   a2_1 * src[srcIdx2_1 + 2] +
                   a2_2 * src[srcIdx2_2 + 2])) / 255.0f;

        tar[tarIdx + tarArea * 1] = 
            round((a1_1 * src[srcIdx1_1 + 1] + 
                   a1_2 * src[srcIdx1_2 + 1] +
                   a2_1 * src[srcIdx2_1 + 1] +
                   a2_2 * src[srcIdx2_2 + 1])) / 255.0f;

        tar[tarIdx + tarArea * 2] = 
            round((a1_1 * src[srcIdx1_1 + 0] + 
                   a1_2 * src[srcIdx1_2 + 0] +
                   a2_1 * src[srcIdx2_1 + 0] +
                   a2_2 * src[srcIdx2_2 + 0])) / 255.0f;
    }
}

9.resize_bilinear_gpu (兩個重載版本):

這兩個函數是對CUDA核函數的封裝,用于在GPU上執行圖像的縮放操作。它們設置CUDA的線程塊和網格尺寸,計算縮放比例,并根據指定的策略(如最鄰近、雙線性插值等)選擇相應的核函數進行圖像處理。一個版本還包括了像素值歸一化的步驟。 這里基本上除了warpffine都是分類網絡的前處理, 各種不同的可以拿來做實驗

4.后處理

重點是下面這種寫法, 因為vector這種數據結構的類型你在中間刪掉了一個,他會動到整個vector, 所以這里bbox會賦予一個flag模式,這樣最后是給通過這個查看是否添加進最后的final_bboxes里面, 這樣就很高效

   vector<bbox> final_bboxes;
    final_bboxes.reserve(m_bboxes.size());
    std::sort(m_bboxes.begin(), m_bboxes.end(), 
              [](bbox& box1, bbox& box2){return box1.confidence > box2.confidence;});

    /*
     * nms在網上有很多實現方法,其中有一些是根據nms的值來動態改變final_bboex的大小(resize, erease)
     * 這里需要注意的是,頻繁的對vector的大小的更改的空間復雜度會比較大,所以盡量不要這么做
     * 可以通過給bbox設置skip計算的flg來調整。
    */
    for(int i = 0; i < m_bboxes.size(); i ++){
        if (m_bboxes[i].flg_remove)
            continue;
        
        final_bboxes.emplace_back(m_bboxes[i]);
        for (int j = i + 1; j < m_bboxes.size(); j ++) {
            if (m_bboxes[j].flg_remove)
                continue;

            if (m_bboxes[i].label == m_bboxes[j].label){
                if (iou_calc(m_bboxes[i], m_bboxes[j]) > nms_threshold)
                    m_bboxes[j].flg_remove = true;
            }
        }
    }
    LOGD("the count of bbox after NMS is %d", final_bboxes.size());

5.后期的提升

這邊的提升思想主要是量化后掉精度的問題, 在這里面韓君給出了一些檢查的方案, 因為我們這里是一個Mutil-Task的任務

  • 是否在input/output附近做了int8量化
  • 如果是multi-task的話,是否所有的task都掉點嚴重
  • calibration的數據集是不是選的不是很好
  • calibration batch size是不是選擇的不是很好
  • calibrator是不是沒有選擇好
  • 某些計算是否不應該做量化
  • 使用polygr分析

原文鏈接:https://mp.weixin.qq.com/s/n9DGA0Wmk1MTiFggoW2e4Q

責任編輯:張燕妮 來源: 自動駕駛之心
相關推薦

2024-07-11 08:25:34

2024-01-29 09:29:02

計算機視覺模型

2025-02-24 09:50:21

2024-07-22 13:49:38

YOLOv8目標檢測開發

2024-11-18 17:31:27

2024-10-25 08:30:57

計算機視覺神經網絡YOLOv8模型

2010-06-13 10:48:33

微軟虛擬化SCVMM2008

2010-12-28 11:17:50

chkrootkitrootkit檢測器

2014-07-17 14:09:31

Spark

2024-05-15 09:16:05

2009-09-11 08:12:36

Windows 7企業部署網絡部署

2025-01-21 11:41:14

2023-02-02 09:00:00

2009-09-18 08:40:56

Windows 7企業部署VHD

2024-09-09 16:35:10

YOLO模型

2010-05-28 12:33:11

IPv6部署

2024-11-28 10:04:14

2009-08-04 09:32:27

部署Silverlig

2010-06-07 15:21:00

hadoop-0.20

2024-04-17 08:00:00

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 成人影院av | 中文字幕免费视频 | 精品久久久久国产 | 国产午夜精品久久 | www.日本在线 | 欧美一级久久 | 久久爱综合 | 在线亚洲一区 | 亚洲传媒在线 | av天天干 | 一级毛片网 | 欧美日韩国产不卡 | 国产精品久久久免费 | 欧美mv日韩mv国产网站91进入 | 成人免费福利视频 | 成人福利电影 | 国产精品一区一区 | 人人种亚洲 | 国产亚韩| 亚洲性视频网站 | 一级a毛片 | 一级毛片在线播放 | 天天澡天天狠天天天做 | 午夜影院在线观看免费 | 国产二区精品视频 | 五月婷婷在线视频 | 欧美精品在线一区 | 99精品国产一区二区三区 | 999久久久国产精品 欧美成人h版在线观看 | 国产一区视频在线 | 3p视频在线观看 | 精品久久久久久久久久久院品网 | 国产乱码精品一品二品 | 国产福利91精品 | 97国产精品视频人人做人人爱 | www.日本国产 | 亚洲精品中文字幕在线观看 | 午夜影晥 | 91精品国产综合久久婷婷香蕉 | 久久综合国产精品 | 久久成人免费视频 |