トップページ > 画像処理 > 二値化(threshold編)



概要

二値化(アルゴリズム編)で二値化のアルゴリズムと書き方を勉強しました。

今回はBitmapData#threshold()を使用して二値化処理のコードを書いてみましょう。
thresholdは日本語で「閾値」という意味であり、読み方は「スレッショルド」です。

このメソッドだと色の平均を取ることが出来ないので、今回は青の値だけ見て二値化処理を行うことにします。


プログラム

private function binarization(bd:BitmapData):BitmapData
{
	var dest:BitmapData = bd.clone();
}
 
まず前回と同じように、binarization()のメソッドを用意して、渡されたBitmapdataのコピーを作っておきます。


dest.threshold(dest, dest.rect, new Point(), "<=", 0x7F, 0xFF000000, 0xFF, false);
dest.threshold(dest, dest.rect, new Point(), "!=", 0x0 , 0xFFFFFFFF, 0xFF, false);
 
threshold()を使用して実際に二値化するコードはこのようになります。

では、コードの意味を一つずつ説明していきましょう。

第一引数
  • sourceBitmapData:BitmapData
    • 対象のBitmapDataを指定します。
dest.threshold(dest, ...続く
 
対象のBitmapDataはもちろんdestなのでそのままdestを指定します。
ここはbdを指定しても構いません。

第二引数
  • sourceRect:Rectangle
    • 第一引数で指定したBitmapDataの範囲をRectangleクラスで指定します。
    • Rectangleに関しては、Rectangleクラスを使用するを参照してください。

dest.threshold(dest, dest.rect, ...続く
 
dest.rectは(0, 0, width, height)が入っているRectangleクラスなので、このrectをそのまま指定してあげましょう。

第三引数
  • destPoint:Point
    • threshold()の処理結果をどこに配置するのかをPointクラスで指定します。
    • Pointに関しては、Pointクラスを利用するを参照してください。

dest.threshold(dest, dest.rect, new Point(), ...続く
 
処理した結果をdestの(0, 0)から描画させたいので、new Point()と指定します。
new Point(0, 0)のように値を入れても構いません。

第四引数
  • operation:String
    • 比較演算子 ("<"、"<="、">"、">="、"=="、"!=")を文字列で指定します。

dest.threshold(dest, dest.rect, new Point(), "<=", ...続く
 
今やりたい二値化の処理は、if (対象ピクセル(青だけ) <= 0x7F)ですよね。
なので"<="の部分をoperationに指定してあげてください。

第五引数
  • threshold:uint
    • 閾値を指定します。

dest.threshold(dest, dest.rect, new Point(), "<=", 0x7F, ...続く
 
if (対象ピクセル(青だけ) <= 0x7F)の閾値は0x7Fですから、その値をthresholdに指定します。

第六引数
  • color:uint
    • 対象のピクセルと閾値を、指定した比較演算子で比べてtrueになるとこのcolorがピクセルに代入されます。

dest.threshold(dest, dest.rect, new Point(), "<=", 0x7F, 0xFF000000, ...続く
 
要は、if (対象ピクセル(青だけ) <= 0x7F)がtrueになったら、対象ピクセルにcolor値を入れるということです。
ただこれは非常に困った引数で、destが透過度をサポートしていなくてもアルファ値の指定をしなければなりません。

今やりたいことは、対象ピクセルが0x7Fよりも小さかったら黒色に変換するという処理です。
しかし、黒だからといって0x0と指定するのは実は間違いです。
正しく書くと0xFF000000になります。0x0は0x00000000と同じ意味でアルファ値の部分まで0になってしまうのでアルファ値の部分だけはFFと書いておきましょう。

普通、透過度をサポートしていない場合は0x0のみの指定でいいのですが、この引数は透過度をサポートしているしていないに関わらずアルファ値まで書き込んでしまうようです。
このあたりは注意しましょう。

第七引数
  • mask:uint = 0xFFFFFFFF
    • 閾値のマスクです。

dest.threshold(dest, dest.rect, new Point(), "<=", 0x7F, 0xFF000000, 0xFF, ...続く
 
例えば対象ピクセルが0x123456、閾値が0x112233だとしましょう。
その場合、if (0x123456 <= 0x112233)の比較になりますが、マスクを使用することで比較の範囲を設定することが出来ます。
たとえば16進数値の下2桁だけ比較したい、つまりif ( 0x56 <= 0x33)にしたい場合はマスクを0x0000FF、もしくは0xFFと記述します。
上2桁同士、if (0x120000 <= 0x110000)を比べたかったらマスクを0xFF0000、もしくは0x00FF0000に設定します。

ARGB アルファ
16進数 2桁 2桁 2桁 2桁
になるのでよく覚えていてください。
詳しくは、16進数表記の色指定についてを参照してください。


((pixelValue & mask) operation (threshold & mask))
((対象のピクセル値 & マスク値) 比較演算子 (閾値 & マスク値))
 
正確に記述すると上のようになります。


閾値は0xFFで下2桁を使用したいのでマスクに入れる値は0xFF、もしくは0x000000FFになります。
つまりアルファ値、赤、青は閾値の対象になりません。

第八引数
  • copySource:Boolean = false
    • 対象ピクセルと閾値の比較結果がfalseになったときに、第一引数で指定した元BitmapDataのピクセルを、このthresholdを呼びだしたBitmapDataにコピーするかどうかを指定します。デフォルト値はfalseなのでコピーしません。

dest.threshold(dest, dest.rect, new Point(), "<=", 0x7F, 0xFF000000, 0xFF, false);
 
何度も同じ事を書いていますが、今やりたいことはif (対象ピクセル(青だけ) <= 0x7F)です。
もしこのif文がtrueになれば、対象ピクセルの色が0xFF000000になります。
では、falseになった時に、対象ピクセルをdestに代入するかどうかを決めるのがこの引数の役割です。

しかし、今回のコードは元のBitmapDataとcolorを入れるBitmapDataが同じオブジェクトなのでこの引数の設定は意味を持ちません。
なので、trueでもfalseでも同じ結果になるので好きなほうを入れてください。


dest.threshold(dest, dest.rect, new Point(), "<=", 0x7F, 0xFF000000, 0xFF, false);
 
これで最初の一行が完成しました。
if (対象ピクセル(青だけ) <= 0x7F)がtrueなら、対象ピクセルの色を黒にするというコードです。

しかし、前回書いたような「falseだったら白にする」という書き方はthreshold()では出来ません。
なので、もう一行threshold()を付け加えて、「対象ピクセルが黒でなければ白にする」というコードを書いてみましょう。
こうすれば二値化処理が完成するはずです。


dest.threshold(dest, dest.rect, new Point(), "!=", 0x0, 0xFFFFFFFF, 0xFF, false);
 
if (対象ピクセル(青だけ) != 0x0)がtrueなら、対象ピクセルの色を白にするというコードです。
特に説明は要らないと思います。


private function binarization(bd:BitmapData):BitmapData
{
	var dest:BitmapData = bd.clone();
	dest.threshold(dest, dest.rect, new Point(), "<=", 0x7F, 0xFF000000, 0xFF, false);
	dest.threshold(dest, dest.rect, new Point(), "!=", 0x0 , 0xFFFFFFFF, 0xFF, false);
 
	return dest;
}
 
最後にreturn destでBitmapDataを返却して完成です。


検証用コード

package
{
	import flash.display.Sprite;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.display.Loader;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Point;
	import flash.net.URLRequest;
	import flash.system.LoaderContext;
	import flash.system.Security;
 
	public class Main extends Sprite
	{
		private const WIDTH:int = 200;
		private const HEIGHT:int = 200;
 
		public function Main()
		{
			Security.loadPolicyFile("http://farm3.static.flickr.com/crossdomain.xml");
			var loader:Loader = new Loader();
			loader.contentLoaderInfo.addEventListener(Event.INIT, initHandler);
			loader.load(new URLRequest("http://farm3.static.flickr.com/2047/2280715132_f0a72c1088.jpg"), new LoaderContext(true)); // 読み込みたい画像URL
		}
 
		private function initHandler(event:Event):void
		{
			var loader:Loader = event.currentTarget.loader;
 
			var matrix:Matrix = new Matrix();
			matrix.scale(WIDTH / loader.width, HEIGHT / loader.height);
 
			var bd:BitmapData = new BitmapData(WIDTH, HEIGHT);
			bd.draw(loader, matrix);
			addChild(new Bitmap(binarization(bd)));
		}
 
		private function binarization(bd:BitmapData):BitmapData
		{
			var dest:BitmapData = bd.clone();
			dest.threshold(dest, dest.rect, new Point(), "<=", 0x7F, 0xFF000000, 0xFF, false);
			dest.threshold(dest, dest.rect, new Point(), "!=", 0x0 , 0xFFFFFFFF, 0xFF, false);
 
			return dest;
		}
	}
}
 

|新しいページ|検索|ページ一覧|RSS|@ウィキご利用ガイド | 管理者にお問合せ
|ログイン|