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

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

3.绘制直线

#include <opencv2/opencv.hpp>
#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);
	}

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

猜你喜欢

转载自blog.csdn.net/wyw0000/article/details/132729596
今日推荐