Android NDK图片模糊处理之高斯模糊算法

效果图:

在这里插入图片描述
参考了c++的高斯模糊的算法 高斯模糊的C++实现(Gaussian Blur),改成了Java版本的和ndk版本的,对比了下效果,Java的效率比较低,用时几十秒,ndk才不到1秒,毕竟安卓是Linux内核,c++跟接近底层。

原理

图像是颜色矩阵组成的,通过改变每个像素点的rgb的颜色值,可以改变图像。
图片模糊就是把像素点的rgb的值取周围像素点的加权平均值,使像素点失去其特性。
高斯模糊(英语:Gaussian Blur),也叫高斯平滑。
正态分布一维表达式:
在这里插入图片描述
二维是2个一维函数的积分:
在这里插入图片描述
这种计算方式时间复杂度大,这里试了下另一种高效的方法,对一维高斯函数分别在水平和垂直方向上继续2次计算。

下面分别贴出Java和dnk2个版本的核心代码。

  • GsUtil.java工具类
/**
 * @author DaQiang
 * @time 2020/12/18 14:25
 * @desc 高斯函数工具类
 */
public class GsUtil {
    public static final float sigma = 1F;
    public static final int r = 5;

    /**
     * 边界处理函数
     * 之所以引入这个函数,是因为在高斯模糊中,每个点的新值都是由临近的矩形区域的平均值得出,
     * 但是若矩形区域超出图像范围,则会产生黑边。为了消除这一影响,我们采用对称的方法,
     * 将矩形的超出部分用内部的对应点代替。
     *
     * @param x
     * @param i
     * @param w
     * @return
     */
    private static int edge(int x, int i, int w) {
        // x为中心位置, i为偏移量,w为宽度
        int inx = x + i;
        if (inx < 0 || inx >= w)
            return x - i;
        return inx;
    }


    // 一维高斯函数
    private static double gaussFunc1D(int x) {
        double A = 1 / (sigma * Math.sqrt(2 * 3.141592653));
        double index = -1.0 * (x * x) / (2 * sigma * sigma);
        return A * Math.exp(index);
    }

    // 获取线性的权值空间
    private static double[] getKernal() {
        double[] weight = new double[2 * r + 1];
        double sum = 0;

        // 获取权值空间weight[]
        for (int i = 0; i < 2 * r + 1; i++) {
            weight[i] = gaussFunc1D(i - r);
            sum += weight[i];
        }
        // 归一化
        for (int i = 0; i < 2 * r + 1; i++) {
            weight[i] /= sum;
        }

        return weight;
    }

    //进行两个方向的高斯模糊获
    // 获取最终的模糊图像
    public static Bitmap getBlurImage(Bitmap originImg) {
        int width = originImg.getWidth();
        int height = originImg.getHeight();
        // 原图为 originImg
        // 第一个方向处理的图像为tmpImg
        Bitmap tmpImg = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        // 经过第二次处理的最终结果newImg
        Bitmap newImg = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);

        double[] weight = getKernal();

        // 在横向进行一次相加
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                double red = 0, green = 0, blue = 0;
                for (int i = -r; i <= r; i++) {
                    // 边界处理后的对应的权值矩阵实际值
                    int inx = edge(x, i, width);
                    int color = originImg.getPixel(inx, y);
                    red += Color.red(color) * weight[r + i];
                    green += Color.green(color) * weight[r + i];
                    blue += Color.blue(color) * weight[r + i];
                }
                int color = Color.rgb(Integer.parseInt(Math.round(red)+""), Integer.parseInt(Math.round(green)+""), Integer.parseInt(Math.round(blue)+""));
                int srcColor = originImg.getPixel(x,y);
                Log.d("GsUtil", "color:" + color+" srcColor: "+srcColor);
                tmpImg.setPixel(x, y, color);
            }
        }
        // 在纵方向对第一次的结果重新进行一次
        for (int y = 0; y < height; y++) {
            for (int x = 0; x < width; x++) {
                double red = 0, green = 0, blue = 0;
                for (int i = -r; i <= r; i++) {
                    int iny = edge(y, i, height);
                    int color = tmpImg.getPixel(x, iny);

                    red += Color.red(color) * weight[r + i];
                    green += Color.green(color) * weight[r + i];
                    blue += Color.blue(color) * weight[r + i];

                }
                int color = Color.rgb(Integer.parseInt(Math.round(red)+""), Integer.parseInt(Math.round(green)+""), Integer.parseInt(Math.round(blue)+""));
                newImg.setPixel(x, y, color);
            }
        }
        return newImg;
    }

}
  • native-lib.cpp
#include <jni.h>
#include <string>
#include<math.h>
#include <android/bitmap.h>
#include <stdlib.h>
#include <android/log.h>

#define TAG "native-lib" // 这个是自定义的LOG的标识
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,TAG ,__VA_ARGS__) // 定义LOGE类型

float sigma = 3;
int r = 9;

int edge(int x, int i, int w) {
    // x为中心位置, i为偏移量,w为宽度
    int inx = x + i;
    if (inx < 0 || inx >= w)
        return x - i;
    return inx;
}


double gaussFunc1D(int x) {
    double A = 1 / (sigma * sqrt(2 * 3.141592653));
    double index = -1.0 * (x * x) / (2 * sigma * sigma);
    return A * exp(index);
}

void getKernal(double *weight) {
//    double *weight = new double[2 * r + 1];

    double sum = 0;

    // 获取权值空间weight[]
    for (int i = 0; i < 2 * r + 1; i++) {
        weight[i] = gaussFunc1D(i - r);
        sum += weight[i];
    }
    // 归一化
    for (int i = 0; i < 2 * r + 1; i++) {
        weight[i] /= sum;
    }
}

extern "C"
JNIEXPORT jintArray JNICALL
Java_com_example_imagendk_ImageUtil_getImageGaoSiMohu(JNIEnv *env, jclass clazz, jintArray arr,
                                                      jint width, jint height) {

    jint *source = (*env).GetIntArrayElements(arr, NULL);
    int newSize = width * height;
    double *weight = new double[2 * r + 1];
    getKernal(weight);
    int *temp = new int[newSize];
    int *re = new int[newSize];


    // 在横向进行一次相加
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            double red = 0, green = 0, blue = 0;
            for (int i = -r; i <= r; i++) {
                // 边界处理后的对应的权值矩阵实际值
                int inx = edge(x, i, width);
                int color = source[y * width + inx];
                int tmpRed = (color >> 16) & 0xFF;
                int g = (color >> 8) & 0xFF;
                int b = color & 0xFF;

                red += tmpRed * weight[r + i];
                green += g * weight[r + i];
                blue += b * weight[r + i];
            }
            int color = 0xff000000 | (int(red) << 16) | (int(green) << 8) | int(blue);
            temp[y * width + x] = color;
        }
    }


    // 在纵方向对第一次的结果重新进行一次
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            double red = 0, green = 0, blue = 0;
            for (int i = -r; i <= r; i++) {
                int iny = edge(y, i, height);
                int color = temp[iny * width + x];

                int tmpRed = (color >> 16) & 0xFF;
                int g = (color >> 8) & 0xFF;
                int b = color & 0xFF;

                red += tmpRed * weight[r + i];
                green += g * weight[r + i];
                blue += b * weight[r + i];

            }
            int color = 0xff000000 | (int(red) << 16) | (int(green) << 8) | int(blue);
            re[y * width + x] = color;
        }
    }


    jintArray result = (*env).NewIntArray(newSize);
    (*env).SetIntArrayRegion(result, 0, newSize, re);
    (*env).ReleaseIntArrayElements(arr, source, 0);
    (*env).ReleaseIntArrayElements(arr, temp, 0);
    (*env).ReleaseIntArrayElements(arr, re, 0);
    return result;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值