2004年09月19日

画像結合ツールの開発 その1

ヤフオクに出品するものの写真の結合をやりながら思う、面倒くせー。
これは明らかに自動化できる作業だ。
で、画像結合ツールを作ると思い立ち、作業に取りかかった。
今回はめずらしくGUIで作る。
いつもは、Perlとかでさっさとやってしまうのに。
まあ、気まぐれだ。
そして、C++Builderを起動してぺこぺこやる。
C++Builderはあまり慣れていないので、少々手こずるけど、GUIアプリを作るのはすごく楽だ。

とりあえず、DrawGridに画像をD&Dして結合するようなIFにしようと思い、DrawGridをいじる。
でも、エクスプローラーからファイルをD&D出来ない。
ドラッグ&ドロップ用のイベントを書いてもうまくできない。
セル同士のドラッグ&ドロップが出来るようになっただけだ。
調べてみると::DragAcceptFiles( Handle, TRUE );とやって、WM_DROPFILESメッセージを処理するようにしないといけないらしい。
メッセージ処理用メソッドを作るにはBEGIN_MESSAGE_MAPを使うらしい。
なんか、MFCみたいだ。
でも、調べてみると、このマクロは単にDispatchに展開されるだけのようだ。
マクロだとデバッグ時にいろいろと面倒なこともあるので、Dispatchは自分でオーバーライドすることにした。
そして、DragAcceptFilesにDrawGridのハンドルを渡しメイク。
カーソルが変わった。
D&D出来るようだ。
で、ドロップ。
・・・
WM_DROPFILESのハンドラに来ない。
なぜ?
って、DrawGridのDispatchにWM_DROPFILESハンドラを書かないといけないんじゃないか。
普通にメインのフォームのやつに書いていた。

DrawGridを継承するコンポーネントを作る。
きちんとWM_DROPFILESハンドラに来るようなった。
次に画像データの保持だが、面倒なのでvectorで10*10決め打ちで作ることにした。ま、ツールだしね。
std::vector > m_Cells; こんな感じ。
そして値初期化の時にm_Cells.reserve(10)とやって作る。当然2次元なので2次元目もreserveし、NULLで初期化。
画像はTJPEGImageで保持させた。
そして、メイクしてインストールしようとしたら、"パッケージ'vcljpg60'にもユニット'Jconsts'が含まれるためパッケージ'dclusr60'は読み込めません。"と出た。
なんだ、コンポーネントでTJPEGImageを使うのはまずいのか?
よくわからん。
じゃ、libjpegを使おうと言うことで、検索。
とりあえず、ここを参考にする。
他にも検索していて、GDI+と言う言葉が目に入った。
そうだ、GDI+を使った方が楽かも。
急遽ligjpegからGDI+に方向転換。
少し調査。

少し時間が空いて、今日。
Cマガを見るとちょうどGDI+の話題だった。
なんと間のいい。
とりあえず購入。

投稿者 Takenori : 10:09 | トラックバック

2004年10月12日

ボタン絵結合ツール

吉里吉里のボタン絵には通常、押下、選択の3つを横に並べたものが必要だ。
単純に横にくっつけると言うのは、プログラムでは簡単だが、普通のペイントツールを使うとかなり面倒だ。(単純に並べるだけなのに、ドットがずれないかどうかに神経を使う)
ペイントツールでこのような機能を持ったものが存在するかもしれないが、知らないので作ることにした。
仕様は、
フォルダをD&Dすればその中にあるBitmapをすべてくっける。
くっつける時のルールは"名前.bmp", "名前_d.bmp", "名前_s.bmp"と言う名前のファイルを一組として、横にくっつくていく。
くっつけられた画像は、D&Dされたフォルダの下にbuttonフォルダを作り、その中へ"名前.bmp"というファイル名で出力する。
とする。
で、なにげにC++Builderでボタンとテキストボックスをくっつけて、D&Dのハンドラ書いて、FindFirstで"*.bmp"を列挙するところまで作った。
そして、くっつける処理はGDI+でも使ってみようと思い、ヘッダーをインクルードするとエラーが山のように出てくる。
あらら。
すごく行き当たりばったりだが、C#で.Net Frameworkを使い作る方向に変更。
少しC#に興味があったからだ。
で、まずはフォームにボタンやエディットボックスを置く。
でも、これはC++Builderに近いなぁ。
これは意外といいかも。
そして、D&Dの処理を調べる。
テキストをD&Dするサンプルがすぐに見つかったので、それを適当に改造して、フォームに対するドロップを処理するようにしようとするが、よくわからない。
そもそも、C#でプログラムを一度も組んだことがないので、文法などもよくわかっていない。
Cを半音上げたものだから、C#って聞いていたので、Cと似たようなものかなぁと思っていたのだが、それほどすんなりとは行かないようだ。
コードの一部に波線が引かれていたので、カーソルを置くとツールチップで説明が出たが、よくわからない。
C#独特の記法のようだ。
C#入門ぐらいは読まないとダメかな。

今やるのはちょっと面倒臭いなぁと思って、どうしようと考えていたらImageMagick(PerlMagick)を使うことを思い付く。
PerlMagickは使ったことがないけど、これなら簡単に出来そうな気がした。(根拠なし)
で、ImageMagickのページのAPI->PerlMagickを読んで適当に作ってみる。(このページの日本語訳ないかなぁ?)
少し悩むが、意外とあっさり出来た。
次のコードで、'b_fast.bmp', 'b_fast_d.bmp', 'b_fast_s.bmp'の3つファイルを横にくっつけて、x.pngという名前で出力する。

use Image::Magick;

$image = Image::Magick->new;
$x = $image->Read('b_fast.bmp', 'b_fast_d.bmp', 'b_fast_s.bmp');
warn "$x" if "$x";
$p = $image->Append(stack=>'false'); # stack falseでLeft to Rightでくっつける
#warn "$x" if "$x";
$x = $p->Write('x.png');
warn "$x" if "$x";

やはり、PerlMagickは便利だ。これから少しずつ使っていきたいなぁと思う。
それはともかく、ここまで出来れば後はいつも通り書けばいい。
次のような感じ。

use Image::Magick;

foreach( glob('*.bmp') ) {
 if( /.+_d\.[bB][mM][pP]/ ) { next; }
 if( /.+_s\.[bB][mM][pP]/ ) { next; }
 /(.+)\.[bB][mM][pP]/;
 $base_name = $1;
 {
  local($image,$x,$p);
  $image = Image::Magick->new;
  $x = $image->Read($base_name.'.bmp', $base_name.'_d.bmp', $base_name.'_s.bmp');
  if( "$x" ) { warn "$x"; next; }
  $p = $image->Append(stack=>'false');
  if( "$x" ) { warn "$x"; next; }
  $x = $p->Write($base_name.'.png');
  if( "$x" ) { warn "$x"; next; }
 }
}

buttonフォルダは作らないが、pngで出力するようにしたのでかまわないだろう。
PerlMagickを使えば楽勝だったな。

投稿者 Takenori : 12:14 | コメント (5) | トラックバック

2005年01月13日

ImageMagickライブラリのビルド

ImageMagickを使えば、かなり多くのフォーマットに対応できるし、C++での使い方もPerlMagickのように簡単に見える。
また、ライセンスもApache-style licenseで、たぶんオープンもプロプラエタリにも使えるとある。
たぶんと書いてあるのは、リンクするライブラリが多いためだろう。
以上のようなことから、ImageMagickを使いたいと思い、ビルドしてみることにした。
バイナリのインストーラーもあるが、いろいろとインストールされてしまうので、必要な物のみスタティックなライブラリとしてビルドし、必要に応じてリンクするような形態にしたいため、ソースからビルドすることにした。

Install-windows.txtを見ながら進めた。
使用したのは、Windows Source 6.1.8-5で、VCは2003。
まずは、ImageMagick-6.1.8/VisualMagick/configure にあるVCのプロジェクトを開き、Releaseビルドでconfigure.exeをビルド。
出来たconfigure.exeをダブルクリックして起動するとダイアログが開く。
"次へ" をクリックした後、Build type setupでStatic Multi-threaded runtimesを選択。
Use X11 stubs to... のチェックははずした。
Build optionsはGenerate Visual Studio 7 formatにだけチェックが入った状態にし"次へ"をクリック。
次のパスの設定などはそのままにし、完了すると、VisualStaticMT.sln がImageMagick-6.1.8/VisualMagick に出来る。他にも各ライブラリのプロジェクトファイルが生成されたようだ。
VisualStaticMT.sln でVCを起動し、ビルド。
しばらく待てばImageMagick-6.1.8/VisualMagick/libにわんさかライブラリが出来る。
また、ImageMagick-6.1.8/VisualMagick/binに実行ファイルがいろいろと出来る。
Debug版、Release版はそれぞれ、途中のDBとRLの文字が異なるライブラリが生成される。
以上で必要なライブラリは生成されたはず。
次は、簡単なサンプルを作れるかどうか確認する。

投稿者 Takenori : 04:37 | トラックバック

ImageMagickを使う

以下は、VC 2003で確認した。
まずはVCを起動し、新規プロジェクトでコンソールアプリのプロジェクトを作った。
そして、インクルードディレクトリに ImageMagick-6.1.8/Magick++/lib とImageMagick-6.1.8 を追加。
ライブラリパスを設定に追加し、ライブラリをフルパスで指定しなくても良いようにした。
ライブラリはいっぱいあって面倒なので、次のように入力して一覧が記述されたテキストファイルを作った。
perl -e "foreach( glob(\"*.lib\") ) { if( /DB/ ) { print $_.\" \"; } }" > DB.txt
Release版も同様に次のようにして一覧テキストを作った。
perl -e "foreach( glob(\"*.lib\") ) { if( /RL/ ) { print $_.\" \"; } }" > RL.txt
で、出力されたテキストファイルからプロジェクトの追加ライブラリにコピペ。
Socket関連も使うようなので、Ws2_32.lib も追加ライブラリに追加。
LIBCMTD を無視するライブラリに追加。
以上で、Magick++を使える環境が整ったはず。
サンプルにあった物とほぼ同じ以下のようなソースをビルド。
#include "stdafx.h"
#include <Magick++.h>
#include <iostream>
using namespace std;
using namespace Magick;

int _tmain(int argc, _TCHAR* argv[])
{
 // Construct the image object. Seperating image construction from the
 // the read operation ensures that a failure to read the image file
 // doesn't render the image object useless.
 Image image;

 try {
  // Read a file into image object
  image.read( "test.jpg" );

  // Crop the image to specified size (width, height, xOffset, yOffset)
  image.crop( Geometry(100,100, 100, 100) );

  // Write the image to a file
  image.write( "x.jpg" );
 }
 catch( Exception &error_ )
 {
  cout << "Caught exception: " << error_.what() << endl;
  return 1;
 }
 return 0;
}
で、実行したらException。
調べると、どうやら ImageMagick-6.1.8/config/magic.xml が実行ファイルと同じディレクトリに必要なようだ。
magic.xml をコピーし、実行したらうまくいった。
以上でVCからはImageMagickが使えるようになった。
ちなみに、上記コードの実行ファイルのサイズは3.6MB。やはりでかいな。

投稿者 Takenori : 05:16 | トラックバック

2005年01月17日

画像を縮小

いままでGIMPを使って1枚1枚縮小していたが、面倒になったので、スクリプトを書くことにした。
で、ImageMagick(PerlMagick)を使おうと思ったが、Perl5.6が入っているマシンだったので、ImageMagickをインストールしてもPerlMagickが動かなかった。
少し悩んだが、コマンドラインツールもついていることを思い出して、コマンドラインツールを呼び出して使うことにした。
初め"-sample"を使ったのだが、どうも汚い。
近傍点を取ってくるだけのようだ。
もう少しリファレンスを調べて"-scale"を発見。
これで綺麗に縮小できるようになった。
で、スクリプトは次のような感じ。

foreach( glob("*.jpg") ) {
  print( "convert -scale 512x384 -quality 80 $_ temp.jpg\n" );
  system( "convert -scale 512x384 -quality 80 $_ temp.jpg" );
  print( "delete $_\n" );
  unlink( $_ );
  print( "rename temp.jpg $_\n" );
  rename( "temp.jpg", $_ );
}
unlink( "temp.jpg" );

同一フォルダにある*.jpgファイルを片っ端から縮小していく。
JPEGのクオリティーは80に設定。

投稿者 Takenori : 01:59 | トラックバック

画像を縮小

いままでGIMPを使って1枚1枚縮小していたが、面倒になったので、スクリプトを書くことにした。
で、ImageMagick(PerlMagick)を使おうと思ったが、Perl5.6が入っているマシンだったので、ImageMagickをインストールしてもPerlMagickは動かなかった。
少し悩んだが、コマンドラインツールもついていることを思い出して、コマンドラインツールを呼び出して使うことにした。
初め"-sample"を使ったのだが、どうも汚い。
近傍点を取ってくるだけのようだ。
もう少しリファレンスを調べて"-scale"を発見。
これで綺麗に縮小できるようになった。
で、スクリプトは次のような感じ。

foreach( glob("*.jpg") ) {
  print( "convert -scale 512x384 -quality 80 $_ temp.jpg\n" );
  system( "convert -scale 512x384 -quality 80 $_ temp.jpg" );
  print( "delete $_\n" );
  unlink( $_ );
  print( "rename temp.jpg $_\n" );
  rename( "temp.jpg", $_ );
}
unlink( "temp.jpg" );

上記スクリプトでは、同一フォルダにある*.jpgファイルを片っ端から縮小していく。
保存されるJPEGのクオリティーは80に設定。

投稿者 Takenori : 01:59 | トラックバック

perlでゴミ箱へ

いつもはperlで削除する場合、unlink()で消してしまうが、いきなり消すのではなく、ゴミ箱へ一度入れたいと思った。
で、SHFileOperationが使えたらゴミ箱へ移動出来るはず。
ActiveStateで検索すると、Win32-ShellExtと言うちょうどいいのがあった。
ppm install Win32-ShellExt とやるが、インストールできない。
上述のページを見るとPlatforms: Hpux-ia64 とある。
これってIA64用ってこと?
うーん、他にないのかなぁ。

投稿者 Takenori : 11:51 | トラックバック

2005年02月11日

SWIGを使ってみる (Perlでゴミ箱へ)

perlでいきなりファイルを削除せずにゴミ箱へ移すために、ゴミ箱へ削除するためのperlモジュールを作ることにした。
で、以前知ったSWIGを使ってみることにした。
マニュアルとサンプルを見てとにかく書いてみる。
まずは、ゴミ箱へ削除する関数を書く。
ファイル名はtoTrashBox.cppとした。

#include <windows.h>
#include <shellapi.h>
#include <string.h>

extern "C" void toTrashBox( const char *p )
{
  SHFILEOPSTRUCT stFile;
  char path[MAX_PATH];

  size_t len = strlen(p);
  strcpy( path, p );
  path[len+1] = '\0';

  memset( &stFile, 0, sizeof(SHFILEOPSTRUCT) );
  stFile.hwnd = NULL;
  stFile.wFunc = FO_DELETE;
  stFile.pFrom = path;
  stFile.pTo = NULL;
  stFile.fFlags = FOF_ALLOWUNDO | FOF_NOCONFIRMATION;
  ::SHFileOperation( &stFile );
}

パスは\0\0で区切らないとならないため、内部でコピーして\0を足している。
後、渡すパスはフルパスでないといけない。
他はたいしたことはやっていない。
次にSWIGスクリプト(?)をW32Shell.iという名前で作る。
中身は次の通り。

/* File : W32Shell.i */
%module W32Shell

extern void toTrashBox( const char *p );

で、コマンドラインでSWIGに渡す。

> swig -perl W32Shell.i

すると、W32Shell_wrap.c と W32Shell.pm が出来る。
次に、W32Shell 用のプロジェクトを作る。
DLLのプロジェクトとして作り、中身は空にする。

C/C++の追加インクルードディレクトリに"C:\Perl\lib\CORE"を追加。
リンカの追加の依存ファイルに"C:\Perl\lib\CORE\perl58.lib"を追加。
これはインストール環境やperlのバージョンによって変わるので、適宜あわせる。
で、プロジェクトにW32Shell_wrap.c と toTrashBox.cpp を追加。
後はビルドすればOK。
W32Shell.dllが出来る。
で、実際に使う場合のperlスクリプトは次のようにする。

use W32Shell;
W32Shell::toTrashBox('C:\test.txt');

これで、C:\test.txt がゴミ箱へ移動される。
もしかしたら需要があるかもしれないので、その内このモジュールは公開するかも。
気ままにメソッドを追加しそうだけど。
と言っても、そんなに使いたい物はないかな?
まあ、欲しくなったときに追加していこう。

投稿者 Takenori : 00:32 | トラックバック

2005年02月12日

サウンドの再生

sndPlaySound を使えば簡単にサウンドを再生できる。

投稿者 Takenori : 17:41 | トラックバック

2005年03月27日

PerlMagickでMPEGファイルを作る

use Image::Magick;

$image = Image::Magick->new(size=>'640x480');
for( $i = 0; $i < 25; $i++ )
{
  image->ReadImage('xc:black');
  text = sprintf("%03d",$i);
  $image->[$i]->Annotate(font=>'Century', pointsize=>100, fill=>'green', text=>$text, gravity=>"Center" );
}
$x = $image->Write(filename=>'test001.mpg');
warn "$x" if "$x";

上のPerlスクリプトを実行すると000~024までの数字が表示されるだけのMPEGムービーが出来る。
FPSは25、アスペクト比は1.42 : 1。
FPSやアスペクト比を変更したいが方法がよくわからない。
FPSはまあいいとしてアスペクト比は何とかしたいところ。
とりあえずはTMPGEncでエンコードしなおせば済むが・・・ テスト用ならそれでもいいかな。
後、5分ぐらいムービーを作ろうとしたらテンポラリファイルが作れないと言われ終了。30GBぐらいは空いているんですけど・・・
長いムービーを作る場合は少しずつ作らないとダメそうだな。

投稿者 Takenori : 23:39 | トラックバック

2005年03月30日

連番画像からMPEGファイルを

PerlMagickでいきなりMPEGを作るのは厳しそうなので、まずは次のようなスクリプトで連番JPEGファイルを作った。

use Image::Magick;
for( $i = 0; $i > 1800; $i++ )
{
  my $image = Image::Magick->new(size=>'640x480');
  $image->ReadImage('xc:black');
  $text = sprintf("%05d",$i);
  print "Create image #".$text.".\n";
  $image->Annotate(font=>'Century', pointsize=>100, fill=>'green', text=>$text, gravity=>"Center" );
  $image->Set(quality=>90);
  $filename = sprintf("img%05d.jpg",$i);
  $x = $image->Write($filename);
  warn "$x" if "$x";
}

で、連番画像からムービーを作るツールを探したのだが、見つからず、自分で作ることを考えていたのだが、TMPGEncで作れることがわかった。
これで、テスト用のムービーが手軽に作れる。

投稿者 Takenori : 22:38 | トラックバック

2005年11月21日

コード補完マクロ

コピーコンストラクタや演算子などを書いていて煩わしくなってきた。
前に+とか入力した後にマクロを実行すると、operator+などと勝手に補完してくれるようなマクロを書いていた。
けど、どこかへ行ってしまった。
で、再び書く。
今回は、演算子系だけではなく、ヘッダーファイルを作った時に毎回書くようなのも補完してくれるようにした。(ただし、演算子などは全部は作っていない。必要になった時に追加予定)
例えば、"fh"と打った後にマクロを実行すると、ヘッダーファイルの定型文を一気に追加してくれるようなものだ。

要は、カーソルの前にあるコマンドを見て何かをしてくれるマクロだ。
このようにしていれば、マクロの名前を入れたりしなくて済む。
また、マクロにショートカットキーを割り当てればさらにお手軽だ。

演算子で補完する文を宣言にするか定義にするかは悩みどころ。
開いているファイル名を得られるのなら拡張子で判断できるが……
とりあえずは、定義が入るようにした。
消すほうが楽だし。
後、クラス名などは自分で置換しないといけないようになっている。
ある程度解析するようにするか、ダイアログで入力を促すようにすれば出来るけど、そこまでするのもどうか。
ま、ほどほどに省力化できればいい。

ヘッダーの定型文補完は結構便利だ。
ヘッダーコメントやnamespaceなどが勝手に入ってくれる。
ダイアログを表示して、クラス名やnamespace名などを入力できるようにすればもっと楽になるかな?
簡易コードジェネレーターみたいなものだし。

投稿者 Takenori : 20:10 | トラックバック

2006年04月09日

Perlでオブジェクトを扱う

クラスとかオブジェクト指向とかの話なんだけど、クラスという感じではないし、オブジェクト指向っぽくなくてもいいので、とりあえずオブジェクトを扱うと言うタイトルにした。このタイトルも微妙だけど。
以降、便宜上それはクラスと呼ぶことにする。

それはともかく、時々使いたくなってそのたびに忘れてしまっているのでここにメモしておく。
内容はそんなに確かじゃないかも。

ファイル名とパッケージ名は同じにする。
パッケージ名は単なる名前空間っぽい。詳しくは知らないけど。
ファイル名の拡張子はpmにする。
ファイルの中身は次のような感じ。

package hello;
use strict;
sub new {
    my $className = shift;
    bless {
        Name => undef,
    }, $className;
}
sub setName {
    my $self = shift;
    $self->{Name} = shift;
}
sub print {
    my $self = shift;
    print "Hello ".$self->{Name}."\n";
}
sub DESTROY {
    print "destory.\n";
}
1;

new 関数の名前は何でもいいみたいだけど、new とするのが慣習のよう。
で、この関数の最初の引数にはそのクラスの名前が渡される。
クラスのデータにしたいものを bless 関数でクラスの名前と関連付ける。
bless 関数は出来上がったクラスのインスタンス(?)を返す。
この関数では return が省略されているけど、関数は最後の式の実行結果を返すので、ここでは bless の返り値、つまりクラスのインスタンス(?)が new の返り値になる。
関連付けた後に初期化などしたい場合は、bless の返り値を変数に入れておいて何かして、bless の返り値を入れた変数を return で返せばいい。
bless の第1引数は関連付けたい変数、第2引数はクラス名。
ここでは第1引数にハッシュを渡している。
別にスカラーや配列でもいいけど、クラスのメンバ変数が値1個だけとか、配列になってたりしたら使い辛いので、特に何も考えずにハッシュにしたほうが楽。
効率を重視するのならスカラーや配列のほうが少し速いかもしれないけど、自分の場合ほとんどバッチ処理用にPerl を使うのでそんなに効率は重視しない。CGI とか書く場合には考えたほうがいいかも。
第2引数のクラス名は固定値にしてもいいけど、このクラスを継承したい場合クラス名が変わるので、new 関数の最初の引数で渡される名前と関連付けたほうが良い。
new 関数の中身はそんな感じ。

これからわかるように、perl のクラスは少し変な感じがする。
いくつのかの機能があって、それを組み合わせるとクラスが実現できるって感じだろうか?
クラス以外にも柔軟に対応できそうな気がする。

setName と print 関数はメンバ関数。
最初の引数にはクラスのインスタンスの参照が入ってくる。
ただし、それは new で作ったやつをアローで呼び出した場合。
そうでない場合、例えば hello::print() と呼び出すとそうはならない。この場合は明示的にインスタンスを引数に入れる必要がある。
関数の中身は見たまま。

DESTROY 関数はデストラクタ。

例の中身は以上のような感じ。
で、これを使うには次のようにする。

use hello;
my $helloInst = new hello();
$helloInst->setName( "Kaede" );
$helloInst->print();

特に説明の必要はないだろうから説明は割愛。

投稿者 Takenori : 12:48 | トラックバック

2007年03月04日

吉里吉里3 Releaser GUI フロントエンド(仮) WSHで

最近は、GUIフロントエンドにHTA ( WSH ) を使うことが多い。
コマンドラインのみでもいいけど、引数を忘れてしまって困ることもあるし、他の人に使ってもらう時などGUIフロントエンドがあったほうがいい。
それと、入力を制限することも出来るし、デフォルト値を入れておくことも出来て楽。
でも、そんなに作るものではないので、HTAの基本的な部分を忘れてしまうのが難点。
てことで、サンプルをこの辺に置いとこうかと吉里吉里3 Releaser用のを作った。
当然だけど、これだけでは動かない。
krkrrel.exe と mingwm10.dll と wxbase26ud_gcc_custom.dll がいる模様。
krkrrel.exe をビルドしなおしたらdll類は何とかなるかもしれないけど、リポジトリから取ってきたものを使ったらそうなっていた。
後、細かいテストはしていません。
たぶん、ちゃんと動きます。

このサンプルはアーカイブファイル名の選択が曲者です。
普通のHTMLのinput type=fileでやっているので、存在しないファイルを選択できません。(笑)
ファイルオープンダイアログで右クリックして、新規作成で適当なテキストファイル作って拡張子を.xp4にしてそれを選べば大丈夫。(大丈夫じゃない気がするけど……)
できれば、名前をつけて保存のダイアログを使いたかったけど、うまく行かなかったので諦めた。
他にアーカイブの作成に時間がかかる場合は、応答がなくなります。
この辺りはちょっとどうかと思われます。
作成時は、標準出力をもらうようにしないほうがいいかもしれません。

HTAは、WSHより制限があり少し使い辛いけど、GUIを作るにはHTAでないと辛い。
ウィンドウサイズは変更できるけど、一瞬デフォルトサイズになってから切り替わるので格好悪いため、サイズ指定は使ってない。うまくやる方法があるのかもしれないけど、知らない。
後、VBScriptでは使えるけど、JScriptでは使えないものがある。(ウィキペディアのVBScriptを参考)
混在可能なので、サンプルでは両方のスクリプトが書かれている。
単に片方で書いてうまく行かなくて、もう片方だとうまく行ったからって理由だけど。(VBScriptもJScriptもあんまり詳しくないのでそうなった)

とりあえず、コマンドに引数渡して終わりなので、HTAのGUIフロントエンドはいつもテキトーです。
CSSとかDOMでがんばれば、見栄えのするものが作れる気はするけど。

投稿者 Takenori : 18:02 | トラックバック

2007年08月10日

OS終了とサスペンドなどからのレジューム

ソフトを起動したまま Windows が終了された時に備えて、WM_QUERYENDSESSION でも終了前処理をした方が良いようだ。
mixi Alert で終了時にデータを保存しているのに保存できていない時があってなぞだったが、WM_QUERYENDSESSION の対処が必要だったわけだ。

サスペンドなどからの復帰は、WM_POWERBROADCAST で検出できるようだ。
[Win32] システムのサスペンド、レジュームを捕まえたい
サスペンドや休止状態からの復帰でタイマーが狂うことに備えて、レジューム時にタイマーを一度リセットしたほうが安全なよう。

投稿者 Takenori : 13:04 | トラックバック

2007年08月13日

C++Builder 2007 Update 2

C++Builder 2007 Update 2 が公開されていた。
これによって致命的な不具合が解決される。
例えば以下の二つ。
----
-O1 もしくは –O2 オプションを使用したときにコンパイラがスタックポインタを破壊する。また、最適化を行うべき状況で、コンパイラが最適化しない場合に比べて遅く、肥大した不要なコードを追加する
プライベートの複数のTRect フィールドを持つDelphiコンポーネントを使用したときにC++コンパイラがTRect 構造体のオフセットアドレスのミスマッチを起こす
----
なんというか、これらのバグのせいですごい悩んだんですが。

C++Builder 6 で作っていたツールを 2007 に持ってきて、プロジェクト変換してエラーがきつくなった所を直して、ビルド通って動いたと思ったら、特定の操作でアクセス違反で落ちる。
起動はしてある程度使えるんだけど、落ちる箇所があってまともに動かない。
落ちるところは直した箇所とは別で、C++Builder 6 で作っていたツールはそれなりに使っていたので、不具合とは考えづらかったが、潜在不具合かと思って追った。

落ちる箇所はリストが空のときに std::find( begin(), end(), a ) みたいにやっているところ。
デバッガで追うと、空のリストなのに begin() != end() で、begin() のアドレスにアクセスして落ちてる。
あれ? list が空の時って、begin() == end() じゃなかったっけ? と言うか、今までそのつもりでいつも書いていて何も問題起きなかったが、正確な仕様は把握していなかった。
で、少し調べるも空のときの begin() が返すイテレーターについての記述が見付からなくて、ソースを見ることにした。
見ると同じになりそうなのだが・・・

そこで、確か同じ dinkumware のSTLを使っている VC2005 のソースを見ると、少し違う。
なんかちょっとチェックが増えてる。
で、VC で簡単なソース組んで動かして見ると問題なし。
じゃ、Builder の STL かなと同じように組んで動かすと問題なし。
あれ?
ちゃんと動いた。と言うことは、自分が書いたソースがまずいのか?
メモリ破壊でもしているんだろうか?
今まで普通に使えていたんだが。
で、自分のソースを追うもよくわからない。
どうしたものかと落ちる箇所をデバッガで何度も動かしているとおかしなことに気付いた。
end() の返すイテレーターが指しているアドレスが毎回変わってる。
何かおかしい。

C++Builder 6 のプロジェクトをコンバートせずに 2007 で作って、ソースを追加してビルドして見る。
ちゃんと動いた!
なんだ、プロジェクトのコンバーターがおかしいのか。
が、共有ライブラリを使わないようにしてビルドしたらやっぱり落ちる orz
リンカが悪いのか?
仕方ないので、共有ライブラリを使うようにして、リリースビルド。
そしたら落ちる。デバッグでは落ちない。
使えないじゃないか!
もしかしたら設定で回避できるかと試行錯誤するも回避できず。
2007 使えない。。。パッチ出るまで待とうと思った。

で、今日。
なんとか回避する方法あるんじゃないかと再びトライ。
起動するとアップデートがあるとか出た。
もしかして!
アップデートしてビルド。
直った。
普通に動く。
当たり前のことがうれしい。
良かった。
でも、自分の中で C++Builder 2007 の信頼性がかなり落ちた。
もう、普通に使っても大丈夫かなぁ。

投稿者 Takenori : 23:02 | トラックバック

2007年10月12日

C++ Builder 2007 Update3

何が修正されたかは見ていないが、大量に修正されている。
で、何気なく気付いたのだが、テンプレートのインライン展開周りが改善されているようだ。
なんか描画が速くなった? と思って、計測したら速くなってた。
テンプレートのインライン展開と最適化 で、C++ Builder 2007 の時は遅いって書いたけど、ほぼ期待通り動くようになっていた。
グラフィック周りはインライン展開を当て込んで作っているので、インライン展開されないと遅い。
今回されるようになって速くなったというか、元々はこれぐらいのはずだったのか。
また、その関係で「常に真です」といった警告も出るようになっていた。
たぶん、これがテンプレートのインライン展開をちゃんと見ている証拠だろう。
今までは出ていなかったと思う。

でも、またコンパイラバグっぽい動きに出会った。
C++Builder 2007 Update 2 のエントリーに書いたのと似たような傾向。
テンプレートクラスと他のクラスを多重継承したクラスのポインタを持っていて、その規定クラスのテンプレートクラスのメンバへアクセスしようとしたらアクセス違反で落ちる。
最小限のコードで再現しようとしたが、再現できず。
メンバに入れれば回避できるかな……

自分のソースが間違ってた

投稿者 Takenori : 16:30 | トラックバック

2008年01月05日

.svn ディレクトリを perl で消す

追記: subversion の場合、ローカルの作業コピーに対してエクスポートを実行できるので、わざわざ以下のようなスクリプトを使わなくても、ローカルの作業コピーをエクスポートする方が楽で速い。

Subversion からエクスポートすれば、.svn ディレクトリは出来ないけど、ローカルに作業コピーがある時は少々時間がかかるのが難点。
そこで、ローカルの作業コピーを適当なディレクトリにコピーして、そのディレクトリで以下のような perl スクリプトを実行すれば、.svn ディレクトリを消すことが出来る。
CVS の場合も以下のスクリプトを少し書き換えるだけで対応出来る。

use File::Find;
use File::Path;

find( sub { rmtree($_, 1, 1) if (-d $_ and $_ eq '.svn');}, '.');

投稿者 Takenori : 16:55 | トラックバック

2008年01月09日

ファイル名に出来ない文字をエスケープ

.NET だと、GetInvalidFileNameChars で使えない文字が得られるようだけど、それに相当する API が見当たらない。
と言うことで、使えないと言われている11文字だけエスケープする関数を作った。
以下のような感じ。

void EscapeFileName( std::string& filename ) {
  char* escapechar = "\\/,;:*?\"<>|";
  std::string::size_type n;
  while( (n = filename.find_first_of(escapechar)) != std::string::npos ) {
    filename.replace(n,1,1,'_');
  }
}

使えない文字を "_" に置換するのみ。
Shift JIS だと5C問題でこれじゃダメ。

投稿者 Takenori : 16:25 | トラックバック

ICU を使う

mlang を動的リンクして使ったら、なぜかリリースビルドで読み込みや書き込みのアクセス違反が出た。
以前は、インポートライブラリを使っていて、特に問題なく動いていたんだが。
と言うことで、ICU を使ってみることにした。

使い方は、ICUの文字コード変換を使いたいのですが... を見ればすぐにわかる。
無駄に動的リンクにしたが、mlang から 1.5時間程度で置きかえれたのですぐ。
後、Windows の Shift-JIS は、windows-31j になるので注意。
それと、変換時に最初のアドレスと、最後のアドレスを指定するが、\0 を含まずに指定しているので、長さを指定しないで std::wstring などに入れるとごみが入る。
その点注意。
まあ、たいしたことじゃないけど、初めミスってて化けた。

でも、よく考えたら、euc-jp 使わないので、WideCharToMultiByte と MultiByteToWideChar でなんとかなった。
icudt38.dll が 10MB とでかいので、結局使わないことになりそう。

投稿者 Takenori : 23:21 | トラックバック

2008年01月11日

エラーコードを調べる

このエラーコードは何かなと、調べたい時がままある。
だいたい grep とかしていたけど、コマンドラインでエラーコード打ったら返してくれるのがあればいいんだと思って作った。
以下のような感じ。
Windows のエラーと DirectX 9 のエラーコードを文字列にして表示してくれる。
たぶん、DirectX 9が入っていないと起動しない。

#include <stdio.h>
#include <windows.h>
#include <sstream>
#include <string>
#include <dxerr9.h>

#pragma comment(lib,"dxerr9.lib")

int main( int argc, char* argv[] )
{
  if( argc < 2 ) {
    printf( "usage:\n" );
    printf( argv[0] );
    printf( " [error code]\n" );
    printf( "\n" );
    printf( "ex.\n" );
    printf( "whaterror 0x01234567\n" );
  } else if( argv[1] ) {
    std::string    valstr( argv[1] );
    unsigned long  errorCode;
    std::stringstream stream( valstr, std::ios::in );
    stream >> std::hex >> errorCode;

    LPVOID lpMsgBuf;
    FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
            NULL, errorCode, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&lpMsgBuf, 0, NULL );
    if( lpMsgBuf ) {
      printf( (LPCTSTR)lpMsgBuf );
    } else {
      const char* dxErrStr = DXGetErrorString9( errorCode );
      const char* dxErrDesc = DXGetErrorDescription9( errorCode );
      if( dxErrStr ) printf( "%s\n", dxErrStr );
      if( dxErrDesc ) printf( "%s\n", dxErrDesc );
    }
    LocalFree(lpMsgBuf);
  }
}


他の人に聞くと、エラーコードでググるとか。
そっちのが楽かも。
whaterror.zip に置いておくので、気が向いたら使ってください。
パスが通っているところにおいておくと、すぐに使える。
16進数かどうか判定して、10進数でも使えるともう少し便利になるかも。

投稿者 Takenori : 19:53 | トラックバック

2008年05月02日

OpenEXR のライブラリ

HDR 画像を格納するためのファイルフォーマットとして、OpenEXR がある。
このファイルフォーマットは、任意のチャンネルをいくつも格納できる柔軟なファイルフォーマットらしい。
ミップマップやレイヤーもサポートするようだ。
他にHDR 画像を格納するファイルフォーマットに hdr ファイルフォーマットがある。
こちらは RGBE で HDR 画像を格納するだけの比較的シンプルな構造。

OpenEXR ファイルの構造は詳しくは調べていないが、読み書きするためのライブラリがオープンソースで存在し、そちらを使えば良いようだ。
ライブラリはこのページにある。
ただ、単純にファイルの読み書きをしたいだけなのに DLL が5個に分かれていて、しかも全て合わせると 2.28 MB とでかい。
いったい何が入っているのか気になり少し見てみることにした。

Half.dll
16ビット浮動小数点を扱うためのクラスライブラリのよう。
HDRI 以外に、最近のビデオカードは16ビット浮動小数点型のサーフェイスを作れるので、それを扱う時に使えるかも。
CPU で処理する場合は普通に float ( 32ビット浮動小数点 ) に変換して処理した方が楽で速いと思う。

Iex.dll
例外やエラー関係

Imath.dll
ベクトルやマトリックス、その他幾何学系
色や色空間の変換など
3D 関係で取り扱う数学関係のもののよう。

IlmThread.dll
スレッド、ミューテックス、セマフォ、スレッドプール・タスク など

IlmImf.dll
ファイルの読み書きなど、上記のもの以外全般。

ソースを見た感じだと以上のような構成のようだ。

後、仕様などを見ていて気付いたけど、これは 3D CG 用のフォーマットだな。
ディフューズ、スペキュラー、アルファ、RGB、Z などチャンネル別に出力しておき、合成方法を変えることで最終イメージを調整するような使い方を想定しているよう。
単に HDR 画像を格納したいだけの用途では少々オーバースペックな感じがする。
ただまあ HDR 画像を格納するのに hdr ファイル と OpenEXR ファイルの2つが主に使われているようなので、オーバースペック な気がしても関係ない。
それに、とりあえずライブラリを使えば読み書きできるので利用するのは楽。

チャンネル別にいろいろと出力しておき、合成方法を変えて複数のイメージを作り出すと言うのは、ゲームでも使えそうな気がしたけど、いろいろ考えるとそれほど用途はなさそう。
実行時に合成してバリエーションを生むと言っても、大してパターンを作れないような。
数が多くないのなら、はじめから全部最終イメージで持っておけばいいだけだし……

投稿者 Takenori : 19:39 | トラックバック

2012年10月11日

ドット絵の拡大

ドット絵を拡大するときはだいたいニアレストネイバーで拡大して、ドットが残ったようにすることが多い。
バイリニア等他の補間方法で拡大すると汚くなってしまう。
ただ、ドットが残っているようなな拡大だと解像度が増していないので拡大する意味がない。
ドット絵のまま解像度を上げて拡大できないかということで試行錯誤してみた。
その結果が以下の動画。

動画にすればどう処理しているのかわかりやすくて楽しいものになると思ったけど、地味でよく分からない動画になった。
フルスクリーンにして見ると少しずつ角が取れて綺麗になっているのがわかる。
アルゴリズムは人がやる方法をできるだけ近似する方法をとっている。
手作業だと縦1ドット、横4ドットの領域を2倍に拡大する場合、4ドットの階段状にするのと綺麗になるのでそうするはず。
つまり、ドット境界で何ドット連続しているかとどちら向きに傾いているかを判定してその傾きの方に階段状に削っていく。
凸状になっているときは、への字型にして、逆に凹状になっている時はV字型にする。
基本処理はこれだけだけど、その判定が難しい。
完全に同じ色が連続している場合以外に、ドットが綺麗に見れるように少し暗い色や明るい色が入っているケースも考えられるが、グラデーション部分などはその少し違う色は別に扱わなくてはいけない。
これは比較的単純に隣接色の色差があるしきい値を超えるかどうかで判断している。
隣接色が近い色の場合は、グラデーションとみなしてすべて違う色として扱い、遠い色の場合は境界部分として近似色も領域がつながっているとして処理している。
色差は、人間の視覚を近似するよう設計されているLab色空間を用いて、色差の距離で判定している。
目で見た時に明るさを除いて、近い色に見えるかどうかが判断基準とも言える。

削るのは常に暗い色の側になるように処理しているため、内部の線などは細く拡大されてしまう傾向がある。
特に眉毛などにその傾向が顕著に出る。
人は周囲とのバランスや描かれているものから判断するが、それをプログラムで行うのは難しい。
この部分は要改善項目としてある。
また、傾斜が検出できない時などが細部でよく発生するため、細部の拡大はうまくいかないことが多い。
まあ、元々細部の情報は失われてしまっているので、それを復元するのは困難と言うのもあるが。
ただ単純に処理を省いているために元のドットが残ってしまっている部分もあるので、その辺りはもう少し作り込む余地がある。

他のアプローチやそれらとの併用も考えたが、現状上述の方法が最もよい結果を残している。
たとえばバイキュービックで拡大して、元の画像の位置の周囲9ピクセルを見て、最も近い色を選んでやる方法や、拡大後にガウスブラーをかけて同様に周囲9ピクセルから選ぶ方法を試したが、どちらもあまりよい結果にはならなかった。
ガウスブラーの場合輪郭はアルファ値をある程度のしきい値で取捨すると、輪郭はなめらかになる。
ただ、鋭利になって欲しいところが丸くなってしまったりもして、困る場合もありそうだった。
画像は全体的にのっぺりしてしまう。
現状の処理で不向きな部分は、これらガウスブラーやバイキュービックの結果から選択的に使用することでよりよい結果を出せるかもしれない。
テレビなどで行われている超解像処理などは、領域によって処理を分けているようなので、似たような処理を行うのが妥当かもしれない。
周囲の画素と元画像で誤差が大きくなってしまっている領域に対しては、別処理を行うなどが妥当かもしれない。
現状でもまずまずの結果が出ているが、もう少し良好な結果が出るように改善したいところである。

作る前に SmillaEnlargerMugenViewer を試してみたが、どちらもドット絵には不向きであった。

ツールの本体はここに置いている。

投稿者 Takenori : 03:16 | トラックバック

2012年10月14日

ドット絵拡大説明動画

ドット絵をそれっぽく拡大するツールを作ってみたの再生数がすごい伸びていたので追加で2個動画を作った。
1個はGUIで機能追加したのでその辺りの説明、もう1個はなぜ必要かとアルゴリズムの簡単な説明。

前回のじわじわ変わっていく動画があまりにも地味だったので、どんどん拡大しても荒れないところを最初に持ってきた。
見た目ベクター画像のように見えるけど、ドット絵を拡大している。
最近はベクター画像も結構あるのでなにげに見ていたら見逃しそうだけど、ドット絵ということを考えると驚異かも。

バイリニアやバイキュービックなどの拡大方法とごっちゃになっている人のコメントが散見されるので、解決すべき課題などを説明する動画を作った。
後半はアルゴリズムの説明。

最初に上げた動画はすごく地味で、分野的にもかなりニッチなのに再生数が妙に伸びていて謎な状態。
処理途中の動画を作ったら面白そうと思って作ってみたら、期待に反してすごい地味な動画が出来上がったんだけど、逆にそれが奏功したのかな。

投稿者 Takenori : 13:49 | トラックバック

2012年10月27日

アルファ付き減色ツール

アルファ付きPNGの減色ツール pngquant にGUI付けてみた。
PNGColorReducer.zip

OS が 64bit で、CPU に SSE2 必須。
32bit版とかその辺りは気が向いたらビルドする。

減色すると PNG のアルファ付きパレット形式になるので、読み込み側がそれに対応していないと正しく読めない。
最近ならだいたい大丈夫だと思う。
減色するとファイルサイズがだいぶ小さくなるので、スマフォとかのデータ用に役立つと思う。
できるだけ小さくしたいから。
減色後の見た目も結構綺麗。
少しおかしなところがある場合は、パラメータやアルゴリズム変更すると解消したりする。

投稿者 Takenori : 01:16 | トラックバック

アルファ付き減色ツール2

PNG Color Reducer
ZIP置くだけじゃなくて、公開ページも作った。
32bit 版もビルドした。
パレット末尾に透明色を置くオプション追加と複数ファイルドロップ対応して、少し使い勝手を上げた。
複数ファイルドロップは自動保存にチェックを入れた場合のみ有効。
別スレッドにはしていないので、UI が固まるけどそこは無視してください。
一応処理しながら表示画像は更新していくので、進んでいるのはわかる。

処理を別スレッドにして UI の応答性能上げて、スライダーの変更に追従すればもっと使い勝手上がるんだろうけど、とりあえずは今の形でもまずまず使えるのでしばらくはこのままかな。

投稿者 Takenori : 14:42 | トラックバック

2012年11月05日

改良版アルファ付き減色ツールの説明

減色ツールを改良したものの説明動画を作った。
まだツール本体は公開していないけど。
PCRtutorial.zip(16.5MB)
YouTube だと細部が確認できないので、元動画をここに置く。
この動画であれば細部もわかるはず。

投稿者 Takenori : 17:37 | トラックバック

2012年11月07日

改良版アルファ付き減色ツールの公開

PNG Color Reducer Pro
公開した。
保存機能を使用するには有料のライセンスキーが必要です。

投稿者 Takenori : 15:59 | トラックバック

2014年07月04日

GIFアニメ変換ツール

Twitter で GIF アニメがサポートされたので、色々な動画を Twitter のタイムライン上で表示できると面白いと言うことで作った。
YabutaGIF - 動画GIFアニメ変換ツール -


実際に試すとノベルゲームなどはタイムライン上でゲームが動いているように感じる動き。
Twitter の GIF アニメは 3MB と言う制限があるのであまり長いものは出来ないが、上記ツールで変換すると意外と圧縮されるのでそこそこの長さでも行ける。
そんなに長くないことも分かっているし、再生されると文字を追ってしまうので、短編を公開するのにもいいかもしれない。
ゲームの一部分を貼り付けて宣伝などもできそう。
もちろんノベルゲームに限らず動くものなら何でも同じようにできそう。

変換元画像は出来るだけ可逆圧縮を使った方がいい。
その方が圧縮が効きやすいし、変換後綺麗。
今の PC 環境なら連番 BMP で保存しても意外と間に合う。

吉里吉里Z で連番 BMP 保存するスクリプトはこんな感じ AfterInit.zip


デバッグメニューに●録画が増えるので、それを選んで開始、選ぶと■停止に代わるから、それを選ぶと停止。
吉里吉里2 でも動くと思うけど未確認。
後、System.setArgument("-contfreq", 25); など指定して FPS を制限しておいた方がいい。

吉里吉里以外では、何か可逆圧縮で保存してくれるキャプチャーツールがあると良さそうだけど、あるかどうかは知らない。
不可逆圧縮で保存されるのはあるけど。

GIFアニメは、1/100秒単位なので、その単位で表せない FPS は等間隔にはならない。
少しずれる。
Twitter に上げる時どれくらいの FPS がいいかはまだ手探り。
今のところ 20 か 25 辺りにしている。

ツール自体もう少し圧縮率上げられそうな改善の余地はある。
後、色数も固定ではなく、減らせるとさらに圧縮できるはず。
見た目劣化するが、サイズを 3MB に納めたい時のオプションとしてあってもいい。

投稿者 Takenori : 14:03 | トラックバック

2015年02月19日

Amazon 購入履歴ダウンローダの更新と課題

以前作ったものが、Amazon側の表示が変わって取得できなくなっていたから更新した。
後、Kindleだけだったけど、Amazonで購入したもの全ての履歴をダウンロードするように対応した。
何度か更新するようになったので、ブログエントリーからダウンロードページを分離した。
前のと違って注文履歴ページから注文番号を取得したのち、順次明細を取得する形にした。


使うとわかるが一つ問題がある。
注文日と発送日が離れている場合、再取得が面倒。
Amazonの注文履歴は、注文日の降順で表示されるので、予約商品など注文日のかなり後に発送される場合、かなりのページさかのぼって読み込まないと取得できない。
注文日に決済されるので、注文日を知りたいわけだけど、この辺りがちょっと面倒なことになっている。
注文日降順ソートで取得できれば問題なくなるのだが……

これを解決するには今のところローカルで注文履歴を保持しておくしかない。
ローカルで保持すれば、注文番号の差分で取得したのち、未発送の商品がある注文を再取得する差分取得が可能になる。
読み込みも待ち時間も少なくなっていいのだが、Excelへ書き出すデータの選択やローカルデータの表示、HTMLが変更された時におかしなデータが取り込まれるとローカルのDBが壊れるなどの問題が出てくる。
ローカルDB壊れたら捨てて再取得すればいいだけではあるが、これら対応するのに1日程度かかりそうなのが難点。

投稿者 Takenori : 22:00

2015年10月06日

タイトルでシリーズ名や巻数を推定する

角川半額タイトルをまとめて表示するためにタイトルからシリーズと巻数を得て並べた。
ある程度予想していたけど、いろいろと細かい処理が必要。

角川全般がそうなのか、今回の半額タイトルに限ったことかはわからないが、シリーズ名は<>で囲われるルールがあるようで、シリーズ名を得るのは楽だった。
他の出版社のタイトルなど見ると、タイトル+巻数のみだったりもするので、そういうものからもシリーズ名を得られるようにするにはもう一工夫必要。

シリーズ名以外にもレーベル名が末尾に()としてつくようだ。
つまり、タイトル名 巻数 <シリーズ名> (レーベル名)と言うのが今回に限っての基本ルールの様子。
シリーズ名とレーベル名で分類すれば、シリーズでまとめて表示することが出来るので、ここまでは簡単。
一部表記ゆれの問題でうまく行っていないものもあったが、ごく少数だったので今回は無視した。
具体的には「目玉焼きの黄身 いつつぶす?」の2巻だけ、? が全角の?になっていてまとめられていない。
表記ゆれの問題はまた今度対策を試みてみたいところ。

巻数を得るのが意外と厄介。
シリーズ名とレーベル名を除いた残りから数字を取り出してくる方法である程度もってこれるが、これだとタイトル中の数値なども拾ってしまう。
また、数値ではなくサブタイトルで分かれているケースもある。
比較的うまく取れるのは、同シリーズ内のタイトルで差分を取って、残りを巻数とする方法。
これでほぼ取れる。
巻数で並べるのは、タイトルでのソートでは不十分。
よくある 1、10、2、3 的な並びになる。
数値は数値として認識して並べる必要がある。
これはアラビア数字以外に漢数字、ローマ数字から数値を得るようにすれば、ほぼ問題なくできた。
ただし、1巻は数値が付かないケースがある。
○○、○○2、○○3 と言うようにタイトル付けされている本が多い。
これは、差分を取った時にブランクとなった場合は1巻とみなし、他の巻で使われている数値種(アラビア数字、ローマ数字等)の1を割り当てることでうまくいく。

ローマ数字は差分の時うまくいかないケースが残っている。
I II III で差分取ると、最初の I が共通なので消えてしまう。
で、消えても I は上述の処理で 1 が入るけど、他は個1ずれる。
ローマ数字の場合は特殊処理が必要そう。

サブタイトルで分かれている場合はうまくいかない。
最初の方にある「ALL AROUND TYPE-MOON ~アーネンエルベ - Bすけ、 TYPE-MOON | ( の日常 ) | ( 狂詩曲 )」がそう。アーネンエルベの日常 と アーネンエルベ狂詩曲 で単純比較だから後半部分だけとれておかしなことになっている。
この辺りは形態素解析か、単純に文字種(記号、アルファベット、漢字平仮名等)から区切りを見つけて、その位置で分離するなどしないとうまくいかなさそう。
ただ、今回は入っていないがぼくらのシリーズのようにぼくらの○○と言うような強敵も存在する。
サブタイトルの場合は、単純ソートだが発行日に差があるのなら、それを利用してソートした方がより精度が上がりそうだ。
一気に電子化されてその日付が入っている場合もあるので、その時はお手上げだが。
後、2巻(1)、2巻(2)などと言うナンバリングされているものもあって、二段階のナンバリングも考慮しないと難しいようだ。
後半の()内の数値が10に届かないなら文字ソートでもうまく並ぶが。

表示するタイトル名はシリーズ名とすると不都合がある場合もあるようなので、シリーズ名をそのまま使うのは好ましくなさそうだった。
【「○○」シリーズ】といったシリーズ名がつけられていたりするから。
そのため、複数巻で前から比較して相違がある場所までをタイトルとすればおおむねうまく行く。
ただ、○○(1)、○○(2) などで差分を取ると ○○( が残るので、末尾の ( などは取り除く。
空になったりした場合はシリーズ名や1巻のタイトルをそのまま使う。

このような処理をしてまとめてみたが、そこそこうまくまとめて表示できた。
対処療法的に処理を追加していき、綺麗に分類されると楽しいので、時間ある時にまた遊んでみたいところ。
抜けるケースを潰すではなく、スマートに処理できる方法があるといいんだろうけど、泥臭い方法も悪くない。

投稿者 Takenori : 17:49

 
Total : Today : Yesterday :