CUDA-中值滤波算法

news2024/12/23 6:15:00

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

实现原理

       中值滤波是一种常用的图像处理方法,特别适用于去除图像中的脉冲噪声(如椒盐噪声)。与均值滤波不同的是,中值滤波通过选择像素邻域中的中值来替代中心像素的值,从而保留图像边缘的细节。中值滤波的原理如下:

  1. 定义滤波器大小:选择一个窗口大小,通常是一个固定的矩形区域,比如3x3、5x5或7x7。这个窗口决定了要参与计算的邻域像素范围。

  2. 遍历图像:对于图像中的每一个像素,将其邻域范围内的所有像素值取出并排列成一个集合。

  3. 计算中值:将邻域中的像素值从小到大排序,取出中间位置的值作为中值。如果邻域的像素数是奇数,则中值为排序后正中间的像素值;如果是偶数,则通常取中间两个值的平均值作为中值。

  4. 更新像素值:将中值替代原像素的值,完成对当前像素的处理。

  5. 重复步骤:对图像中的所有像素都重复上述过程,从而对整幅图像进行中值滤波处理。

       中值滤波的一个显著优势是它能够在去除噪声的同时,较好地保留图像中的边缘信息,这是由于中值操作不会将像素邻域的极值(如噪声点)引入新的像素值中。相比于均值滤波,中值滤波在处理图像中的脉冲噪声时效果更为显著。

       中值滤波的缺点在于其计算复杂度相对较高,尤其是在处理大尺寸图像时,计算时间较长。此外,如果邻域内噪声比例较大,中值滤波的效果可能不如其他更复杂的滤波方法。

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

功能函数代码

// 中值滤波核函数
__global__ void medianFilter_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)
	{
		// 参数预设
		uchar datas[25];
		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);
		// 赋值
		int count = 0;
		for (int m = ms; m <= me; ++m)
		{
			for (int n = ns; n <= ne; ++n)
			{
				datas[count++] = inputImage[m * width + n];
			}
		}
		// 选择排序
		for (int i = 0; i < count - 1; i++)
		{
			int minIndex = i;
			for (int j = i + 1; j < count; j++)
			{
				if (datas[j] < datas[minIndex])
				{
					minIndex = j;
				}
			}
			uchar temp = datas[i];
			datas[i] = datas[minIndex];
			datas[minIndex] = temp;
		}
		outputImage[row * width + col] = datas[count / 2];
	}
}

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();

// 中值滤波-CPU
cv::Mat filterMedian_CPU(cv::Mat input, int FilterWindowSize);

// 中值滤波-GPU
cv::Mat filterMedian_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);
}

// 中值滤波-CPU
cv::Mat filterMedian_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)
	{
		vector<uchar> datas;
		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);
			// 求窗口内有效数据的中值
			datas.clear();
			for (int m = ms; m <= me; ++m)
			{
				for (int n = ns; n <= ne; ++n)
				{
					datas.push_back(input.at<uchar>(m, n));
				}
			}
			sort(datas.begin(), datas.end());
			output.at<uchar>(i, j) = datas[datas.size() / 2];
		}
	}

	return output;
}
// 中值滤波核函数
__global__ void medianFilter_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)
	{
		// 参数预设
		uchar datas[25];
		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);
		// 赋值
		int count = 0;
		for (int m = ms; m <= me; ++m)
		{
			for (int n = ns; n <= ne; ++n)
			{
				datas[count++] = inputImage[m * width + n];
			}
		}
		// 选择排序
		for (int i = 0; i < count - 1; i++)
		{
			int minIndex = i;
			for (int j = i + 1; j < count; j++)
			{
				if (datas[j] < datas[minIndex])
				{
					minIndex = j;
				}
			}
			uchar temp = datas[i];
			datas[i] = datas[minIndex];
			datas[minIndex] = temp;
		}
		outputImage[row * width + col] = datas[count / 2];
	}
}
// 中值滤波-GPU
cv::Mat filterMedian_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(TILE_WIDTH, TILE_WIDTH);
	dim3 gridSize((col + blockSize.x - 1) / blockSize.x, (row + blockSize.y - 1) / blockSize.y);

	// 调用CUDA内核
	medianFilter_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"

void main()
{
    // 预准备
	warmupCUDA();

	cout << "medianFilter test begin." << endl;
	// 加载
	cv::Mat src = imread("test pic/test1.jpg", 0);
	int winSize = 5;
	cout << "filterWindowSize:" << winSize << endl;
	cout << "size: " << src.cols << " * " << src.rows << endl;

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

	// GPU版本
	clock_t s2, e2;
	s2 = clock();
	cv::Mat output2 = filterMedian_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))
			{
				cout << "i:" << i << " j:" << j << endl;
				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 << "medianFilter test end." << endl;
	
}

测试效果 

       如上图所示,分别是原图、CPU结果和GPU结果,在速度方面,对1920*1080的图像,在窗口尺寸为5*5时,我的电脑运行速度分别是0.526s和0.018s。

       将滤波窗尺寸增加到9*9,注意核函数里申请空间也要增加,如下所示。速度差距依然很大。当滤波窗尺寸继续增加时,GPU和CPU的速度差异会越来越小,主要原因就是CUDA核函数中进行了过多申请空间的操作,这个开销不容小觑。

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

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

基于IOT的供电房监控系统(实物)

aliyun_mqtt.cpp 本次设计利用ESP8266作为系统主控&#xff0c;利用超声波检测门的状态&#xff0c;利用DHT11检测环境温湿度、烟雾传感器检测空气中的气体浓度&#xff0c;利用火焰报警器模块检测火焰状态&#xff0c;使用OLED进行可视化显示&#xff0c;系统显示传感器数据&a…

同相放大器电路设计

1 简介 同相放大电路输入阻抗为运放的极高输入阻抗&#xff08;GΩ级&#xff09;&#xff0c;因此可处理高阻抗输入源信号。同相放大器的共模电压等于输入信号。 2 设计目标 2.1 输入 2.2 输出 2.3 频率 2.4 电源 3 电路设计 根据设计目标&#xff0c;最终设计的电路结构…

python-确定进制

题目描述 6 942 对于十进制来说是错误的&#xff0c;但是对于 13 进制来说是正确的。即 6(13)​ 9(13)​42(13)​&#xff0c;而 42(13)​4 13^12 13^054(10)​。 你的任务是写一段程序读入三个整数 p,q 和 r&#xff0c;然后确定一个进制 B(2≤B≤16) 使得 p qr 。如果 B 有…

Vue3: 使用ref自动补齐.value

目录 一.老版本&#xff08;已经弃用TypeScript Vue Plugin (Volar)&#xff09; 二.新版本&#xff08;Vue - Official&#xff09; 三.勾选后重启VScode 四.效果 VScode中搜索Vue - Official插件 一.老版本&#xff08;已经弃用TypeScript Vue Plugin (Volar)&#xff0…

学习之git的远程仓库操作的常用命令

1 git remote -v 查看当前所有远程地址别名 2 git remote add 别名 远程地址 3 git push 别名 分支&#xff08;本地分支名称&#xff09; 推送本地分支到远程仓库 4 git pull 远程库别名 远程分支别名 拉取远程库分支&#xff08;更新代码&#xff09; 5 git clone 远程库地址…

【时间盒子】-【6.任务页面】在同一个页面新建、编辑任务

Tips: Column组件的使用&#xff1b; color.json资源文件的使用。 一、页面布局 页面分为三个部分&#xff0c;从上往下分别是&#xff1a;标题菜单栏、时间选择器和任务列表。每个部分都可以设计为独立的组件&#xff0c;后续文章分别介绍。 二、新建页面 右击pages目录&…

Vue:指令

目录 指令概念内容渲染指令**{{ }}****v-text****v-html** 属性绑定指令 v-bind绑定属性**绑定class****绑定style**动态绑定属性**绑定对象** 事件绑定指令 v-onv-on 基础event**$event** 双向绑定指令 v-modelv-model 基础v-model 值绑定**v-model 指令的修饰符** 条件渲染指…

解锁全球机遇:澳大利亚服务器租用市场的独特魅力

在浩瀚的全球数字版图中&#xff0c;澳大利亚以其独特的地理位置、丰富的资源禀赋、以及日益增长的数字经济活力&#xff0c;成为了众多互联网企业竞相布局的重要市场。特别是当谈及服务器租用这一关键环节时&#xff0c;澳大利亚以其稳定的网络环境、先进的基础设施和开放的市…

使用原生HTML的drag实现元素的拖拽

HTML 拖放&#xff08;Drag and Drop&#xff09;接口使应用程序能够在浏览器中使用拖放功能。例如&#xff0c;用户可使用鼠标选择可拖拽&#xff08;draggable&#xff09;元素&#xff0c;将元素拖拽到可放置&#xff08;droppable&#xff09;元素&#xff0c;并释放鼠标按…

GPIO 简介(STM32F407)

一、GPIO简介 什么是GPIO GPIO即通用输入输出端口&#xff0c;全称General Purpose Input Output&#xff0c;是控制或者采集外部器件的信息的外设&#xff0c;即负责输入输出。 它按组分配存在&#xff0c;每组最多16个IO口&#xff0c;组数视芯片而定。比如STM32F407ZGT6是…

今年中秋节买什么东西划算?精选五款好物清单合集推荐!

谈及中秋佳节选购什么好物&#xff0c;你可真是找对人了&#xff01;作为一位专注于节日氛围与生活美学的博主&#xff0c;对于哪些中秋好物能为您的佳节增添温馨与喜悦&#xff0c;我可是了如指掌。恰逢中秋佳节临近&#xff0c;各大商家纷纷推出精彩纷呈的优惠活动&#xff0…

AI产品经理必备技能:技术与能力升级图谱

目 录 CONTENTS 前言 第1章 AI产品经理——不是简单的“当产品经理遇上“AI” 001 1.1 三大浪潮看AI技术发展 002 1.2 AI产品和AI产品经理 003 1.3 成为AI产品经理三步走 011 1.3.1 第一步&#xff1a;找到自己的糖山 011 1.3.2 第二步&#xff1a;找到自己的比较优势 …

联合谱低秩先验和深度空间先验的高光谱图像无监督去噪

高光谱图像&#xff08;Hyperspectral Image, HSI&#xff09;具有丰富的光谱信息&#xff0c;广泛应用于遥感、环境监测和医学成像等领域。然而&#xff0c;高光谱图像常常受到噪声的干扰&#xff0c;这会影响后续的数据分析和应用。因此&#xff0c;设计有效的去噪算法是高光…

计算机毕业设计选题推荐-线上花店系统-鲜花配送系统-Java/Python项目实战

✨作者主页&#xff1a;IT研究室✨ 个人简介&#xff1a;曾从事计算机专业培训教学&#xff0c;擅长Java、Python、微信小程序、Golang、安卓Android等项目实战。接项目定制开发、代码讲解、答辩教学、文档编写、降重等。 ☑文末获取源码☑ 精彩专栏推荐⬇⬇⬇ Java项目 Python…

玩归龙潮手机配置低、卡顿发烫、内存不足 GameViewer远程助你手机畅玩归龙潮

国潮RPG动作游戏《归龙潮》终于在9月13日迎来公测&#xff01;要是你担心自己的手机配置低&#xff0c;会出现卡顿发烫、内存不足等问题&#xff0c;可以通过网易GameViewer远程助你手机远控电脑畅玩归龙潮。 GameViewer远程作为专为游戏玩家打造的远程控制软件&#xff0c;用它…

价值流:从理论框架到实践落地的系统化指南

价值流如何从理论转化为实践中的企业增长引擎 随着全球化和数字化进程的加快&#xff0c;企业面临的竞争压力日益加剧。如何在竞争激烈的市场中立足并实现持续增长&#xff0c;已经成为每一个企业管理者需要面对的重要议题。作为一种强调客户价值创造的工具&#xff0c;《价值…

2024年8月国产数据库大事记-墨天轮

本文为墨天轮社区整理的2024年8月国产数据库大事件和重要产品发布消息。 目录 2024年8月国产数据库大事记 TOP102024年8月国产数据库大事记&#xff08;时间线&#xff09;产品/版本发布兼容认证代表厂商大事记厂商活动 【VLDB2024分享和相关论文】其它活动相关资料 厂商财报…

【STM32】OLED

OLED显示原理 OLED使用的是I2C协议&#xff0c;使用ssd1306芯片对所有灯进行控制&#xff0c;如果一个灯需要一个引脚的话&#xff0c;屏幕分为8页&#xff0c;每页有128列&#xff0c;8行 . 共有128* 88个灯 这样引脚是算不过来的&#xff0c;所以我们使用了ssd1306芯片&…

C/C++语言基础--预编译指令、宏定义(带参宏、宏函数)、头文件重复包含解决方法等

本专栏目的 更新C/C的基础语法&#xff0c;包括C的一些新特性 前言 宏定义是C/C最伟大的发明之一&#xff0c;甚至有人认为他比指针还伟大&#xff0c;它能够极大简化代码&#xff0c;因此学习宏定义是非常有必要的但是由于他只是简单的替换&#xff0c;故在C的efficiency书籍中…

说话人脸生成

说话人脸生成是一种技术&#xff0c;它通过音频信号来驱动和合成视频中的人脸图像&#xff0c;使得人脸的口型和表情与音频中的声音同步。这种技术主要应用于视频制作、虚拟现实、动画电影等领域&#xff0c;能够提升视听媒体的自然性和沉浸感。 使用的技术 说话人脸生成通常…