下面blabla一段废话心急的同志们可以跳过。

        周所周知,实际获得的图像再形成、传输、接收和处理的过程中,不可避免地存在着外部干扰和内部干扰,如光电转换过程中敏感原件灵敏度的不均匀性、数字化过程的量化噪声、传输过程中的误差以及人为因素等,均会存在一定程度的噪声干扰。噪声恶化了图像质量,使图像模糊,特征淹没,给分析带来困难。

        噪声干扰一般是随机产生的,分布不规则,大小也不规则。噪声像素的灰度是空间不相关,与临近的像素显著不同。因此,去除噪声、恢复原始图像是图像处理中的一个重要内容。去除图像噪声的工作称为图像平滑或滤波。图像去噪是一种信号滤波的方法,目的是消除噪声,保留游泳信号,降低干扰,改善图像质量。同时,在提取较大目标前,去除太小的细节,或将目标内的小间断连接起来,平滑也起到模糊作用。平滑滤波对图像的低频分量增强,同时削弱高频分量,用于消除图像中的随机噪声,起到平滑作用。

        由于噪声源众多(如光栅扫描、底片颗粒、机械原件、信道传输等),噪声种类复杂(如加强噪声、乘性噪声、量化噪声等),所以平滑方法也多种多样。平滑可以在空间域进行,也可以在频率域进行。空间域法采用在原图像上直接对像素的灰度值进行处理,分为两类:点运算和局部运算。

下面介绍记中图像平滑的常用方法。

tips:几乎下面的所有处理函数均会用到此方法,为方便广大读者动手实践,特记录在此

其作用是将传入的图像以100为阈值进行二值化,并返回二值化之后的图像。

private Bitmap twoGray(Bitmap bm) {
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color;
        int r, g, b, a;

        Bitmap bmp = Bitmap.createBitmap(width, height
                , Bitmap.Config.ARGB_8888);

        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];
        bm.getPixels(oldPx, 0, width, 0, 0, width, height);

        for (int i = 0; i < width * height; i++) {
            color = oldPx[i];
            r = Color.red(color);
            g = Color.green(color);
            b = Color.blue(color);
            a = Color.alpha(color);

            int gray = (int)((float)r*0.3+(float)g*0.59+(float)b*0.11);

            if(gray > 100) {
                gray = 255;
            } else {
                gray = 0;
            }

            newPx[i] = Color.argb(a,gray,gray,gray);
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);

        return bmp;
    }

一、二值图像的黑白点噪声滤波

      本函数消除二值图像f(i,j)上的黑白点噪声,当f(i,j)周围的8个像素的平均值为a时,若|f(i,j)-a|的值大于127.5,则对f(i,j)的黑白进行反转;若小于127.5,则f(i,j)不变。

思路:

  1. 传入需要处理的图像
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 计算所需处理点周围8个点灰度的平均值average
  6. 所需处理点的灰度值为gray,如果|gray-average|>127.5,则令gray=average
  7. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  8. 创建一个高度、宽度和原图完全一样的新图
  9. 将存新数组中的颜色值赋给新图
  10. 将新图像返回
private void twoGrayWhiteAndBlack(Bitmap bm) {
        Bitmap grayBitmap = twoGray(bm);
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color,gray,average;
        int r, g, b, a = 0;

        Bitmap bmp = Bitmap.createBitmap(width, height
                , Bitmap.Config.ARGB_8888);



        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];
        grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);

        for (int i = 1; i < height - 1; i++) {
            for(int j = 1;j < width - 1;j++) {
                color = oldPx[i*width+j];
                r = Color.red(color);//已经进行了二值化,因此r,g,b分量都等于灰度值
                a = Color.alpha(color);
                average = 0;
                average = (int)((Color.red(oldPx[(i-1)*width+j-1]) + Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i-1)*width+j+1])
                        + Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1]) + Color.red(oldPx[(i+1)*width+j-1])
                        + Color.red(oldPx[(i+1)*width+j]) + Color.red(oldPx[(i+1)*width+j+1])) / 8);
                //Log.d("xyz","average = " + average + "    i = " + i*width+j);
                if(Math.abs(r - average) > 127.5) {
                    r = average;
                }
                newPx[i*width+j] = Color.argb(a,r,r,r);
            }
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        mImageView.setImageBitmap(bmp);
    }

效果(为防止大家看不清楚,先来两张细节图)

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_02

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_均值滤波_03

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_Android _04

二、消除孤立黑像素点

        一幅图像往往可能受到各种噪声源的干扰,这些噪声使图像表现为一些孤立像素点,它们像雪花落在画面上一样。

        本函数在二值图像f中消除孤立于周围的黑像素点(变成白的)。像素的四邻域和八邻域关系如下所示

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_05

        在四邻域的情况下,若黑像素f(i,j)的上下左右4个像素全为白(255),则f(i,j)也取255。在八邻域的情况下,若黑像素f(i,j)的周围8个像素全为白(255),则f(i,j)也取255。

思路:

  1. 传入需要处理的图像
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 计算所需处理点周围8(或4)个点灰度的和sum
  6. 所需处理点灰度值为gray,如果sum>255*8(或sum>255*4),则令gray = 255
  7. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  8. 创建一个高度、宽度和原图完全一样的新图
  9. 将存新数组中的颜色值赋给新图
  10. 将新图像返回
private void isolatedBlack(Bitmap bm, int count) {
        Bitmap grayBitmap = twoGray(bm);
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color,gray,average;
        int r, g, b, a = 0;

        Bitmap bmp = Bitmap.createBitmap(width, height
                , Bitmap.Config.ARGB_8888);



        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];
        grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);

        if(count == 4) {
            for(int i = 1;i < height - 1;i++) {
                for (int j = 1;j < width;j++) {
                    color = oldPx[i*width+j];
                    r = Color.red(color);//已经进行了二值化,因此r,g,b分量都等于灰度值
                    a = Color.alpha(color);
                    if(r == 255)
                        continue;
                    if(Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1])
                            + Color.red(oldPx[(i+1)*width+j]) == 255 * 4) {
                        r = 255;
                    }
                    newPx[i*width+j] = Color.argb(a,r,r,r);
                }
            }
        }

        if(count == 8) {
            for(int i = 1;i < height - 1;i++) {
                for (int j = 1;j < width;j++) {
                    color = oldPx[i*width+j];
                    r = Color.red(color);//已经进行了二值化,因此r,g,b分量都等于灰度值
                    a = Color.alpha(color);
                    if(r == 255)
                        continue;
                    if((Color.red(oldPx[(i-1)*width+j-1]) + Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i-1)*width+j+1])
                            + Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1]) + Color.red(oldPx[(i+1)*width+j-1])
                            + Color.red(oldPx[(i+1)*width+j]) + Color.red(oldPx[(i+1)*width+j+1])) == 255 * 8) {
                        r = 255;
                    }
                    newPx[i*width+j] = Color.argb(a,r,r,r);
                }
            }
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        mImageView.setImageBitmap(bmp);

    }

第二行用到的twoGray()函数记录在本文上面。

效果(四邻域)

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_06

 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_07

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_08

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_09

三、邻域平均法

        一幅图像往往受到各种噪声的干扰,噪声常为一些孤立的像素点,往往是叠加在图像上的随机噪声,像雪花一样使图像被污染,而图像灰度应该是相对连续变化的,一般不会突然变大或变小,这种噪声可以用邻域平均法是它得到抑制。噪声点像素的灰度与它们临近像素有显著不同,根据噪声点的这一空间特性,我们用邻域平均法和阈值平均法进行处理。

(一)3*3均值滤波器

                         

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_10

  * 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_11

           

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_12

  *  

         

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_13

   *   

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_14

          

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_均值滤波_15

   *  

    

                                模板一                         模板二                           模板三                  模板四

 将所需处理点及其周围8个点按照模板设置的权值求加权平均值,在除以总权值,即为处理后该点的灰度值,我们选择最简单的模板三。

private void average33(Bitmap bm) {
        Bitmap grayBitmap = twoGray(bm);
        int width = bm.getWidth();
        int height = bm.getHeight();
        int color,gray,average;
        int r, g, b, a = 0;

        Bitmap bmp = Bitmap.createBitmap(width, height
                , Bitmap.Config.ARGB_8888);



        int[] oldPx = new int[width * height];
        int[] newPx = new int[width * height];
        grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);

        for (int i = 1; i < height - 1; i++) {
            for(int j = 1;j < width - 1;j++) {
                color = oldPx[i*width+j];
                a = Color.alpha(color);
                average = 0;
                average = (int)((Color.red(oldPx[(i-1)*width+j-1]) + Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i-1)*width+j+1])
                        + Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1]) + Color.red(oldPx[(i+1)*width+j-1])
                        + Color.red(oldPx[(i+1)*width+j]) + Color.red(oldPx[(i+1)*width+j+1])) / 8);
                newPx[i*width+j] = Color.argb(a,average,average,average);
            }
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);
        mImageView.setImageBitmap(bmp);
    }

第二行用到的twoGray()函数记录在本文上面。

效果

 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_16

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_Android _17

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_18

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_Android _19

(二)N×N均值滤波

        当灰度图像f中以像素f(i,j)为中心的N×N屏蔽窗口(N=3,5,7,……)内平均灰度为average时,无条件作f(i,j)=average处理,N由用户给定,且取N值越大,噪声被减少越明显。但此方法是以图像的模糊为代价的。

思路

  1. 传入需要处理的图像
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 取得N值
  6. 算出以该点为中心的N×N屏蔽窗口内平均值。
  7. 把该点灰度值置为平均值
  8. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  9. 创建一个高度、宽度和原图完全一样的新图
  10. 将存新数组中的颜色值赋给新图
  11. 将新图像返回
private void averagenn(final Bitmap bm) {

        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("n*n均值滤波法");
        builder.setMessage("请输入n(奇数)");
        View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
        final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
        builder.setView(view1);

        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {

                Bitmap grayBitmap = twoGray(bm);
                int width = bm.getWidth();
                int height = bm.getHeight();
                int color,gray,average;
                int r, g, b, a = 0;
                int xx,yy,n2 = 0,sum;

                Bitmap bmp = Bitmap.createBitmap(width, height
                        , Bitmap.Config.ARGB_8888);



                int[] oldPx = new int[width * height];
                int[] newPx = new int[width * height];
                grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);

                String str = mSingleThresholdEt.getText().toString();
                if("".equals(str)) {
                    n = 0;
                } else {
                    n = Integer.valueOf(str);
                }

                if(n < 3 || n % 2 != 1) {
                    return;
                }

                if(n >= 3 && n % 2 == 1) {
                    n2 = (n - 1) / 2;
                }


                for (int j = n2;j < height - n2;j++) {
                    for(int k = n2;k < width - n2;k++) {
                        color = oldPx[j*width+k];
                        a = Color.alpha(color);
                        sum = 0;
                        for(yy = j - n2;yy <= j + n2;yy++) {
                            for(xx = k - n2;xx <= k + n2;xx++) {
                                sum += Color.red(oldPx[yy * width + xx]);
                            }
                        }
                        r = (int)((float)sum/(n*n) + 0.5f);
                        //Log.d("xyz","sum = " + sum + "    i = " + j*width+k);
                        newPx[j*width+k] = Color.argb(a,r,r,r);
                    }
                }
                bmp.setPixels(newPx, 0, width, 0, 0, width, height);
                mImageView.setImageBitmap(bmp);
            }
        });

        AlertDialog dialog = builder.create();
        dialog.show();
    }

下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="阈值:"/>
    <EditText
        android:id="@+id/digit_dialog"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

效果

 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_均值滤波_20

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_21

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_22

 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_23

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_均值滤波_24

(三)超限邻域平均法 

        邻域平均法虽然简单,但它存在着边缘模糊效应,本来不是噪声的边缘处,应该保留原有的灰度差,而邻域平均法使边缘处的灰度趋向均匀,造成了边缘模糊。为了减少模糊效应,需寻求改进的途径,力求找到解决清除噪声和边缘模糊这对矛盾的最佳方法。

        阈值邻域平均法以某个灰度值T作为阈值,如果某个像素的灰度大于其临近像素的平均值,并超过阈值,才使用平均灰度值置换这个像素灰度,它的数学表达式为

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_均值滤波_25

此式表明,若某点值与其邻域平均值相差超过T,则用平均值代替,进行平均处理,可去除噪声;否则还保留原值,不进行平均处理,从而减少模糊。这种算法对抑制椒盐噪声比较有效,同时也能较好地保留仅有微小灰度差的图像细节。

思路

  1. 传入需要处理的图像
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 取得T值
  6. 算出以该点为中心的N×N屏蔽窗口内平均值average。
  7. 该点灰度值为gray,如果|gray-average|>T,则将该点灰度值置换为average
  8. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  9. 创建一个高度、宽度和原图完全一样的新图
  10. 将存新数组中的颜色值赋给新图
  11. 将新图像返回
private void overLimitAverage(final Bitmap bm) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("超限邻域平均法");
        builder.setMessage("请输入阈值");
        View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
        final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
        builder.setView(view1);

        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int k) {

                Bitmap grayBitmap = twoGray(bm);
                int width = bm.getWidth();
                int height = bm.getHeight();
                int color,gray,average;
                int r, g, b, a = 0;

                Bitmap bmp = Bitmap.createBitmap(width, height
                        , Bitmap.Config.ARGB_8888);



                int[] oldPx = new int[width * height];
                int[] newPx = new int[width * height];
                grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);

                String str = mSingleThresholdEt.getText().toString();
                if("".equals(str)) {
                    T = 0;
                } else {
                    T = Integer.valueOf(str);
                }

                for (int i = 1; i < height - 1; i++) {
                    for(int j = 1;j < width - 1;j++) {
                        color = oldPx[i*width+j];
                        r = Color.red(color);
                        a = Color.alpha(color);
                        average = 0;
                        average = (int)((Color.red(oldPx[(i-1)*width+j-1]) + Color.red(oldPx[(i-1)*width+j]) + Color.red(oldPx[(i-1)*width+j+1])
                                + Color.red(oldPx[(i)*width+j-1]) + Color.red(oldPx[(i)*width+j+1]) + Color.red(oldPx[(i+1)*width+j-1])
                                + Color.red(oldPx[(i+1)*width+j]) + Color.red(oldPx[(i+1)*width+j+1])) / 8);

                        if(Math.abs(r - average) > T) {
                            r = average;
                        }
                        newPx[i*width+j] = Color.argb(a,r,r,r);
                    }
                }
                bmp.setPixels(newPx, 0, width, 0, 0, width, height);
                mImageView.setImageBitmap(bmp);
            }
        });

        AlertDialog dialog = builder.create();
        dialog.show();
    }

下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="阈值:"/>
    <EditText
        android:id="@+id/digit_dialog"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

效果

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_Android _26

 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_27

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_28

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_29

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_30

四、中值滤波法

中值滤波能够抑制噪声又保持细节,是比较好的选择。

对一些细节特别多,尤其是点、线、尖顶细节过的图像,不宜采用中值滤波方法。

如下图所示,取3×3窗口,从小到大排列:

        33        200        201        202        205        206        207        208        210

取中间值205代替原来的数值202。

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_31

        二维中值滤波的窗口形状和尺寸设计对滤波的效果影响较大,对于不同的图像内容和不同的应用要求,往往采用不同的形状和尺寸。

        常用的二维中值滤波窗口有线状、方形、圆形、十字型及圆环形等。 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_Android _32

(一)N×N中值滤波

思路

  1. 传入需要处理的图像
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 取得N值
  6. 循环,对以某点为中心的N×N屏蔽窗口包括各点灰度值进行排序,得到中间值
  7. 把该点灰度值置为中间值
  8. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  9. 创建一个高度、宽度和原图完全一样的新图
  10. 将存新数组中的颜色值赋给新图
  11. 将新图像返回
private void middlenn(final Bitmap bm) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("n*n中值滤波");
        builder.setMessage("请输入n值(大于3的奇数)");
        View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
        final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
        builder.setView(view1);

        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int k) {

                Bitmap grayBitmap = twoGray(bm);
                int width = bm.getWidth();
                int height = bm.getHeight();
                int color,gray,average;
                int r, g, b, a = 0;
                int yy,xx,n2 = 0,nn,chuo,chg = 0,m,medi,madom;
                int[] mado = new int[1000];

                Bitmap bmp = Bitmap.createBitmap(width, height
                        , Bitmap.Config.ARGB_8888);



                int[] oldPx = new int[width * height];
                int[] newPx = new int[width * height];
                grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);

                String str = mSingleThresholdEt.getText().toString();
                if("".equals(str)) {
                    na = 0;
                } else {
                    na = Integer.valueOf(str);
                }

                if(na < 3 || na % 2 != 1) {
                    Toast.makeText(getActivity(),"请输入一个大于等于3的奇数",Toast.LENGTH_SHORT).show();
                    return;
                }

                if(na >= 3 && na % 2 == 1) {
                    n2 = (na - 1) / 2;
                }

                nn = na * na;
                chuo = (nn - 1) / 2;

                for(int j = n2;j < height - n2;j++) {
                    for(int i = n2;i < width - n2;i++) {
                        m = 0;
                        color = oldPx[j*width+i];
                        a = Color.alpha(color);
                        for(yy = j - n2;yy <= j + n2;yy++) {
                            for (xx = i - n2; xx <= i + n2; xx++) {
                                mado[m] = Color.red(oldPx[yy * width + xx]);
                                m++;
                            }
                        }
                        //把mado[m]中的值按下降顺序用冒泡排序
                        do {
                            chg = 0;
                            for(m = 0;m < nn - 1;m++) {
                                if(mado[m] < mado[m+1]) {
                                    madom = mado[m];
                                    mado[m] = mado[m+1];
                                    mado[m+1] = madom;
                                    chg = 1;
                                }
                            }
                        }while(chg == 1);
                        //求中值medi
                        medi = mado[chuo];
                        //把中值代入显示图像中
                        //Log.d("xyz","medi + " + medi);
                        newPx[j*width+i] = Color.argb(a,medi,medi,medi);
                    }
                }
                bmp.setPixels(newPx, 0, width, 0, 0, width, height);
                mImageView.setImageBitmap(bmp);
            }
        });

        AlertDialog dialog = builder.create();
        dialog.show();
    }

下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="阈值:"/>
    <EditText
        android:id="@+id/digit_dialog"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

效果 

 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_33

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_34

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_均值滤波_35

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_Android _36

(二)十字型中值滤波

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_37

 思路

  1. 传入需要处理的图像
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 取得N值
  6. 循环,计算以某像素为中心的十字型屏蔽窗口(十字型的纵向和横向长度为N)内灰度中值
  7. 把该点灰度值置为中间值
  8. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  9. 创建一个高度、宽度和原图完全一样的新图
  10. 将存新数组中的颜色值赋给新图
  11. 将新图像返回
private void cross(final Bitmap bm) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("十字型中值滤波");
        builder.setMessage("请输入n值(大于3的奇数)");
        View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
        final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
        builder.setView(view1);

        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int k) {

                Bitmap grayBitmap = twoGray(bm);
                int width = bm.getWidth();
                int height = bm.getHeight();
                int color,gray,average;
                int r, g, b, a = 0;
                int yy,xx,n2 = 0,nn,chuo,chg = 0,m,medi,madom;
                int[] mado = new int[1000];

                Bitmap bmp = Bitmap.createBitmap(width, height
                        , Bitmap.Config.ARGB_8888);



                int[] oldPx = new int[width * height];
                int[] newPx = new int[width * height];
                grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);

                String str = mSingleThresholdEt.getText().toString();
                if("".equals(str)) {
                    nc = 0;
                } else {
                    nc = Integer.valueOf(str);
                }

                if(nc < 3 || nc % 2 != 1) {
                    Toast.makeText(getActivity(),"请输入一个大于等于3的奇数",Toast.LENGTH_SHORT).show();
                    return;
                }

                if(nc >= 3 && nc % 2 == 1) {
                    n2 = (nc - 1) / 2;
                }

                nn = nc + nc + 1;
                chuo = (nn - 1) / 2;

                for(int j = n2;j < height - n2;j++) {
                    for(int i = n2;i < width - n2;i++) {
                        m = 0;
                        color = oldPx[j*width+i];
                        a = Color.alpha(color);
                        for(yy = j - n2;yy <= j + n2;yy++) {
                            mado[m] = Color.red(oldPx[yy*width+i]);
                            m++;
                        }

                        for(xx = i - n2; xx <= i + n2; xx++) {
                            if(xx == i)
                                continue;
                            mado[m] = Color.red(oldPx[j*width+xx]);
                            m++;
                        }

                        //把mado[m]中的值按下降顺序用冒泡排序
                        do {
                            chg = 0;
                            for(m = 0;m < nn - 1;m++) {
                                if(mado[m] < mado[m+1]) {
                                    madom = mado[m];
                                    mado[m] = mado[m+1];
                                    mado[m+1] = madom;
                                    chg = 1;
                                }
                            }
                        }while(chg == 1);
                        //求中值medi
                        medi = mado[chuo];
                        //把中值代入显示图像中
                        //Log.d("xyz","medi + " + medi);
                        newPx[j*width+i] = Color.argb(a,medi,medi,medi);
                    }
                }
                bmp.setPixels(newPx, 0, width, 0, 0, width, height);
                mImageView.setImageBitmap(bmp);
            }
        });

        AlertDialog dialog = builder.create();
        dialog.show();
    }

下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="阈值:"/>
    <EditText
        android:id="@+id/digit_dialog"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

效果

 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_38

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_39

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_40

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_41

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_图像平滑_42

(三)N×N最大值滤波

思路

  1. 传入需要处理的图像
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 取得N值
  6. 循环,取得以某点为中心的N×N屏蔽窗口包括的各点像素值中的最大值
  7. 把该点灰度值置为最大值
  8. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  9. 创建一个高度、宽度和原图完全一样的新图
  10. 将存新数组中的颜色值赋给新图
  11. 将新图像返回

 

private void maxnn(final Bitmap bm) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
        builder.setTitle("n*n最大值滤波");
        builder.setMessage("请输入n值(大于3的奇数)");
        View view1 = LayoutInflater.from(getContext()).inflate(R.layout.threshold,null);
        final EditText mSingleThresholdEt = view1.findViewById(R.id.digit_dialog);
        builder.setView(view1);

        builder.setPositiveButton("确定", new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int k) {

                Bitmap grayBitmap = twoGray(bm);
                int width = bm.getWidth();
                int height = bm.getHeight();
                int color,gray,average;
                int r, g, b, a = 0;
                int yy,xx,n2 = 0,nn,chg = 0,m,medi,madom,madomax;
                int[] mado = new int[1000];

                Bitmap bmp = Bitmap.createBitmap(width, height
                        , Bitmap.Config.ARGB_8888);



                int[] oldPx = new int[width * height];
                int[] newPx = new int[width * height];
                grayBitmap.getPixels(oldPx, 0, width, 0, 0, width, height);

                String str = mSingleThresholdEt.getText().toString();
                if("".equals(str)) {
                    nm = 0;
                } else {
                    nm = Integer.valueOf(str);
                }

                if(nm < 3 || nm % 2 != 1) {
                    Toast.makeText(getActivity(),"请输入一个大于等于3的奇数",Toast.LENGTH_SHORT).show();
                    return;
                }

                if(nm >= 3 && nm % 2 == 1) {
                    n2 = (nm - 1) / 2;
                }

                nn = nm*nm;

                for(int j = n2;j < height - n2;j++) {
                    for(int i = n2;i < width - n2;i++) {
                        m = 0;
                        color = oldPx[j*width+i];
                        a = Color.alpha(color);
                        for(yy = j - n2;yy <= j + n2;yy++) {
                            for (xx = i - n2; xx <= i + n2; xx++) {
                                mado[m] = Color.red(oldPx[yy * width + xx]);
                                m++;
                            }
                        }

                        madomax = mado[0];
                        for(m = 1;m < nn;m++) {
                            if(madomax<mado[m])
                                madomax = mado[m];
                        }

                        newPx[j*width+i] = Color.argb(a,madomax,madomax,madomax);
                    }
                }
                bmp.setPixels(newPx, 0, width, 0, 0, width, height);
                mImageView.setImageBitmap(bmp);
            }
        });

        AlertDialog dialog = builder.create();
        dialog.show();
    }

下面是我们在接收用户输入时使用的对话框所引入的自定义布局threshold.xml 

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="阈值:"/>
    <EditText
        android:id="@+id/digit_dialog"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

效果

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_数字图像处理_43

 

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_均值滤波_44

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_中值滤波_45

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_Android _46

Android使用NoiseSuppression去掉AudioRecorder噪音 android 降噪_均值滤波_47

 代码已上传到github,点击这里可以下载体验