すこし落ち着いてきたので久しぶりにFlashを。
今回はSymmetric Nearest Neighbor (SNN)という手法を画像に適用します。
シンメトリック ニアレスト ネイバー。訳すと対称最近傍法。
画素値の差の絶対値を “距離” として、その距離が近い画素の値を選んでいきます。
対称としているのは注目画素に対して対称方向の位置にある2つの画素を参照するからです。
つまり、
1 2 3
4 5 6
7 8 9
とあって5が注目画素なら、1と9、2と8、3と7、4と6… の位置の画素を参照し、
注目画素との距離が近い方の画素の値を採用します。
メディアンフィルタやKuwaharaフィルタと同様にエッジを残したまま画像をぼかしますが、
両者より高品質な結果が得られることが多い手法で、写真が絵画っぽくなってくれます。
・ソースコード
[as]
package {
import __AS3__.vec.Vector;
import flash.display.*;
[SWF(width = “600”, height = “250”, backgroundColor = “#000000”, frameRate = “30”)]
public class SNN extends Sprite{
[Embed(source = ‘assets/picture.jpg’)]
public var SrcImg:Class;
private var src:Bitmap;
private var dst:Bitmap;
private var srcData:Vector.
private var w:int;
private var h:int;
public function SNN() {
stage.scaleMode = “noScale”;
src = new SrcImg() as Bitmap;
w = src.width;
h = src.height;
dst = new Bitmap(new BitmapData(w, h, false));
srcData = new Vector.
srcData = src.bitmapData.getVector(src.bitmapData.rect);
dst.bitmapData.setVector(dst.bitmapData.rect, filterSNN(srcData, 3));
addChild(src);
addChild(dst).x += w;
}
// Symmetric Nearest Neighbor
private function filterSNN(src:Vector.
var dst:Vector.
var sumR:uint, sumG:uint, sumB:uint;
var rc:int, gc:int, bc:int, r1:int, g1:int, b1:int, r2:int, g2:int, b2:int;
var cnt:int = 0;
var xyPos:int, uvPos:int;
for (var y:int=0;y
gc = src[xyPos] >> 8 & 0xff;
bc = src[xyPos] & 0xff;
for (var v:int=-radius;v<=radius;v++) {
for (var u:int=-radius;u<=radius;u++,cnt++) {
uvPos = w*v + u;
try{
r1 = src[xyPos + uvPos] >> 16 & 0xff;
g1 = src[xyPos + uvPos] >> 8 & 0xff;
b1 = src[xyPos + uvPos] & 0xff;
r2 = src[xyPos – uvPos] >> 16 & 0xff;
g2 = src[xyPos – uvPos] >> 8 & 0xff;
b2 = src[xyPos – uvPos] & 0xff;
}catch(e:RangeError) {
break;
}catch(e:ReferenceError) {
break;
}
if (delta(rc, gc, bc, r1, g1, b1) < delta(rc, gc, bc, r2, g2, b2)) {
sumR += r1;
sumG += g1;
sumB += b1;
}else {
sumR += r2;
sumG += g2;
sumB += b2;
}
}
}
dst[xyPos] = sumR/cnt << 16 | sumG/cnt << 8 | sumB/cnt;
}
}
return dst;
}
private function delta(rc:int, gc:int, bc:int, r1:int, g1:int, b1:int):Number {
return Math.sqrt((rc - r1)*(rc - r1) + (gc - g1)*(gc - g1) + (bc - b1)*(bc - b1));
}
}
}
[/as]
少し冗長ですがアルゴリズムがわかりやすいように書きました。
画像境界外を参照したときはbreakしているだけなのでそんなに品質は良くないかも。
あわせて読む:
hey this is a very interesting article!