快速中值滤波
在中值滤波中,需要计算每一块小的直方图的中值,如果每次都算一个新的直方图就会很麻烦,但是像素点每右移一个单位,直方图实际上只改变了最左侧一列和最右侧一列,其余的数据可以继续使用,并且这个中值的改变也不会太大,用这种方法避免了重复访问和重复运算。
代码如下:
int calMidValue(int hist[], int midLocation)
{
int sum = 0;
for (int i = 0; i < 256; ++i)
{
sum += hist[i];
if (sum >= midLocation)
return i;
}
return 255;
}
void midFilt(BYTE *pImg, int width, int height,int windowLength)
{
BYTE *pSrc=pImg;
int hist[256];
int edge = (windowLength - 1) >> 1;//未处理边界大小
int windowSize = windowLength * windowLength;//矩形窗大小
int midLocation = (windowSize >> 1) + 1;//中值的位置
int realWidth = width - edge, realHeight = height - edge;//实际处理的宽高
for (int i = edge; i < realWidth; i++)
for (int j = edge; j < realHeight; j++)
{
if (j == edge)
{
memset(hist, 0, 256 * sizeof(int));
for(int h=i-edge;h<=i+edge;h++)
for (int w = j - edge; w <= j + edge; w++) {
BYTE value = pSrc[h*width + w];
hist[value]++;
}
}
else
{
int left = j - edge - 1, right = j + edge;
for (int y = i - edge; y <= i + edge; y++)
{
int l = y * width + left, r = y * width + right;
hist[pSrc[l]]--;
hist[pSrc[r]]++;
}
}
pSrc[i*width + j] = calMidValue(hist, midLocation);
}
return;
}
3*3模板滤波结果:
快速均值滤波
快速均值滤波可以用类似于快速中值滤波的方法,移动边界时,仅仅关注改变的区域,减少了数据的访存
代码如下:
int calAverValue(int hist[],int windowSize)
{
int sum = 0;
for (int i = 0; i < 256; ++i)
{
sum += hist[i] * i;
}
return (sum / windowSize);
}
void averFilt1(BYTE *pImg, int width, int height, int windowLength)
{
BYTE *pSrc = pImg;
int hist[256];
int edge = (windowLength - 1) >> 1;//未处理边界大小
int windowSize = windowLength * windowLength;//矩形窗大小
int realWidth = width - edge, realHeight = height - edge;//实际处理的宽高
for (int i = edge; i < realWidth; i++)
for (int j = edge; j < realHeight; j++)
{
if (j == edge)
{
memset(hist, 0, 256 * sizeof(int));
for (int h = i - edge; h <= i + edge; h++)
for (int w = j - edge; w <= j + edge; w++) {
BYTE value = pSrc[h*width + w];
hist[value]++;
}
}
else
{
int left = j - edge - 1, right = j + edge;
for (int y = i - edge; y <= i + edge; y++)
{
int l = y * width + left, r = y * width + right;
hist[pSrc[l]]--;
hist[pSrc[r]]++;
}
}
pSrc[i*width + j] = calAverValue(hist, windowSize);
}
}
快速积分图滤波
`
另一种方法就是基于积分图的算法,图像积分图指的是积分图上的点的值=所有该点及该点左上方的像素值之和,即s(x,y)=g(x,y)+s(x-1,y)+s(x,y-1)-s(x-1,y-1)用这个方法可以快速算出某区域的像素值之和,即sum=s(x,y)-s(x-m,y)-s(x,y-n)+s(x-m,y-n)再除以窗口大小,即为均值。
代码如下:
void averFilt2(BYTE *pImg, int width, int height, int windowLength,BYTE *pDst)
{
int windowSize = windowLength * windowLength;
int edge = (windowLength - 1) >> 2;
int realWidth = width - edge, realHeight = height - edge;//实际处理的宽高
for (int i = 0; i < width*height; i++)
{
pDst[i] = pImg[i];
}
for (int i = 0; i < height; i++)
for (int j = 0; j < height; j++)
{
pDst[i*width + j] = pDst[i*width + j - 1] + pDst[(i - 1)*width + j] - pDst[(i - 1)*width + j - 1] + pImg[i*width + j];
}
for (int i = 0; i < realWidth; i++)
for (int j = 0; j < realHeight; j++)
{
pDst[i*width + j] = (pDst[(i + edge)*width + j + edge] - pDst[(i - edge)*width + j + edge] - pDst[(i + edge)*width + j - edge] + pDst[(i - edge)*width + j - edge]) / windowSize;
}
return;
}
滤波结果: