画像処理の移動ブラー


- created by gloomyfish
ボリュームブラーまたはボリュームスムーズフィルタリングは、画像ノイズを除去したり、一般的な画像ブラーの特効を生じたりすることができるが、
モバイル・ファジイの特効もボリュームベースであり、Box Blurに比べてGaussian Blurのアルゴリズムでは、モバイル・ファジイは完了するだけである.
1回の1次元ボリュームは、1次元ボリュームの完了とは異なり、水平と垂れではなく一定の角度に基づいています.
2つの方向にまっすぐ.ファジイの1 Dボリュームを移動するには、次の3つの要因を考慮します.
a.操作数の多少-即ち距離(Distance)
b.1次元オペランドの画素配列における移動方向、すなわち角度(Angle)
c.一次元オペランドの影引き効果–すなわちScale(拡大と縮小の程度)(Zoom/Scale)
距離と角度の関係は、三角ジオメトリで次のように表すことができます.
距離と角度が既知であることを想定して中心点(ターゲット画素点)を知ると、各オペランドの画素点座標、偽を求めることができる
中心点座標をBase(x 0,y 0)とすると、オペランドP(a,b)の座標式は次のように表される.
             a = sinx * c +y0
             b = cosx * c +x0
縮小機能は実はXYの2つの方向で画像に対して1次元の画素を計算して結合して、更にこれらの画素の平均値を求めます
すなわち、中心点画素がx 0、y 0、縮退防止比率がs 0であると仮定すると、オペランド毎の画素点座標は以下のように表すことができる.
         a= x0-x0 * s0 + a
         b= y0-y0*so + b
 
原理部分の解釈はおおよそそうであるが,次に,実際の画像処理効果とソースコードの詳細な解釈を見る.
プログラムの実行効果は次のとおりです.距離50ピクセル、角度0の場合:
角度30時のフィルター運転結果:
ブラー効果の縮小:
角度、距離、縮小の3つのパラメータの総合効果:
キーコードの説明:
三角ジオメトリ角度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;

オペランドのピクセル座標を計算するコードは次のとおりです.
 
// calculate the operatorsource pixel 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);

ピクセルの平均値を求め、1次元ボリュームを完了するコードは以下の通りです.
// 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 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; 	}  }

後記:本フィルタの基点である中心画素は、調整可能です.興味のある方は自分で作成してください.