如何在cv::cuda::GpuMat上创建指定的多边形遮罩?
OpenCV CUDA 环境下GpuMat填充三角形遮罩实现方案

方案1:CPU-GPU拷贝折中方案
适合遮罩尺寸小、调用频率不高的场景,代码改动量最小:
- 在CPU内存创建同规格的
cv::Mat遮罩,沿用原有cv::fillConvexPoly完成三角形绘制 - 调用
upload方法将CPU遮罩上传到GPU的cv::cuda::GpuMat即可
代码示例:
// 入参准备:三个顶点坐标、遮罩尺寸 std::vector<cv::Point> triangle_pts = {A, B, C}; int mask_w = 1920; int mask_h = 1080; // CPU侧绘制遮罩 cv::Mat mask_cpu(mask_h, mask_w, CV_8UC1, cv::Scalar(0)); cv::fillConvexPoly(mask_cpu, triangle_pts, cv::Scalar(255)); // 上传到GPU cv::cuda::GpuMat mask_gpu; mask_gpu.upload(mask_cpu);
优缺点:实现简单无额外开发成本,仅多一次CPU到GPU的数据拷贝,性能要求不高的场景可直接使用。
方案2:纯CUDA管线实现
适合高性能场景,全流程跑在GPU上无数据拷贝开销,自己实现三角形光栅化逻辑:
核心思路是用重心坐标法判断每个像素是否在三角形内部,直接在CUDA核函数中写入遮罩值。
代码示例:
// CUDA核函数:逐像素判断是否在三角形内,生成遮罩 __global__ void fill_triangle_kernel(uint8_t* mask_data, int width, int height, size_t step, float ax, float ay, float bx, float by, float cx, float cy) { int x = blockIdx.x * blockDim.x + threadIdx.x; int y = blockIdx.y * blockDim.y + threadIdx.y; if (x >= width || y >= height) return; // 计算重心坐标权重 float v0x = cx - ax, v0y = cy - ay; float v1x = bx - ax, v1y = by - ay; float v2x = x - ax, v2y = y - ay; float dot00 = v0x * v0x + v0y * v0y; float dot01 = v0x * v1x + v0y * v1y; float dot02 = v0x * v2x + v0y * v2y; float dot11 = v1x * v1x + v1y * v1y; float dot12 = v1x * v2x + v1y * v2y; float denom = dot00 * dot11 - dot01 * dot01; // 避免三个点共线导致除零错误 if (fabs(denom) < 1e-6) { mask_data[y * step + x] = 0; return; } float inv_denom = 1.0f / denom; float u = (dot11 * dot02 - dot01 * dot12) * inv_denom; float v = (dot00 * dot12 - dot01 * dot02) * inv_denom; // 权重均非负且和不超过1即为三角形内部点 mask_data[y * step + x] = (u >= 0 && v >= 0 && u + v <= 1.0f) ? 255 : 0; } // 核函数调用逻辑 cv::cuda::GpuMat mask_gpu(mask_h, mask_w, CV_8UC1, cv::Scalar(0)); dim3 block_size(16, 16); dim3 grid_size((mask_w + block_size.x - 1) / block_size.x, (mask_h + block_size.y - 1) / block_size.y); fill_triangle_kernel<<<grid_size, block_size>>>(mask_gpu.data, mask_gpu.cols, mask_gpu.rows, mask_gpu.step, A.x, A.y, B.x, B.y, C.x, C.y); // 可选:同步等待核函数执行完成,后续要立刻操作mask_gpu的情况下需要加 cudaDeviceSynchronize();
注意点:如果需要抗锯齿效果,可对边缘像素的权重做插值计算,生成带透明度的遮罩。
内容的提问来源于stack exchange,提问作者user16532502
相关产品推荐
相关产品推荐

