Android数字图像处理之非线性变换

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

        这是一种输出灰度级与输入灰度级呈非线性关系的点运算。前面讲过的“非0即1法”,固定阈值法,双固定阈值法等都属于非线性变换。这里再补充几种常用的非线性变换。

一、灰度对数变换

        对数变换实现了图像的灰度扩展和压缩的功能。它扩展低灰度值而压缩高灰度值,让图像的分布更加符合人的视觉特征。

灰度对数变换公式如下(图像就不画了,不同的a和b产生不同的对数图像,对图像的扩展和压缩也相应改变)

                                                    y = a+log(1+x)/b

下面的例子中我们取a = 10,b = 0.025

思路:

  1. 传入需要处理的图像    
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 令某点的灰度值gray = log(gray+1)/0.025+10
  6. 如果某点的灰度值小于阈值下限则将其灰度值置为0
  7. 如果某点的灰度值大于阈值上限则将其灰度值置为255
  8. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  9. 创建一个高度、宽度和原图完全一样的新图
  10. 将新图像返回
   private Bitmap logarithm(Bitmap bm) {
        int width = bm.getWidth();//原图像宽度
        int height = bm.getHeight();//原图高度
        int color;//用来存储某个像素点的颜色值
        int gray;//用来存储计算得到的灰度值
        int r, g, b, a;//红,绿,蓝,透明度
        //创建空白图像,宽度等于原图宽度,高度等于原图高度,用ARGB_8888渲染,这个不用了解,这样写就行了
        Bitmap bmp = Bitmap.createBitmap(width, height
                , Bitmap.Config.ARGB_8888);

        int[] oldPx = new int[width * height];//用来存储原图每个像素点的颜色信息
        int[] newPx = new int[width * height];//用来处理处理之后的每个像素点的颜色信息
        /**
         * 第一个参数oldPix[]:用来接收(存储)bm这个图像中像素点颜色信息的数组//The array to receive the bitmap’s colors
         * 第二个参数offset:oldPix[]数组中第一个接收颜色信息的下标值// The first index to write into pixels[]
         * 第三个参数width:在行之间跳过像素的条目数,必须大于等于图像每行的像素数//The number of entries in pixels[] to skip between rows (must be >= bitmap’s width). Can be negative.
         * 第四个参数x:从图像bm中读取的第一个像素的横坐标 The x coordinate of the first pixel to read from the bitmap
         * 第五个参数y:从图像bm中读取的第一个像素的纵坐标The y coordinate of the first pixel to read from the bitmap
         * 第六个参数width:每行需要读取的像素个数The number of pixels to read from each row
         * 第七个参数height:需要读取的行总数The number of rows to read
         */
        bm.getPixels(oldPx, 0, width, 0, 0, width, height);

        for (int i = 0; i < width * height; i++) {//循环处理图像中每个像素点的颜色值
            color = oldPx[i];//取得某个点的像素值
            r = Color.red(color);//取得此像素点的r(红色)分量
            g = Color.green(color);//取得此像素点的g(绿色)分量
            b = Color.blue(color);//取得此像素点的b(蓝色分量)
            a = Color.alpha(color);//取得此像素点的a通道值
            //此公式将r,g,b运算获得灰度值,经验公式不需要理解
            gray = (int)((float)r*0.3+(float)g*0.59+(float)b*0.11);

            gray = (int)(Math.log((float)gray + 1.0f)/0.025f + 10.0f);
            //溢出处理
            if(gray < 0) {
                gray = 0;
            } else if(gray > 255) {
                gray = 255;
            }

            newPx[i] = Color.argb(a,gray,gray,gray);
        }
        bmp.setPixels(newPx, 0, width, 0, 0, width, height);//将处理后的透明度(没变),r,g,b分量重新合成颜色值并将其存储在数组中
        return bmp;//返回处理后的图像
    }

效果

二、灰度指数变换

        指数变换的作用是扩展图像的高灰度级,压缩低灰度级。虽然幂次变换也有这个功能,但是图像经过指数变换后对比度更高,高灰度级也扩展到了更宽的范围。 

灰度指数变换的基本公式如下

下面的例子中我们取b = 1.5,c = 0.065,a = 0

思路:

  1. 传入需要处理的图像    
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 令某点的灰度值gray = 1.5^{0.065*(gray-0)}-1
  6. 如果某点的灰度值小于阈值下限则将其灰度值置为0
  7. 如果某点的灰度值大于阈值上限则将其灰度值置为255
  8. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  9. 创建一个高度、宽度和原图完全一样的新图
  10. 将新图像返回
   private void exponetial(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);
            gray = (int)(Math.pow(1.5f,0.065f * (gray - 0)) - 1.0f);
            if(gray < 0) {
                gray = 0;
            } else if(gray > 255) {
                gray = 255;
            }

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

效果

 

 

 

 

 

 

 

 

 

 

三、灰度幂次变换

        幂次变换将部分灰度区域映射到更宽的区域中。

灰度幂次变换的基本公式如下(取r = 1时变为线性变换)

由于直接进行幂次变换会使大部分像素的灰度值超过255,因此我们先将某点的灰度值除以255,进行灰度变换后再将结果乘以255。

思路

  1. 传入需要处理的图像    
  2. 遍历整个图像取出图像中每个点的像素值存到一个数组中(oldPix)
  3. 在循环中获取每个像素点的颜色值,并抽取每个像素中的r,g,b,a分量准备处理
  4. 利用灰度公式计算出每个点的灰度值(范围0-255),并做溢出处理
  5. 令某点的灰度值gray = (gray/255)^{1.7}*255+20
  6. 如果某点的灰度值小于阈值下限则将其灰度值置为0
  7. 如果某点的灰度值大于阈值上限则将其灰度值置为255
  8. 将处理后的灰度值合成像素点的颜色值,存到一个新数组中(newPix)
  9. 创建一个高度、宽度和原图完全一样的新图
  10. 将新图像返回
    private void power(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);
            gray = (int)(1.0f * Math.pow(gray/255.0f,1.7f) * 255 + 20);
            if(gray < 0) {
                gray = 0;
            } else if(gray > 255) {
                gray = 255;
            }

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

效果

 

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

猜你喜欢

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