[12] 使用 CUDA 进行图像处理

使用 CUDA 进行图像处理

  • 当下生活在高清摄像头的时代,这种摄像头能捕获高达1920*1920像素的高解析度画幅。想要实施的处理这么多的数据,往往需要几个TFlops地浮点处理性能,这些要求CPU也无法满足
  • 通过在代码中使用CUDA,可以利用GPU提供的强大地计算能力
  • CUDA支持多维地Grid和块,因此可以根据图像地尺寸、数据量大小,合理的分配块和线程进行图像处理
  • 简单图像处理过程地特定编程模式:
for(int i=0;i<image_height;i++)
{
    for(int j=0;j<image_width;j++)
    {
        //Pixel Processing code for pixel located at(i,j)
    }
}
  • 将像素处理映射到CUDA地一批线程上:
int i = blockidx.y * blockDim.y + threadIdx.y
int j = blockidx.x * blockDim.x + threadIdx.x

1. 在GPU上通过CUDA进行直方图统计

  • 首先介绍CPU版本的直方图统计,实现如下:
int h_a[1000] = Random values between 0 and 15

//假设图像取值范围在【0-15】,定义数组并初始化
int histogram[16];
for(int i=0;i<16;i++)
{
    histogram[i] = 0;
}
//统计每个值的个数
for(int i=0;i<1000;i++)
{
    histogram[h_a[i]]+=1;
}
  • 下面写一个同样功能的GPU代码,我们将使用3种不同的方法写这个代码,前两种方法的内核代码如下:
__global__ void histogram_without_atomic(int* d_b, int* d_a)
{
	int tid = threadIdx.x + blockDim.x * blockIdx.x;
	int item = d_a[tid];
	if (tid < SIZE)
	{
		d_b[item]++;
	}

}

__global__ void histogram_atomic(int* d_b, int* d_a)
{
	int tid = threadIdx.x + blockDim.x * blockIdx.x;
	int item = d_a[tid];
	if (tid < SIZE)
	{
		atomicAdd(&(d_b[item]), 1);
	}
}
  • 第一个函数是最简单方式实现的直方图统计,每个线程读取 1 个元素值。使用线程ID作为输入数组的索引获取该元素的数值,然后此值再将对应的d_b结果数组中的索引位置处进行 +1 操作。最后d_b数组应该包含输入数据中0-15之间每个值的频次,这种方式将得出错误的结果,因为对相同的存储器位置将有大量的线程试图同时进行不安全的修改,其运行结果如下:
    在这里插入图片描述
  • 第二个函数用原子操作实现统计,避免多线程并行时的资源占用导致的计算异常问题,其计算结果如下:
    在这里插入图片描述
  • main函数如下:
int main()
{
	//定义设备变量并分配内存
	int h_a[SIZE];
	for (int i = 0; i < SIZE; i++) {

		h_a[i] = i % NUM_BIN;
	}
	int h_b[NUM_BIN];
	for (int i = 0; i < NUM_BIN; i++) {
		h_b[i] = 0;
	}

	// 声明GPU指针变量
	int* d_a;
	int* d_b;

	// 分配GPU变量内存
	cudaMalloc((void**)&d_a, SIZE * sizeof(int));
	cudaMalloc((void**)&d_b, NUM_BIN * sizeof(int));

	// transfer the arrays to the GPU
	cudaMemcpy(d_a, h_a, SIZE * sizeof(int), cudaMemcpyHostToDevice);
	cudaMemcpy(d_b, h_b, NUM_BIN * sizeof(int), cudaMemcpyHostToDevice);

	// 进行直方图统计
	//histogram_without_atomic << <((SIZE + NUM_BIN - 1) / NUM_BIN), NUM_BIN >> > (d_b, d_a);
	histogram_atomic << <((SIZE+NUM_BIN-1) / NUM_BIN), NUM_BIN >> >(d_b, d_a);

	// copy back the sum from GPU
	cudaMemcpy(h_b, d_b, NUM_BIN * sizeof(int), cudaMemcpyDeviceToHost);
	printf("Histogram using 16 bin without shared Memory is: \n");
	for (int i = 0; i < NUM_BIN; i++) {
		printf("bin %d: count %d\n", i, h_b[i]);
	}

	// free GPU memory allocation
	cudaFree(d_a);
	cudaFree(d_b);
	return 0;
}
  • 当我们试图测量使用了原子操作的该代码的性能的时候,你会发现相比CPU的性能,对于很大规模的数组,GPU的实现更慢。这就引入了一个问题:我们真的应当使用CUDA进行直方图统计吗?如果必须能否将这个计算更快些?
  • 这两个问题的答案都是:YES 。如果我们在一个块中用共享内存进行直方图统计,最后再将每个块的部分统计结果叠加到全局内存上的最终结果上去。这样就能加速该操作。这是因为整数加法满足交换律。我需要补充的是:只有当原始数据就在GPU的显存上的时候,才应当考虑使用GPU计算,否则完全不应当 cudaMemcpy 过来再计算,因为仅 cudaMemcpy 的时间就将等于或者大于 CPU 计算的时间,用共享内存进行直方图统计的内核函数代码实现如下:
#include <stdio.h>
#include <cuda_runtime.h>

#define SIZE 1000
#define NUM_BIN 256

__global__ void histogram_shared_memory(int* d_b, int* d_a)
{
	int tid = threadIdx.x + blockDim.x * blockIdx.x;
	int offset = blockDim.x * gridDim.x;
	__shared__ int cache[256];
	cache[threadIdx.x] = 0;
	__syncthreads();

	while (tid < SIZE)
	{
		atomicAdd(&(cache[d_a[tid]]), 1);
		tid += offset;
	}
	__syncthreads();
	atomicAdd(&(d_b[threadIdx.x]), cache[threadIdx.x]);
}
  • 我们要为当前的每个块都统计一次局部结果,所以需要先将共享内存清空,然后用类似之前的方式在共享内存中进行直方图统计。这种情况下,每个块只会统计部分结果存储在各自的共享内存中,并非像以前那样直接统计为全局内存上的总体结果。
  • 本例中,块中256个线程进行共享内存上的256个元素的访问,而原本的代码则在全局内存上的16个元素位置上进行访问。因为共享内存本身要比全局内存具有更高效的并行访问性能,同时将16个统一的竞争访问的位置放宽到了每个共享内存上的256个竞争位置,这两个因素共同缩小了原子操作累计统计的时间。
  • 最终还需要进行一次原子操作,将每个块的共享内存上的部分统计结果累加到全局内存上的最终统计结果。因为整数加法满足交换律,我们不需要担心每个块执行的顺序。
  • main函数如上一个类似:
int main()
{
	// generate the input array on the host
	int h_a[SIZE];
	for (int i = 0; i < SIZE; i++) {
		//h_a[i] = bit_reverse(i, log2(SIZE));
		h_a[i] = i % NUM_BIN;
	}
	int h_b[NUM_BIN];
	for (int i = 0; i < NUM_BIN; i++) {
		h_b[i] = 0;
	}

	// declare GPU memory pointers
	int* d_a;
	int* d_b;

	// allocate GPU memory
	cudaMalloc((void**)&d_a, SIZE * sizeof(int));
	cudaMalloc((void**)&d_b, NUM_BIN * sizeof(int));

	// transfer the arrays to the GPU
	cudaMemcpy(d_a, h_a, SIZE * sizeof(int), cudaMemcpyHostToDevice);
	cudaMemcpy(d_b, h_b, NUM_BIN * sizeof(int), cudaMemcpyHostToDevice);

	// launch the kernel
	histogram_shared_memory << <SIZE / 256, 256 >> > (d_b, d_a);

	// copy back the result from GPU
	cudaMemcpy(h_b, d_b, NUM_BIN * sizeof(int), cudaMemcpyDeviceToHost);
	printf("Histogram using 16 bin is: ");
	for (int i = 0; i < NUM_BIN; i++) {
		printf("bin %d: count %d\n", i, h_b[i]);
	}

	// free GPU memory allocation
	cudaFree(d_a);
	cudaFree(d_b);

	return 0;
}
  • 执行结果:
    在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/703568.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

简单项目——前后端分离实现博客系统

文章目录 一、项目实现的准备工作二、数据库的设计以及构建三、封装数据库连接、创建实体类四、封装数据库的增删查改操作五、实现博客系统核心操作1.获取博客列表页2.获取博客详情页3. 实现博客登录页4. 实现所有页面检查并强制登录5.退出登录状态6. 实现博客发布7. 实现删除文…

联想电脑 调节屏幕亮度不起使用,按F5,F6,屏幕上的hotkeys进度条是在改变,但是屏幕没有一些作用的处理方法

1、查看驱动是否正常 Win键X &#xff0c;设备管理器 发现似乎挺正常的。 查看原厂驱动&#xff1a;联想电脑管家 这样看来&#xff0c;驱动是没有问题了。 2、看看设置电池模式 其实还是这个电池模式的问题导致。 如果处于养护模式的话&#xff0c;充电只在75%~80%&#x…

重生之 SpringBoot3 入门保姆级学习(18、事件驱动开发解耦合)

重生之 SpringBoot3 入门保姆级学习&#xff08;18、事件驱动开发解耦合&#xff09; 5、SpringBoot3 核心5.1 原始开发5.2 事件驱动开发 5、SpringBoot3 核心 5.1 原始开发 LoginController package com.zhong.bootcenter.controller;import com.zhong.bootcenter.service.A…

嵌入式实训day2

1、 counteval(input("请输入两位数")) jincount//16 liangcount%16 print(jin,"斤",liang,"两") 2、 numeval(input("请输入一个三位数:")) res0 resnum%10 resnum%100//10 resres//100 print("res",res) 3、 4、字符串比大…

一个Anki填空题模板

Anki自带的填空题模板无法输入答案&#xff0c;显示也极为简陋。通过对Anki自带的填空题模板进行改造&#xff0c;做出了下面的填空题模板。这个模板有两个字段——题面和章节。前者保存题目及正确答案&#xff0c;后者保存与本题相关的知识在教材中的章节。题面可以用类似{{c1…

C++发送邮件的性能如何优化?有哪些方法?

C发送邮件怎么配置SMTP服务器&#xff1f;如何使用C库发信&#xff1f; 在现代应用程序中&#xff0c;电子邮件发送是一个常见的功能。尤其对于需要发送大量邮件的企业级应用&#xff0c;优化邮件发送性能变得尤为重要。AokSend将探讨在使用C发送邮件时&#xff0c;如何通过各…

为什么代理IP都没有100%可用性?

在当今高度互联的网络环境中&#xff0c;代理IP已成为许多网络活动的重要支撑工具&#xff0c;从数据收集到业务推广&#xff0c;无所不包。然而&#xff0c;代理IP在很多场景中发挥着重要作用&#xff0c;却很难实现100%的可用性。 这种情况并非偶然&#xff0c;而是受到多重复…

如何保证数据库和缓存的一致性

背景&#xff1a;为了提高查询效率&#xff0c;一般会用redis作为缓存。客户端查询数据时&#xff0c;如果能直接命中缓存&#xff0c;就不用再去查数据库&#xff0c;从而减轻数据库的压力&#xff0c;而且redis是基于内存的数据库&#xff0c;读取速度比数据库要快很多。 更新…

大型语言模型(LLMs)的后门攻击和防御技术

大型语言模型&#xff08;LLMs&#xff09;通过训练在大量文本语料库上&#xff0c;展示了在多种自然语言处理&#xff08;NLP&#xff09;应用中取得最先进性能的能力。与基础语言模型相比&#xff0c;LLMs在少样本学习和零样本学习场景中取得了显著的性能提升&#xff0c;这得…

一文详解:信息化/数字化以及数智化的区别与联系

数字化转型是现代企业在竞争激烈的市场环境中保持竞争力的关键策略。数字化转型通常被分为三个阶段&#xff1a;信息化、数字化和数智化。 每个阶段都有其独特的特点和挑战&#xff0c;下面将详细阐述这三个阶段&#xff0c;并通过实际案例来说明其应用的区别和效果。 低成本起…

收银系统源码推荐

1.收银系统源码开发语言 核心开发语言: PHP、HTML5、Dart后台接口: PHP7.3后台管理网站: HTML5vue2.0element-uicssjs收银端【安卓/PC收银】: Dart3&#xff0c;框架&#xff1a;Flutter 3.11.0-6.0.pre.27商家小程序助手端: uniapp线上商城: uniapp 2.功能介绍 支持测试体验…

ipv6有状态分配地址

RA报文M/O标志位 设备在获取IPv6地址等信息时&#xff0c;会先发送RS报文请求链路上的路由设备&#xff0c;路由设备受到RS报文后会发送相应的RA报文来表示自身能够提供的IPv6服务类型。 对于RA报文&#xff0c;根据其M字段和O字段确定其获取IPv6地址的模式&#xff1a; M/O都…

(八)Mybatis持久化框架原理之不同Executor对比和Spring事务关系

文章目录 1. SqlSession的差异2. Executor的差异2.1 SimpleExecutor流程说明2.2 ReuseExecutor流程说明2.3 BatchExecutor流程说明 3. Mybatis事务4. Spring事务5. 总结 本篇文章主要是由一次批量插入数据而引起的思考与探究&#xff0c;在这篇文章中将会分析不同的Executor和S…

QT——设计概述

一、QT的概述 1、QT是什么? Qt是一个跨平台的 C++ 开发库,主要用来开发图形用户界面(Graphical User Interface,GUI)程序,当然也可以开发不带界面的命令行(Command User Interface,CUI)程序。 2、QT可以做什么? Qt 虽然经常被当做一个 GUI 库,用来开发图形界面应…

Vue3 + Element Plus项目el-table表格里使用el-switch开关按钮效果

期望结果&#xff1a; 表格中组件&#xff1a; 在开关外层用插槽包裹&#xff0c;里面写v-model用来绑定字段 <!--用插槽包裹el-switch开关--><template #default"scope"><el-switch active-text"启用" :active-value1 active-color"…

快捷键专栏 IDEA、Navicat、电脑、Excle、Word等

标题 电脑篇windowsR 配合以下常用命令连上公司网线WiFi速度变慢问题解决Windows10 设置鼠标右键在此处打开cmd和Powershell窗口、关机打开电脑诊断工具系统设置常用设置查看电脑出场日期 systeminfo删除文件显示已在另一个程序打开&#xff1f;找回回收站删除的文件WindowsR输…

如何用Pycharm把python代码打包成exe文件

在terminal 里面输入pyinstaller --onefile --noconsole chuli_v2.py –noconsole 这个选项会生成一个不带控制台窗口的 .exe 文件

STM32微控制器库指南:函数特性、应用范围与实践

在嵌入式系统的设计和开发中&#xff0c;STM32系列微控制器因其卓越的处理能力和多样的外设选项而广受推崇。STM32库函数作为开发流程中不可或缺的工具&#xff0c;扮演着至关重要的角色。本文将详细阐述STM32库函数的主要特性、应用场景及其在实际开发中的应用实例。 什么是ST…

记录一下:vue3+antd-vue a-form包含a-table实现表格行内校验, 清除 指定行 指定字段 的校验

问题描述下&#xff1a; 目标VLAN值可以输入&#xff0c; 也可以点击后面的按钮进行弹窗选择。由于检验原因&#xff0c;光标失焦但是没有填写就会校验爆红&#xff0c;然后点击选择之后由于没有失焦过程没有触发校验&#xff0c;依然还是红的。这个时候就需要清除 目标VLAN值 …

推荐网站(22)GeoSpy,根据图片显示地理位置

今天推荐一款名为GeoSpy的AI工具。它利用人工智能技术&#xff0c;通过分析照片中的光线、植被、建筑风格等细节线索&#xff0c;实现对拍摄地点的精确定位。令人难以置信的是,它对位置的定位准确度非常高。 GeoSpy之所以智能如此,是因为它将输入的照片与大量的街景和地理图像…