图像像素点赋值_C++ 图像高斯滤波

f905725e77c18d5877b5d1856fb12f57.png

高斯滤波是图像四大滤波之一,四大滤波包括均值滤波,中值滤波,高斯滤波,双边滤波。均值滤波的原理简单直接,就是遍历图像像素点,以当前像素点为中心,将卷积模板内的所有像素点取平均值并设置到当前像素点,虽然也起到了平滑作用,但由于引入了噪声成分,去噪效果不理想,但速度快。高斯滤波的原理是假设像素灰度值成正态分布,以当前像素为中心,离当前像素越远,则占的权重越低。高斯滤波其实也引入了噪声成分。均值滤波和高斯滤波时间复杂度相同。中值滤波则是取卷积模板范围内像素点的中间值,由于每次都需要对卷积模板内的像素值进行排序,所以时间复杂度高,由于中间值基本能排除了噪点,因为噪点的灰度值一般比较突兀,中值滤波去燥效果好,但出来的图像平滑度不高,锯齿现象严重。

二维高斯函数公式:

7cb6e1d9ec5f1e6c80188b261ae43dd2.png

用代码实现该公式:

PI 的计算方式:const double PI = 4.0*std::atan(1.0);

e的x次方我们用std::exp函数进行计算。

算法中我们需要设置sigma的值,即上述公式中的参数,根据sigma值的不同,每个卷积模板的权重也会不同,权重可以用下图形象表示:

96357980c94f03072bda5349144379d6.png

我们先贴一下权重的计算函数:

/**
* @brief 简单概述
* @brief 计算高斯核卷积
* @brief gaus:高斯核卷积(出参)
* @brief k:卷积模板尺寸,3*3,5*5,7*7等
* @brief sigma:权重控制参数
*/
void  CaclGaussianKernel(double *gaus, Kernel k, const double sigma)
{
	//圆周率π赋值
	const double PI = 4.0*std::atan(1.0);
	int center = k /2;
	double param = 1/(2*sigma*sigma);
	double sum = 0;
	int count = 0;
	for (int i = 0; i<k; i++)
	{
		for (int j = 0; j<k; j++)
		{
			gaus[count] = (1/PI*param)*std::exp(-((i-center)*(i-center)+
                                       (j-center)*(j-center))*param);
			sum += gaus[count];
			count++;
		}
	}
	count = 0;
	for (int i = 0; i<k; i++)
	{
		for (int j = 0; j<k; j++)
		{
			gaus[count] /= sum;
			count++;
		}
	}
}

sigma取值对高斯卷积模板的影响:

对于3*3的卷积矩阵,当sigma=0.1时,卷积模板为:
0.000000,0.000000,0.000000,
0.000000,1.000000,0.000000,
0.000000,0.000000,0.000000,

对于3*3的卷积矩阵,当sigma=0.5时,卷积模板为:
0.011344,0.083820,0.011344,
0.083820,0.619347,0.083820,
0.011344,0.083820,0.011344,

对于3*3的卷积矩阵,当sigma=1.0时,卷积模板为:
0.075114,0.123841,0.075114,
0.123841,0.204180,0.123841,
0.075114,0.123841,0.075114,

对于3*3的卷积矩阵,当sigma=5.0时,卷积模板为:
0.109630,0.111844,0.109630,
0.111844,0.114104,0.111844,
0.109630,0.111844,0.109630,

对于3*3的卷积矩阵,当sigma=10.0时,卷积模板为:
0.110741,0.111296,0.110741,
0.111296,0.111854,0.111296,
0.110741,0.111296,0.110741,

对于9*9的卷积矩阵,当sigma=5.0时,卷积模板为:
0.008386,0.009646,0.010661,0.011320,0.011549,0.011320,0.010661,0.009646,0.008386,
0.009646,0.011096,0.012263,0.013021,0.013284,0.013021,0.012263,0.011096,0.009646,
0.010661,0.012263,0.013553,0.014391,0.014681,0.014391,0.013553,0.012263,0.010661,
0.011320,0.013021,0.014391,0.015281,0.015589,0.015281,0.014391,0.013021,0.011320,
0.011549,0.013284,0.014681,0.015589,0.015904,0.015589,0.014681,0.013284,0.011549,
0.011320,0.013021,0.014391,0.015281,0.015589,0.015281,0.014391,0.013021,0.011320,
0.010661,0.012263,0.013553,0.014391,0.014681,0.014391,0.013553,0.012263,0.010661,
0.009646,0.011096,0.012263,0.013021,0.013284,0.013021,0.012263,0.011096,0.009646,
0.008386,0.009646,0.010661,0.011320,0.011549,0.011320,0.010661,0.009646,0.008386,

由此可见,模板较小时当sigma值大于1时,各个像素的权重值近似,所以对于小卷积核sigma值取值范围
应该设置在0-1之间,对于较大的模板,sigma取值可以大于1。

数据结构定义:

/**
* @brief 简单概述
* @brief 图像格式
*/
enum ImageFormat
{
	IMAGE_BINARY	= 0x0001,	// 二值图像
	IMAGE_GREY	= 0x0008,	// 灰度图像
	IMAGE_RGB	= 0x0018,	// 彩色RGB图像
	IMAGE_BGR	= 0x0019,	// 彩色BGR图像
	IMAGE_RGBA	= 0x0020,	// 彩色RGBA图像
	IMAGE_BGRA	= 0x0021	// 彩色BGRA图像
};

/**
* @brief 简单概述
* @brief 图像数据结构
*/
typedef struct
{
	ImageFormat format;		// 图像格式
	int width;			// 图像宽度
	int height;			// 图像高度
	unsigned long iBufSize;		// 像素数据缓冲区大小
	unsigned char* pBuffer = NULL;	// 像素数据
}Image;

算法声明:

/*****************************************************************************
*  C++ 图像算法
*  Copyright (C) 2020 1158292387@qq.com
*
*  @file
*  @brief    对文件的简单概述
*  Details.
*
*  @author   Leeme
*  @email    1158292387@qq.com
*  @version  1.0.0.1(版本)
*  @date     2020.11.21
*  @license  GNU General Public License (GPL)
*
*----------------------------------------------------------------------------
*  Remark         : Description
*----------------------------------------------------------------------------
*  Change History :
*  <Date>     | <Version> | <Author>       | <Description>
*----------------------------------------------------------------------------
*  2020/10/30 | 1.0.0.1   | Leeme          | Create file
*----------------------------------------------------------------------------
*
*****************************************************************************/
#ifndef ALGORITHM_H
#define ALGORITHM_H

#include "algdef.h"
#include <vector>

/**
* @brief 简单概述
* @brief 单幅图像作为输入的算法基类
*/
class algorithm
{
public:
	algorithm(){}
	~algorithm()
	{
		delete m_Image.pBuffer;
		m_Image.pBuffer = NULL;
	}

	/**
	* @brief 简单概述
	* @brief 设置图像
	*/
	bool SetInputData(const Image& src)
	{
		if (src.pBuffer == NULL || src.width == 0 || src.height == 0)
			return false;

		m_Image.width = src.width;
		m_Image.height = src.height;
		m_Image.format = src.format;
		m_Image.iBufSize = src.iBufSize;
		if (m_Image.pBuffer != NULL)
		{
			delete[]m_Image.pBuffer;
			m_Image.pBuffer = NULL;
		}
		m_Image.pBuffer = new unsigned char[m_Image.iBufSize];
		memcpy(m_Image.pBuffer, src.pBuffer, m_Image.iBufSize);
		return true;
	}

	/**
	* @brief 简单概述
	* @brief 获取图像计算结果
	*/
	bool GetOutputData(Image* src)
	{
		if (m_Image.pBuffer == NULL)
			return false;
		src->width = m_Image.width;
		src->height = m_Image.height;
		src->format = m_Image.format;
		src->iBufSize = m_Image.iBufSize;
		if (src->pBuffer != NULL)
		{
			delete[] src->pBuffer;
			src->pBuffer = NULL;
		}
		src->pBuffer = new unsigned char[m_Image.iBufSize];
		memcpy(src->pBuffer, m_Image.pBuffer, m_Image.iBufSize);
		return true;
	}

	/**
	* @brief 简单概述
	* @brief 设置算法类型
	*/
	virtual bool SetAlgType(int) = 0;
	/**
	* @brief 简单概述
	* @brief 图像算法实现接口
	*/
	virtual bool UpdateData() = 0;

protected:
	Image	m_Image;
};

class IMAGECORE_EXPORT GussianFilter : public algorithm
{
public:
	GussianFilter()
	{
		m_Sigma = 0.0; 
		m_Kernel = K_3X3;
	}
	~GussianFilter(){}

	enum Kernel
	{
		K_3X3 = 0x0003,
		K_5X5 = 0x0005,
		K_7X7 = 0x0007,
		K_9X9 = 0x0009
	};

	/**
	* @brief 简单概述
	* @brief 设置算法类型
	*/
	bool SetAlgType(int type)
	{
		if (type < K_3X3 || type > K_9X9)
			return false;
		m_Kernel = (Kernel)type;
		return true;
	}

	void SetSigma(double s) { m_Sigma = s; }

	/**
	* @brief 简单概述
	* @brief 图像算法实现接口
	*/
	bool UpdateData();

private:
	/**
	* @brief 简单概述
	* @brief 计算高斯卷积核模板
	*/
	void CaclGaussianKernel(double *gaus, Kernel k, const double sigma);

	Kernel m_Kernel;
	double m_Sigma;
};

#endif

算法实现:

/**
* @brief 简单概述
* @brief 图像算法实现接口
*/
bool GussianFilter::UpdateData()
{
	if (m_Image.pBuffer == NULL || m_Image.width == 0 || m_Image.height == 0 || 
                    (m_Sigma-0.0)<0.0001)
		return false;

	short channel = 0;
	if (m_Image.format == IMAGE_RGB || m_Image.format == IMAGE_BGR)
		channel = 3;
	else if (m_Image.format == IMAGE_RGBA || m_Image.format == IMAGE_BGRA)
		channel = 4;
	else
		return false;

	// 计算高斯卷积模板
	double* pK = new double[m_Kernel*m_Kernel];
	this->CaclGaussianKernel(pK, m_Kernel, m_Sigma);

	unsigned char* pData = m_Image.pBuffer;
	unsigned char* pTemp = m_Image.pBuffer;
	short k = m_Kernel / 2;
	for (unsigned long i = 0; i < m_Image.iBufSize; i += channel)
	{
		int x = (i + 1) % (m_Image.width*channel) / channel;
		int y = i / (m_Image.width*channel);
		int total_r = 0;
		int total_g = 0;
		int total_b = 0;
		short count = 0;
		short weight_index = 0;
		for (short m = 0; m < m_Kernel; m++)
		{
			int temp_y = y - k + m;
			if (temp_y >= 0 && temp_y < m_Image.height)
			{
				for (short n = 0; n < m_Kernel; n++)
				{
					int temp_x = x - k + n;
					if (temp_x >= 0 && temp_x < m_Image.width)
					{
						unsigned long fix = temp_y*m_Image.width*
                                                            channel + temp_x*channel;
						pTemp += fix;
						double weight = pK[weight_index];
						short r = ((short)pTemp[0])*weight;
						short g = ((short)pTemp[1])*weight;
						short b = ((short)pTemp[2])*weight;

						total_r += r;
						total_g += g;
						total_b += b;
						count += 1;
						pTemp -= fix;
						weight_index++;
					}
					else
					{
						weight_index++;
					}
				}
			}
			else
			{
				weight_index += m_Kernel;
			}
		}
		pData[0] = (unsigned short)total_r;
		pData[1] = (unsigned short)total_g;
		pData[2] = (unsigned short)total_b;
		pData += channel;
	}
	delete[] pK;
	return true;
}

/**
* @brief 简单概述
* @brief 计算高斯核卷积
* @brief gaus:高斯核卷积(出参)
* @brief k:卷积模板尺寸,3*3,5*5,7*7等
* @brief sigma:权重控制参数
*/
void GussianFilter::CaclGaussianKernel(double *gaus, Kernel k, const double sigma)
{
	//圆周率π赋值
	const double PI = 4.0*std::atan(1.0);
	int center = k /2;
	double param = 1/(2*sigma*sigma);
	double sum = 0;
	int count = 0;
	for (int i = 0; i<k; i++)
	{
		for (int j = 0; j<k; j++)
		{
			gaus[count] = (1/PI*param)*std::exp(-((i-center)*(i-center)+
                                          (j-center)*(j-center))*param);
			sum += gaus[count];
			count++;
		}
	}
	count = 0;
	for (int i = 0; i<k; i++)
	{
		for (int j = 0; j<k; j++)
		{
			gaus[count] /= sum;
			count++;
		}
	}
}

测试代码:

	GussianFilter* mFilter = new GussianFilter();
	mFilter->SetInputData(m_Image);
	mFilter->SetAlgType(GussianFilter::K_3X3);
	mFilter->SetSigma(0.5);
	mFilter->UpdateData();
	mFilter->GetOutputData(&m_Image);

529a68fa1de907253fdc9559440b7320.png

测试代码:

	GussianFilter* mFilter = new GussianFilter();
	mFilter->SetInputData(m_Image);
	mFilter->SetAlgType(GussianFilter::K_5X5);
	mFilter->SetSigma(0.5);
	mFilter->UpdateData();
	mFilter->GetOutputData(&m_Image);

2cb974f8faf17d3181ac08d44a066484.png

测试代码:

	GussianFilter* mFilter = new GussianFilter();
	mFilter->SetInputData(m_Image);
	mFilter->SetAlgType(GussianFilter::K_7X7);
	mFilter->SetSigma(0.5);
	mFilter->UpdateData();
	mFilter->GetOutputData(&m_Image);

ac13720f0caf936b14bf4ad257cdfecf.png

测试代码:

	GussianFilter* mFilter = new GussianFilter();
	mFilter->SetInputData(m_Image);
	mFilter->SetAlgType(GussianFilter::K_9X9);
	mFilter->SetSigma(0.5);
	mFilter->UpdateData();
	mFilter->GetOutputData(&m_Image);

b4c6a0152972a86377d5945242fef3bc.png

测试代码:

	GussianFilter* mFilter = new GussianFilter();
	mFilter->SetInputData(m_Image);
	mFilter->SetAlgType(GussianFilter::K_9X9);
	mFilter->SetSigma(0.8);
	mFilter->UpdateData();
	mFilter->GetOutputData(&m_Image);

58b2ec53ddc6b2f7acc5398b0b00a380.png

测试代码:

	GussianFilter* mFilter = new GussianFilter();
	mFilter->SetInputData(m_Image);
	mFilter->SetAlgType(GussianFilter::K_9X9);
	mFilter->SetSigma(5.0);
	mFilter->UpdateData();
	mFilter->GetOutputData(&m_Image);

6680bf5ec445110f87462ec71a43e47c.png

图像大小:1000*667 卷积模板大小9*9 耗时:0.551秒。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值