图像处理之移动模糊

       - created by gloomyfish

卷积模糊或者卷积平滑滤波,可以消除图像噪声,也可以产生一些常见的图像模糊特效,但

是移动模糊特效也是基于卷积,相比于Box Blur, Gaussian Blur的算法,移动模糊只需要完成

一次的一维卷积,所不同的是一维卷积的完成,要基于一定的角度,而不是只是在水平和垂

直两个方向上。移动模糊的一维卷积要考虑一下三个因素:

                     a. 操作数的多少 - 即距离(Distance)

                     b.一维操作数在像素数组中的移动方向 – 即角度(Angle)

                     c.一维操作数的拉影效应 – 即Scale(放大和缩小程度)(Zoom/Scale)

距离和角度的关系可以用三角几何表示如下:


假设距离和角度已知的情知道中心点(目标像素点)则可以求出每个操作数的像素点坐标,假

设中心点坐标为Base(x0, y0) 则操作数P(a, b)坐标公式可以表示如下:

             a = sinx * c +y0

             b = cosx * c +x0

放缩功能其实是在XY两个方向对图像计算得到一个一维像素结合,再求这些像素的平均值

即可,假设中心点像素为x0, y0, 防缩比率为s0则每个操作数像素点坐标可以表示为:

         a= x0-x0 * s0 + a

         b= y0-y0*so + b

 

原理部分的解释大概如此,下面来看一下,实际图像处理效果和源代码详细解释。

程序运行效果如下– 距离50个像素,角度 0 时:

 

角度30时候的滤镜运行结果:


放缩模糊效果:


角度,距离,放缩三个参数综合效果:


关键代码解释:

计算三角几何角度sin与cos值的代码如下:

// calculate the trianglegeometry value  
float sinAngle = (float)Math.sin(angle/180.0f * onePI);  
float coseAngle = (float)Math.cos(angle/180.0f * onePI);  

 计算距离半径代码如下:

// calculate the distance,same as box blur  
float imageRadius = (float)Math.sqrt(cx*cx + cy*cy);  
float maxDistance = distance + imageRadius * zoom;  

计算操作数像素坐标的代码如下:

if(distance > 0) {  
newY = (int)Math.floor((newY+ i*sinAngle));  
newX = (int)Math.floor((newX + i*coseAngle));  
}  

完成Scale的代码如下:


// scale the pixels  
float scale = 1-zoom*f;  
m11 = cx - cx*scale;  
m22 = cy - cy*scale;  
newY = (int)(newY * scale +m22);  
newX = (int)(newX * scale + m11); 

求取像素平均值,完成一维卷积的代码如下:

// blur the pixels, here  
count++;  
int rgb= inPixels[newY*width+newX];  
ta += (rgb >> 24) & 0xff;  
tr += (rgb >> 16) & 0xff;  
tg += (rgb >> 8) & 0xff;  
tb += rgb & 0xff;  

重新填充目标像素的代码如下:

ta = clamp((int)(ta/count));  
tr = clamp((int)(tr/count));  
tg = clamp((int)(tg/count));  
tb = clamp((int)(tb/count));  
outPixels[index] = (ta << 24) | (tr<< 16) | (tg << 8) | tb;  

移动模糊算法的完全源代码如下(不包含测试代码):

package com.gloomyfish.process.blur.study;  
  
import java.awt.image.BufferedImage;  
  
public class MotionFilter {  
  
    private float distance = 0;// default;  
    private float onePI = (float)Math.PI;  
    private float angle = 0.0f;  
    private float zoom = 0.4f;  
  
    public float getDistance() {  
        return distance;  
    }  
  
    public void setDistance(float distance) {  
        this.distance = distance;  
    }  
  
    public float getAngle() {  
        return angle;  
    }  
  
    public void setAngle(float angle) {  
        this.angle = angle;  
    }  
  
    public BufferedImage filter(BufferedImage src, BufferedImage dst) {  
        int width = src.getWidth();  
        int height = src.getHeight();  
  
        if ( dst == null )  
            dst = createCompatibleDestImage( src, null );  
  
        int[] inPixels = new int[width*height];  
        int[] outPixels = new int[width*height];  
        getRGB( src, 0, 0, width, height, inPixels );  
        int index = 0;  
        int cx = width/2;  
        int cy = height/2;  
          
        // calculate the triangle geometry value  
        float sinAngle = (float)Math.sin(angle/180.0f * onePI);  
        float coseAngle = (float)Math.cos(angle/180.0f * onePI);  
          
        // calculate the distance, same as box blur  
        float imageRadius = (float)Math.sqrt(cx*cx + cy*cy);  
        float maxDistance = distance + imageRadius * zoom;  
          
        int iteration = (int)maxDistance;  
        for(int row=0; row<height; row++) {  
            int ta = 0, tr = 0, tg = 0, tb = 0;  
            for(int col=0; col<width; col++) {  
                int newX= col, count = 0;  
                int newY = row;  
                  
                // iterate the source pixels according to distance  
                float m11 = 0.0f, m22 = 0.0f;  
                for(int i=0; i<iteration; i++) {  
                    newX = col;  
                    newY = row;  
                      
                    // calculate the operator source pixel  
                    if(distance > 0) {  
                        newY = (int)Math.floor((newY + i*sinAngle));  
                        newX = (int)Math.floor((newX + i*coseAngle));  
                    }  
                    float f = (float)i/iteration;  
                    if (newX < 0 || newX >= width) {  
                        break;  
                    }  
                    if (newY < 0 || newY >= height) {  
                        break;  
                    }  
                      
                    // scale the pixels  
                    float scale = 1-zoom*f;  
                    m11 = cx - cx*scale;  
                    m22 = cy - cy*scale;  
                    newY = (int)(newY * scale + m22);  
                    newX = (int)(newX * scale + m11);  
                      
                    // blur the pixels, here  
                    count++;  
                    int rgb = inPixels[newY*width+newX];  
                    ta += (rgb >> 24) & 0xff;  
                    tr += (rgb >> 16) & 0xff;  
                    tg += (rgb >> 8) & 0xff;  
                    tb += rgb & 0xff;  
                }  
                  
                // fill the destination pixel with final RGB value  
                if (count == 0) {  
                    outPixels[index] = inPixels[index];  
                } else {  
                    ta = clamp((int)(ta/count));  
                    tr = clamp((int)(tr/count));  
                    tg = clamp((int)(tg/count));  
                    tb = clamp((int)(tb/count));  
                    outPixels[index] = (ta << 24) | (tr << 16) | (tg << 8) | tb;  
                }  
                index++;  
            }  
        }  
  
        setRGB( dst, 0, 0, width, height, outPixels );  
        return dst;  
    }  
      
    public int clamp(int c) {  
        if (c < 0)  
            return 0;  
        if (c > 255)  
            return 255;  
        return c;  
    }  
  
}  



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值