图像中值滤波也是一种常见的图像去噪和模糊算法,其原理是在卷积核范围内查找像素的中间值,对于彩色图像,我们取图像亮度的中间值,亮度值的计算方法为对每个通道的值取平均值,即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);
图像大小:1023*682. 耗时:19.813s。
测试代码:
MedianFilter* filter = new MedianFilter();
filter->SetInputData(m_Image);
filter->SetAlgType(MeanFilter::K_5X5);
filter->UpdateData();
filter->GetOutputData(&m_Image);
图像大小:1023*682. 耗时:47.871s。
测试代码:
MedianFilter* filter = new MedianFilter();
filter->SetInputData(m_Image);
filter->SetAlgType(MeanFilter::K_7X7);
filter->UpdateData();
filter->GetOutputData(&m_Image);
图像大小:1023*682. 耗时:84.292s。
测试代码:
MedianFilter* filter = new MedianFilter();
filter->SetInputData(m_Image);
filter->SetAlgType(MeanFilter::K_9X9);
filter->UpdateData();
filter->GetOutputData(&m_Image);
图像大小:1023*682. 耗时:128.217s。
中值滤波器与均值滤波器比较的优势:
在均值滤波器中,由于噪声成分被放入平均计算中,所以输出受到了噪声的影响,但是在中值滤波器中,由于噪声成分很难选上,所以几乎不会影响到输出。因此同样用3x3区域进行处理,中值滤波消除的噪声能力更胜一筹。中值滤波无论是在消除噪声还是保存边缘方面都是一个不错的方法。
中值滤波器与均值滤波器比较的劣势:
中值滤波花费的时间是均值滤波的5倍以上