今度はバイキュービック法を試してみました。バイキュービック法は元画像のポイントの周囲16ピクセルも調べて、それぞれのピクセルの色に距離による重みを加えた上で新しい画像のポイントの色にするというやり方です。当然、ニアレストネイバー法よりもコストがかかります。
一般的にバイキュービックは以下のような式で重みを導き出します。
t : ターゲットの座標と対象のピクセルとの距離
0<= |t| < 1 なら
(a + 2)|t|^3 - (a + 3)|t|^2 + 1
1<= |t| < 2 なら
a|t|^3 - 5a|t|^2 + 8a|t| - 4a
2 <= |t| なら
0
aは定数で、-1としている人が多いようです。ただ、ぼくが試したときは画像によってノイズが出るときがありました。-0.5くらいがいいのかも。また、処理速度もかなり遅くなります。その上、ぼくが見るかぎりはニアレストネイバー法と大きく差違を感じませんでした。高速にサムネイルを作成するという目的から、ニアレストネイバー法を採用しようと思います。
ちなみにバイキュービック法によるサムネイルの作成のコードは以下のような感じです。
// 重み計算
double color_weight(double origin, int target) {
double distance = fabs(origin - target);
double weight = 0.0;
if (distance < 1.0) {
weight = (CONST_W + 2) * pow(distance, 3.0) - (CONST_W + 3) * pow(distance, 2.0) + 1;
} else if(distance < 2.0) {
weight = CONST_W * pow(distance, 3.0) - 5 * CONST_W * pow(distance, 2.0) + 8 * CONST_W * distance - 4 * CONST_W;
}
return weight;
}
// バイキュービック法による計算
void bicubic(JSAMPARRAY in_buffer, JSAMPARRAY out_buffer, int origin_width, int origin_height, int x, int y, double origin_x, double origin_y, int components) {
int nearest_x = (int)floor(origin_x);
int nearest_y = (int)floor(origin_y);
double red = 0.0;
double blue = 0.0;
double green = 0.0;
for(int round_x = nearest_x -1; round_x <= nearest_x + 2; round_x++){
for(int round_y = nearest_y - 1; round_y <= nearest_y + 2; round_y++){
if(round_x >= 0 && round_x < origin_width && round_y >= 0 && round_y < origin_height) {
double weight_x = color_weight(origin_x, round_x);
double weight_y = color_weight(origin_y, round_y);
red += in_buffer[round_y][round_x * components] * weight_x * weight_y;
green += in_buffer[round_y][round_x * components + 1] * weight_x * weight_y;
blue += in_buffer[round_y][round_x * components + 2] * weight_x * weight_y;
}
}
}
int n_red = (int)floor(red + 0.5);
int n_green = (int)floor(green + 0.5);
int n_blue = (int)floor(blue + 0.5);
n_red = n_red > 255 ? 255 : n_red;
n_green = n_green > 255 ? 255 : n_green;
n_blue = n_blue > 255 ? 255 : n_blue;
out_buffer[y][x * components] = n_red;
out_buffer[y][x * components + 1] = n_green;
out_buffer[y][x * components + 2] = n_blue;
}
// サムネイル生成
void create_thumbnail(JSAMPARRAY in_buffer, JSAMPARRAY out_buffer, int origin_width, int origin_height, int width, int height, int components, int algorithm) {
double width_factor = (double)origin_width / width;
double height_factor = (double)origin_height / height;
double width_pos[width];
for(int x = 0; x < width; x++) {
width_pos[x] = x * width_factor;
}
for(int y = 0; y < height; y++) {
double height_pos = y * height_factor;
for(int x = 0; x < width; x++) {
bicubic(in_buffer, out_buffer, origin_width, origin_height, x, y, width_pos[x], height_pos, components);
}
}
}
今まで作ってきたspeedpetalをRubyの拡張ライブラリSpeedpetalとして作ってみた。
require 'speedpetal'
Speedpetal::resize(100, "in.jpg", "out.jpg")
Speedpetal::resize_square(100, "in.jpg", "out_square.jpg")
という感じでモジュールメソッドで作りました。まだコードが汚いし、速度のチューニングをしていない素の状態なので、そのあたりが一段落したらオープンソースで出そうかと思います。本当は今日中に出すつもりだったのですが、ユルさんのためにカレーを作るという大切な仕事を優先したのでした。
とりあえず現時点でのベンチマークです。
RMagickの場合
user system total real
RMagick 4.340000 2.410000 6.750000 ( 6.779993)
avg 0.043400 0.024100 0.067500 ( 0.067800)
Speedpetalの場合
user system total real
speedpetal 0.400000 0.040000 0.440000 ( 0.469999)
avg 0.004000 0.000400 0.004400 ( 0.004700)
14.4倍ほど、RMagickより高速です。まだまだいけそう。
仕事で作ってもなかなか表に出せなかったりします。個人的に使いたかったりするのですが。これからは仕事でツールやライブラリを作るのはやめておきましょう。
speedpetalはRubyのライブラリとして公開したあとは、apacheのモジュールにしてみようかと思います。もともとは会社の同僚の人のアイデアなのです。
webサービスなどよりも、ちょっとしたライブラリとかを作るほうがぼくには合っている気がします。C言語はやっぱり楽しいね。もっとCでRubyのライブラリを作りたいな。
勉強が止まってますが、Erlangもちゃんとやりましょう。
カピバラさんはすばらしいです。長崎に行かなくては。
今日から5時に起きて仕事に行くことにしました。会社に着くのが7時過ぎです。早く行って早く帰りたいお年頃なのだ。いいことがたくさんあるので、最初からやっておけばよかったです。
都内は雨だったのに帰ってきたらめっちゃ雪が降っていて積もったりしていました。しろさんは大丈夫でしょうか。
会社で風邪をうつされました。speedpetalの続きをやろうと思ったけど頭が働かなくなっていたのでやめました。カピバラを見て英気を養います。
Speedpetalは正方形サムネイル生成処理のリファクタリングを行いました。gemファイルにしてrubyforgeにアップしようと思っているのですが、すっかりやり方を忘れていて途方にくれてます。時間がないので今日はここまで。
帰ってきたらユルさんからプログラミング禁止令が発令されてしまいしまた。暴君による戒厳令みたい。しょうがないのでニコニコでも見てすごします。
ライブラリやツールは個人的に作ったほうがよい、などと言っておきながら、舌の根も乾かぬうちに仕事でRailsプラグインを作ったりしています。こういうものって、やっぱり仕事だからこそ欲しい機能に気づかされるという面があって、そうすると家に帰って作るのなんて待ってられないから仕事中に作ってしまうのでした。
作ったプラグインはコントローラ中でアクションメソッドと例外処理を分離して記述できるという単純なもの。rescue_fromみたいに例外単位に補足するようなものではなく、アクション単位で分けて書けるというだけです。開発していて、例外処理がアクションメソッドのお尻についているのがどうも汚く思えて我慢ならなくなってしまったのでした。作ってから、絶対どこかで誰かが作ってそうだなと思い至りましたが、おもしろかったのでよしとしましょう。
Rails2.3もそろそろリリースでしょうか。kaeruspoonはRailsを継続してバージョンアップしてきましたが、2.3が出たら新規に作り直そうかと思います。
Speedpetalは高速にサムネイル画像を生成できるRubyのライブラリです。現在のところ、jpegのみに対応しています。
1.インストール
sudo gem install tsukasaoishi-speedpetal
gemのソースにGitHubを加えていないときは以下を先に実行してください。
gem sources -a http://gems.github.com
2.使い方
以前にも書きましたが、以下のとおりです。
require 'rubygems'
require 'speedpetal'
Speedpetal::resize(100, "in.jpg", "out.jpg")
Speedpetal::resize_square(100, "in.jpg", "out_square.jpg")
簡単。
Rubyforgeに絶望してGitHubで公開しました。GitHubはシンプルで触り心地がいいですね。ぼくのソースは全部GitHubに集約しようっと。
高校生の頃、みんながこれを持っていました。ぼくは小食なのでこんなに大きな弁当は食べられないのですが、ちょっとうらやましくも思っていたりしました。
アメリカで象印の保温弁当箱「Mr. Bento」が大好評【米amazon顧客レビュー】
正式リリースを待てなかったので、RC2をインストールしました。最近、Railsの新機能などを追いかけていなかったのでここら辺で一気に取り返そうと思います。
sudo gem install rails --source http://gems.rubyonrails.org
Rails2.3からRackを使うようになったので、当然Rackも必要です。ぼくはすでにRackを持っているのですが、持っていない人は
sudo gem install rack
でインストールしてください。
どうも風邪がなおらなくてずっとダルい今日この頃です。ぼくは風邪をひくと熱いお風呂に入って汗をかいて毛布いっぱいくるまって汗をかいてとにかく水を大量に飲んで汗をかいてなおしていたりしたのですが、最近は年のせいかそういうヘヴィなことができなくなってきています。なので薬を飲んでごまかしつつなおしているのです。
過去に書きためた小説をまとめて文庫本でも作ろうかなと思っていたりします。
会社の冷蔵庫に納品されるアイスが本当にふざけています。ぷんぷん。パナップをどうしていれないのか。かといって、グリコのお姉さんに「パナップをいれてください」とお願いするのも年齢が年齢だけに恥ずかしくてできない今日この頃です。
今年の夏くらいにサーバを一新したいなと考えています。静音・省電力・省スペースで作りましょう。そういえば、昔つかっていたVAIO typeFというノートPCが余っているですが、微妙に使い道がなくて困っています。分散処理でもさせようかな。
毎朝、MXTVのTOKYOモーニングサプリを観るのが楽しみです。
Rails2.3のApplication Templateを使ってRspecやI18nの設定を自動化 - func09
ここの記事を参考にAplicationTemplateを使ってみました。Railsを使うときのもろもろの初期設定がコマンド一発で終わるので超便利です。
めちゃくちゃカッコいい。欲しいです。

- Railsデプロイ
- 作者/アーティスト: Ezra Zygmuntowicz,Bruce A. Tate,Clinton Begin
- 出版社/メーカー: オライリージャパン
- メディア: 大型本
- 発売日: 2009-03-16
自分メモ:今月はこれを買おう。
TokyoTyrantをActiveRecordのように気軽に使える、MiyazakiResistanceを作りました。Cabinet(内閣)、Tyrant(傀儡)という意味を見て、地方から傀儡政権に抵抗するというイメージが勝手にわいてきたのでした。
MiyazakiResistanceは、TokyoTyrantのテーブルデータベースを対象にしています。データスキーマのないTokyoTyrantに、あえてスキーマの制約を与えることで管理をしやすくしています。TokyoTyrantでは異なる種類のデータをひとつのデータベースに置くことはあまりないと想定してのことです(キー値がかぶるし)。
MiyazakiResistanceは、レプリケーションにも対応しています。マスターと複数のスレーブ構成でも、デュアルマスタ構成でも大丈夫です。デュアルマスタはアクティブ-スタンバイで運用することを想定していて、アクティブ側がタイムアウトしたときはスタンバイ側にフェイルオーバーします。
まだ開発中なのでバグはあちらこちらに残っているのですが、適宜解消していく予定です。
github上で公開しています。http://github.com/tsukasaoishi/miyazakiresistance/tree/master
1.インストール(gemcutterで公開しています)
sudo gem install miyazakiresistance
2.モデルクラスの定義
require 'rubygems'
require 'miyzakiresistance'
class Example < MiyazakiResistance::Base
set_server "localhost", 1975, :write
set_timeout 60
set_column :name, :string
set_column :age, :string
set_column :created_at, :datetime
end
MiyazakiResistance::Baseを継承して定義します。
set_server でTokyoTyrantのサーバーを指定します。第一引数がホスト名、第二引数がポート番号です。第三引数はオプションで指定しなければ:readonlyと解釈されます。:writeは読み書き両用のサーバーです。
マスターと複数のスレーブで構成するときは、
set_server "master", 1975, :write
set_server "slave1", 1975
set_server "slave2", 1975
と宣言します。読みのときにどのサーバーが選択されるかはランダムに決定されます。
デュアルマスタ構成のときは、
set_server "active", 1975, :write
set_server "standby", 1975, :standby
:standbyで指定されたサーバには、読み込みのみアクセスします。さらにフェイルオーバーの対象に指定されます。
set_timeout でタイムアウト値を宣言します。単位は秒です。タイムアウト値を超えると、対象のサーバーはコネクションプールから排除されます。対象のサーバーがマスタの場合、デュアルマスタでスタンバイが存在しているときはフェイルオーバーします。スタンバイが存在しなければ例外を吐きます。
set_columnでデータ構造を宣言します。キー値はself.idとして規約で定義されているので宣言の必要はありません。第一引数にカラム名、第二引数にデータの型を指定します。データの型は、:string(文字列)、:integer(数値)、:date(日付)、:datetime(日時時刻)の4種類です。:integerは実際には実数でも問題ありません。:dateはDateクラス、:datetimeはTimeクラスに対応しています。
実際のCRUD操作はActiveRecordとほぼ同様です。conditionsの書き方だけが少し違っていて、条件同士は必ずAND結合されるので、条件の間にAND, ORなどを書く必要はありません。
work = Example.new(:name => "tsukasa")
work.age = 34
work.save
Example.count
Example.find(:all, :conditions => ["name = ? age = ?", "tsukasa", 34], :order => "created_at DESC", :limit => 10)
TokyoTyrantのオプティマイザは最初に型が一致したカラムのインデックスを使用します。
Example.find(:all, :conditions => ["created_at >= ? created_at <= ?", yesterday, today])
だと、最初の created_at > ? にはインデックスが適用されますが、二つ目の条件 created_at < ?にはインデックスが適用されません。この場合は、
Example.find(:all, :conditions => ["created_at between ?", [yesterday, today]])
とすればいいでしょう。
すげー
090325golden.pdf (application/pdf オブジェクト)
もう観られないと思ってショボーンとしてたら、こんなピックニュースが。あのお別れ会みたいな雰囲気はなんだったんだろう。朝だと出勤の時間もあって途中までしか観られなかったんだけど、夜ならずっと観られるね。すばらしい。楽しみです。
kaeruspoonのDBのバックアップはmysqldumpで定期的に行っていたのだけど、開発環境のcoLinux上のMySQLをスレーブにして、レプリケーションでバックアップを取ることにしました。
MySQLのレプリケーションに最近ほとんど触っていなかったので、すっかりいろいろ忘れていました。定期的に触ってないといけませんね。
ついでに、リンク元の集計に使っているTokyoTyrantも、スレーブをcoLinux上に置いてバックアップをとることにしました。