news 2026/8/20 20:05:52

(cuda学习)临近点插值与双线性插值的实现以及相关的算子融合

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
(cuda学习)临近点插值与双线性插值的实现以及相关的算子融合

1.临近点插值:

__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)

这次学习cuda,我发现一个技巧就是,在对矩阵或者图像进行计算时,我们的线程是面向结果的,就是说将线程安排好与结果相对,然后去反推计算过程,得到最初的数据与线程的关系。

有这个思想,就可以想到对于临近点插值,我们第一步就说把tar的二维索引与cuda的线程对齐。

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

这两步得到thread的全局索引,我们把它也视为是tar的二维索引。

现在我们开始反推,去得到src_x与src_y.

无论是临近点插值还是双线性插值,我们第一步都是算scale_w和scale_h。

float scaled_h = (float)srcH / tarH; float scaled_w = (float)srcW / tarW;

建议这一步可以放到最后,因为无论是临近还是双线性都会用到,没必要两次核函数都算一次。

有了scale后我们可以去还原坐标。

int src_y = floor((float)y * scaled_h); int src_x = floor((float)x * scaled_w);

这里我们要注意的是,我们求出来的这两个坐标都是比真实的小的。由于floor函数直接把浮点变成整型了,他会直接无视小数。

此时两张图的x,y已经有了映射关系,就差遍历了。

这次我们不仅做了resize,而且在遍历的时候要把BGR2RGB,nhwc2nchw,norm这三个算子也融合在一起。

这里面要讲一件事情,对于opencv来说一张图片的格式是[rgb,rgb,rgb......],而我们在tensorrt还是别的架构,一般是[rrrrrrrrr...,gggggggggg.......,bbbbbbbb.......],所以两个的遍历方式也是不同的,我们要做的是要把src的r,g,b都单独挑出来,然后齐齐整整的放到tar中。

对于tar来说,第一步,对于每张图片,我们都要把二维的坐标去转换为一维的索引。这里有个公式:

int tarIdx = y * tarW + x;

这个公式把我们一张二维图片的所有坐标转化为索引。

我们要分成3个空间,每个空间的大小为一张图片的size。也就是说一张图片的size应该是

(h*w*c*sizeof(float))才能储存一张图片数据。

那仔细想想我们要把tar分成3段,每段大小为[h*w].则:

int tarArea = tarW * tarH;

现在我们如果存储r的像素值:则是

tar[tar_idx+tar_area*0]

如果是g则把0换成1,b则是换成2.

现在对于src来说,我们要去拿:由于结构不同,我们遍历的方式也不同。

但是第一步都是去把二维转化为一维:

src_idx = src_y*src_w +src_x

如果我们想拿g,bgrbgrbgr.....,这三个循环,第一个g的索引为1,第二个为4,第三个为7,发现规律就是:1+src_idx*3

如果是r能,则是2+src_idx*3,发现就说最初索引值+一维索引值×3,我们可以把一维索引值*3看成一步走多宽,如果*1,则走的很短,而×3则恰好每一步都走到相同的像素上。

两个遍历我们都完成了,剩下就说直接去赋值了

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];

这一步我们不仅做了赋值,也把BGR2RGB,nhwc2nchw,norm给做了。

__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]; } }

现在来做双线性插值,我们发现直接找临近值明显误差很大,不平滑,而双线性插值则解决了这个问题:

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;

我们不仅优化了最初的临近点的选择,又增加了一个坐标。根据排列组合,现在有4个点,那该如何处理把这个像素值完美的赋值给tar呢,我们去算一个权重,这个权重能算这4个点到底那个与tar(x,y)有关。原理是这样的

我们看阴影的面积,阴影面积越大代表什么,p点离a点越近,所以点对应的面积就是这个权重,我们可以算出来ABCD这四个点对应的面积也就是权重。

// 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; //左上

我们算出来每个点对tar(x,y)的重要性了,现在对于tar来说遍历还是那样遍历

int tarIdx = y * tarW + x; int tarArea = tarW * tarH;

但是我们要把ABCD这4个点的二维都转成一维的索引,这四个像素值×权重后加一起得到的那个最终的像素值就是最适合tar(x,y)的值:

这是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; //右下

现在我们索引结束了,剩下的就是传值了

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];

似乎不需要用round取证会更好。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/19 13:13:00

Android HAL开发中的常见陷阱与优化策略

Android HAL开发中的常见陷阱与优化策略 在移动设备开发领域&#xff0c;硬件抽象层&#xff08;HAL&#xff09;作为连接Android框架与底层硬件的桥梁&#xff0c;其稳定性和性能直接影响用户体验。许多开发者在初次接触HAL开发时&#xff0c;往往会在架构设计、接口实现和性能…

作者头像 李华
网站建设 2026/8/19 1:29:32

小白必看:Qwen3-ASR-0.6B语音识别工具快速上手教程

小白必看&#xff1a;Qwen3-ASR-0.6B语音识别工具快速上手教程 你是否遇到过这些场景&#xff1a; 会议录音堆在文件夹里迟迟没整理&#xff1f; 采访素材听一遍写不出三句话&#xff1f; 学生课堂录音想转成笔记却卡在第一步&#xff1f; 又或者&#xff0c;只是想把一段播客…

作者头像 李华
网站建设 2026/8/19 13:13:17

Gemma-3-270m体验报告:Ollama部署下的文本生成效果实测

Gemma-3-270m体验报告&#xff1a;Ollama部署下的文本生成效果实测 1. 为什么选Gemma-3-270m&#xff1f;轻量不等于将就 你可能已经注意到&#xff0c;现在大模型圈里有个新趋势&#xff1a;不是参数越多越好&#xff0c;而是“刚刚好”才最聪明。Gemma-3-270m就是这个思路的…

作者头像 李华
网站建设 2026/8/19 14:24:37

REX-UniNLU与YOLOv8:智能安防系统

REX-UniNLU与YOLOv8&#xff1a;智能安防系统 1. 当监控画面里突然出现异常&#xff0c;系统能“看懂”并“说清楚”吗 安防系统最怕的不是摄像头不够多&#xff0c;而是画面里发生了什么&#xff0c;系统却一无所知。比如深夜仓库门口有人徘徊&#xff0c;系统只记录下一段视…

作者头像 李华
网站建设 2026/8/19 14:21:27

YOLOv8 vs YOLOv5性能对比:实时检测精度与速度实测分析

YOLOv8 vs YOLOv5性能对比&#xff1a;实时检测精度与速度实测分析 1. 为什么这场对比值得你花三分钟看完 你有没有遇到过这样的情况&#xff1a;在部署一个目标检测系统时&#xff0c;面对 YOLOv5 和 YOLOv8 两个选项&#xff0c;犹豫不决&#xff1f; 一边是久经考验、文档…

作者头像 李华
网站建设 2026/8/19 14:31:17

ChatGLM3-6B本地化部署价值:规避API调用限频、费用与供应商锁定风险

ChatGLM3-6B本地化部署价值&#xff1a;规避API调用限频、费用与供应商锁定风险 1. 为什么非得把大模型搬回自己服务器&#xff1f; 你有没有遇到过这些情况&#xff1f; 刚写到一半的代码解释&#xff0c;API突然返回“Rate limit exceeded”&#xff1b; 给客户演示智能文档…

作者头像 李华