专栏首页音视频技术学习笔记图像处理基础-均值滤波

图像处理基础-均值滤波

普通均值滤波

R是卷积核半径

image

图像处理中,有几种常见的基础算法,比如“模糊”、“灰度”、“浮雕”、“黑白”、“底片”、“锐化”。这篇文章讲述采用“均值滤波”的算法实现“模糊”。

一、均值滤波原理

原理非常简单,相信你看完,也能很快实现

1)设定一个均值区域,一般定义滤波半径R,半径越大越模糊

2)逐次移动坐标,求该区域内的所有像素的平均值

二、标准均值滤波

逻辑实在是太简单,直接贴代码

如果对Bitmap的RGB解析不了解,可以参考我之前写的:理解Bitmap的ARGB格式,实现颜色选择器

//std mean filter
/**
* srcData:原图数据
* destData:存放处理结果的图片数据
* width:图片宽
* height:图片高度
* stride:图片一行的步幅(>= width)
* radius:模糊半径
*/

#define MIN2(a, b) ((a) < (b) ? (a) : (b))
#define MAX2(a, b) ((a) > (b) ? (a) : (b))
#define CLIP3(x, a, b) MIN2(MAX2(a,x), b)

int MeanFilter(unsigned char *srcData, unsigned char* destData, int width, int height, int stride, int radius)
{
    int ret = 0;
    if(radius == 0)
        return ret;
    int offset = stride - width * 4;

    unsigned char* destData = (unsigned char*)malloc(sizeof(unsigned char) * height * stride);

    int M = (radius * 2 + 1) * (radius * 2 + 1);
    int sumr = 0, sumg = 0, sumb = 0;
    for(int j = 0; j < height; j++)
    {
        for(int i = 0; i < width; i++)
        {
            sumr = sumg = sumb = 0;
            // 对(radius+1) * (radius +1) 的矩形范围求像素的平均值
            for(int n = -radius; n <=radius; n++)
            {
                for(int m = -radius; m <= radius; m++)
                {
                    // 注意图片的边界处,坐标会溢出,需要校正
                    int ny = CLIP3(j + n, 0, height - 1);
                    int nx = CLIP3(i + m, 0, width - 1);
                    int pos = nx * 4 + ny * stride;
                    sumb += srcData[pos];
                    sumg += srcData[pos + 1];
                    sumr += srcData[pos + 2];
                }
            }
            destData[0] = sumb / M;
            destData[1] = sumg / M;
            destData[2] = sumr / M;
            destData += 4;
        }
        destData += offset;
    }

    return ret;
};

不同的模糊半径处理的效果:

三、快速均值滤波

标准均值滤波算法,有大量的重复值的计算,如果图片计算量比较大,可以考虑采用“快速均值滤波”

当计算的点沿x轴移动一个像素,diff只有最左侧的一列和最右侧的一列发生变化,如下图所示。可以将上一次计算的结果减去最左侧,再加上最右侧,可以大幅度提升效率。

y轴移动原理相同。

代码稍微有点绕,逻辑不复杂,有兴趣的同学可以读一读

//Fast mean filter based histagram computation
int FastMeanFilter(unsigned char* srcData, unsigned char* dstData, int width, int height ,int stride, int radius)
{
    int ret = 0;
    if(radius == 0)
        return ret;
    if(radius > MIN2(width,height) / 2)
        radius = (MIN2(width, height) / 2-0.5);

    memset(dstData, 255, sizeof(unsigned char) * height * stride);
    int unit = 4, t = 0, t1 = 0;
    int i,j,k,len = width * height * unit;
    int block = (radius << 1) + 1;
    int winSize = block * block;
    long sumB = 0, sumG = 0,sumR = 0;
    unsigned char* pSrc = srcData;
    int* temp = (int*)malloc(sizeof(int)* width * unit);
    memset(temp,0,sizeof(int) * width * unit);
    // 一次性求出第一行像素周边的和,存在temp中,
    // 后面随着y值移动,不断更新temp值
    // 设radius = 2, 求出 -2 -1 0 1 2 五行颜色的和,存储在temp中
    for(k = -radius; k <= radius; k++)
    {
        for(j = 0; j< width; j++)
        {
            t = j * unit;
            // 小于0在图片外面,没有值,对称映射到图片里面来取值
            t1 = abs(k) * stride;
            temp[t] += pSrc[t + t1];
            temp[t + 1] += pSrc[t + 1 + t1];
            temp[t + 2] += pSrc[t + 2 + t1];
        }
    }
   // 开始从第一行扫描,沿y方向迭代
    for (i = 0; i < height; i++)
    {
        sumB = sumG = sumR = 0;
        // 求出坐标每一行第一个点的卷积
        // -2 -1处的值不存在,取绝对值,映射成2 和 1出的值
        for (j = -radius; j <= radius; j++)
        {
            // j < 0时,图片的左边没有值,映射到图片的右边来取值,这里也可以取(0,y)处的值,不一定要abs(j)
             t = abs(j) * unit;
             sumB += temp[t];
             sumG += temp[t + 1];
             sumR += temp[t + 2];
        }
        // 计算每一行的卷积平均值
        for (j = 0; j < width; j++)
        {
            // 计算i行j列处的平均值
             t = j * unit + i * stride;
             dstData[t] = (sumB / winSize);
             dstData[t + 1] = (sumG / winSize);
             dstData[t + 2] = (sumR / winSize);

             // sumRGB用完一次,往前推进一个像素,这个if是为了减少最后一次计算,最后一次不用算了
             if (j < width - 1)
             {
                 t = abs(j - radius) * unit;
                 t1 = (j + radius + 1) % width * unit;
                 sumB = sumB - temp[t] + temp[t1];
                 sumG = sumG - temp[t + 1] + temp[t1 + 1];
                 sumR = sumR - temp[t + 2] + temp[t1 + 2];
             }
        }

        // 这个if是为了减少最后一次计算,减去卷积核
        if (i < height - 1)
        {
             for (k = 0; k < width; k++)
             {
                 t = k * unit + abs(i - radius) * stride;  // 卷积核第一行(注意abs绝对值,是应对边缘处的像素,映射到正值处的位置,增加容错)
                 t1 = k * unit + (i + radius + 1) % height * stride;  // 紧邻卷积核的下一行
                 temp[k * unit] = temp[k * unit] - pSrc[t] + pSrc[t1];
                 temp[k * unit + 1] = temp[k * unit + 1] - pSrc[t + 1] + pSrc[t1 + 1];
                 temp[k * unit + 2] = temp[k * unit + 2] - pSrc[t + 2] + pSrc[t1 + 2];
             }
        }
    }
    free(temp);
    return ret;
};

第一篇图像处理先到这,对图像处理感兴趣的朋友,欢迎交流

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 15.opengl高级-混合

    纹理缓和的计算也不复杂,根据alpha通道值做叠加或减除融合,详细可参考opengl-混合

    用户1068165
  • 照片处理-阿宝色滤镜

    阿宝色是一位摄影师名为阿宝(网名:aibao),原名董立竑,由他在2008年左右所创的一种特别的色彩。这种色彩主要是,橘色的肤色和偏青色的背景色调为主,整体的视...

    用户1068165
  • 25.opengl高级光照-Gamma校正

    简单理解:人对光强度的感知是非线性的。亮度的范围如果是[0,1],0是黑色,1是纯白色,那么0.5应该代表的是中间灰色吗?NO!!人能感知到的中间灰度值是亮度为...

    用户1068165
  • 3(文件IO,不带缓冲的IO)

    成功则返回文件描述符,失败则返回-1 第三个参数写成/*mode_t mode */ 表示这个参数仅在创建新文件时使用 Pathname表示要打开或者创...

    提莫队长
  • slice 去重

    solate
  • 什么是文件格式?

    有了之前 4 篇对文件的操作工具之后,终于到了文件格式的介绍部分!本文介绍文件格式的定义,并实现一个自己的文件格式。这个文件格式十分简单,只用来说明原理。

    Apache IoTDB
  • 北斗对时装置(NTP时钟服务器)助力政务云建设

    以宣城“智慧城市”建设总体思路为指导,建设政务云计算中心,实现统一建设、统一管理、统一使用,为智慧城市和全市各部门的业务应用系统提供统一的机房空间、网络资源、存...

    NTP网络同步时钟
  • 栅格化系统的原理以及实现

    在一个有限的、固定的平面上,用水平线和垂直线(虚拟的线,“参考线”),将平面划分成有规律的一系列“格子”(虚拟的格子),并依托这些格子、或以格子的边线为基准线,...

    谭光志
  • 总是听别人说响应式布局,原来这么简单

    作者:麻酱 原文:http://www.majiang.life/blog/head-first-responsive-web-design/ 前言 总听别人...

    程序猿DD
  • 3分钟理解响应式布局

    总听别人说响应式布局,觉得是一个很高大上的东西,近日做的一个项目需要适配不同的屏幕尺寸,于是就简单研究了一下 Web响应式布局,其实原理很简单,下面就简单整理了...

    用户1093975

扫码关注云+社区

领取腾讯云代金券