中值滤波窗口大小设置_中值滤波窗口大小设置

图像中值滤波也是一种常见的图像去噪和模糊算法,其原理是在卷积核范围内查找像素的中间值,对于彩色图像,我们取图像亮度的中间值,亮度值的计算方法为对每个通道的值取平均值,即grey=(r+g+b)/3.我们对这些亮度值进行排序,最终取出中间的值。由于每次卷积都需要排序,所以算法的耗时相对比较高。

数据结构定义:

/**
* @brief 简单概述
* @brief 图像文件类型
*/
enum ImageFileType
{
	FILE_TYPE_BMP=0x0000,
	FILE_TYPE_PNG,
	FILE_TYPE_JPG,
	FILE_TYPE_JPEG,
	FILE_TYPE_TIF
};

/**
* @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;

/**
* @brief 简单概述
* @brief RGB像素点
*/
typedef struct
{
	short r;
	short g;
	short b;

	void SetValue(const short& rs, const short& gs, const short& bs)
	{
		r = rs;
		g = gs;
		b = bs;
	}
}PointRGB;

算法头文件定义:

/*****************************************************************************
*  C++ 图像读取

*
*  @file
*  @brief    对文件的简单概述
*  Details.
*
*  @author   Leeme
* 
*  @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 MedianFilter : public algorithm
{
public:
    MedianFilter() {}
    ~MedianFilter() {}

    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;
    }

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

private:
    Kernel  m_Kernel;
};

#endif

算法实现文件:

#include "algorithm.h"
#include <map>
bool MedianFilter::UpdateData()
{
    if (m_Image.pBuffer == NULL || m_Image.width == 0 || m_Image.height == 0)
        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;

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

        std::map<unsigned short, PointRGB> map_pixel;
        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;
                        short r = pTemp[0];
                        short g = pTemp[1];
                        short b = pTemp[2];

                        short median = (r+g+b)/3;

                        PointRGB p;
                        p.SetValue(r,g,b);
                        map_pixel[median] = p;
                        pTemp -= fix;
                    }
                }
            }
        }

        auto itr = map_pixel.begin();
        PointRGB pixel;
        pixel.SetValue(itr->second.r, itr->second.g, itr->second.b);
        unsigned short count = 0;
        unsigned short median_size = map_pixel.size()/2;
        for ( ;itr != map_pixel.end(); itr++)
        {
            if (count == median_size)
            {
                pixel.SetValue(itr->second.r, itr->second.g, itr->second.b);
                break;
            }
            count++;
        }
       
        pData[0] = pixel.r;
        pData[1] = pixel.g;
        pData[2] = pixel.b;
        pData += channel;
    }
    return true;
}

测试代码:

MedianFilter* filter = new MedianFilter();
    filter->SetInputData(m_Image);
    filter->SetAlgType(MeanFilter::K_3X3);
    filter->UpdateData();
    filter->GetOutputData(&m_Image);

中值滤波窗口大小设置_Image_02

图像大小:1023*682. 耗时:19.813s。

测试代码:

MedianFilter* filter = new MedianFilter();
    filter->SetInputData(m_Image);
    filter->SetAlgType(MeanFilter::K_5X5);
    filter->UpdateData();
    filter->GetOutputData(&m_Image);

中值滤波窗口大小设置_Image_03

图像大小:1023*682. 耗时:47.871s。

测试代码:

MedianFilter* filter = new MedianFilter();
    filter->SetInputData(m_Image);
    filter->SetAlgType(MeanFilter::K_7X7);
    filter->UpdateData();
    filter->GetOutputData(&m_Image);

中值滤波窗口大小设置_中值滤波_04

图像大小:1023*682. 耗时:84.292s。

测试代码:

MedianFilter* filter = new MedianFilter();
    filter->SetInputData(m_Image);
    filter->SetAlgType(MeanFilter::K_9X9);
    filter->UpdateData();
    filter->GetOutputData(&m_Image);

中值滤波窗口大小设置_中值滤波_05

图像大小:1023*682. 耗时:128.217s。

中值滤波器与均值滤波器比较的优势:

在均值滤波器中,由于噪声成分被放入平均计算中,所以输出受到了噪声的影响,但是在中值滤波器中,由于噪声成分很难选上,所以几乎不会影响到输出。因此同样用3x3区域进行处理,中值滤波消除的噪声能力更胜一筹。中值滤波无论是在消除噪声还是保存边缘方面都是一个不错的方法。

中值滤波器与均值滤波器比较的劣势:

中值滤波花费的时间是均值滤波的5倍以上