把檢測器加進(jìn)來,YOLOv8部署實戰(zhàn)!
本文經(jīng)自動駕駛之心公眾號授權(quán)轉(zhuǎn)載,轉(zhuǎn)載請聯(lián)系出處。
0 把檢測器加進(jìn)來
本文是我在學(xué)習(xí)韓博《CUDA與TensorRT部署實戰(zhàn)課程》第六章的課程部分輸出的個人學(xué)習(xí)筆記,歡迎大家一起討論學(xué)習(xí)!
1 導(dǎo)出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 .
然后導(dǎo)出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長這樣,但是為了速度能夠更好一些,因為內(nèi)存是連續(xù)的,為了提升速度我們需要把這個輸出變成1x8400x84, 因為這樣我們的數(shù)據(jù)才是8400 個bbox信息(cx, cy, w, h)連在一起, 不然根據(jù)官方的信息這里是每8400個cx, 然后是每8400個cy這樣的,所以這里需要對他進(jìn)行一個轉(zhuǎn)置,簡單的代碼如下:
# 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)
這里加了一個轉(zhuǎn)置之后,他就會變成
圖片
這樣我們的yolov8的onnx就成功的導(dǎo)出了
2 看一下在分類器的框架上這里修改了哪些地方
這里再復(fù)習(xí)一下這個推理框架的操作
首先, Worker 是一個類,它負(fù)責(zé)管理模型的生命周期,包括模型的初始化、加載圖像、進(jìn)行推理等。它根據(jù)任務(wù)類型(分類或檢測)來創(chuàng)建相應(yīng)的模型實例。
然后,我們有兩種類型的模型:Classifier 和 Detector 。這兩種模型都繼承自 Model 基類。
Classifier 是用于圖像分類任務(wù)的模型。它包含了一些特定于分類任務(wù)的方法,如預(yù)處理和后處理。
Detector 是用于目標(biāo)檢測任務(wù)的模型。它也包含了一些特定于檢測任務(wù)的方法,如預(yù)處理和后處理。
這兩種模型都有自己的 setup 方法來初始化模型,包括創(chuàng)建推理引擎、設(shè)置輸入/輸出綁定、分配內(nèi)存等。此外,它們還有自己的 preprocess 和 postprocess 方法來處理輸入圖像和輸出結(jié)果。
圖片
主要更改下面幾個文件
- 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的數(shù)據(jù)結(jié)構(gòu),
所以這里重點看這個前后處理
3 前處理
讀取圖片, 然后調(diào)用一下前處理函數(shù), 這里的以GPU版本的前處理為例子, 下面是在Detector類實現(xiàn)的, 先讀取圖片, 然后從param讀取信息, 然后這里調(diào)用
bool Detector::preprocess_gpu() {
/*Preprocess -- yolo的預(yù)處理并沒有mean和std,所以可以直接skip掉mean和std的計算 */
/*Preprocess -- 讀取數(shù)據(jù)*/
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進(jìn)行warpAffine, 并將結(jié)果返回到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;
}
這里就是正常的核函數(shù)的一個步驟, 通過分配內(nèi)存然后把數(shù)據(jù)從Host搬到Device然后開始執(zhí)行cu里面的函數(shù), 再借由執(zhí)行cu文件里面的函數(shù)調(diào)用kernel函數(shù)
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的內(nèi)存
CUDA_CHECK(cudaMalloc(&d_src, src_size));
// 將數(shù)據(jù)拷貝到device上
CUDA_CHECK(cudaMemcpy(d_src, h_src.data, src_size, cudaMemcpyHostToDevice));
// device上處理resize, BGR2RGB的核函數(shù)
resize_bilinear_gpu(d_tar, d_src, tar_w, tar_h, width, height, tac);
// host和device進(jìn)行同步處理
CUDA_CHECK(cudaDeviceSynchronize());
CUDA_CHECK(cudaFree(d_src));
// 因為接下來會繼續(xù)在gpu上進(jìn)行處理,所以這里不用把結(jié)果返回到host
}
這個是cu里面的核函數(shù), 這里主要是用來調(diào)用核函數(shù),這里有很多種, 通過選擇的方法來進(jìn)行不同的核函數(shù):
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);
}
}
這里是對這些函數(shù)的解釋:
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);
}
初始化仿射變換所需的參數(shù)。它設(shè)置了源圖像和目標(biāo)圖像的高度和寬度,并初始化一個AffineMatrix對象。
2.affine_transformation:
這是一個在CPU和GPU上都可以調(diào)用的函數(shù),用于執(zhí)行仿射變換。它根據(jù)傳入的變換矩陣和源坐標(biāo)計算目標(biāo)坐標(biāo)。
__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核函數(shù),用于將圖像從BGR格式轉(zhuǎn)換為RGB格式,同時將圖像從NHWC格式轉(zhuǎn)換為NCHW格式,并應(yīng)用最鄰近插值方法進(jìn)行縮放。它還包括對像素值進(jìn)行歸一化處理(減去均值,除以標(biāo)準(zhǔn)差)。
__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上的坐標(biāo)
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// nearest neighbour -- 計算最近坐標(biāo)
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 -- 對于越界的部分,不進(jìn)行計算
} else {
// nearest neighbour -- 計算tar中對應(yīng)坐標(biāo)的索引
int tarIdx = y * tarW + x;
int tarArea = tarW * tarH;
// nearest neighbour -- 計算src中最近鄰坐標(biāo)的索引
int srcIdx = (src_y * srcW + src_x) * 3;
// nearest neighbour -- 實現(xiàn)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:
類似于上一個函數(shù),但使用雙線性插值方法進(jìn)行縮放。它也執(zhí)行BGR到RGB的顏色轉(zhuǎn)換、NHWC到NCHW的格式轉(zhuǎn)換,并對像素值進(jìn)行歸一化。
__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上的坐標(biāo)
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// // bilinear interpolation -- 計算x,y映射到原圖時最近的4個坐標(biāo)
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 -- 對于越界的坐標(biāo)不進(jìn)行計算
} else {
// bilinear interpolation -- 計算原圖上的坐標(biāo)(浮點類型)在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個坐標(biāo)所對應(yīng)的索引
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 -- 實現(xiàn)bilinear interpolation的resize + BGR2RGB + NHWC2NCHW normalization
// 注意,這里tar和src進(jìn)行遍歷的方式是不一樣的
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:
這個函數(shù)也執(zhí)行雙線性插值縮放、BGR到RGB顏色轉(zhuǎn)換、格式轉(zhuǎn)換,但在縮放過程中還考慮了坐標(biāo)的平移(shift),并進(jìn)行了像素值歸一化。
__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上的坐標(biāo)
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// bilinear interpolation -- 計算x,y映射到原圖時最近的4個坐標(biāo)
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 -- 對于越界的坐標(biāo)不進(jìn)行計算
} else {
// bilinear interpolation -- 計算原圖上的坐標(biāo)(浮點類型)在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個坐標(biāo)所對應(yīng)的索引
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 -- 計算原圖在目標(biāo)圖中的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 -- 實現(xiàn)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:
這兩個函數(shù)類似于之前描述的_norm版本的函數(shù),但它們不進(jìn)行像素值的歸一化處理。
__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上的坐標(biāo)
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// nearest neighbour -- 計算最近坐標(biāo)
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 -- 對于越界的部分,不進(jìn)行計算
} else {
// nearest neighbour -- 計算tar中對應(yīng)坐標(biāo)的索引
int tarIdx = y * tarW + x;
int tarArea = tarW * tarH;
// nearest neighbour -- 計算src中最近鄰坐標(biāo)的索引
int srcIdx = (src_y * srcW + src_x) * 3;
// nearest neighbour -- 實現(xiàn)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,但考慮了坐標(biāo)的平移(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上的坐標(biāo)
int x = blockIdx.x * blockDim.x + threadIdx.x;
int y = blockIdx.y * blockDim.y + threadIdx.y;
// bilinear interpolation -- 計算x,y映射到原圖時最近的4個坐標(biāo)
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 -- 對于越界的坐標(biāo)不進(jìn)行計算
} else {
// bilinear interpolation -- 計算原圖上的坐標(biāo)(浮點類型)在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個坐標(biāo)所對應(yīng)的索引
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 -- 計算原圖在目標(biāo)圖中的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 -- 實現(xiàn)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:
執(zhí)行仿射變換,并將圖像從BGR格式轉(zhuǎn)換為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 (兩個重載版本):
這兩個函數(shù)是對CUDA核函數(shù)的封裝,用于在GPU上執(zhí)行圖像的縮放操作。它們設(shè)置CUDA的線程塊和網(wǎng)格尺寸,計算縮放比例,并根據(jù)指定的策略(如最鄰近、雙線性插值等)選擇相應(yīng)的核函數(shù)進(jìn)行圖像處理。一個版本還包括了像素值歸一化的步驟。 這里基本上除了warpffine都是分類網(wǎng)絡(luò)的前處理, 各種不同的可以拿來做實驗
4.后處理
重點是下面這種寫法, 因為vector這種數(shù)據(jù)結(jié)構(gòu)的類型你在中間刪掉了一個,他會動到整個vector, 所以這里bbox會賦予一個flag模式,這樣最后是給通過這個查看是否添加進(jìn)最后的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在網(wǎng)上有很多實現(xiàn)方法,其中有一些是根據(jù)nms的值來動態(tài)改變final_bboex的大小(resize, erease)
* 這里需要注意的是,頻繁的對vector的大小的更改的空間復(fù)雜度會比較大,所以盡量不要這么做
* 可以通過給bbox設(shè)置skip計算的flg來調(diào)整。
*/
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的任務(wù)
- 是否在input/output附近做了int8量化
- 如果是multi-task的話,是否所有的task都掉點嚴(yán)重
- calibration的數(shù)據(jù)集是不是選的不是很好
- calibration batch size是不是選擇的不是很好
- calibrator是不是沒有選擇好
- 某些計算是否不應(yīng)該做量化
- 使用polygr分析
原文鏈接:https://mp.weixin.qq.com/s/n9DGA0Wmk1MTiFggoW2e4Q