AS3 でヒストグラムを作る (4) - 周辺分布
最近、画像処理について興味をもって勉強しているのですが、ディジタル画像処理の基礎と応用―基本概念から顔画像認識まで (ディジタル信号処理シリーズ) という本を読んでいます。その中で、「周辺分布」の図が面白かったので AS3 で実装してみました。
周辺分布って何?
周辺分布というのは、2値化した画像においてドットの登場頻度を X軸、Y軸のそれぞれについて集計したもののようです。パターン認識の特徴ベクトルとして利用されるらしいです。
サンプルはこんな感じ。

イメージとしては、画像を縦と横のそれぞれにべちゃっと圧縮した感じ。例えば、「し」の縦棒の下は長くなってますよね。
ActionScript で作ってみた
で、これをリアルタイムで解析できたら楽しそうだったので、作ってみたのがこちらの Flash。
- 枠の中でドラッグして線を描画できます
- 枠の外でクリックすると絵を消去できます
適当にマウスを動かすと、スプラッター映画の血痕のようにヒストグラムが延びていくのが愉快です。
技術的にはたいしたことしてません。例によって threshold でピクセル数を集計しているだけです。
本の紹介
ちなみに、この ディジタル画像処理の基礎と応用―基本概念から顔画像認識まで (ディジタル信号処理シリーズ) という本、C# を例に説明はしてますが、実践的な画像処理の情報が詰まってるので、なかなか楽しいです。フィルタやラベリングに始まり、パターン認識からニューラルネット、顔認識などの幅広い内容をサンプルコードつきで扱ってます。他の本を読んだわけではないので、「これがベストだ!」と自信をもって言い切ることはできませんが、入門には分かりやすくていい具合です。
それはそうと、誰か「ActionScript で実践する画像処理」という本を書かないかなぁ。Graphics クラスの初歩から始まり、パーティクルアニメーションを解説したり、BitmapData を使った加工ネタを取り上げたあとに Web カメラや FLV 動画の加工に応用、さらには PV3D で3次元…などなど、魅力的な内容になって面白そうなのに。だったらお前が書けよ、といわれそうだけど、ご覧のように画像処理については初心者なのでいかんとも…。
ディジタル画像処理の基礎と応用―基本概念から顔画像認識まで (ディジタル信号処理シリーズ)
- 作者: 酒井 幸市
- 出版社/メーカー: CQ出版
- 発売日: 2007-02
- メディア: 単行本
- Amazon のレビューを見る
今回のソースコードは以下に(89行)。
package {
import flash.display.*;
import flash.events.Event;
import flash.geom.*;
[SWF(width="400", height="400")]
public class Histogram4 extends Sprite {
private var canvas:Sprite;
private static var SIZE:uint = 200;
public function Histogram4() {
stage.align = "TL";
stage.scaleMode = "noScale";
// 描画用キャンバス準備
canvas = new Sprite();
addChild(canvas);
initCanvas();
// ドラッグで描画
var dragging:Boolean = false;
var count:int = 0; // 間引き用のカウンタ
canvas.addEventListener("mouseDown", function(e:*):void {
canvas.graphics.moveTo(mouseX, mouseY);
canvas.graphics.lineTo(mouseX, mouseY);
dragging = true;
});
canvas.addEventListener("mouseMove", function(e:*):void {
if(dragging) {
count = count < 12 ? count + 1 : 0;
canvas.graphics.lineTo(mouseX, mouseY);
// 12回に1回、ヒストグラムを更新する
if(count == 0) {
updateHistogram();
}
}
});
canvas.addEventListener("mouseUp", function(e:*):void {
dragging = false;
});
// キャンバスの外でクリックしたらクリア
stage.addEventListener("click", function(event:Event):void {
if(event.target == stage) {
initCanvas();
}
});
}
// キャンバスを初期化する
private function initCanvas():void {
canvas.graphics.clear();
canvas.graphics.lineStyle(1, 0x808080);
canvas.graphics.beginFill(0xffffff);
canvas.graphics.drawRect(0, 0, SIZE, SIZE);
canvas.graphics.endFill();
canvas.graphics.lineStyle(2, 0);
updateHistogram();
}
// ヒストグラムを描画する
private function updateHistogram():void {
graphics.clear();
// BitmapData に描画
var bmd:BitmapData = new BitmapData(SIZE, SIZE);
bmd.draw(canvas);
var num:uint;
graphics.lineStyle(1, 0);
// ヒストグラム分析&描画
for(var i:int = 0; i < SIZE; i++) {
// X 成分のヒストグラム
num = bmd.threshold(bmd, new Rectangle(0, i, bmd.width, 1), new Point(), "==", 0xff000000);
graphics.moveTo(SIZE, i);
graphics.lineTo(SIZE + num, i);
// Y 成分のヒストグラム
num = bmd.threshold(bmd, new Rectangle(i, 0, 1, bmd.height), new Point(), "==", 0xff000000);
graphics.moveTo(i, SIZE);
graphics.lineTo(i, SIZE + num);
}
bmd.dispose();
}
}
}