模拟Gaussian模糊计算

本文介绍了一个使用CUDA实现的高斯模糊模拟程序。该程序通过计算矩阵中每个元素与其周围元素的和来模拟高斯模糊效果,并展示了如何利用CUDA进行并行计算。
就是计算一个矩阵,其中的每个值是周围所有值的和!
/**************************************************
*功能描述:此函数完成一个高斯模糊模拟过程,即
*			对于一张图片(这里简单为一个矩阵)
*			每一个像素值等于它周围所有像素值的和。
*			有考虑到了有补齐的问题,以后就只考虑
*			对齐情况了,知道补齐的思想就好了。
**************************************************/

#include<iostream>
#include<stdlib.h>
#include<stdio.h>
#include<cuda_runtime.h>
#include<device_launch_parameters.h>

#define M 64
#define N 64
#define size M*N*sizeof(float)
using namespace std;

void showResult(float *p);
void init(float *p,int num,int value);
__global__ void Raussian_kernel(float *d_a,float *d_b);
int main()
{
	float *h_a=(float*)malloc(size);//定义主机指针并分配空间
	float *h_b=(float*)malloc(size);
	//初始化输入数据
	init(h_a,M*N,1);//输入矩阵初始化为1
	//showResult(h_a);
	init(h_b,M*N,0);//输出矩阵初始化为0
	//showResult(h_b);
	cudaError_t err=cudaSuccess;
	float *d_a,*d_b;//定义设备指针,分配空间并初始化为0
	err=cudaMalloc((void**)&d_a,size);cudaMemset(d_a,0,size);
	cudaMalloc((void**)&d_b,size);cudaMemset(d_b,0,size);
	if(err!=cudaSuccess)
	{
		fprintf(stderr,"error=%s\n",cudaGetErrorString(err));
		exit(EXIT_FAILURE);
	}
	err=cudaMemcpy(d_a,h_a,size,cudaMemcpyHostToDevice);//主机向设备拷贝数据
	if(err!=cudaSuccess)
	{
		fprintf(stderr,"error=%s\n",cudaGetErrorString(err));
		exit(EXIT_FAILURE);
	}
	dim3 threadsPerBlock(32,32);
	dim3 blocksPerGrid(N/32,M/32);//这里只考虑对齐的情况,所以就不搞那么复杂的判断了
	//启动核函数
	Raussian_kernel<<<blocksPerGrid,threadsPerBlock>>>(d_a,d_b);
	cudaMemcpy(h_b,d_b,size,cudaMemcpyDeviceToHost);
	//显示结果
	showResult(h_b);
	return 0;
}
//初始化函数
void init(float *p,int num,int value)//num表示有多少个数
{
	for(int i=0;i<num;i++)
	{
		p[i]=value;
	}
}
//显示函数
void showResult(float *p)
{
	for(int i=0;i<M;i++)
	{
		for(int j=0;j<N;j++)
		{
			cout<<p[i*N+j]<<'\t';
		}
		cout<<endl;
	}
}
__global__ void Raussian_kernel(float *d_a,float *d_b)//M*N矩阵
{
	int tid_in_grid_x=blockIdx.x*blockDim.x+threadIdx.x;
	int tid_in_grid_y=blockIdx.y*blockDim.y+threadIdx.y;
	float sum=0.0f;
	for(int i=tid_in_grid_y-1;i<=tid_in_grid_y+1;i++)//i是行,j是列,注意线程中的的区别,x是列,y是行
	{
		for(int j=tid_in_grid_x-1;j<=tid_in_grid_x+1;j++)
		{
			if(i>=0&&i<=(M-1)&&j>=0&&j<=(N-1))//四个条件必须同时满足才进行取值求和操作
				sum+=d_a[i*N+j];
		}
	}
	sum-=d_a[tid_in_grid_y*N+tid_in_grid_x];//把自己本身再减掉
	d_b[tid_in_grid_y*N+tid_in_grid_x]=sum;
}

<think> 当在OpenCV的GaussianBlur函数中将ksize参数设置为0时,OpenCV会根据提供的sigma值自动计算内核大小。 具体来说,内核大小是通过sigma计算得到的,公式为:ksize = int(2 * round(3 * sigma) + 1) 这样做的目的是确保高斯核覆盖了高斯分布的99.7%的区域(3σ原则)。 步骤: 1. 如果ksize设置为0,那么OpenCV会根据sigma值(如果sigma没有指定,则根据sigmaX和sigmaY)来计算内核大小。 2. 如果sigma也设置为0,那么OpenCV会根据ksize来自动计算sigma(但此时ksize为0,所以会使用默认的sigma值)。 注意:在OpenCV中,高斯模糊函数有两种常见的调用方式: - 指定ksize和sigma:如果ksize不为0,则使用指定的ksize;如果ksize为0,则根据sigma计算ksize。 - 如果sigma也不指定,那么sigma将根据ksize计算(但此时ksize为0,就会使用默认的ksize和sigma?实际上,OpenCV有默认规则)。 具体在OpenCV的文档中: - 如果ksize设置为0,那么内核大小由sigma计算而来。 - 同样,如果sigma设置为0,那么sigma由内核大小计算而来(但此时ksize为0,所以sigma会使用默认值?实际上,当ksize为0且sigma不为0时,内核大小由sigma计算;当ksize为0且sigma也为0时,那么sigma将取默认值0.3*((ksize-1)*0.5-1)+0.8,但此时ksize为0,所以这个公式不适用。因此,OpenCV内部实际上会有一个默认的sigma值吗?) 实际上,在OpenCV的源代码中,处理逻辑如下(以C++为例,但逻辑相同): if (ksize.width == 0 && sigma1 > 0) ksize.width = cvRound(sigma1 * (depth == CV_8U ? 3 : 4)*2 + 1)|1; if (ksize.height == 0 && sigma2 > 0) ksize.height = cvRound(sigma2 * (depth == CV_8U ? 3 : 4)*2 + 1)|1; 如果sigma1或sigma2没有被设置(即<=0),那么它们会由ksize计算: if( sigma1 <= 0 ) sigma1 = ((ksize.width-1)*0.5 - 1)*0.3 + 0.8; if( sigma2 <= 0 ) sigma2 = ((ksize.height-1)*0.5 - 1)*0.3 + 0.8; 但在Python接口中,我们通常这样调用:cv2.GaussianBlur(src, ksize, sigmaX[, dst[, sigmaY[, borderType]]]) 因此,当ksize=(0,0)时: - 如果sigmaX>0,则ksize.width会根据sigmaX计算:ksize.width = round(sigmaX * 3 * 2 + 1) | 1 (即奇数) - 如果sigmaX<=0(未指定或指定为0),那么就会使用默认的sigmaX(由ksize.width计算,但ksize.width为0,所以OpenCV会报错?) 实际上,在OpenCV的Python接口中,当ksize=(0,0)且sigmaX=0时,会引发错误吗?还是使用默认值? 我们通过实验来验证: 在OpenCV 4.5.5中,如果调用: img = np.zeros((100,100), np.uint8) blur = cv2.GaussianBlur(img, (0,0), 0) 会抛出错误:error: (-215:Assertion failed) ksize.width > 0 && ksize.width % 2 == 1 && ksize.height > 0 && ksize.height % 2 == 1 in function 'GaussianBlur' 因此,当ksize为(0,0)时,必须指定一个大于0的sigmaX,这样OpenCV才能计算内核大小。 结论: 当ksize设置为(0,0)时,OpenCV会根据sigmaX和sigmaY的值自动计算内核大小。 计算规则是:对于每个维度,如果ksize为0且sigma>0,则内核大小设置为:round(3*sigma)*2+1(或者对于非8U图像可能是4*sigma,但通常对于8U图像是3倍sigma),然后确保为奇数。 示例代码: 假设我们设置sigmaX=1.0,ksize=(0,0),那么OpenCV会自动计算ksize为: ksize = int(6*sigmaX + 1) 即 6*1+1=7 -> ksize=(7,7) 因此,我们可以这样使用: blur = cv2.GaussianBlur(img, (0,0), sigmaX=1.0) 如果我们同时指定sigmaY,例如: blur = cv2.GaussianBlur(img, (0,0), sigmaX=1.0, sigmaY=2.0) 那么宽度方向的内核大小根据sigmaX=1.0计算,高度方向根据sigmaY=2.0计算。 注意:在调用时,sigmaY可以省略,如果省略则等于sigmaX。 所以,当ksize为0时的行为总结如下: 1. ksize必须为奇数且正数,或者设为(0,0)以根据sigma计算。 2. 当ksize=(0,0)时,sigmaX必须大于0(否则报错)。 3. 内核大小由公式:ksize = round(2 * 3 * sigma) + 1 计算得到(即6*sigma,然后四舍五入取整,再加1,保证是奇数)。 下面用一个函数来模拟OpenCV的行为: </think> # OpenCV中`ksize=0`时的高斯模糊计算原理 在OpenCV的`cv2.GaussianBlur()`函数中,当`ksize`参数设置为`(0,0)`时,OpenCV会根据提供的`sigmaX`和`sigmaY`值**自动计算**合适的内核大小。这是OpenCV提供的一个智能特性,允许开发者专注于模糊程度控制,而无需手动计算内核尺寸。 ## 核心计算原理 ### 1. 内核大小自动推导公式 当`ksize=0`时,OpenCV使用以下公式计算内核尺寸: ``` ksize = 2 * round(3 * sigma) + 1 ``` 例如: ```python sigma = 3.0 计算过程: 3 * sigma = 9 round(9) = 9 2 * 9 + 1 = 19 最终内核大小:(19,19) ``` ### 2. 边界情况处理 ```python # 边界情况验证 sigma_values = [0.5, 1.0, 2.0, 3.5] for s in sigma_values: ksize = 2 * round(3 * s) + 1 print(f"sigma={s:.1f} => ksize={ksize}") # 输出: # sigma=0.5 => ksize=5 # sigma=1.0 => ksize=7 # sigma=2.0 => ksize=13 # sigma=3.5 => ksize=23 ``` ## 实现机制解析 ### OpenCV内部等效代码 ```python def calculate_ksize(sigma): """计算高斯核尺寸的等效代码""" # 确保sigma为浮点数 sigma = float(sigma) # 核心计算公式 ksize = int(2 * round(3 * sigma) + 1) # 确保奇数且不小于3 ksize = max(3, ksize) if ksize % 2 == 0: ksize += 1 # 保证奇数 return (ksize, ksize) def auto_gaussian_blur(image, sigmaX, sigmaY=None): """ksize=0时的高斯模糊等效实现""" # 处理默认值 if sigmaY is None: sigmaY = sigmaX # 计算内核大小 ksizeX = calculate_ksize(sigmaX)[0] ksizeY = calculate_ksize(sigmaY)[0] # 应用高斯模糊 return cv2.GaussianBlur(image, (ksizeX, ksizeY), sigmaX, sigmaY) # 使用示例 image = cv2.imread('example.jpg') blurred = auto_gaussian_blur(image, sigmaX=1.5) ``` ### 数学原理 高斯核的权重分布服从二维高斯函数: ``` G(x,y) = (1/(2πσ²)) * exp(-(x²+y²)/(2σ²)) ``` 其中: - `σ`(sigma)控制分布的宽度 - 3σ原则:99.7%的权重分布在距离中心3σ范围内 OpenCV使用`ksize = 6σ + 1`(取整)确保覆盖99.73%的权重分布区域。 ## 实际应用场景 ### 1. 自适应模糊处理 ```python def adaptive_blur(image, min_sigma=0.5, max_sigma=5.0): """根据图像内容动态调整模糊程度""" # 计算图像梯度方差作为模糊程度指标 gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) grad = cv2.Laplacian(gray, cv2.CV_32F).var() # 根据梯度自动计算sigma sigma = max_sigma - ((grad - 10) / (100 - 10)) * (max_sigma - min_sigma) sigma = np.clip(sigma, min_sigma, max_sigma) # 应用自动大小的高斯模糊 return cv2.GaussianBlur(image, (0,0), sigma) # 使用示例 image = cv2.imread('input.jpg') result = adaptive_blur(image) ``` ### 2. 多尺度模糊处理 ```python def multi_scale_blur(image, sigmas=[1.0, 2.5, 4.0]): """多尺度高斯模糊融合""" blurred_images = [] for sigma in sigmas: # 使用自动内核大小 blurred = cv2.GaussianBlur(image, (0,0), sigma) blurred_images.append(blurred) # 创建融合结果 result = np.zeros_like(image, dtype=np.float32) # 加权融合 weights = [1.0, 0.7, 0.3] # 不同模糊程度的权重 for img, weight in zip(blurred_images, weights): result += img.astype(np.float32) * weight result /= sum(weights) return result.astype(np.uint8) ``` ## 与手动指定ksize的对比 | 参数方式 | 优点 | 缺点 | 适用场景 | |---------|------|------|---------| | **ksize=0** | 自动适配sigma值<br>减少参数调整负担<br>保证模糊效果一致性 | 无法精确控制内核大小<br>计算开销略
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值