CUDA-均值滤波算法

作者:翟天保Steven
版权声明:著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处

实现原理

       均值滤波是一种常见的图像处理方法,用于去除图像中的噪声。其原理很简单:对图像中的每个像素,取其周围邻域像素的均值作为新的像素值。这个邻域可以是一个矩形、圆形或其他形状的区域,取决于具体的实现。

       具体来说,均值滤波的原理可以分为以下几个步骤:

  1. 定义滤波器大小:确定用于计算均值的邻域大小。通常使用一个固定大小的窗口,例如3x3、5x5或7x7的矩形窗口。

  2. 遍历图像:对于图像中的每个像素,将邻域范围内的所有像素值相加。

  3. 计算均值:将邻域内所有像素值的总和除以像素数量,得到均值。

  4. 更新像素值:用计算得到的均值替换原始像素的值。

       这个过程会对图像中的每个像素都进行处理,从而实现对整个图像的平滑处理,去除噪声。

       均值滤波的优点是简单易实现,并且能有效地去除图像中的噪声。然而,它也有一些缺点,比如可能会导致图像边缘模糊,因为它只是简单地取邻域内像素的平均值,而不考虑像素之间的空间关系。

       本文主要目的在于展示CUDA版本的性能提升效果,采用常规思路实现,CPU版本应用了并行提速,与GPU并行客观对比。

功能函数代码

// 均值滤波核函数
__global__ void meanFilter_CUDA(uchar* inputImage, uchar* outputImage, int width, int height, int windowSize)
{
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;

    if (row < height && col < width) 
    {
        int r = windowSize / 2;
        int ms = max(row - r, 0);
        int me = min(row + r, height - 1);
        int ns = max(col - r, 0);
        int ne = min(col + r, width - 1);

        float sum = 0.0f;
        int count = 0;

        for (int m = ms; m <= me; ++m) 
        {
            for (int n = ns; n <= ne; ++n) 
            {
                sum += inputImage[m * width + n];
                count++;
            }
        }
        outputImage[row * width + col] = uchar(sum / count);
    }
}

C++测试代码

Filter.h

#pragma once
#include <cuda_runtime.h>
#include <iostream>
#include <vector>
#include <opencv2/opencv.hpp>
#include <device_launch_parameters.h>

using namespace cv;
using namespace std;

// 预准备过程
void warmupCUDA();

// 均值滤波-GPU
cv::Mat filterAverage_GPU(cv::Mat input, int FilterWindowSize);

Filter.cu

#include "Filter.h"

// 预准备过程
void warmupCUDA()
{
    float* dummy_data;
    cudaMalloc((void**)&dummy_data, sizeof(float));
    cudaFree(dummy_data);
}

// 均值滤波核函数
__global__ void meanFilter_CUDA(uchar* inputImage, uchar* outputImage, int width, int height, int windowSize)
{
    int row = blockIdx.y * blockDim.y + threadIdx.y;
    int col = blockIdx.x * blockDim.x + threadIdx.x;

    if (row < height && col < width) 
    {
        int r = windowSize / 2;
        int ms = max(row - r, 0);
        int me = min(row + r, height - 1);
        int ns = max(col - r, 0);
        int ne = min(col + r, width - 1);

        float sum = 0.0f;
        int count = 0;

        for (int m = ms; m <= me; ++m) 
        {
            for (int n = ns; n <= ne; ++n) 
            {
                sum += inputImage[m * width + n];
                count++;
            }
        }
        outputImage[row * width + col] = uchar(sum / count);
    }
}

// 均值滤波-GPU
cv::Mat filterAverage_GPU(cv::Mat input, int FilterWindowSize)
{
    int row = input.rows;
    int col = input.cols;

    // 分配GPU内存
    uchar* d_inputImage, * d_outputImage;
    cudaMalloc(&d_inputImage, row * col * sizeof(uchar));
    cudaMalloc(&d_outputImage, row * col * sizeof(uchar));

    // 将输入图像数据从主机内存复制到GPU内存
    cudaMemcpy(d_inputImage, input.data, row * col * sizeof(uchar), cudaMemcpyHostToDevice);

    // 计算块和线程的大小
    dim3 blockSize(16, 16);
    dim3 gridSize((col + blockSize.x - 1) / blockSize.x, (row + blockSize.y - 1) / blockSize.y);

    // 调用CUDA内核
    meanFilter_CUDA << <gridSize, blockSize >> > (d_inputImage, d_outputImage, col, row, FilterWindowSize);

    // 将处理后的图像数据从GPU内存复制回主机内存
    cv::Mat output(row, col, CV_8UC1);
    cudaMemcpy(output.data, d_outputImage, row * col * sizeof(uchar), cudaMemcpyDeviceToHost);

    // 清理GPU内存
    cudaFree(d_inputImage);
    cudaFree(d_outputImage);

    return output;
}

main.cpp

#include "Filter.h"

// 均值滤波-CPU
cv::Mat filterAverage_CPU(cv::Mat input, int FilterWindowSize)
{
	int row = input.rows;
	int col = input.cols;

	// 预设输出
	cv::Mat output = input.clone();

	// 均值滤波
	int r = FilterWindowSize / 2;
#pragma omp parallel for
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			// 卷积窗口边界限制,防止越界
			int ms = ((i - r) > 0) ? (i - r) : 0;
			int me = ((i + r) < (row - 1)) ? (i + r) : (row - 1);
			int ns = ((j - r) > 0) ? (j - r) : 0;
			int ne = ((j + r) < (col - 1)) ? (j + r) : (col - 1);
			// 求窗口内有效数据的均值
			int count = 0;
			float sum = 0.0f;
			for (int m = ms; m <= me; ++m)
			{
				for (int n = ns; n <= ne; ++n)
				{
					sum += input.at<uchar>(m, n);
					count++;
				}
			}
			output.at<uchar>(i, j) = uchar(sum / count);
		}
	}

	return output;
}

void main()
{
	// 加载
	cv::Mat src = imread("test1.jpg", 0);
	int winSize = 51;
	// 预准备过程
	warmupCUDA();
	cout << "filterWindowSize:" << winSize << endl;
	cout << "size: " << src.cols << " * " << src.rows << endl;

	// CPU版本
	clock_t s1, e1;
	s1 = clock();
	cv::Mat output1 = filterAverage_CPU(src, winSize);
	e1 = clock();
	cout << "CPU time:" << double(e1 - s1) / 1000 << endl;

	// GPU版本
	clock_t s2, e2;
	s2 = clock();
	cv::Mat output2 = filterAverage_GPU(src, winSize);
	e2 = clock();
	cout << "GPU time:" << double(e2 - s2) / 1000 << endl;

	// 检查
	int row = src.rows;
	int col = src.cols;
	bool flag = true;
	for (int i = 0; i < row; ++i)
	{
		for (int j = 0; j < col; ++j)
		{
			if (output1.at<uchar>(i, j) != output2.at<uchar>(i, j))
			{
				flag = false;
				break;
			}
		}
		if (!flag)
		{
			break;
		}
	}
	if (flag)
	{
		cout << "ok!" << endl;
	}
	else
	{
		cout << "error!" << endl;
	}

	// 查看输出
	cv::Mat test1 = output1.clone();
	cv::Mat test2 = output2.clone();
	cout << "mean test." << endl;
	
}

测试效果 

       如上图所示,分别是原图、CPU结果和GPU结果,在速度方面,对5120*2880的图像,在窗口尺寸为51*51时,我的电脑运行速度分别是1.15s和0.03s。

       我在不同窗尺寸下进行了几轮测试,测试结果仅供参考。

       在CUDA编程中,内存访问是一个关键的性能瓶颈。均值滤波相较中值滤波,只需要对数据进行求和平均即可,不需要额外申请内存进行排序或其他操作,因此速度提升非常明显。

       如果函数有什么可以改进完善的地方,非常欢迎大家指出,一同进步何乐而不为呢~

       如果文章帮助到你了,可以点个赞让我知道,我会很快乐~加油!

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

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

相关文章

高频电源模块TL22010-T2整流模块TL22010-T3用途

直流屏充电模块TL22010-T2电源模块TL22010-T3&#xff0c;整流模块TL11010-T3&#xff0c;其他TL系列型号有&#xff1a;TL11020-T3&#xff0c;TL11010-T2&#xff0c;TL11020-T2&#xff0c;TL22010-T&#xff0c;TL-22010-T&#xff0c;TL-11010-T&#xff0c;TL-22005-T&am…

排序-选择排序(selection sort)

选择排序&#xff08;selection sort&#xff09;的工作原理非常简单&#xff1a;开启一个循环&#xff0c;每轮从未排序区间选择最小的元素&#xff0c;将其放到已排序区间的末尾。选择排序的主要特点包括&#xff1a; 时间复杂度&#xff1a; 无论最好、最坏还是平均情况&…

【图像增强(空域)】基于灰度变换的图像增强及Matlab仿真

1. 摘要&#xff1a; 空域内的图像增强就是调整灰度图像的明暗对比度&#xff0c;对图像中各个像素的灰度值直接进行处理。常用的方法包括&#xff1a;灰度变换增强和直方图增强。 2. 原理&#xff1a; 灰度变换增强是空域ne对图像进行增强的一种简单且直接的方法。灰度变换…

26版SPSS操作教程(高级教程第二十章)

目录 前言 粉丝及官方意见说明 第二十章一些学习笔记 第二十章一些操作方法 神经网络与支持向量机 人工神经网络&#xff08;artificial neural network&#xff0c;ANN&#xff09; 假设数据 具体操作 结果解释 对案例的进一步分析 结果解释 ​编辑 尝试将模型复…

Pikachu 靶场 XXE 通关解析

前言 Pikachu靶场是一种常见的网络安全训练平台&#xff0c;用于模拟真实世界中的网络攻击和防御场景。它提供了一系列的实验室环境&#xff0c;供安全专业人士、学生和爱好者练习和测试他们的技能。 Pikachu靶场的目的是帮助用户了解和掌握网络攻击的原理和技术&#xff0c;…

软件测评中心详细介绍

软件产品已成为企业和个人工作中不可或缺的一部分。然而&#xff0c;面对市场上琳琅满目的软件产品&#xff0c;用户如何判断其质量、性能及是否满足自身需求呢&#xff1f;这时&#xff0c;软件测评中心的作用就显得尤为重要。本文将详细介绍软件测评中心的定义、功能及其重要…

【全开源】商会招商项目系统基于FastAdmin+ThinkPHP+Uniapp(源码搭建/上线/运营/售后/维护更新)

一款基于FastAdminThinkPHPUniapp开发的商会招商项目系统&#xff0c;是一个集PC和移动端功能于一体的解决方案&#xff0c;线上线下进行服务&#xff0c;围绕 活动报名、在线课程、项目大厅、线下签到、会员系统等。为商会提供了更加便捷高效的管理方式&#xff0c;提升了商会…

令牌桶算法:如何优雅地处理突发流量?

令牌桶算法的介绍 在网络流量控制和请求限流中&#xff0c;令牌桶算法是一种常用的策略。那么&#xff0c;令牌桶算法到底是什么呢&#xff1f;它的工作原理又是怎样的呢&#xff1f;让我们一起来探索一下。 令牌桶算法&#xff0c;顾名思义&#xff0c;就是有一个存放令牌的…

【Redis】Redis数据类型

目录 Redis五种数据类型 String字符串类型 字符串中最常使用的命令 List列表类型 List命令 Set与Zset集合类型 Redis五种数据类型 String字符串类型 有效的字符串类型不仅可以是字符&#xff0c;还可以是数字&#xff0c;以上都是有效的字符串类型。 String最大容量为51…

革新中小学食堂体验:智能消费管理系统方案

背景挑战 中小学校园的食堂总是一幅熙熙攘攘的景象。每到用餐高峰期&#xff0c;不仅排队时间漫长&#xff0c;而且维护良好的就餐秩序也成为了一项挑战。针对这些问题&#xff0c;优化学校食堂的管理并提升师生的就餐体验显得尤为迫切。 痛点与需求分析 在现有的食堂管理模…

python微信小程序 uniapp高校打印店预约服务系统

本系统是针对校园自助打印开发的工作管理系统&#xff0c;包括到所有的工作内容。可以使自助打印的工作合理化和流程化。本系统包括手机端设计和电脑端设计&#xff0c;有界面和数据库。本系统的使用角色分为管理员和用户、店长三个身份。管理员可以管理系统里的所有信息。店长…

Colab微调LLaMA3模型(大模型的微调)

Colab微调LLaMA3模型 1.使用的数据集 在hugging face上搜索 kigner/ruozhiba-llama3 使用的是弱智吧的数据 2.打开Colab 选择Gpu版本T2就可以了&#xff0c;然后下载unsloth这个微调框架&#xff0c;使用该框架的主要原因在于对硬件要求比较低。 在安装这个前先看一下本文…

Python 运筹优化12 eps greedy 解读

说明 Epsilon-Greedy&#xff08;ε-Greedy&#xff09;是一种用于解决多臂LH机问题&#xff08;Multi-Armed Bandit Problem&#xff09;的策略&#xff0c;通常在强化学习中使用。在多臂LH机问题中&#xff0c;有多个选项&#xff08;臂&#xff09;&#xff0c;每个选项都有…

CST电磁仿真的查看2D/3D结果和查看端口模式【基础教学】

查看2D/3D结果 场结果的定制化显示&#xff01; Navigation Tree > 2D/3D Results 当我们使用CST软件时&#xff0c;可以在Field Monitor中设置好需要查看的频点后运行仿真&#xff0c;仿真完成后就可以在NavigationTree中确认场结果。可以有多种类型的绘图展示仿真分析得…

ROM修改进阶教程------twrp下使用一键adb脚本进行清除 格式化data和刷写分区操作

一键刷机大多用于fast模式刷写,今天给友友来讲讲如何一键刷入twrp后不用触摸操作手机 。纯手动指令来清除分区 格式化data和刷写固件的操作。此操作不是sideload方式哦。边搜网络貌似也没有相关的文章。而twrp一般都习惯于手动触摸操作卡刷卡刷包。玩机类的经验主要是有一点点…

树莓派nmap扫描

debian系统安装nmap&#xff1a; sudo apt install nmap安装nmap完成后&#xff0c;输入 ip route 来查看当前Wi-Fi路由器的ip地址。 第一行的default via后显示的便是网关地址&#xff0c;也就是路由器地址。 获取到路由器ip地址后&#xff0c;在终端中输入&#xff1a; …

微信小程序Vue+uniapp餐饮美食订餐骑手配送系统9g60o

本小程序uniapp菜品帮采用Java语言和Mysql数据库进行设计&#xff0c;技术采用微信小程序&#xff0c;可以不安装App软件就实现订餐。本系统实现管理员和用户、商家、配送员四个角色的功能。用户主要在微信端操作&#xff0c;内容有菜品信息&#xff0c;用户可以在线点餐和管理…

Mac安装jadx

1、使用命令brew安装 : brew install jadx 输入完命令,等待安装完毕 备注&#xff08;关于Homebrew &#xff09;&#xff1a; Homebrew 是 MacOS 下的包管理工具&#xff0c;类似 apt-get/apt 之于 Linux&#xff0c;yum 之于 CentOS。如果一款软件发布时支持了 homebrew 安…

浮点数的由来及运算解析

数学是自然科学的皇后&#xff0c;计算机的设计初衷是科学计算。计算机的最基本功能是需要存储整数、实数&#xff0c;及对整数和实数进行算术四则运算。 但是在计算机从业者的眼中&#xff0c;我们知道的数学相关的基本数据类型通常是整型、浮点型、布尔型。整型又分为int8&a…

[AI OpenAI-doc] 延迟优化

本指南涵盖了一系列核心原则&#xff0c;您可以应用这些原则来改善在各种LLM相关用例中的延迟。这些技术来自于与广泛的客户和开发人员在生产应用程序上的合作&#xff0c;因此无论您正在构建什么——从细粒度的工作流程到端到端的聊天机器人&#xff0c;都应该适用&#xff01…