• cuda画线改进版


    1.计算直线上的所有像素点

    1.1 原理

    使用Bresenham算法计算直线上的所有像素点。

    Bresenham算法是一种用于在离散网格上绘制直线的算法。它是由美国计算机科学家Jack E. Bresenham于1962年提出的。

    该算法的基本思想是从起始点开始,以最小的步长沿着直线绘制像素。以直线斜率的绝对值小于等于1为例,算法通过在x方向上递增一个固定的步长,并在y方向上根据直线斜率进行适当的调整来绘制像素。

    具体步骤如下:

    1. 计算起始点和结束点在x和y方向上的差值,分别记为dx和dy。
    2. 初始化两个变量:d代表决策变量,表示当前像素点的误差;y代表当前像素点的y坐标。
    3. 对于每个x坐标,从起始点的x坐标开始递增,直到达到结束点的x坐标:
      • 绘制当前像素点。
      • 如果决策变量d小于0,表示应该向上移动一个像素,即y坐标减1,并更新决策变量d。
      • 否则,表示应该继续向右上方移动一个像素,即y坐标不变,并更新决策变量d。
      • 更新决策变量d。
    4. 完成绘制。

    1.2 代码

    std::vector<cv::Point> calculatePixelsOnLine(cv::Point start, cv::Point end, int lineWidth)
        {
            std::vector<cv::Point> pixels;
    
            int dx = std::abs(end.x - start.x);
            int dy = std::abs(end.y - start.y);
            int sx = (start.x < end.x) ? 1 : -1;
            int sy = (start.y < end.y) ? 1 : -1;
            int err = dx - dy;
    
            while (true) {
                for (int i = -lineWidth / 2; i <= lineWidth / 2; i++) {
                    for (int j = -lineWidth / 2; j <= lineWidth / 2; j++) {
                        cv::Point pixel = { start.x + i, start.y + j };
                        pixels.push_back(pixel);
                    }
                }
    
                if (start.x == end.x && start.y == end.y) {
                    break;
                }
    
                int e2 = 2 * err;
                if (e2 > -dy) {
                    err -= dy;
                    start.x += sx;
                }
                if (e2 < dx) {
                    err += dx;
                    start.y += sy;
                }
            }
    
            return pixels;
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    Bresenham算法的优点是使用了整数运算,避免了浮点运算,从而提高了计算效率。它适用于在离散网格上绘制直线,如计算机图形学中的像素绘制、线段绘制等应用场景。

    2. CUDA 核函数,将直线像素值画到图像上

    __global__ void drawLineOnImage(cv::cuda::PtrStep<uchar> nv12Data, int nv12Pitch, int imageWidth, int imageHeight, int* linePixels, int numPixels, uchar Y, uchar U, uchar V) {
    		int index = blockIdx.x * blockDim.x + threadIdx.x;
    		if (index < numPixels) {
    			int x = linePixels[index * 2];
    			int y = linePixels[index * 2 + 1];
    
    			// 计算图像数组中对应的索引
    			int imageIndex = y * nv12Pitch + x;
    
    			// 直接在图像数组中写入像素值
    			nv12Data.data[imageIndex] = Y;
    
    			// 在UV分量中写入像素值
    			int uvImageWidth = nv12Pitch / 2;
    			int uvImageHeight = imageHeight / 2;
    			int uvIndex = (y >> 1) * nv12Pitch + (x >> 1) * 2;
    
    			nv12Data.data[nv12Pitch * imageHeight + uvIndex] = U;
    			nv12Data.data[nv12Pitch * imageHeight + uvIndex + 1] = V;
    		}
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    3.绘制直线

    #include 
    #include "cuda_runtime.h"
    
    using namespace cv;
    
    void RGBtoYUV(unsigned char R, unsigned char G, unsigned char B, int& Y, int& U, int& V) {
    	Y = 0.2126 * R + 0.7152 * G + 0.0722 * B;
    	U = -0.1146 * R - 0.3854 * G + 0.5000 * B + 128;
    	V = 0.5000 * R - 0.4542 * G - 0.0458 * B + 128;
    }
    
    void drawLine(cv::cuda::GpuMat& nv12Frame, TyPoint start, TyPoint end, int thickness, cv::Scalar color)
    	{
    		int Y = 0, U = 0, V = 0;
    		RGBtoYUV(color[2], color[1], color[0], Y, U, V);
    
    		vector<cv::Point> linePixels = typonteq::AIUtils::calculatePixelsOnLine({ start.x, start.y }, { end.x, end.y }, thickness);
    
    		int numPixels = linePixels.size();
    
    		// 定义CUDA核函数的块大小和网格大小
    		int blockSize = 256;
    		int numBlocks = (numPixels + blockSize - 1) / blockSize;
    		if (!cudaStream) {
    			cudaSafeCall(cudaStreamCreate(&cudaStream));
    		}
    
    		int* d_linePixels;
    		cudaMalloc((void**)&d_linePixels, linePixels.size() * 2 * sizeof(int));
    		cudaMemcpy(d_linePixels, linePixels.data(), linePixels.size() * 2 * sizeof(int), cudaMemcpyHostToDevice);
    
    		drawLineOnImage << <numBlocks, blockSize, 0, cudaStream >> > (nv12Frame, nv12Frame.step, nv12Frame.cols, nv12Frame.rows / 3 * 2, d_linePixels, numPixels, (uchar)Y, (uchar)U, (uchar)V);
    
    		int ret = cudaGetLastError();
    		if (ret != cudaSuccess) {
    			printf("%s cudaGetLastError:%d\n", __FUNCTION__, ret);
    		}
    		if (!cudaStream)
    			cudaSafeCall(cudaStreamSynchronize(cudaStream));
    
    		// 释放设备内存
    		cudaFree(d_linePixels);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    注意:这里输入image为nv12格式

  • 相关阅读:
    【大型电商项目开发】性能压测-优化-中间件对性能的影响-40
    【C++】类型转换
    Linux下swap(交换分区)的增删改
    Gateway基础使用
    小白学爬虫:通过关键词搜索1688商品列表数据接口|1688商品列表数据接口|1688商品列表数据采集|1688API接口
    Java学习之路 —— IO、特殊文件
    21天学习挑战:经典算法---冒泡排序
    php公用方法
    请问WPF的LiveCharts怎么解决这种X轴Labels自动跟随数据点的情况
    使用 gpg 对Linux下的文件加密
  • 原文地址:https://blog.csdn.net/wyw0000/article/details/132729596