Android数字图像处理之图像平滑

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/huangxin388/article/details/82952949

下面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);
    }

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

二、消除孤立黑像素点

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

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

        在四邻域的情况下,若黑像素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()函数记录在本文上面。

效果(四邻域)

 

三、邻域平均法

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

(一)3*3均值滤波器

                         \frac{1}{9}  * \begin{bmatrix} 1 & 1 & 1\\ 1 & 1 & 1\\ 1 & 1 & 1 \end{bmatrix}            \frac{1}{5}  *  \begin{bmatrix} 0 & 1 & 0\\ 1 & 1 & 1 \\ 0 & 1 & 0 \end{bmatrix}          \frac{1}{8}   *   \begin{bmatrix} 1 & 1 & 1\\ 1 & 0 & 1\\ 1& 1 & 1 \end{bmatrix}          \frac{1}{16}   *  \begin{bmatrix} 1 & 2 & 1\\ 2 & 4 & 2\\ 1 & 2 & 1 \end{bmatrix}    

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

 将所需处理点及其周围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()函数记录在本文上面。

效果

 

(二)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>

效果

 

 

(三)超限邻域平均法 

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

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

此式表明,若某点值与其邻域平均值相差超过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>

效果

 

四、中值滤波法

        邻域平均法属于低通滤波的处理方法。它在抑制噪声的同时使图像变得模糊,即图像的细节(例如边缘信息)被削弱,因此,平均滤波往往不只是把干扰去除,还常使图像的边缘模糊,因而造成视觉上的失真,如果目的只是把干扰去除,而不是刻意让图像模糊,中值滤波能够抑制噪声又保持细节,是比较好的选择。

        中值滤波将窗口中奇数个数据按大小顺序排列,处于中心位置的数作为处理结果,它是一种非线性的信号处理方法,与其对应的中值滤波器也是一种非线性的滤波器。中值滤波器在一定的条件下可以克服线性滤波器如最小均方滤波、平均值滤波等所带来的图像细节模糊,而且对滤波脉冲干扰及图像扫描噪声最为有效。特别适合用在很强的胡椒粉式或脉冲式的干扰时,因为这些干扰值与其临近像素的灰度值有很大的差异,因此经排序后取中值的结果是强迫将此干扰变成与其临近的某些像素的灰度值一样,达到去除干扰的效果。在实际运算过程中并不需要图像的统计特性,这也带来不少方便,但是对一些细节特别多,尤其是点、线、尖顶细节过的图像,不宜采用中值滤波方法。

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

        33        200        201        202        205        206        207        208        210

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

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

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

(一)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>

效果 

 

(二)十字型中值滤波

 思路

  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>

效果

 

(三)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>

效果

 

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

                  

猜你喜欢

转载自blog.csdn.net/huangxin388/article/details/82952949
今日推荐