BitmapData.histogram()

BitmapData.histogram()
>> Histogram.swf
(要 FlashPlayer 10 & Webカメラ)

昨日はFlashPlayer 10で追加されたBitmapDataクラスのメソッドである get/setVector() を紹介しましたが、
今日は histogram() というメソッドを使ってみます。
以下、livedocsより

histogram() メソッド
public function histogram(hRect:Rectangle = null):Vector.<Vector$Number>

言語バージョン: ActionScript 3.0
ランタイムバージョン: 10, 1.5
 
BitmapData オブジェクトの 256 値のバイナリ数のヒストグラムを計算します。このメソッドは 4 つの Vector を含む Vector オブジェクトを返します。<Number> インスタンス(Number オブジェクトを含む 4 つの Vector オブジェクト)です。4 つの Vector インスタンスは、赤、緑、青およびアルファの順の各コンポーネントを表します。各 Vector インスタンスには、各コンポーネントの値のポピュレーションカウント(0 ~ 255)を表す 256 値が含まれています。
 
パラメータ
 hRect:Rectangle (default = null) — BitmapData オブジェクトの使用領域です。

戻り値
 Vector.<Vector$Number>

とりあえず、カメラ画像のヒストグラムを毎フレーム描画するだけの簡単なソースコードを載せます。
なお、ここではアルファチャンネルは無視しています。

・ソースコード

package {
	import __AS3__.vec.Vector;

	import flash.display.*;
	import flash.events.Event;
	import flash.geom.*;
	import flash.media.*;

	[SWF(width = "576", height = "300", backgroundColor = "#000000", frameRate = "30")]

	public class Test extends Sprite {
		private var capture:Camera;
		private var video:Video;
		private var bmpData:BitmapData;
		private var hist:Sprite;
		private var bins:Vector.<Vector.<Number>>;

		public function Test() {
			stage.scaleMode = "noScale";
			capture = Camera.getCamera();
			if(capture != null) {
				capture.setMode(320, 240, 30);
				video = new Video();
				video.attachCamera(capture);
				addChild(video);
				bmpData = new Bitmap(new BitmapData(video.width, video.height, false));
				hist = new Sprite();
				addChild(hist);
				hist.x += video.width;
				bins = new Vector.<Vector.<Number>>();
				this.addEventListener(Event.ENTER_FRAME, function(e:Event):void {
					var g:Graphics = hist.graphics;
					bmpData.draw(video);
					bins = bmpData.histogram(bmpData.rect);
					g.clear();
					g.beginFill(0xffffff);
					g.drawRect(0, 0, 256, video.height);
					g.endFill();
					drawHistogram(g);
				});
			}
		}
		private function drawHistogram(g:Graphics):void {
			var h:Number = video.height, max:Number = 0.0, sh:Number = 16;
			for(var ch:int=0; ch<3; ch++) {
				for(var i:int = 0; i<256; i++) max = bins[ch][i]>max ? bins[ch][i]:max;
				i = 0;
				for each(var val:Number in bins[ch]) {
					g.lineStyle(1.0, i<<sh, 0.8) ;
					g.moveTo(i, h);
					g.lineTo(i++, Math.max(0.0, h - val*h/max));
				}
				sh -= 8;
			}
		}
	}
}

0〜255の範囲の値(1つのチャンネルにつき8bit)が256個のビンに入っていて、
返り値のVector[0]が赤チャンネル、順に緑、青、アルファとなっています。

ここで、色空間をHSVに変換して色相(H)値のみのヒストグラムを計算すれば、

hue_hist

のようなカラーヒストグラムも作れます。
ちなみに、RGB値から色相(H)値だけ求めたい場合は色空間の変換までする必要はないです。

// RGB値から色相(H)値を計算する
// val はRGBのピクセルデータ
// 返り値は実数(ラジアン),+ で色相環を時計回り
private function getHue(val:uint):Number {
	return Math.atan2(2*(val>>16 & 0xff) - (val>>8 & 0xff) - (val & 0xff),
				 Math.sqrt(3)*(val>>8 & 0xff) - Math.sqrt(3)*(val & 0xff));
}

ただ、色相値は照明変動に弱く、取得した値が不安定になりやすいという理由から、
動画像処理においては色相値だけを使うということはあまりしません。
一番簡単な対応策のひとつとしては、彩度および輝度にしきい値処理を施し、
それらがしきい値以上の画素の色相値のみを採用するといったアプローチがあります。

ーーーーーーーー
バレンタインFlashのおかえしを催促されても作らなかった僕は、決してクリエイターにはなれないんだと悟りました。
たくさんfavoritesされるようなFlashはどうがんばっても僕には作れません;;
したがって、今回は機械的なメソッド紹介をもってホワイトデープレゼントに代えさせていただきます。

 

Tags:

Comments

No comments so far.

  • Leave a Reply
     
    Your gravatar
    Your Name