方框滤波归一化之后就是均值滤波,图像的高斯模糊进程就是图像与正态分布做卷积

高斯模糊(葡萄牙语:Gaussian Blur),也叫高斯平滑,是在Adobe
Photoshop、GIMP以及Paint.NET等图像处理软件中普遍使用的拍卖作用,平日用它来压缩图像噪声以及下降细节层次。那种歪曲技术转移的图像,其视觉效果就像经过一个半晶莹剔透屏幕在察看图像,那与画面焦外成像效果散景以及常见照明阴影中的效果都驾驭例外。高斯平滑也用于统计机视觉算法中的预先处理阶段,以增进图像在不一致比重大小下的图像效果(参见尺度空间表示以及尺度空间完结)。
从数学的角度来看,图像的高斯模糊进度固然图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以那项技术就叫作高斯模糊。图像与圆圈方框模糊做卷积将会变卦尤其规范的焦外成像效果。由于高斯函数的傅立叶变换是此外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波器。

高斯模糊

高斯模糊(泰语:Gaussian Blur),也叫高斯平滑,是在Adobe
Photoshop、GIMP以及Paint.NET等图像处理软件中常见选拔的处理效果,常常用它来裁减图像杂讯以及下跌细节层次。那种歪曲技术生成的图像,其视觉效果似乎经过一个半透明屏幕在观看图像,那与画面焦外成像效果散景以及常见照明阴影中的效果都了解例外。高斯平滑也用于统计机视觉算法中的预先处理阶段,以提升图像在差距比例大小下的图像效果。
从数学的角度来看,图像的高斯模糊进度固然图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以那项技能就叫作高斯模糊。图像与圆圈方框模糊做卷积将会转变尤其规范的焦外成像效果。由于高斯函数的傅立叶变换是此外一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波器。

高斯模糊使用了高斯的正态分布的密度函数,总括图像中种种像素的转换。

图片 1

gaussian-function.png

据悉一维高斯函数,可以推导获得二维高斯函数:

图片 2

二维高斯函数.png

图片 3

二维的正太分布.png

其中r是歪曲半径,r^2 = x^2 +
y^2,σ是正态分布的专业不是。在二维空间中,那些公式生成的曲面的等高线是从中心开头呈正态分布的同心圆。分布不为零的像素组成的卷积矩阵与原来图像做变换。每个像素的值都是四周相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也更加小。那样进行模糊处理比任何的年均模糊滤波器更高地保存了边缘效果。

实际,在iOS上贯彻高斯模糊是件很简单的事体。早在iOS 5.0就有了Core
Image的API,而且在CoreImage.framework库中,提供了大气的滤镜已毕。

+(UIImage *)coreBlurImage:(UIImage *)image withBlurNumber:(CGFloat)blur 
{ 
    CIContext *context = [CIContext contextWithOptions:nil]; 
    CIImage *inputImage= [CIImage imageWithCGImage:image.CGImage]; 
    //设置filter
    CIFilter *filter = [CIFilter filterWithName:@"CIGaussianBlur"]; 
    [filter setValue:inputImage forKey:kCIInputImageKey];
    [filter setValue:@(blur) forKey: @"inputRadius"]; 
    //模糊图片
    CIImage *result=[filter valueForKey:kCIOutputImageKey]; 
    CGImageRef outImage=[context createCGImage:result fromRect:[result extent]];       
    UIImage *blurImage=[UIImage imageWithCGImage:outImage];           
    CGImageRelease(outImage); 
    return blurImage;
}

在Android上落到实处高斯模糊也足以利用原生的API—–RenderScript,然而要求Android的API是17以上,也就是Android
4.2版本。

    /**
     * 使用RenderScript实现高斯模糊的算法
     * @param bitmap
     * @return
     */
    public Bitmap blur(Bitmap bitmap){
        //Let's create an empty bitmap with the same size of the bitmap we want to blur
        Bitmap outBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);
        //Instantiate a new Renderscript
        RenderScript rs = RenderScript.create(getApplicationContext());
        //Create an Intrinsic Blur Script using the Renderscript
        ScriptIntrinsicBlur blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs));
        //Create the Allocations (in/out) with the Renderscript and the in/out bitmaps
        Allocation allIn = Allocation.createFromBitmap(rs, bitmap);
        Allocation allOut = Allocation.createFromBitmap(rs, outBitmap);
        //Set the radius of the blur: 0 < radius <= 25
        blurScript.setRadius(20.0f);
        //Perform the Renderscript
        blurScript.setInput(allIn);
        blurScript.forEach(allOut);
        //Copy the final bitmap created by the out Allocation to the outBitmap
        allOut.copyTo(outBitmap);
        //recycle the original bitmap
        bitmap.recycle();
        //After finishing everything, we destroy the Renderscript.
        rs.destroy();

        return outBitmap;

    }

俺们付出的图像框架cv4j也提供了一个滤镜来完结高斯模糊。

GaussianBlurFilter filter = new GaussianBlurFilter();
filter.setSigma(10);

RxImageData.bitmap(bitmap).addFilter(filter).into(image2);

图片 4

采纳RenderScript完结高斯模糊.png

图片 5

动用cv4j落成高斯模糊.png

可以看来,cv4j贯彻的高斯模糊跟RenderScript完毕的效用同样。

里面,GaussianBlurFilter的代码如下:

public class GaussianBlurFilter implements CommonFilter {
    private float[] kernel;
    private double sigma = 2;
    ExecutorService mExecutor;
    CompletionService<Void> service;

    public GaussianBlurFilter() {
        kernel = new float[0];
    }

    public void setSigma(double a) {
        this.sigma = a;
    }

    @Override
    public ImageProcessor filter(final ImageProcessor src){
        final int width = src.getWidth();
        final int height = src.getHeight();
        final int size = width*height;
        int dims = src.getChannels();
        makeGaussianKernel(sigma, 0.002, (int)Math.min(width, height));

        mExecutor = TaskUtils.newFixedThreadPool("cv4j",dims);
        service = new ExecutorCompletionService<>(mExecutor);

        // save result
        for(int i=0; i<dims; i++) {

            final int temp = i;
            service.submit(new Callable<Void>() {
                public Void call() throws Exception {
                    byte[] inPixels = src.toByte(temp);
                    byte[] temp = new byte[size];
                    blur(inPixels, temp, width, height); // H Gaussian
                    blur(temp, inPixels, height, width); // V Gaussain
                    return null;
                }
            });
        }

        for (int i = 0; i < dims; i++) {
            try {
                service.take();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        mExecutor.shutdown();
        return src;
    }

    /**
     * <p> here is 1D Gaussian        , </p>
     *
     * @param inPixels
     * @param outPixels
     * @param width
     * @param height
     */
    private void blur(byte[] inPixels, byte[] outPixels, int width, int height)
    {
        int subCol = 0;
        int index = 0, index2 = 0;
        float sum = 0;
        int k = kernel.length-1;
        for(int row=0; row<height; row++) {
            int c = 0;
            index = row;
            for(int col=0; col<width; col++) {
                sum = 0;
                for(int m = -k; m< kernel.length; m++) {
                    subCol = col + m;
                    if(subCol < 0 || subCol >= width) {
                        subCol = 0;
                    }
                    index2 = row * width + subCol;
                    c = inPixels[index2] & 0xff;
                    sum += c * kernel[Math.abs(m)];
                }
                outPixels[index] = (byte)Tools.clamp(sum);
                index += height;
            }
        }
    }

    public void makeGaussianKernel(final double sigma, final double accuracy, int maxRadius) {
        int kRadius = (int)Math.ceil(sigma*Math.sqrt(-2*Math.log(accuracy)))+1;
        if (maxRadius < 50) maxRadius = 50;         // too small maxRadius would result in inaccurate sum.
        if (kRadius > maxRadius) kRadius = maxRadius;
        kernel = new float[kRadius];
        for (int i=0; i<kRadius; i++)               // Gaussian function
            kernel[i] = (float)(Math.exp(-0.5*i*i/sigma/sigma));
        double sum;                                 // sum over all kernel elements for normalization
        if (kRadius < maxRadius) {
            sum = kernel[0];
            for (int i=1; i<kRadius; i++)
                sum += 2*kernel[i];
        } else
            sum = sigma * Math.sqrt(2*Math.PI);

        for (int i=0; i<kRadius; i++) {
            double v = (kernel[i]/sum);
            kernel[i] = (float)v;
        }
        return;
    }
}

图像滤波:
尽量保存图像细节特征的原则下对目的图像的噪声举办遏制和平滑处理,是图像预处理中不可缺失的操作,其处理效果的上下将直接影响到持续图像处理和分析的有效性和可信性。
滤波器
低通滤波器(允许低频通过),高通滤波器,带通滤波器,带阻滤波器,全通滤波器,陷波滤波器。
自己在此刻主要介绍OpenCV中所提供的滤波操作,线性滤波与非线性滤波
opencv中线性滤波操作有:正方滤波、均值滤波和高斯滤波。非线性滤波操作有:中值滤波和两边滤波。

 

空间卷积

二维卷积在图像处理中会平日蒙受,图像处理中用到的大半是二维卷积的离散格局。

图片 6

二维卷积的离散方式.png

以下是cv4j落到实处的各个卷积效果。

图片 7

种种卷积效果1.png

图片 8

各样卷积效果2.png

cv4j
近日协理如下的长空卷积滤镜

filter 名称 作用
ConvolutionHVFilter 卷积 模糊或者降噪
MinMaxFilter 最大最小值滤波 去噪声
SAPNoiseFilter 椒盐噪声 增加噪声
SharpFilter 锐化 增强
MedimaFilter 中值滤波 去噪声
LaplasFilter 拉普拉斯 提取边缘
FindEdgeFilter 寻找边缘 梯度提取
SobelFilter 梯度 获取x、y方向的梯度提取
VarianceFilter 方差滤波 高通滤波
MaerOperatorFilter 马尔操作 高通滤波
USMFilter USM 增强

正方滤波

高斯模糊是一种图像模糊滤波器,它用正态分布计算图像中每个像素的变换。N维空间正态分布方程为

总结

cv4j
gloomyfish和自家一块付出的图像处理库,近来还处于先前期间的本子。

现阶段早就落到实处的功用:

图片 9

cv4j.png

这周,我们对
cv4j
做了较大的调整,对全体架构举行了优化。还抬高了上空卷积功用(图片增强、锐化、模糊等等)。接下来,我们会做二值图像的分析(腐蚀、膨胀、开闭操作、概略提取等等)

讲到方框滤波,大家必须提及的是线性邻域滤波算子,即用不一致的权重去结合一个小邻域内的像素,来得到应有的拍卖成效。邻域滤波(卷积):左侧图像与中间图像的卷积暴发左侧图像。目的图像中红色标记的像素是拔取原图像中青色标记的像素统计得到的。线性滤波处理的输出像素值g(i,j)是输入像素值f(j+k,j+l)的加权和
,其中加权和称之为“核”,滤波器的加权全面,即滤波器的“滤波周全”。

图片 10

此处写图片描述

图片 11

图片 12

在二维空间定义为

此间写图片描述

图片 13

  /**
      * opencv中方框滤波函数
      * 第一个参数:输入的图片
      * 第二个参数:输出的图片
      * 第三个参数:图片的深度(存储每个像素所用的位数)一般情况使用-1也就是原有的图像深度
      * 第四个参数:核心的大小
      * 第五个参数:锚点的位置,就是我们要进行处理的点,默认值(-1,-1)表示锚点在核的中心
      * 第六个参数:normalize默认值为true,表示内核是否被其区域归一化(normalized)了,当normalize=true的时候,方框滤波就变成了我们熟悉的均值滤波。也就是说,均值滤波是方框滤波归一化(normalized)后的特殊情况。其中,归一化就是把要处理的量都缩放到一个范围内,比如(0,1),以便统一处理和直观量化。而非归一化(Unnormalized)的方框滤波用于计算每个像素邻域内的积分特性,比如密集光流算法(dense optical flow algorithms)中用到的图像倒数的协方差矩阵(covariance matrices of image derivatives)如果我们要在可变的窗口中计算像素总和,可以使用integral()函数
      * 第七个参数:边界模式,默认值BORDER_DEFAULT
      */
CV_EXPORTS_W void boxFilter( InputArray src, OutputArray dst, int ddepth,
                             Size ksize, Point anchor = Point(-1,-1),
                             bool normalize = true,
                             int borderType = BORDER_DEFAULT );

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_xy_opencv_ndk_1opencv002_MainActivity_blurImage(JNIEnv *env, jclass type,jintArray pixels_, jint w, jint h) {
    jint *pixels = env->GetIntArrayElements(pixels_, NULL);
    Mat img(h, w, CV_8UC4, pixels);
    boxFilter(img,img,-1,Size(30,30));
    int size = w * h;
    jintArray array = env->NewIntArray(size);
    env->SetIntArrayRegion(array, 0, size, pixels);
    env->ReleaseIntArrayElements(pixels_, pixels, 0);
    return array;
    }

其中r是模糊半径
图片 14),σ是正态分布的正式不是。在二维空间中,这一个公式生成的曲面的等高线是从主题开端呈正态分布的同心圆。分布不为零的像素组成的卷积矩阵与原本图像做变换。每个像素的值都是四周相邻像素值的加权平均。原始像素的值有最大的高斯分布值,所以有最大的权重,相邻像素随着距离原始像素越来越远,其权重也越来越小。那样举行模糊处理比任何的户均模糊滤波器更高地保留了边缘效果,参见尺度空间落成

图片 15

此处写图片描述

答辩上来讲,图像中每点的分布都不为零,那也就是说每个像素的计量都急需包含整幅图像。在事实上行使中,在测算高斯函数的离散近似时,在大约3σ距离之外的像素都足以看做不起功效,那么些像素的计量也就可以忽略。平日,图像处理程序只必要统计图片 16的矩阵就足以确保相关像素影响。对于边界上的点,平常使用复制周围的点到另一面再开展加权平均运算。

均值滤波
方框滤波归一化之后就是均值滤波。也就是出口图像的每一个像素是核窗口内输入图像对应像素的像素平均值(
所有像素加权周密相等)。

除去圆形对称之外,高斯模糊也得以在二维图像上对八个独立的一维空间分别展开测算,那叫作线性可分。那也就是,使用二维矩阵变换得到的功能也得以透过在档次方向举行一维高斯矩阵变换加上竖直方向的一维高斯矩阵变换获得。从统计的角度来看,那是一项立见成效的特征,因为这么只要求图片 17次统计,而不可分的矩阵则要求图片 18次计算,其中图片 19,图片 20是急需举行滤波的图像的维数,图片 21图片 22是滤波器的维数。

 /**
 * 均值滤波(归一化之后又进行了方框滤波)
 * 第一个参数:输入的图片
 * 第二个参数:输出的图片
 * 第三个参数:核心的大小
 * 第四个参数:锚点的位置,就是我们要进行处理的点,默认值(-1,-1)表示锚点在核的中心
 * 第五个参数:边界模式,默认值BORDER_DEFAULT
 */

CV_EXPORTS_W void blur( InputArray src, OutputArray dst,
                        Size ksize, Point anchor = Point(-1,-1),
                        int borderType = BORDER_DEFAULT );

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_xy_opencv_ndk_1opencv002_MainActivity_blurImage(JNIEnv *env, jclass type,jintArray pixels_, jint w, jint h) {
    jint *pixels = env->GetIntArrayElements(pixels_, NULL);
    Mat img(h, w, CV_8UC4, pixels);
    blur(img,img,Size(30,30));
    int size = w * h;
    jintArray array = env->NewIntArray(size);
    env->SetIntArrayRegion(array, 0, size, pixels);
    env->ReleaseIntArrayElements(pixels_, pixels, 0);
    return array;
    }

对一幅图像进行频仍总是高斯模糊的作用与三次更大的高斯模糊可以爆发相同的效率,大的高斯模糊的半径是所用多少个高斯模糊半径平方和的平方根。例如,使用半径分别为6和8的三遍高斯模糊变换得到的成效同样四次半径为10的高斯模糊效果,图片 23。根据那一个涉及,使用七个一连较小的高斯模糊处理不会比单个高斯较大拍卖时间要少。

图片 24

引用块内容

在回落图像尺寸的场馆常常应用高斯模糊。在进展欠采样的时候,常常在采样以前对图像进行低通滤波处理。那样就能够有限支撑在采样图像中不会现出虚假的再三音讯。高斯模糊有很好的特性,如没有明了的疆界,那样就不会在滤波图像中形成震荡。

高斯滤波

上述资料摘自维基百科(高斯模糊词条):

高斯滤波是一种线性平滑滤波,适用于消除高斯噪声,广泛应用于图像处理的减噪进程。通俗的讲,高斯滤波就是对整幅图像进行加权平均的长河,每一个像素点的值,都由其自己和邻域内的任何像素值经过加权平均后拿走。高斯滤波的具体操作是:用一个模板(或称卷积、掩模)扫描图像中的每一个像素,用模板确定的邻域内像素的加权平均灰度值去替代模板中央像素点的值。从数学的角度来看,图像的高斯模糊进程就是图像与正态分布做卷积。由于正态分布又叫作高斯分布,所以那项技术就叫作高斯模糊。图像与圆圈方框模糊做卷积将会变动更加准确的焦外成像效果。由于高斯函数的傅立叶变换是其余一个高斯函数,所以高斯模糊对于图像来说就是一个低通滤波操作。高斯滤波器是一类依据高斯函数的形状来摘取权值的线性平滑滤波器。高斯平滑滤波器对于抑制坚守正态分布的噪声十分实用。一维零均值高斯函数为:G(x)=exp(-x2/2sigma2)。其中,高斯分布参数Sigma决定了高斯函数的幅度。对于图像处理的话,常用二维零均值离散高斯函数作平滑滤波器。

图片 25

那边写图片描述

https://zh.wikipedia.org/wiki/%E9%AB%98%E6%96%AF%E6%A8%A1%E7%B3%8A

   /**
     * 高斯滤波
     * 第一个参数:传入的图片
     * 第二个参数:传出的图片
     * 第三个参数:核心(必须是正数和奇数)
     * 第四个参数:sigmaX代表高斯函数在x方向的标准偏差
     * 第五个参数:sigmaY代表高斯函数在Y方向的标准偏差,有默认值为0
     * 第六个参数:边界模式,使用默认值BORDER_DEFAULT
     */
CV_EXPORTS_W void GaussianBlur( InputArray src, OutputArray dst, Size ksize, double sigmaX, double sigmaY = 0, int borderType = BORDER_DEFAULT );

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_xy_opencv_ndk_1opencv002_MainActivity_blurImage(JNIEnv *env, jclass type,jintArray pixels_, jint w, jint h) {
    jint *pixels = env->GetIntArrayElements(pixels_, NULL);
    Mat img(h, w, CV_8UC4, pixels);
    GaussianBlur(img,img,Size(31,31),0);
    int size = w * h;
    jintArray array = env->NewIntArray(size);
    env->SetIntArrayRegion(array, 0, size, pixels);
    env->ReleaseIntArrayElements(pixels_, pixels, 0);
    return array;
    }

那就是说具体怎么着得以完成啊?

中值滤波

代码献上:

中值滤波很简短,一种非线性滤波操作,就是孔径范围内的有所像素举行排序,然后取中位数,赋值给要旨。

inline int* buildGaussKern(int winSize, int sigma)
{
    int wincenter, x;
    float   sum = 0.0f;
    wincenter = winSize / 2;
    float *kern = (float*)malloc(winSize*sizeof(float));
    int *ikern = (int*)malloc(winSize*sizeof(int));
    float SQRT_2PI = 2.506628274631f;
    float sigmaMul2PI = 1.0f / (sigma * SQRT_2PI);
    float divSigmaPow2 = 1.0f / (2.0f * sigma * sigma);
    for (x = 0; x < wincenter + 1; x++)
    {
        kern[wincenter - x] = kern[wincenter + x] = exp(-(x * x)* divSigmaPow2) * sigmaMul2PI;
        sum += kern[wincenter - x] + ((x != 0) ? kern[wincenter + x] : 0.0);
    }
    sum = 1.0f / sum;
    for (x = 0; x < winSize; x++)
    {
        kern[x] *= sum;
        ikern[x] = kern[x] * 256.0f;
    }
    free(kern);
    return ikern;
}

void GaussBlur(unsigned char*  pixels, unsigned int    width, unsigned int  height, unsigned  int channels, int sigma)
{
    width = 3 * width;
    if ((width % 4) != 0) width += (4 - (width % 4));

    unsigned int  winsize = (1 + (((int)ceil(3 * sigma)) * 2));
    int *gaussKern = buildGaussKern(winsize, sigma);
    winsize *= 3;
    unsigned int  halfsize = winsize / 2;

    unsigned char *tmpBuffer = (unsigned char*)malloc(width * height* sizeof(unsigned char));

    for (unsigned int h = 0; h < height; h++)
    {
        unsigned int  rowWidth = h * width;

        for (unsigned int w = 0; w < width; w += channels)
        {
            unsigned int rowR = 0;
            unsigned int rowG = 0;
            unsigned int rowB = 0;
            int * gaussKernPtr = gaussKern;
            int whalfsize = w + width - halfsize;
            unsigned int  curPos = rowWidth + w;
            for (unsigned int k = 1; k < winsize; k += channels)
            {
                unsigned int  pos = rowWidth + ((k + whalfsize) % width);
                int fkern = *gaussKernPtr++;
                rowR += (pixels[pos] * fkern);
                rowG += (pixels[pos + 1] * fkern);
                rowB += (pixels[pos + 2] * fkern);
            }

            tmpBuffer[curPos] = ((unsigned char)(rowR >> 8));
            tmpBuffer[curPos + 1] = ((unsigned char)(rowG >> 8));
            tmpBuffer[curPos + 2] = ((unsigned char)(rowB >> 8));

        }
    }
    winsize /= 3;
    halfsize = winsize / 2;
    for (unsigned int w = 0; w < width; w++)
    {
        for (unsigned int h = 0; h < height; h++)
        {
            unsigned    int col_all = 0;
            int hhalfsize = h + height - halfsize;
            for (unsigned int k = 0; k < winsize; k++)
            {
                col_all += tmpBuffer[((k + hhalfsize) % height)* width + w] * gaussKern[k];
            }
            pixels[h * width + w] = (unsigned char)(col_all >> 8);
        }
    }
    free(tmpBuffer);
    free(gaussKern); 
}
  /**
     * 中值滤波,孔径范围内的所有像素进行排序,然后取中位数,赋值给核心。
     * 第一个参数:传入的图片
     * 第二个参数:传出的图片
     * 第三个参数:孔径的线性尺寸,必须是大于1的奇数
     */
CV_EXPORTS_W void medianBlur( InputArray src, OutputArray dst, int ksize );

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_xy_opencv_ndk_1opencv002_MainActivity_blurImage(JNIEnv *env, jclass type,jintArray pixels_, jint w, jint h) {
    jint *pixels = env->GetIntArrayElements(pixels_, NULL);
    Mat img(h, w, CV_8UC4, pixels);
    medianBlur(img,img,31);
    int size = w * h;
    jintArray array = env->NewIntArray(size);
    env->SetIntArrayRegion(array, 0, size, pixels);
    env->ReleaseIntArrayElements(pixels_, pixels, 0);
    return array;
    }

备注:

双面滤波

之于原始算法,我做了部分小改变,重即使为了考虑一点点性能上的题材。

双方滤波(Bilateral
filter)是一种非线性的滤波方法,是组成图像的上空邻近度和像素值相似度的一种折中处理,同时考虑空域音信和灰度相似性,达到保边去噪的目的。具有简易、非迭代、局地的特点
。双边滤波器的好处是足以做边缘保存(edge
preserving),一般过去用的维纳滤波或者高斯滤波去降噪,都会较强烈地歪曲边缘,对于频仍细节的有限支撑作用并不明明。双边滤波器顾名思义比高斯滤波多了一个高斯方差sigma-d,它是根据空间分布的高斯滤波函数,所以在边缘附近,离的较远的像素不会太多影响到边缘上的像素值,这样就保证了边缘附近像素值的保存。不过由于保存了过多的高频音讯,对于彩色图像里的一再噪声,双边滤波器不可见彻底的滤掉,只好够对于低频音讯举行较好的滤波。

偶尔会写太多注释反而显示啰嗦,所以将就着看哈。

 /**
   * 双边滤波
   * 第一个参数:传入的图片(必须是CV_8UC1或者CV_8UC3)
   * 第二个参数:传出的图片
   * 第三个参数:每个像素领域的直径
   * 第四个参数:sigmaColor,这个值越大,该像素领域内会有更广的颜色被混合到一起
   * 第五个参数:sigmaSpace,这个值越大,越远的像素会互相影响,第三个参数大于0时,领域的大小和这个值无关,否则成正比
   * 第六个参数:使用默认值BORDER_DEFAULT
   */
CV_EXPORTS_W void bilateralFilter( InputArray src, OutputArray dst, int d,
double sigmaColor, double sigmaSpace, int borderType = BORDER_DEFAULT );

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_xy_opencv_ndk_1opencv002_MainActivity_blurImage(JNIEnv *env, jclass type,jintArray pixels_, jint w, jint h) {
    jint *pixels = env->GetIntArrayElements(pixels_, NULL);
    Mat img(h, w, CV_8UC3, pixels);
    Mat out(h,w,CV_8UC4);
    bilateralFilter(img,out,25,25*2,25/2);
    int size = w * h;
    jint *re = (jint *) out.data;
    jintArray array = env->NewIntArray(size);
    env->SetIntArrayRegion(array, 0, size, re);
    env->ReleaseIntArrayElements(pixels_, pixels, 0);
    return array;
    }

那份代码,实测速度越发不佳,处理一张5000×3000在半径大小5左右都要耗时十来秒至几十秒不等,实在麻烦接受。

图片 26

是因为速度的题目,网上就有无数优化算法的兑现。

那边写图片描述

以前自己也发过一篇《快快高斯模糊算法》,在同等条件下,这么些算法已经比如经济法快上十几倍。

本小说小说版权所属:微笑面对,请关心自己的CSDN博客:博客地址

由于那份代码实在难以阅读学习,所以,我对其开展了更进一步的调动和优化。

void GaussianBlur(unsigned char* img,  unsigned int width, unsigned int height, unsigned int channels, unsigned int radius)
{
    radius = min(max(1, radius), 248);
    unsigned int kernelSize = 1 + radius * 2;
    unsigned int* kernel = (unsigned int*)malloc(kernelSize* sizeof(unsigned int));
    memset(kernel, 0, kernelSize* sizeof(unsigned int));
    int(*mult)[256] = (int(*)[256])malloc(kernelSize * 256 * sizeof(int));
    memset(mult, 0, kernelSize * 256 * sizeof(int));

    int xStart = 0;
    int yStart = 0;
    width = xStart + width - max(0, (xStart + width) - width);
    height = yStart + height - max(0, (yStart + height) - height);
    int imageSize = width*height;
    int widthstep = width*channels;
    if (channels == 3 || channels == 4)
    {
        unsigned char *    CacheImg = nullptr;
        CacheImg = (unsigned char *)malloc(sizeof(unsigned char) * imageSize * 6);
        if (CacheImg == nullptr) return;
        unsigned char *    rCache = CacheImg;
        unsigned char *    gCache = CacheImg + imageSize;
        unsigned char *    bCache = CacheImg + imageSize * 2;
        unsigned char *    r2Cache = CacheImg + imageSize * 3;
        unsigned char *    g2Cache = CacheImg + imageSize * 4;
        unsigned char *    b2Cache = CacheImg + imageSize * 5;
        int sum = 0;
        for (int K = 1; K < radius; K++){
            unsigned int szi = radius - K;
            kernel[radius + K] = kernel[szi] = szi*szi;
            sum += kernel[szi] + kernel[szi];
            for (int j = 0; j < 256; j++){
                mult[radius + K][j] = mult[szi][j] = kernel[szi] * j;
            }
        }
        kernel[radius] = radius*radius;
        sum += kernel[radius];
        for (int j = 0; j < 256; j++){
            mult[radius][j] = kernel[radius] * j;
        }
        for (int Y = 0; Y < height; ++Y) {
            unsigned char*     LinePS = img + Y*widthstep;
            unsigned char*     LinePR = rCache + Y*width;
            unsigned char*     LinePG = gCache + Y*width;
            unsigned char*     LinePB = bCache + Y*width;
            for (int X = 0; X < width; ++X) {
                int     p2 = X*channels;
                LinePR[X] = LinePS[p2];
                LinePG[X] = LinePS[p2 + 1];
                LinePB[X] = LinePS[p2 + 2];
            }
        }
        int kernelsum = 0;
        for (int K = 0; K < kernelSize; K++){
            kernelsum += kernel[K];
        }
        float fkernelsum = 1.0f / kernelsum;
        for (int Y = yStart; Y < height; Y++){
            int heightStep = Y * width;
            unsigned char*     LinePR = rCache + heightStep;
            unsigned char*     LinePG = gCache + heightStep;
            unsigned char*     LinePB = bCache + heightStep;
            for (int X = xStart; X < width; X++){
                int cb = 0;
                int cg = 0;
                int cr = 0;
                for (int K = 0; K < kernelSize; K++){
                    unsigned    int     readPos = ((X - radius + K + width) % width);
                    int * pmult = mult[K];
                    cr += pmult[LinePR[readPos]];
                    cg += pmult[LinePG[readPos]];
                    cb += pmult[LinePB[readPos]];
                }
                unsigned int p = heightStep + X;
                r2Cache[p] = cr* fkernelsum;
                g2Cache[p] = cg* fkernelsum;
                b2Cache[p] = cb* fkernelsum;
            }
        }
        for (int X = xStart; X < width; X++){
            int WidthComp = X*channels;
            int WidthStep = width*channels;
            unsigned char*     LinePS = img + X*channels;
            unsigned char*     LinePR = r2Cache + X;
            unsigned char*     LinePG = g2Cache + X;
            unsigned char*     LinePB = b2Cache + X;
            for (int Y = yStart; Y < height; Y++){
                int cb = 0;
                int cg = 0;
                int cr = 0;
                for (int K = 0; K < kernelSize; K++){
                    unsigned int   readPos = ((Y - radius + K + height) % height) * width;
                    int * pmult = mult[K];
                    cr += pmult[LinePR[readPos]];
                    cg += pmult[LinePG[readPos]];
                    cb += pmult[LinePB[readPos]];
                }
                int    p = Y*WidthStep;
                LinePS[p] = (unsigned char)(cr * fkernelsum);
                LinePS[p + 1] = (unsigned char)(cg * fkernelsum);
                LinePS[p + 2] = (unsigned char)(cb* fkernelsum);


            }
        }
        free(CacheImg);
    }
    else if (channels == 1)
    {
        unsigned char *    CacheImg = nullptr;
        CacheImg = (unsigned char *)malloc(sizeof(unsigned char) * imageSize * 2);
        if (CacheImg == nullptr) return;
        unsigned char *    rCache = CacheImg;
        unsigned char *    r2Cache = CacheImg + imageSize;

        int sum = 0;
        for (int K = 1; K < radius; K++){
            unsigned int szi = radius - K;
            kernel[radius + K] = kernel[szi] = szi*szi;
            sum += kernel[szi] + kernel[szi];
            for (int j = 0; j < 256; j++){
                mult[radius + K][j] = mult[szi][j] = kernel[szi] * j;
            }
        }
        kernel[radius] = radius*radius;
        sum += kernel[radius];
        for (int j = 0; j < 256; j++){
            mult[radius][j] = kernel[radius] * j;
        }
        for (int Y = 0; Y < height; ++Y) {
            unsigned char*     LinePS = img + Y*widthstep;
            unsigned char*     LinePR = rCache + Y*width;
            for (int X = 0; X < width; ++X) {
                LinePR[X] = LinePS[X];
            }
        }
        int kernelsum = 0;
        for (int K = 0; K < kernelSize; K++){
            kernelsum += kernel[K];
        }
        float fkernelsum = 1.0f / kernelsum;
        for (int Y = yStart; Y < height; Y++){
            int heightStep = Y * width;
            unsigned char*     LinePR = rCache + heightStep;
            for (int X = xStart; X < width; X++){
                int cb = 0;
                int cg = 0;
                int cr = 0;
                for (int K = 0; K < kernelSize; K++){
                    unsigned    int     readPos = ( (X - radius + K+width)%width);
                    int * pmult = mult[K];
                    cr += pmult[LinePR[readPos]];
                }
                unsigned int p = heightStep + X;
                r2Cache[p] = cr * fkernelsum;
            }
        }
        for (int X = xStart; X < width; X++){
            int WidthComp = X*channels;
            int WidthStep = width*channels;
            unsigned char*     LinePS = img + X*channels;
            unsigned char*     LinePR = r2Cache + X;
            for (int Y = yStart; Y < height; Y++){
                int cb = 0;
                int cg = 0;
                int cr = 0;
                for (int K = 0; K < kernelSize; K++){
                    unsigned int   readPos = ((Y - radius + K+height)%height) * width;
                    int * pmult = mult[K];
                    cr += pmult[LinePR[readPos]];
                }
                int    p = Y*WidthStep;
                LinePS[p] = (unsigned char)(cr* fkernelsum);
            }
        }
        free(CacheImg);
    } 
    free(kernel);
    free(mult);
}

  其中有一对算法优化技术,想来也能起到一点引玉之砖的效应。

贴个功用图:

图片 27

正文只是投砾引珠一下,若有其他相关题材仍旧必要也得以邮件联系自身商量。

 邮箱地址是:

gaozhihan@vip.qq.com

相关文章