newQX でプログラミングしている時に落ちる場合の対処

エディタは newQX α0.31 を使っているのですが、C++ のプログラミングをしている時にアプリケーションごと落ちます…というか、ブログの下書きを書いているときにコードを貼りつけて何かやっている時に落ちます。

「何かやっているとき」ってのがプログラマらしからぬ意見なので、現象の元ネタが分かったらメーリングリストに報告に行こうかなぁと思ってしばし…3ヶ月ほど?

どうやら、変数表示のツールチップを出す時に落ちていることが最近わかりました。フォントの関係かなぁと思って(Osakaフォントを使っていたりするので)、しばし、MS ゴシックに戻したりしていたのですが、落ちる頻度としては C 言語のプログラミングをしている時が多いし、なんだろうと思っていました。

のように、変数を定義している位置を知らせてくれるのですが、どうもこのタイミングで落ちることが多い…ツールチップの表示自体ではなくて、何らかの組み合わせのようなのですが、ちょっと不明です。

で、対処なのですが「共通設定」の「開発」タブで、「c言語のコンテキスト補完」「c言語のコンテキストツールチップ」のあたりを全て OFF にします。

すると、ぴたりと落ちる現象が消えるので…このあたりの組み合わせっぽいですね。

落ちるタイミングがツールチップなので「c言語のコンテキストツールチップ」だけ OFF にすればいいような気もしましたが、それだとまだ落ちていたんですよね。なので、コンテキスト補完の不備なのかなぁ、と想像したり。

カテゴリー: QXエディタ | newQX でプログラミングしている時に落ちる場合の対処 はコメントを受け付けていません

使ってはいけない定数定義の一例

実際は、Visual Basic 2005 なんだけど、C# でも同じ、まぁ、実は C 言語でも同じ。

「マジックナンバーを使ってはいけません」というのを小さい頃(プログラミングを始めたばかりの頃ってこと)に教えられた人は、コードの中に即値を入れないようにします。だから、次のように書く…って、書くなぁッ!!!

const int ZERO = 0;
const int TEN  = 10;

for ( int i = ZERO; i < TEN; i++ ) {
	// 何かの処理
}

その…なんというか、顎が外れた。言葉が出ない。

これの応用編(?)として、こんな風なコードもあった…あるなぁッ!!!


public class CONSTS {
	public const int ZERO = 0;
	public const int TEN  = 10;
}

...
for ( int i = CONSTS.ZERO; i < CONSTS.TEN; i++ ) {
	// 何かの処理
}

ええっ、思いっきり応用していますね。応用です。鷹揚に構えないと(ぜはぜは)。

で、この手のものをどう書くのですか?と聞かれると、C言語だとこう書きます(実はC#だと悩ましかったり)。


#define MAX 10;

...

for ( int i=0; i<MAX; i++ ) {
   ...
}

「MAX」のところは最大値を示す意味のある言葉を入れるわけで、望ましくないのは「LOOPMAX」とか「COUNTMAX」とかですね。そうなる位であれば「MAX」のほうが潔い。
どうせならば「MAX_TABLES」とか「TABLE_COUNTS」な感じです。

さて、同じようなものをC#で書くと、

const int MAX = 10;
...
for ( int i=0; i<MAX; i++ ) {
   ...
}

という感じになりますが、ちょっと違う。この MAX という定義は、C 言語の場合は、関数(メソッド)の外に書くことができるけど、C# や VB の場合は、メソッドの中になってしまう。なので、オブジェクト指向のメリットを活かせば、

class A {
	const int MAX = 10;
	public method() {
		...
		for ( int i=0; i<MAX; i++ ) {
		   ...
		}
	}
}

な風に、クラス内の private フィールドにするのが適しているかと。
実は、更に悩ましいことに、この MAX ってのは、共通で使うことが多いので、B クラスでも C クラスでも使うってことになると、

public class CONSTS {
	public const MAX = 10;
}

なんていう CONSTS クラスを作っておいて、


class A {
	public method() {
		...
		for ( int i=0; i<CONSTS.MAX; i++ ) {
		   ...
		}
	}
}

class B {
	public method() {
		...
		for ( int i=0; i<CONSTS.MAX; i++ ) {
		   ...
		}
	}
}

のようにすれば良いのだが、実はこれは隠蔽化を崩している。
新しく D クラスを作った時に、CONSTS.MAX というのが別の意味で使いたい場合に名前が重複してしまう。実務的には、アセンブリを変えるとか、定義名を別にするとかいう方法もあるのですが、実はインターフェースという感覚で

public class CONSTS {
	public const int MAX = 10;
}
public class A : CONSTS {
	public void method() {
		for ( int i=0; i<MAX; i++ ) {
			...
		}
	}
}

とするのが妥当かと思うのですが、.NET のクラスでは多重継承ができないので、CONSTS を複数作って継承させるってことができない。

なので、実務的なところでは、

public class TABLE_CONSTS {
	public const int MAX = 10;
}
public class PEOPLE_CONSTS {
	public const int MAX = 100;
}
public class A {
	public void method() {
		for ( int i=0; i<TABLE_CONSTS.MAX; i++ ) {
			...
		}
	}
}

な風に、CONSTS クラスをあらかじめ分類しておいて、名前空間風に使ってみる、っていうのが普通です…というか、今回はそうしました。
まぁ、CONSTS クラスが多くなるとちょっと…何が何やら分からなくなりますがね。

と・も・か・くッ!!! ONE, TWO THREE のような定義はやめておくなまし。

カテゴリー: 雑談, C# | 3件のコメント

Azure の Strage Emulator が作成するテーブル構造

Azure をローカルコンピュータで動かすと、ローカルにストレージアカウントが作成されます。
これは、.\SQLEXPRESS というインスタンスに、「DevelopmentStrageDb<作成日付?>」な風なデータベースが作成されていて、

テーブル構造はこんな感じ。

キー情報としては、

  • AccountName アカウント名
  • ContainerName コンテナ名
  • BlobName ブロブ名
  • VersionTimestamp タイムスタンプ
  • TableName テーブル名
  • PartitionKey テーブルストレージのパーミッションキー
  • RowKey 列名

な簡単な構造をしています。

# 実際は Blob の Block 構造のために、Blob ストレージのほうはややこしいみたいですが。

DDL は、以下のようになっているので、

CREATE TABLE [dbo].[Blob](
	[AccountName] [varchar](24) NOT NULL,
	[ContainerName] [varchar](63) NOT NULL,
	[BlobName] [nvarchar](256) NOT NULL,
	[VersionTimestamp] [datetime] NOT NULL,
	[BlobType] [int] NULL,
	[CreationTime] [datetime] NOT NULL,
	[LastModificationTime] [datetime] NULL,
	[ContentLength] [bigint] NOT NULL,
	[ContentType] [varchar](128) NULL,
	[ContentMD5] [binary](16) NULL,
	[ServiceMetadata] [varbinary](max) NULL,
	[Metadata] [varbinary](max) NULL,
	[LeaseId] [uniqueidentifier] NULL,
	[LeaseTypeInt] [int] NULL,
	[LeaseDuration] [bigint] NULL,
	[LeaseEndTime] [datetime] NULL,
	[SequenceNumber] [bigint] NULL,
	[IsCommitted] [bit] NULL,
	[HasBlock] [bit] NULL,
	[UncommittedBlockIdLength] [int] NULL,
	[MaxSize] [bigint] NULL,
	[FileName] [nvarchar](260) NULL,
 CONSTRAINT [PK_dbo.Blob] PRIMARY KEY CLUSTERED 
(
	[AccountName] ASC,
	[ContainerName] ASC,
	[BlobName] ASC,
	[VersionTimestamp] ASC
)
CREATE TABLE [dbo].[TableRow](
	[AccountName] [varchar](24) NOT NULL,
	[TableName] [varchar](63) NOT NULL,
	[PartitionKey] [nvarchar](256) NOT NULL,
	[RowKey] [nvarchar](256) NOT NULL,
	[Timestamp] [datetime] NOT NULL,
	[Data] [xml] NULL,
 CONSTRAINT [PK_dbo.TableRow] PRIMARY KEY CLUSTERED 
(
	[AccountName] ASC,
	[TableName] ASC,
	[PartitionKey] ASC,
	[RowKey] ASC
)
  • AccountName: 24 文字以内
  • ContainerName: 63 文字以内
  • BlobName: 256 文字以内
  • TableName: 63 文字以内

と想像がつくわけですが、果たして、実際の Azure システムでは如何に?

Windows Azureストレージ・サービスの命名規則を調べてみた – waりとnaはてな日記
http://d.hatena.ne.jp/waritohutsu/20100404/1270414253

ちなみに、SQL Server の場合、(デフォルトでは)比較時に大文字小文字を判別しないので、アカウント名やコンテナ名などには大文字小文字で区別させるのは厳禁。エミュレータ環境で動かなくなるという罠が。

カテゴリー: 開発, Azure | Azure の Strage Emulator が作成するテーブル構造 はコメントを受け付けていません

アプリを作ろう!iPhone入門―ゼロから学ぶアプリの作成から公開まで…を買う

ええと、業務的な観点から、アプリの公開部分が欲しくって買い…と言いますか、日経BPさんから貰ってもいいのだけど、今回は他社でのアレだからなぁ、という訳で資料的に。

入門書としては非常に薄いので、ちょっと iPhone のプログラミングをやっている人であれば、だから何ができるの?ってな感じになりかねませんが、AppStore の公開まで丁寧に書かれているのがいいです。私としてはこの1点買いです。
美女 Linux の iPhone アプリのお試しを作ろうとして、最初に困っていたのが、

  • iOS SDK ってどうダウンロードするのか?
  • 会員にならないと、実機で動かせないらしいのだが、どうすればよいのか?
  • 最終的には無料公開ぐらいまではしたいのだが、どうすればよいのか?

ってなところです。google を使ってサーチしてみたものの、断片的なものしかなくて(実際、課金アプリなどを公開している方は、その断片的な…というか、それ以前の英語版のヘルプを見て公開している模様)、なかなか一気に作ると、「どの程度手間がかかりそうなのか?」というのが分かりません。

いわゆる、「どの程度手間が掛かりそうなのか」を見積もるには、どの程度時間がかかりそうなのか?(by ワインバーグ)の問題でして、なんやかやと調べていくと、見積もりの見積りに3日位かかった覚えがあります。
そのあたりを短縮してくれます、ってことで買いました。

で、公開までにどのくらい掛かるかというと、落ち着いてやれば1週間弱なのかなぁと。

カテゴリー: 雑談 | アプリを作ろう!iPhone入門―ゼロから学ぶアプリの作成から公開まで…を買う はコメントを受け付けていません

経済的な視点を持ってソフトウェア開発を見直す

先に書いたエントリーで、「経済的な」という単語が出てきたので、ちょっと補足。

「経済的な」というのは、費用のことを考えてプログラミングをする(ソフトウェア開発をする)ということで、マクロ的なところではオフショアな話から、ミクロ的なところでは2時間悩むぐらいであれば3000円の書籍を買ったほうがよい、という話だったりします。

対費用効果を考えるとき、学生時代には時間がたくさんあった(たくさんあった、というよりもお金がなかった)ので、費用(経費)のほうが時間よりも大切で、自分で調べるなり先輩に聞くなりネットで調べるなり(当時はネットなぞなかったので、図書館でってことになりますが)する方法を取るわけですが、いざ社会人になって「プロ」になる、時間の費用のほうが高くつくので、なにかと費用を掛けたほうが手早くできあがったりします(無料にこだわらなくてもよくて、ある程度の費用ならば捻出可能であるという意味で)。

これをソフトウェア開発のプロジェクトという枠で捉えると、プロジェクト自体の予算があり、人件費があり、ということで、割り算すれば単位時間あたりの費用が簡単にでてきます。この単位時間あたりの費用を如何に有効に使うかとうことが優先される…はずなのですが、まぁ、そうでないこともたびたびなのですが。

なので、作業時間というものを見積もった上で、その作業時間が自分の単価により非常に高くつくのであれば、別の手段に置き換えたほうが安く済むイコール「効率が良い」ということになります。ここでいう「効率」というのは、対費用効果という視点からですね。
なので、「2時間ほど悩むのであれば、3000円の書籍を買うほうがよい」というパターンは、あれこれとネットで探して悩むよりは、さくっと初心者本なりを買って、その数ページだけを活用してしまえばよいということです。それで、その本の価値(この場合は私から見た価値)は十分足りているということになります。
なんか、非常に効率が悪いように見えますが、本屋での立ち読みよりも、さっくりと amazon で買うなり、本屋でざっくりと選別して買うとい方式を取ります。
業務に使う場合は、専らこのパターンです。資料的に切り取りという感じで。

これを、もう少しマクロな視点からプロジェクトでの開発工程全体に広げてみると、

  • ツール購入の対費用効果
  • 自動化ツール作成の対費用効果
  • 重箱の隅的な議論の対費用効果
  • 複雑すぎる仕様に対する対費用効果

などを考えていきます。IT 屋さんの営業的には、ツール購入の対費用効果が持ち出されるわけで、過剰なところでは「5秒の作業時間が削減できます」とかなんとか。昔、実際にあった宣伝文句です。
が、そのツールを購入して習熟して実行するところまで考えたときには、ツール自体に費用に値するかどうか、は結構別な話で、むしろ、「習熟して実行する」ところまでの対価(イコール人件費)のほうが大きくて、対費用効果がマイナスということもあります。つまり、ツール自体が0円であっても、習熟までの人件費を考えれば、マイナスうん十万円という損が出る可能性があるということです。

どこかの経済書か経営書にありましたが、結果的には人には「時間」のみが平等に割り振られているわけで(時間の少ない方もいるんので、必ずしもそうという訳ではありませんが)、どの位の時間で何をするのか、それがオーバーしそうなのか、オーバーするのであれば作業を止めるべきか、ということまで考えて行動すると、「経済的な」ソフトウェア開発に近づきます。

と言うほど、私自身はできていないわけですが、業務的なところは業務的なところで追及するとして、人生の楽しみとしては楽しみとして追及させていただくという話です。談志のように?

カテゴリー: プロジェクト管理, 雑談 | 1件のコメント

オブジェクト指向と手続きプログラミングの組み合わせ

オブジェクト指向型のプログラム言語を使うと、オブジェクト指向のプログラミングが自然にできて、開発効率がアップする、というのは幻想で、オブジェクト指向的な考え方というか、ノウハウを利用することによってでしか、開発効率はアップしないとか、極論を書いてみるテスト。

研究的なプログラミングとか、ライトウェイトなプログラミングをしている場合は別として、いわゆる業務で使うアプリケーションのようなもの全般、いわゆる SIer が請け負うアプリケーションに関して云えば、オブジェクト指向一辺倒で行くのはプロジェクトの成功率的に云えば、危険である。プロジェクトの成功率というのは、以前書いた(と思うけど)、プロジェクトが赤字にならないことであって、その他の個々人のスキルとか将来性とか、更に云えば会社の将来性とかいうのとは別ので、目の前のプロジェクトを如何に完遂するのか?というのが焦点になる。
良いとか、悪いとかは別として、それが「プロジェクトの成功」と定義するワケ。

で、このプロジェクトの成功に関してどのようなプログラミング的な知識が適用/寄与できるかというと、一般的に言えば、オブジェクト指向であったり、プロトタイピングであったり、アジャイルであったり、諸々の手法であったりするわけだが、失敗する可能性を低くしたいのであれば(ローリスク、ローリターンという意味で)、既存の成功パターンに少しだけ他の成功パターンを加えるほうがいい。
全面的に変える必要がある場合は、

  • この予算では、アプリケーションが完成できない、という見込み。
  • このメンバでは、アプリケーションを完成させられない、という見込み。
  • この期間では、アプリケーションを完成させらんれない、という見込み。

といった、局所的な最適化あるいは改善策では、到底達成さできない場合に入れ替えが必要になる。

さて、改善の積み重ね、あるいは地道な努力、という程度(という言い方も悪いかもしれないが)で済む話であるならば、全面的なオブジェクト指向の手法の取り入れは止めておいたほうがベター…というか、SIer にとっては、リスキーなパターンを取るよりも、安全策を取るほうが良いのだ。
が、何をすれば安全であるのかというと、2つのパターンがあって、

  • 従来の設計書、プログラミング手法、試験手法を続ける。
  • オブジェクト指向の手法を少しだけ取り入れて、設計、プログラミング、試験の手法を変えていく。

というものがある。「従来の」と書くと、旧態依然という感じがして最新技術を追う業界(これは疑問なのだけど)にとっては、ちょっと…と二の足を踏むかもしれないのだが、建築業界のようにゼネコンを目指すのであれば、より実績のある技術に負うところが多い、と言い換えてもよい。従来の技術であっても、10 年以上続けているのであれば(場合によっては、20 年以上前でもよい)それは、それで実行時に効率化されているであろうし、変えなくてもよい、と言い切れる。

が、従来通りの手法で作っていると、やはり間に合わなくなってくる部分があり…というか、最近の開発の傾向として、

  • 昔よりも、不具合に関して、厳しくなくなっている。
  • 昔よりも、運用時のバージョンアップが可能になっている。

という点を汲み入れると、

  • ある程度、強固でなくても良い部分がある。
  • ある程度、後に切り替えられる部分がある。

という結論になる。
建前上は、「完成品」が求められるものの、一般的な受託開発のソフトウェアは自動車や航空機ほど完成度を求められていない。となれば、求められてないところは、求められてないなりの「経済性」で開発をする、ということが暗に求めらているのである、と言い切る。

そこで、アプリケーションの中では、強固であることが必須な部分と、さほど強固でなくてよい部分があることがわかる。これをどのように分けるかは、いくつかの手法があるのだが、

  • 強固な部分 → ライブラリ化(オブジェクト指向)
  • 緩い部分 → 一般的な手続き的手法で、プログラミング

という分け方をする。よく「共通化」とか「共通基盤」とか言われるのが、ライブラリ化にあたり、このあたりは昔からベテランが対応することになっている。また、ベテランがプログラミングする故に品質がよく、落ちにくいという頑健さが求められる。
で、逆に言えば、この強固ではない部分

  • GUI 部品を使った画面のプログラミング
  • 共通ライブラリを使ったプログラミング
  • コピー&ペーストを繰り返すプログラミング

のようなところは、一般的なスキル(設計から試験も、ほどよくという「経済性」を保つ)で開発を行うという方法がある。

このあたり、MVC パターンと絡むところがあるのだが、設計から試験まで、2 つの視点を使いプロジェクトの「経済性」を保ちつつ、成功率を高めるという手法になる。

カテゴリー: 開発, プロジェクト管理 | オブジェクト指向と手続きプログラミングの組み合わせ はコメントを受け付けていません

Windows Azure SDK 1.6 日本語版をインストールする

出遅れた…というか、気づかなかった。
「某ひと目Azureなんとか」(既に伏字になっていない)を直していると、SDK のバージョンが 1.4 から 1.5 から 1.6 に変わっているという…うーむ。発行時には、大丈夫なんでしょうか?

で、1.5 の時は、日本語版がなかなか出なくて困っていたのですが、1.6 は英語版が出てから1週間後に出ているのですね。これだと、次のバージョンでも安心かも。

Windows Azure SDK 1.6 日本語版
http://blogs.msdn.com/b/bluesky/archive/2011/11/24/windows-azure-sdk-1-6-jpn-release-and-windows-azure-guest-os-2-8-amp-1-16-minor-update.aspx

以前は、MSDN のダウンロードを探し回らなくては駄目だったのですが、今回は Web Platform Installer から検索するとすんなりいけそうです。「windows azure」で検索すると出てきます。

Visual Studio 2010 に「Webコンポーネントのインストール」というツールバーが追加されるので、これをクリックすると簡単にインストールできますね。

# NuGet のほうは、まだ試していないという…状態ですが。

カテゴリー: Azure | Windows Azure SDK 1.6 日本語版をインストールする はコメントを受け付けていません

色相のみでヒストグラムを計算表示してみる

大雑把に、HSV 変換をした後に色相(Hue)だけを取り出して、ヒストグラムにしてみる。
こうすると、対象物(この場合はゲームの駒)が持っている「色」という特徴量がわかる…ハズ。原子分析みたいなものかも。

#include "stdafx.h"
#include <iostream>
#include "opencv/cv.h"
#include "opencv/highgui.h"
using namespace std;

int main(int argc, char* argv[])
{
	char *fname = argv[1];
	cv::Mat image = cv::imread( fname );
	cv::Mat imageHsv;

	cv::cvtColor( image, imageHsv, CV_RGB2HSV );
	for ( int y=0; y<imageHsv.rows; ++y ) {
		for ( int x=0; x<imageHsv.cols; ++x ) {
			cv::Vec3b &v = imageHsv.at<cv::Vec3b>(y,x);
			v[0] = v[0]; // H
			if ( v[1] < 64 || v[2] < 64 ) v[0] = 300; // 閾値以下は範囲外とする
			v[1] = ( v[1] < 64 )? 0: 255; // S 閾値以下は白とみなす
			v[2] = ( v[2] < 64 )? 0: 255; // V 閾値以下は黒とみなす
		}
	}

	// ヒストグラム用
	const int ch_width = 180, ch_height=200;
	cv::Mat imageHist( cv::Size( ch_width, ch_height), CV_8UC3, cv::Scalar::all(255));
	cv::cvtColor( imageHist, imageHist, CV_RGB2HSV );
	for ( int y=0; y<imageHist.rows; ++y ) {
		for ( int x=0; x<imageHist.cols; ++x ) {
			cv::Vec3b &v = imageHist.at<cv::Vec3b>(y,x);
			v[0] = x;		// H
			v[1] = 100;		// S
			v[2] = 255;		// V
		}
	}
	cv::cvtColor( imageHist, imageHist, CV_HSV2RGB );

	cv::Mat hist;
	const int hdims[] = {180}; // 次元毎のヒストグラムサイズ
	const float hranges[] = {0,180};
	const float* ranges[] = {hranges}; // 次元毎のビンの下限上限
	const int chs[] = {0};
	double max_val = .0;
	cv::calcHist(&imageHsv, 1, chs, cv::Mat(), hist, 1, hdims, ranges);
	// 最大値の計算
	cv::minMaxLoc(hist, 0, &max_val);
	cv::Scalar color = cv::Scalar::all(100);
	hist = hist * (max_val? ch_height/max_val:0.);
	for(int j=0; j<hdims[0]; ++j) {
		int bin_w = cv::saturate_cast<int>((double)ch_width/hdims[0]);
		cv::rectangle(imageHist,
			cv::Point(j*bin_w, imageHist.rows),
			cv::Point((j+1)*bin_w, imageHist.rows-cv::saturate_cast<int>(hist.at<float>(j))),
			color, -1);
	}
	cv::cvtColor( imageHsv, imageHsv, CV_HSV2RGB );

	cv::namedWindow("original", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
	cv::namedWindow("result", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
	cv::namedWindow("hist", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
	cv::imshow("original",image);
	cv::imshow("result",imageHsv);
	cv::imshow("hist",imageHist);
	cv::waitKey(0);

	return 0;
}




 

背景がオレンジ色なので、それにヒストグラムが引っ張られてしまうのは後で検討するとして、色相が180の幅のままだと分解能が細かく過ぎてピークがとんがり過ぎるかなと。

試しに実画像を調べてみると、



のように広がりを持つので、相関係数でマッチさせるか、もっと単純に色を積算して max だけを取り出すか。色の検出自体は、おおまかで良い(白黒+6色ぐらい)ので、このぐらいであれば、どれだけ検出箇所が多くてもあっという間に終わるハズ。

カテゴリー: C++, OpenCV | 色相のみでヒストグラムを計算表示してみる はコメントを受け付けていません

OpenCV で機械学習を試して…みた

OpenCV で機械学習を試してみる…とまだ終わらず | Moonmile Solutions Blog
http://www.moonmile.net/blog/archives/2537

を、「-m 500」に設定して4時間程動かすと XML ファイルが出来上がりました。

検出用のプログラムをざっと作って、

#include "stdafx.h"
#include <iostream>
#include "opencv/cv.h"
#include "opencv/highgui.h"
using namespace std;

int main(int argc, char* argv[])
{
	/* 画像のロード */
	char *fname = argv[1];
	cv::Mat image = cv::imread( fname );
	/* 検出器のロード */
	// char *cascadeName = "C:\\OpenCV2.3\\data\\haarcascades\\haarcascade_frontalface_default.xml";
	char *cascadeName = "D:\\work\\OpenCV\\src\\PiyoDetect\\PiyoML\\data\\koma01.xml";
	cv::CascadeClassifier cascade;
	cascade.load(cascadeName);

	/* 検出 */
	vector<cv::Rect> komas;
	cascade.detectMultiScale( image, komas );

	/* 検出領域の描画 */
	for ( vector<cv::Rect>::iterator it=komas.begin(); it!=komas.end(); ++it )
	{
		cv::rectangle( image, cv::Rect(
			it->x, it->y, it->width, it->height ), cv::Scalar(0,255,0));
	}
	cout << "count: " << komas.size() << endl;
	/* 画像の表示 */
	cv::namedWindow("result", CV_WINDOW_AUTOSIZE|CV_WINDOW_FREERATIO);
	cv::imshow("result", image );
	cv::waitKey(0);
	return 0;
}

を動かしてみた結果が、次の図です。

全然あっていないじゃんッ!!! ってな状態です。どうも正解画像のサイズが大きすぎた(検出対象は30×30程度なのに、正解画像が45×45なのがちょっと間違い?)らしくって、検出先の画像は倍のサイズにして検出させました。

OpenCV に入っている haarcascade_frontalface_default.xml を使うと、顔検出ができるのでプログラムのほうは大丈夫らしい。

ちなみに、ver.2.3 のほうを使うと、実行時に cv::CascadeClassifier あたりで落ちるので、何故か ver.2.3.1 を使っています。実行時のエラーなので、DLL のビルド間違いとか(リビルドしていないし)かもしれません。

OpenCVによるアニメ顔検出ならlbpcascade_animeface.xml – デー
http://d.hatena.ne.jp/ultraist/20110718/1310965532
anime.udp.jp
http://anime.udp.jp/

探してみると、アニメ絵の顔検出もあって、結構な率で検出できているようです。正解画像を集めて特徴量を抽出してとプラス諸々をやっているそうです。動画のほうの顔検出も結構なスピードで動くので、実行時のスピードは十分ではないかなと。
色特徴によりキャラクター分類なんてのは、他での応用も大きいと思います<エラそうに。

で、自分の実験に話を戻すと、アクションパズルゲームの駒のようなものは、

  • そもそも、顔や人型のような「特徴」はない。
  • そもそも、ゲームの盤上から、判別すべき「特徴」が浮き出ている。

という特質があります。
最初の「特徴」のほうは、顔とか人型とかの「モデル」ですね。目があるとか口があるとか、それぞれの顔には必要な要素があって、それの要素が個別には少しずつ違う(画像によっては、半分だけとか)ので、これを多数の正解画像(顔の画像)から、共通部分を見つけるというスタイル。
2番目の「特徴」の方は、ゲーム盤面(背景画像)に埋もれないような特徴(色とか輝度とか形)とかが「駒」自体にはあるはずです。でないと、背景に埋もれてしまうし、それは背景ともいえる。背景差分はその手法のひとつで、背景はだいたい止まっているという前提のもとに、動いているもの(オブジェクト)を検出する方法。

で、ゲームの駒というのは、

  • ひょっとすると、盤面は動いていないかもしれない。
  • ひょっとすると、駒も盤面の動いていないかもしれない。
  • でも、明らかに盤面の上に駒がある(将棋の駒とか碁石とかもそう)。

なわけで、もっと大雑把に「特徴」を見極めるのでもよいかな、と昨晩考えていました。
Haar-Like のように細かく画像を調べるのは大袈裟で、もっと大雑把に、

  • HSV 画像の色相だけを取り出して、大まかに白黒+6色で特徴量とみなすとか、
  • 輝度のヒストグラムを取って、明るい駒と暗い駒を判別するとか、
  • 丸っこいとか、四角っぽいとか、ひし形っぽいとか、おおまかな形状で判別するとか、

のような特徴量だけでも十分なのかなと。どうも、駒の特徴量をそのままコーナー検出などに当てはめてしまうと、過学習っぽい気もするし。検出するときにカスケードのルートがひとつしかない(複数のカスケードを使えばそれでいいのでしょうが)のがちょっと気になっています。ひとつの検出器(検出ルール?)を用いるよりも、いくつかの検出器を併用して相互補完するのが誤検出に対しては強いのかなぁと。まだ妄想段階にすぎませんが。

カテゴリー: OpenCV | OpenCV で機械学習を試して…みた はコメントを受け付けていません

OpenCV で機械学習を試してみる…とまだ終わらず

テンプレートマッチングで画像から取り出すには無理がありそうなので、やはり、独自なオブジェクト検出器を作ってみないとだめか、と思い、

OpenCVで学ぶ画像認識:第4回 オブジェクト検出器の作成方法|gihyo.jp … 技術評論社
http://gihyo.jp/dev/feature/01/opencv/0004

を読んで、試しに、AdaBoost を使ってカスケードを作ろうと思ったのだが、いやあ、ちょっと時間が掛かりすぎる。

■学習用の正解ファイルの作成

C:\OpenCV2.3\build\bin\opencv_createsamples.exe ^
 -img images\koma01.png ^
 -vec koma01.vec ^
 -num 1000 ^
 -bg NG.txt ^
 -w 45 -h 45 ^
 -show

■オブジェクトの学習

C:\OpenCV2.3\build\bin\opencv_haartraining.exe ^
 -data koma01 ^
 -vec  koma01.vec ^
 -bg NG.txt ^
 -npos 1000 ^
 -nneg 467 ^
 -w 45 -h 45 ^
 -mem 200 ^

な感じで動かしていますが、4時間ほど経っても終わらず。

駒を検出したいので、今回の場合は正解画像は1つ(ゲーム中に出てくる画像)になるのですが、光の関係や画面を映す関係からいくつかの正解画像を用意します。そのあたりは、opencv_createsamples を使って、1000 枚の画像に水増しします。ファイルは別々にできずに、koma01.vec のように1つのファイルにまとめられます。

最初は、駒の画像が 90×90 だったので、そのまま指定したのですが、あえなくメモリーオーバーしてしまい opencv_haartraining がダウン。サイズを 45×45 にすると動くようになったので、対象画像はそこそこ小さいサイズにしないと駄目なのかも。

不正解の画像をどのように集めるのか?とも思ったのですが、マッチングしなければ何でもいいわけで、

Caltech101
http://www.vision.caltech.edu/Image_Datasets/Caltech101/Caltech101.html

にある適当なフォルダ(BACKGROUND_Google というランダムっぽい画像フォルダがある)を使っています。

うまく実行できると、下記のように、(多分)テンプレート画像を切り替えながら、閾値を切り替えながら学習し始めるわけですが…

D:\work\OpenCV\src\PiyoDetect\PiyoML\data>opencv_haartraining.exe  -d
xml  -vec  koma01.vec  -bg NG.txt  -npos 1000  -nneg 467  -w 45 -h 45

Data dir name: koma01.xml
Vec file name: koma01.vec
BG  file name: NG.txt, is a vecfile: no
Num pos: 1000
Num neg: 467
Num stages: 14
Num splits: 1 (stump as weak classifier)
Mem: 200 MB
Symmetric: TRUE
Min hit rate: 0.995000
Max false alarm rate: 0.500000
Weight trimming: 0.950000
Equal weights: FALSE
Mode: BASIC
Width: 45
Height: 45
Applied boosting algorithm: GAB
Error (valid only for Discrete and Real AdaBoost): misclass
Max number of splits in tree cascade: 0
Min number of positive samples per cluster: 500
Required leaf false alarm rate: 6.10352e-005

Tree Classifier
Stage
+---+
|  0|
+---+

Number of features used : 1007032

Parent node: NULL

*** 1 cluster ***
POS: 1000 1000 1.000000
NEG: 467 1
BACKGROUND PROCESSING TIME: 0.22
Precalculation time: 0.05
+----+----+-+---------+---------+---------+---------+
|  N |%SMP|F|  ST.THR |    HR   |    FA   | EXP. ERR|
+----+----+-+---------+---------+---------+---------+
|   1|100%|-|-0.763750| 1.000000| 1.000000| 0.123381|
+----+----+-+---------+---------+---------+---------+
|   2|100%|+|-0.660565| 0.999000| 0.837259| 0.123381|
+----+----+-+---------+---------+---------+---------+
|   3| 94%|-|-1.246627| 0.996000| 0.490364| 0.104294|
+----+----+-+---------+---------+---------+---------+
Stage training time: 889.36
Number of used features: 3

Parent node: NULL
Chosen number of splits: 0

Total number of splits: 0

Tree Classifier
Stage
+---+
|  0|
+---+

   0

Parent node: 0

*** 1 cluster ***
POS: 996 1000 0.996000
NEG: 465 0.524831
BACKGROUND PROCESSING TIME: 0.03
Precalculation time: 0.05
+----+----+-+---------+---------+---------+---------+
|  N |%SMP|F|  ST.THR |    HR   |    FA   | EXP. ERR|
+----+----+-+---------+---------+---------+---------+
|   1|100%|-|-0.635385| 1.000000| 1.000000| 0.182752|
+----+----+-+---------+---------+---------+---------+
|   2|100%|+|-0.333012| 0.996988| 0.544086| 0.149213|
+----+----+-+---------+---------+---------+---------+
|   3|100%|-|-0.999921| 0.996988| 0.544086| 0.082820|
+----+----+-+---------+---------+---------+---------+
|   4|100%|+|-0.692312| 0.996988| 0.389247| 0.063655|
+----+----+-+---------+---------+---------+---------+
Stage training time: 1205.64
Number of used features: 4

Parent node: 0
Chosen number of splits: 0

Total number of splits: 0

Tree Classifier
Stage
+---+---+
|  0|  1|
+---+---+

   0---1

Parent node: 1

*** 1 cluster ***
POS: 993 1000 0.993000
NEG: 463 0.230923
BACKGROUND PROCESSING TIME: 0.13
Precalculation time: 0.05
+----+----+-+---------+---------+---------+---------+
|  N |%SMP|F|  ST.THR |    HR   |    FA   | EXP. ERR|
+----+----+-+---------+---------+---------+---------+
|   1|100%|-|-0.912574| 1.000000| 1.000000| 0.160027|
+----+----+-+---------+---------+---------+---------+
|   2|100%|+|-1.372380| 1.000000| 1.000000| 0.298077|
+----+----+-+---------+---------+---------+---------+
|   3|100%|-|-1.029186| 1.000000| 1.000000| 0.073489|
+----+----+-+---------+---------+---------+---------+
|   4|100%|+|-1.168364| 0.996979| 0.796976| 0.073489|
+----+----+-+---------+---------+---------+---------+
|   5| 98%|-|-0.856654| 0.996979| 0.455724| 0.049451|
+----+----+-+---------+---------+---------+---------+
Stage training time: 1487.86
Number of used features: 5

Parent node: 1
Chosen number of splits: 0

Total number of splits: 0

Tree Classifier
Stage
+---+---+---+
|  0|  1|  2|
+---+---+---+

   0---1---2

Parent node: 2

*** 1 cluster ***
POS: 990 1000 0.990000
NEG: 462 0.1232
BACKGROUND PROCESSING TIME: 0.53
Precalculation time: 0.05
+----+----+-+---------+---------+---------+---------+
|  N |%SMP|F|  ST.THR |    HR   |    FA   | EXP. ERR|
+----+----+-+---------+---------+---------+---------+
|   1|100%|-|-0.606609| 1.000000| 1.000000| 0.199036|
+----+----+-+---------+---------+---------+---------+
|   2|100%|+|-0.848618| 1.000000| 1.000000| 0.199036|
+----+----+-+---------+---------+---------+---------+
|   3|100%|-|-0.527684| 0.996970| 0.614719| 0.152204|
+----+----+-+---------+---------+---------+---------+
|   4|100%|+|-0.781167| 0.996970| 0.623377| 0.154959|
+----+----+-+---------+---------+---------+---------+
|   5| 79%|-|-1.579804| 0.997980| 0.632035| 0.112948|

そもそもの目的が、ゲーム画面から既知の駒(時には未知の駒?)を見つけ出したいわけで、顔認識ほど正確でなくてもよいし、ある程度の場所を確定してから再マッチさせるという方式がやっぱりよさそうか、と思い直している次第です。

OpenCVで学ぶ画像認識:第3回 オブジェクト検出してみよう|gihyo.jp … 技術評論社
http://gihyo.jp/dev/feature/01/opencv/0003?page=2

にあるように、何もないところから特定のものを見つける(検出する率が低い)場合には、このような学習がよいのかもしれませんが、あらかじめ駒があるとわかっているゲームの盤上から探すには、もっと前処理をして絞り込んでもよいかなと。オブジェクト検出器の中で Ture/False の判断をするよりも、その前処理として Ture/False を大雑把に判断するロジックを挟む必要がありそうです。いちいち、駒の形状が変わるたびに、学習をさせるわけにはいかないし、学習自体にそれほど時間を掛ける意味はなさそうだし。

なので、決定木あたりかクラスタリングを使って、駒のツリー(今回は8種類程度だけど)を作るのがよいのでしょう。

のように、色味で分割で大雑把に分割ができるのは明らかなので、なんらかの特徴量を元に背景画像から抜き出した後に、相互に駒を判断するために再び特徴量を使うという2段階になるのでしょう。

そんな訳でオライリーの OpenCV を再読。

カテゴリー: 開発, OpenCV | OpenCV で機械学習を試してみる…とまだ終わらず はコメントを受け付けていません