ImageProcessing
写真をペイント調に編集したくて画像変換やカメラ、画像のロード、保存などの勉強のために
Air for Android でカメラアプリ「toooycam」を作ってみました。その制作過程の画像処理部分です。
最終的にはこのような感じになりました。
http://rettuce.com/toooycam/
参考URL
*Flash Memo for Designers: 色を操作する 2 - ColorMatrixFilter
http://casualplay.net/blog/2005/11/_2_colormatrixfilter.html
*voglia.jp - AS3:ColorMatrixFilterを使いこなす
http://voglia.jp/?p=260
*ConvolutionFilterのsample めちゃくちゃ詳細な説明
http://www.imajuk.com/blog/archives/2009/03/convolutionfilter_4.html
/**
* Copyright rettuce ( http://wonderfl.net/user/rettuce )
* MIT License ( http://www.opensource.org/licenses/mit-license.php )
* Downloaded from: http://wonderfl.net/c/cA4V
*/
package
{
import flash.display.Bitmap;
import flash.display.BitmapData;
import flash.display.BitmapDataChannel;
import flash.display.Loader;
import flash.display.LoaderInfo;
import flash.display.Sprite;
import flash.events.Event;
import flash.events.MouseEvent;
import flash.filters.ColorMatrixFilter;
import flash.filters.ConvolutionFilter;
import flash.geom.Point;
import flash.geom.Rectangle;
import flash.net.URLRequest;
import flash.utils.ByteArray;
import flash.events.ProgressEvent;
import flash.net.FileReference;
import flash.net.FileFilter;
import flash.system.LoaderContext;
[SWF(width = 465, height = 465, backgroundColor = 0x000000, frameRate = 60)]
/**
* ...
* @author rettuce
*/
public class DocumentClass extends Sprite
{
private var bm:Bitmap;
private var bmd:BitmapData;
private var _file:FileReference = new FileReference;
private var _loader:Loader = new Loader;
public function DocumentClass()
{
if (stage) init();
else addEventListener(Event.ADDED_TO_STAGE, init);
}
private function init(e:Event = null):void
{
removeEventListener(Event.ADDED_TO_STAGE, init);
stage.addEventListener(MouseEvent.CLICK, imgChange );
imageProcessing();
}
private function imageProcessing():void
{
// LoaderContextを準備
var context:LoaderContext = new LoaderContext(true);
var loader:Loader = new Loader();
var info:LoaderInfo = loader.contentLoaderInfo;
var url:URLRequest = new URLRequest("http://lab.rettuce.com/common/src/lettuce.jpg");
loader.load(url, context);
info.addEventListener(Event.COMPLETE, function(e:Event):void
{
bmd = new BitmapData(loader.width, loader.height, false, 0xFFFFFF00);
bmd.draw(loader.content);
bm = new Bitmap(Tone_filter(bmd));
bm.smoothing = true;void
resize();
addChild(bm);
} );
}
public function laplacian_filter(s:BitmapData):BitmapData
{
var d:BitmapData = new BitmapData(s.width, s.height);
var l:Array = [0.08, 0.22, 0.08,
0.22, 0.05, 0.22,
0.08, 0.22, 0.08,]; // ぼかし
d.applyFilter(s, new Rectangle(0, 0, s.width, s.height), new Point(0, 0),
new ConvolutionFilter(3, 3, l ));
return d;
}
/* ConvolutionFilter Array
var l:Array = [0, 0, 0.11,
-0.01, 0.72, -0.2,
0, -0.01, 0.22]; // アニメーション
var l:Array = [-0.02, -0.02, -0.02,
0.02, 1.5, -0.02,
0.02, 0.02, 0.02]; // 白飛び
var l:Array = [0.2, 0.2, 0.2,
0.2, -1.59, 0.2,
0.2, 0.2, 0.2]; // エッジ光彩
var l:Array = [-1, -1, -1,
-1, 8, -1,
-1, -1, -1]; // ラプラシアンフィルタ
var l:Array = [0.08, 0.22, 0.08,
0.22, 0.05, 0.22,
0.08, 0.22, 0.08]; // ぼかし
*/
// ノイズ除去
public function median_smooth_filter(s:BitmapData):BitmapData {
var d:BitmapData = new BitmapData(s.width, s.height);
var a:Array = new Array(9);
for (var x:int = 0; x < s.width; x++) {
for (var y:int = 0; y < s.height; y++) {
a[0] = s.getPixel(x - 1, y - 1) ;
a[1] = s.getPixel(x - 1, y) ;
a[2] = s.getPixel(x - 1, y + 1) ;
a[3] = s.getPixel(x, y - 1) ;
a[4] = s.getPixel(x, y) ;
a[5] = s.getPixel(x, y + 1) ;
a[6] = s.getPixel(x + 1, y - 1) ;
a[7] = s.getPixel(x + 1, y) ;
a[8] = s.getPixel(x + 1, y + 1) ;
a[9] = s.getPixel(x - 2, y - 2) ;
a[10] = s.getPixel(x - 1, y - 2) ;
a[11] = s.getPixel(x , y - 2) ;
a[12] = s.getPixel(x + 1, y - 2) ;
a[13] = s.getPixel(x + 2, y - 2) ;
a[14] = s.getPixel(x + 2, y - 1) ;
a[15] = s.getPixel(x + 2, y ) ;
a[16] = s.getPixel(x + 2, y + 1) ;
a[17] = s.getPixel(x + 2, y + 2) ;
a[18] = s.getPixel(x + 1, y + 2) ;
a[19] = s.getPixel(x , y + 2) ;
a[20] = s.getPixel(x - 1, y + 2) ;
a[21] = s.getPixel(x - 2, y + 2) ;
a[22] = s.getPixel(x - 2, y + 1) ;
a[23] = s.getPixel(x - 2, y ) ;
a[24] = s.getPixel(x - 2, y - 1) ;
a.sort(Array.NUMERIC); // ソートして
if ( 1200000 >= a[24] - a[0] ) {
var c:int = a[12]; // 真ん中を取る
var _red : uint = (c >> 16) & 0xFF;
var _green : uint = (c >> 8) & 0xFF;
var _blue : uint = (c >> 0) & 0xFF;
d.setPixel(x, y, (_red << 16) | (_green << 8) | (_blue) ); // 中央値による色の設定
//d.setPixel(x, y, 0xFF0000 ); // 中央値による色の設定
}else {
var c2:int = s.getPixel(x, y); // 元データ
var _red2 : uint = (c2 >> 16) & 0xFF;
var _green2 : uint = (c2 >> 8) & 0xFF;
var _blue2 : uint = (c2 >> 0) & 0xFF;
d.setPixel(x, y, (_red2 << 16) | (_green2 << 8) | (_blue2) ); // 中央値による色の設定
//d.setPixel(x, y, 0x000000 ); // 中央値による色の設定
}
}
}
return d;
}
public function Tone_filter(s:BitmapData):BitmapData {
s.lock();
// s = median_smooth_filter(s);
s = laplacian_filter(s);
var stream:ByteArray = s.getPixels(new Rectangle( 0, 0, s.width, s.height ));
for(var i:int = 0; i < stream.length; i+=4){
var _alpha:Number = stream[i];
var _red:Number = stream[i+1];
var _green:Number = stream[i+2];
var _blue:Number = stream[i+3];
stream[i] = _alpha;
stream[i+1] = Tone4(_red);
stream[i+2] = Tone4(_green);
stream[i+3] = Tone2(_blue);
}
stream.position = 0;
var rect : Rectangle = new Rectangle(0, 0, s.width, s.height);
s.setPixels(rect, stream);
s.unlock();
return s;
}
private function Tone2(num:uint):uint
{
if ( num <= 128 ) {
num = 0;
} else {
num = 255;
}
return num
}
private function Tone3(num:uint):uint
{
if ( num <= 96 ) {
num = 0;
} else if ( num > 96 && num <= 160 ) {
num = 128;
} else {
num = 255;
}
return num
}
private function Tone4(num:uint):uint
{
if ( num <= 64 ) {
num = 0;
} else if ( num > 64 && num <= 128 ) {
num = 96;
} else if ( num > 128 && num <= 192 ) {
num = 160;
} else {
num = 255;
}
return num
}
public function imgChange(e:MouseEvent = null):void
{
_file.addEventListener(Event.SELECT, _onSelect);
_file.addEventListener(Event.COMPLETE, _onComplete);
_loader.contentLoaderInfo.addEventListener(Event.COMPLETE, _finish);
_file.browse([new FileFilter("Images", "*.jpg;*.jpeg;*.gif;*.png")]);
}
// ファイル選択
private function _onSelect(e:Event):void
{
_file.load();
}
// ファイルロード完了
private function _onComplete(e:Event):void
{
_loader.loadBytes(_file.data);
}
// loader 読み込み完了
private function _finish(e:Event):void
{
bmd.dispose();
var bd:BitmapData = new BitmapData(_loader.content.width, _loader.content.height);
bd.draw(_loader.content);
bmd = bd;
bm.bitmapData = Tone_filter(bmd);
resize();
}
private function resize():void
{
var _w:Number = stage.stageWidth;
var _h:Number = stage.stageHeight;
var perW:Number = _w / bm.width ;
var perH:Number = _h / bm.height ;
var _per:Number = Math.max( perW, perH );
bm.width = Math.floor( bm.width * _per);
bm.height = Math.floor( bm.height * _per);
bm.x = Math.floor(( _w - bm.width ) / 2 );
bm.y = Math.floor(( _h - bm.height ) / 2 );
}
}
}