cuda上使用remap函数

在使用opencv中的remap函数时,发现运行时间太长了,如果使用视频流进行重映射时根本不能实时,因此只能加速

1.使用opencv里的cv::cuda::remap函数

cv::cuda::remap函数头文件是#include <opencv2/cudawarping.hpp>,编译opencv时需要用cuda进行编译

    //1.重映射矩阵转成cuda处理的数据格式
    //map_x,map_y是重映射表,数据类型是CV_32FC1
	cv::cuda::GpuMat m_mapx = ::cv::cuda::GpuMat(map_x);
	cv::cuda::GpuMat m_mapy = ::cv::cuda::GpuMat(map_y);
    //2.原图像转成cuda处理的数据格式
	cv::cuda::GpuMat src(img);

    //3.计算结果
    cv::cuda::GpuMat gpuMat2;
	cv::cuda::remap(src, gpuMat2, m_mapx, m_mapy, cv::INTER_LINEAR);
    //4.结果转成Mat
	cv::Mat dstimage;		
	gpuMat2.download(dstimage);

示例

#include <iostream>
#include <opencv2/opencv.hpp>
#include <opencv2/cudawarping.hpp>

using namespace cv;

int main(int argc, char** argv) 
{
	Mat img = imread("image.jpg", IMREAD_COLOR);
	if (img.empty())
	{
		std::cout << "Could not open the input image" << std::endl;
		exit(1);
	}

	int in_width = img.cols;
	int in_height = img.rows;

	Mat map_x(in_height, in_width, CV_32FC1);
	Mat map_y(in_height, in_width, CV_32FC1);

	// 创建重映射映射表
	for (int y = 0; y < in_height; y++) 
	{
		for (int x = 0; x < in_width; x++) 
		{
			map_x.at<float>(y, x) = (x + 20) / (float)in_width * in_width;
			map_y.at<float>(y, x) = y / (float)in_height * in_height;
		}
	}
	
	cv::cuda::GpuMat m_mapx = ::cv::cuda::GpuMat(map_x);
	cv::cuda::GpuMat m_mapy = ::cv::cuda::GpuMat(map_y);
	cv::cuda::GpuMat gpuMat1(img);

	double time0 = static_cast<double>(cv::getTickCount());//记录起始时间
	
	cv::cuda::GpuMat gpuMat2;
	cv::cuda::remap(gpuMat1, gpuMat2, m_mapx, m_mapy, cv::INTER_LINEAR);

	cv::Mat GPUimage;
	gpuMat2.download(GPUimage);	
	
	time0 = ((double)cv::getTickCount() - time0) / cv::getTickFrequency();
	std::cout << "GPU运行remap函数的时间为:" << time0 * 1000 << "ms" << std::endl;

	double time1 = static_cast<double>(cv::getTickCount());//记录起始时间

	cv::Mat CPUimage;
	cv::remap(img, CPUimage, map_x, map_y, cv::INTER_LINEAR);

	time1 = ((double)cv::getTickCount() - time1) / cv::getTickFrequency();
	std::cout << "CPU运行remap函数的时间为:" << time1 * 1000 << "ms" << std::endl;

	return 0;
}

经过实际运行,在我电脑上速度快了15倍左右

2.在cuda上重写remap函数

这是在csdn上看到的一篇文章上写的代码,在我的实际应用中变换的结果是错误的,由于我实际的应用时,我的图像输入尺寸和输出尺寸是不相同的,因此运行错误,但是在输入输出是相同尺寸时是正确的,因为使用了cv::cuda::remap,我也没修改这个程序。
建立.cu文件,可以生成静态库使用,也可以不生成使用

#include "cuda_runtime.h"
#include "device_launch_parameters.h"
#include <cuda_runtime_api.h>
#include <stdio.h>
#include <math.h>

__global__ void remap_kernel(const unsigned char* src, int src_width, int src_height,
    unsigned char* dst, int dst_width, int dst_height,
    const float* map_x, const float* map_y) 
{
    int x = threadIdx.x + blockIdx.x * blockDim.x;
    int y = threadIdx.y + blockIdx.y * blockDim.y;

    if (x < dst_width && y < dst_height) 
    {
        int index = (y * dst_width + x) * 3;

        float src_x = map_x[index / 3];
        float src_y = map_y[index / 3];

        if (src_x >= 0 && src_x < src_width - 1 && src_y >= 0 && src_y < src_height - 1) {
            int x0 = floorf(src_x);
            int y0 = floorf(src_y);
            int x1 = x0 + 1;
            int y1 = y0 + 1;

            float tx = src_x - x0;
            float ty = src_y - y0;

            int src_index00 = (y0 * src_width + x0) * 3;
            int src_index10 = (y0 * src_width + x1) * 3;
            int src_index01 = (y1 * src_width + x0) * 3;
            int src_index11 = (y1 * src_width + x1) * 3;

            for (int i = 0; i < 3; i++) {
                float value00 = src[src_index00 + i];
                float value10 = src[src_index10 + i];
                float value01 = src[src_index01 + i];
                float value11 = src[src_index11 + i];

                float value0 = value00 * (1.0f - tx) + value10 * tx;
                float value1 = value01 * (1.0f - tx) + value11 * tx;

                float value = value0 * (1.0f - ty) + value1 * ty;

                dst[index + i] = static_cast<unsigned char>(value);
            }
        }
    }
}

extern "C"  void remap_gpu(const unsigned char* in, int in_width, int in_height,
    unsigned char* out, int out_width, int out_height,
    const float* map_x, const float* map_y) {
    unsigned char* d_in, * d_out;
    float* d_map_x, * d_map_y;

    cudaMalloc((void**)&d_in, in_width * in_height * 3);
    cudaMalloc((void**)&d_out, out_width * out_height * 3);
    cudaMalloc((void**)&d_map_x, out_width * out_height * sizeof(float));
    cudaMalloc((void**)&d_map_y, out_width * out_height * sizeof(float));

    cudaMemcpy(d_in, in, in_width * in_height * 3, cudaMemcpyHostToDevice);
    cudaMemcpy(d_map_x, map_x, out_width * out_height * sizeof(float), cudaMemcpyHostToDevice);
    cudaMemcpy(d_map_y, map_y, out_width * out_height * sizeof(float), cudaMemcpyHostToDevice);

    dim3 block(32, 32, 1);
    dim3 grid((out_width + block.x - 1) / block.x, (out_height + block.y - 1) / block.y, 1);

    remap_kernel << <grid, block >> > (d_in, in_width, in_height, d_out, out_width, out_height, d_map_x, d_map_y);

    cudaMemcpy(out, d_out, out_width * out_height * 3, cudaMemcpyDeviceToHost);

    cudaFree(d_in);
    cudaFree(d_out);
    cudaFree(d_map_x);
    cudaFree(d_map_y);
}

重新新建一个.cpp文件

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;

extern "C"  void remap_gpu(const unsigned char* in, int in_width, int in_height,
	unsigned char* out, int out_width, int out_height,
	const float* map_x, const float* map_y);

int main(int argc, char** argv) 
{
	cv::Mat img = imread("image.jpg", IMREAD_COLOR);
	if (img.empty()) 
	{
		std::cout << "Could not open the input image" << std::endl;
		exit(1);
	}

	int in_width = img.cols;
	int in_height = img.rows;

	cv::Mat map_x(in_height, in_width, CV_32FC1);
	cv::Mat map_y(in_height, in_width, CV_32FC1);

	// 创建重映射映射表
	for (int y = 0; y < in_height; y++) 
	{
		for (int x = 0; x < in_width; x++) 
		{
			map_x.at<float>(y, x) = (x + 20) / (float)in_width * in_width;
			map_y.at<float>(y, x) = y / (float)in_height * in_height;
		}
	}

	double time0 = static_cast<double>(cv::getTickCount());//记录起始时间
	cv::Mat CPUimage;
	remap(img, CPUimage, map_x, map_y, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0, 0, 0));
	
	time0 = ((double)cv::getTickCount() - time0) / cv::getTickFrequency();
	std::cout << "CPU 运行remap函数时间为:" << time0 * 1000 << "ms" << std::endl;
	
	int out_width = in_width;
	int out_height = in_height;	
	unsigned char* out = (unsigned char*)malloc(out_width * out_height * 3);

	double time1 = static_cast<double>(cv::getTickCount());//记录起始时间
	
	unsigned char* in = (unsigned char*)img.data;
	remap_gpu(in, in_width, in_height, out, out_width, out_height, (float*)map_x.data, (float*)map_y.data);
	cv::Mat GPUimage(out_height, out_width, CV_8UC3, out);
	
	time1 = ((double)cv::getTickCount() - time1) / cv::getTickFrequency();
	std::cout << "GPU 运行remap函数时间为:" << time1 * 1000 << "ms" << std::endl;

	free(out);
	return 0;
}

只运行一帧时cpu上运行的remap较快,运行多帧时,GPU上运行的remap函数要比CPU上运行快5倍左右

总结

如果自己编译的opencv带cuda,最好还是使用cv::cuda::remap函数,耗时较少

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
OpenCV中的remap函数可以实现图像的重映射操作。它可以用于实现各种图像处理操作,例如图像缩放、旋转、镜像等。具体来说,remap函数可以将输入图像中的每个像素映射到输出图像中的新位置。 remap函数的原型如下: ```c++ void cv::remap(InputArray src, OutputArray dst, InputArray map1, InputArray map2, int interpolation, int borderMode, const Scalar& borderValue) ``` 参数说明: - src:输入图像。 - dst:输出图像。 - map1:第一个映射矩阵,可以是浮点型的二维数组或者单通道的浮点型图像。 - map2:第二个映射矩阵,可以是浮点型的二维数组或者单通道的浮点型图像。 - interpolation:插值方法,可以取以下值之一:INTER_NEAREST、INTER_LINEAR、INTER_CUBIC、INTER_AREA、INTER_LANCZOS4。 - borderMode:边界模式,可以取以下值之一:BORDER_CONSTANT、BORDER_REPLICATE、BORDER_REFLECT、BORDER_WRAP、BORDER_REFLECT_101。 - borderValue:边界值,当borderMode为BORDER_CONSTANT时,用于填充边界的像素值。 具体来说,remap函数将输入图像中的每个像素(x,y)通过map1(x,y)和map2(x,y)映射到输出图像中的位置(x',y'),然后使用指定的插值方法计算输出图像中位置(x',y')处的像素值。如果映射矩阵的尺寸与输入图像的尺寸不匹配,则只处理匹配部分。 下面是一个简单的例子,演示如何使用remap函数实现图像的水平镜像: ```c++ cv::Mat input_image = cv::imread("input_image.jpg"); cv::Mat map_x(input_image.size(), CV_32FC1); cv::Mat map_y(input_image.size(), CV_32FC1); for (int i = 0; i < input_image.rows; i++) { for (int j = 0; j < input_image.cols; j++) { map_x.at<float>(i, j) = input_image.cols - j - 1; map_y.at<float>(i, j) = i; } } cv::Mat output_image; cv::remap(input_image, output_image, map_x, map_y, cv::INTER_LINEAR); cv::imshow("Input Image", input_image); cv::imshow("Output Image", output_image); cv::waitKey(0); ``` 上述代码中,我们首先创建两个映射矩阵map_x和map_y,用于实现水平镜像。然后,我们将输入图像和映射矩阵作为参数调用remap函数,得到输出图像output_image。最后,我们使用imshow函数显示输入图像和输出图像。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值