類似画像を検索する方法を調べみた その1

Google画像検索のような類似画像を検索する方法を調べてみた。

ぱっと思いつくのは、色や形状などの特徴量を比較し、近似しているものを調べていく方法だ。
この手の情報やライブラリは、けっこう出回っているので、一番お手軽にできそうだ。

他のアプローチとしては、特徴量が近似していないものを探しいくことで、類似性を判定する方法もあるそうだ。

画像同士をつきあわせて類似性を判定するのではなく、彼らは問題をまったく別の角度から捉えた。彼らの方法では、ターゲットの画像(A)を大量のランダムな画像と比較して、それらと当の画像との、もっとも著しい違いを記録する(Ra)。そして、もう一つのターゲット画像(B)に対しても同じ記録を作成する(Rb)。この、RaとRbがほぼ同じなら、画像AとBは類似性が高いだろう。

重要なのは'類似'ではなく'違い'だ–まったく新しい着想に基づく高精度の画像検索アルゴリズム | TechCrunch Japan

画像検索とは

テキストキーワードを用いる方法を、TBIR(Text Based Image Retrieval)、
画像特徴を用いる方法を、CBIR(Content Based Image Retrieval)と呼ぶみたいだ。

TBIRは、画像にキーワードを紐付けて、そのキーワードに類似している画像を検索する方法とのこと。
今回はCBIRについて調べていきたいと思う。

形状特徴を利用する方法

検索していると「モノクロ画像検索のための形状特徴」という論文を発見した。
SobelやCannyフィルタを使ってエッジの抽出をおこなった画像を、3×3に分割し、各領域のエッジ割合を比較する、という方法のようだ。

気になったのがaHash(AverageHash)という方法だ。

この手法は「Perceptual Hash」という、「比較可能なハッシュ」を生成するための一手法です。

一般的にMD5SHA1などのハッシュ値は、1バイトでもデータが違えば、まったく違うハッシュ値を返してきますが、「Perceptual Hash」は似たようなデータには似たようなハッシュ値を返してきます。

簡単な画像の類似度計算手法「Average Hash」 » Untitled Blog

どんな方法だろうと見てみると、グレースケール化した画像を8×8ピクセルに縮小化し、色の濃淡でビット列を作成する、ということらしい。

「Average Hash」は以下のような手順で生成することができます。

画像のサイズを縮小 (ブログによれば8×8に縮小)
色をグレースケールにする
画像の各ビクセルを使って色の平均値を計算
それぞれのピクセルで色の濃淡を調べ、その色が平均値よりも濃い場合は1を、薄い場合は0を設定する
結果的に8×8=64ビットのビット列ができあがる

pHashというものもあるらしく、DCT(離散コサイン変換)を使っているらしい。
こちらはライブラリ化しており、下記のサイトからダウンロードできるが、ライセンスはGPLとのこと。
pHash.org: Home of pHash, the open source perceptual hash library

libpuzzle

libpuzzleというライブラリが有名なようで、使っている記事がたくさんヒットした。
PHPから簡単に使えるようになっているらしく、BSDライセンスで扱いやすそうだ。
ラッパーを作れば他の言語からも利用できると思う。
Libpuzzle – A library to find similar pictures

しかもけっこう速そうだ。

なお、うちのCoreSolo(初代Intel Mac mini)な環境では、
シグネチャの生成x100画像で大体1分~1分半。
近似度判定x10000回で大体2~3秒だったので、
シグネチャをどっかに保存しておけば、10万画像の線形比較くらいなら許容範囲ではないかと思われる。

OpenCVをいじってみた【後編】

前回の記事ではサンプル画像を集めるところまで書いた。
ポジティブサンプルをコツコツ処理するのは面倒なので、opencv_createsamplesコマンドを使って大量のサンプル画像を作ってみた。

opencv_createsamplesを使う

opencv_createsamplesコマンドの使い方は簡単だ。引数なしで実行すると以下のような引数の説明が表示される。

$ opencv_createsamples
Usage: opencv_createsamples
[-info <collection_file_name>]
[-img <image_file_name>]
[-vec <vec_file_name>]
[-bg <background_file_name>]
[-num <number_of_samples = 1000>]
[-bgcolor <background_color = 0>]
[-inv] [-randinv] [-bgthresh <background_color_threshold = 80>]
[-maxidev <max_intensity_deviation = 40>]
[-maxxangle <max_x_rotation_angle = 1.100000>]
[-maxyangle <max_y_rotation_angle = 1.100000>]
[-maxzangle <max_z_rotation_angle = 0.500000>]
[-show [<scale = 4.000000>]]
[-w <sample_width = 24>]
[-h <sample_height = 24>]

「-info」と「-img」はどちらか一方を指定するようで、ネガティブサンプルとポジティブサンプルを組み合わせる場合は「-img」を使用する。
サンプルデータは「-vec」引数で指定したファイルに出力されて、この出力されたサンプルダータをもとに学習処理をおこなう。
「-bg」で指定するネガティブサンプルは、画像ファイルのパスをリスト化したものを指定する。

findコマンドを利用することで簡単に画像ファイルのパスをリスト化することができる。

$ find negative_images/ -name '*.jpg' > negative_images.txt

以下のような引数を指定してサンプルデータを作成した。
「-num」が生成するサンプルデータ数になり、「-bgcolor」は背景とみなす色を指定するようだ。今回は255(白)を指定した。また、「-bgthresh」は背景色とみなす範囲?ということで5を指定してみた。

$ opencv_createsamples -vec positive.vec -img positive_images/1.jpg -bg negative_images.txt -num 7000 -bgcolor 255 -bgthresh 5 -w 24 -h 24

無事にポジティブサンプルの生成に成功したはいいが、これをもとに学習させてみてもいい結果を出すことは出来なかった。
たぶんこの方法でポジティブサンプルを用意する場合、文字など検出対象物として認識しやすいようなものではないとだめなのかもしれない。

地道にポジティブサンプルを集める

やっぱり地道に集めるしかないと思いつつ、なるべく楽に集められないか色々調べてみることにした。
あれこれ調べていると以下のサイトを見つけた。どうやらOpenCVでアニメの顔や目を認識させる、ということをやっているようだ。
anime.udp.jp

ここで公開されている便利な加工ツール「SC」を見つけたので使ってみることにした。ちなみにWindowsアプリのみのようだ。
加工元の画像はネガティブサンプルを集める時と同じように、Yahoo画像検索のAPIを利用して集めた。ひたすらカレーライスだけ切り出すのは正直しんどかった。
そして、色々なカレーライスの画像を見ていくうちに、これを認識させるのはかなり難しいのではないかと思い始めた…。

意外にカレーライスの画像は少なくて、どれも同じような画像が多いので結局800枚ほどしか用意することはできなかった。
これも同じようにopencv_createsamplesコマンドを利用してサンプルデータ化する。先ほどと違うのは、「-img」ではなく、「-info」引数を利用することだ。
「-info」には、「-bg」と同じくリスト化したファイルを指定する。この時に注意したいのは、単純に画像ファイルのパスを指定するだけではなく、「その画像に含まれる検出対象物の数」と「座標」、「大きさ」も書き出す必要があるようだ。

ImageMagickに含まれる「identify」コマンドを使うことで画像の大きさが分かるので、findコマンドと組み合わせて書き出してやればいいようだ。

$ find positive_images/ -name '*.png' -exec identify -format '%i 1 0 0 %w %h' \{\} \; > positive_images.txt
$ opencv_createsamples -vec positive.vec -info positive_images.txt -num 800 -bgcolor 255 -bgthresh 5 -w 24 -h 24

サンプルデータから学習させる

opencv_haartrainingコマンドか、opencv_traincascadeコマンドを利用してサンプルデータを学習させる。opencv_traincascadeコマンドの方が新しいようで、マルチコア対応やLBPにも対応しているのでこちらを利用するのがいいようだ。しかし、学習後に生成されるファイルも新しいフォーマットになるため、opencv_preformanceコマンドで利用することができないようなので注意。

opencv_traincascadeコマンドを引数なしで実行した結果は以下のようになる。

Usage: opencv_traincascade
-data <cascade_dir_name>
-vec <vec_file_name>
-bg <background_file_name>
[-numPos <number_of_positive_samples = 2000>]
[-numNeg <number_of_negative_samples = 1000>]
[-numStages <number_of_stages = 20>]
[-precalcValBufSize <precalculated_vals_buffer_size_in_Mb = 256>]
[-precalcIdxBufSize <precalculated_idxs_buffer_size_in_Mb = 256>]
[-baseFormatSave]
--cascadeParams--
[-stageType <BOOST(default)>]
[-featureType <{HAAR(default), LBP, HOG}>]
[-w <sampleWidth = 24>]
[-h <sampleHeight = 24>]
--boostParams--
[-bt <{DAB, RAB, LB, GAB(default)}>]
[-minHitRate <min_hit_rate> = 0.995>]
[-maxFalseAlarmRate <max_false_alarm_rate = 0.5>]
[-weightTrimRate <weight_trim_rate = 0.95>]
[-maxDepth <max_depth_of_weak_tree = 1>]
[-maxWeakCount <max_weak_tree_count = 100>]
--haarFeatureParams--
[-mode <BASIC(default) | CORE | ALL
--lbpFeatureParams--
--HOGFeatureParams--

「-data」で指定したパスに分類器が生成される。「-featureType」で学習方法を指定できるが、今回はLBPを利用することにした。

以下のような引数を指定して分類器を作成してみた。
「-numPos」でポジティブサンプルの数を指定するが、バグがあるようできっちり実際の数を指定すると途中で失敗してしまうようだ。実際の数より少なめに指定することで、失敗せずに実行できる。
OpenCV2.4.0でHAAR分類器を学習させるときの問題と対策 (ゆめ技:ゆめみスタッフブログ)

$ opencv_traincascade -data ./data -vec positive.vec -bg negative_images.txt -numPos 750 -numNeg 3000 -numStages 20 -mode ALL -w 24 -h 24 -precalcValBufSize 5000 -precalcIdxBufSize 1000 -featureType LBP

「-data」引数で指定したパスに「cascade.xml」が生成されるので、これが分類器となる。

作った分類器を試してみる

さっそく作った分類器を試してみた。結果からお伝えするとかなり認識率が低い…というかほぼ認識しない。
学習データが少ないのか、やり方が間違っているのか分からない。

OpenCVのサンプルプログラムを利用して作った分類器を試してみた。
引数に認識させたい画像ファイルを指定すると、認識された部分が緑の線で囲われて表示されるものだ。

#include "opencv2/objdetect/objdetect.hpp"
#include "opencv2/highgui/highgui.hpp"
#include <iostream>
#include <stdio.h>
using namespace std;
using namespace cv;
int main(int argc, char** argv) {
Mat image = imread(argv[1]);
String cascadeName = "cascade.xml";
CascadeClassifier cascade;
cascade.load(cascadeName);
vector<Rect> komas;
cascade.detectMultiScale(image, komas);
for (vector<Rect>::iterator it=komas.begin(); it!=komas.end(); ++it) {
rectangle(image, Rect( it->x, it->y, it->width, it->height),
Scalar(0,255,0));
}
cout << "count: " << komas.size() << endl;
namedWindow("result", CV_WINDOW_AUTOSIZE | CV_WINDOW_FREERATIO);
imshow("result", image);
waitKey(0);
return 0;
}

以下のコマンドでコンパイルして実行した。

$ g++ -I/opt/local/include -L/opt/local/lib -lm -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_objdetect -g -o sample sample.cpp
$ ./sample test.jpg // 実行

結果はというと…。
f:id:happytar0:20120708193503j:plain
一応成功したっぽいけど、もう少し認識率を上げていかないと使いものにならなそう。というか、何に使い道があるんだろ…。

参考サイト
Haar状特徴に基づくブースト分類器のカスケードを利用する高速物体検知 – penny
Tutorial: OpenCV haartraining (Rapid Object Detection With A Cascade of Boosted Classifiers Based on Haar-like Features) – Naotoshi Seo

OpenCVをいじってみた【前編】

しばらくぶりの更新となってしまった…少しずつでも書いていかなきゃだめですね。
物体認識を試してみたかったので有名なOpenCVを使ってみた記録。

OpenCVのインストール

Mac環境なのでMacPortsを利用して、OpenCVとObjectMakerをインストールした。ObjectMarkerは画像から必要な部分を切り出せるツールらしい。

# port install opencv objectmarker

ちなみにWindows環境にもインストールしてみたが少し面倒だった。
ここのインストールガイドを参考にするといいようだ。
InstallGuide – OpenCV Wiki

まず、OpenCVをコンパイルする環境が何も入っていなかったので、
VisualStudioC++ 2010 Expressってのをインストール。
Microsoft Visual Studio Express

CMakeも必要なようなので、以下のサイトからCMakeをインストール。
CMake – Cross Platform Make

CMakeGUIでOpenCVのパスを指定して、Configureを実行することで、VisualStudioで開けるファイルが生成されるようだ。
コンパイラにVisualStudio2010 Win64を指定したところ、Expressバージョンには64bitのコンパイラが含まれていないようで、Windows SDK 7.1のインストールも必要になった。
この際、マルチコア環境ならばTBBを有効化した方がいいと思う。(「USE TBB: ON」にする)

あとは、VisualStudioでOpenCVのプロジェクトファイルを開いてコンパイルするだけ。

物体検出するための分類器を作る

人間の顔や目などを検出するための分類器は、サンプルとしてもう用意されているようで、それらをそのまま使えばいいだけのようだ。
それだと面白くないので、カレーライスを検出する分類器を作ってみることにした。

分類器を作る流れをおおまかに説明すると以下のようになる。

  1. ネガティブサンプル(検出対象物が写っていない画像)を用意する。3000枚ほどあると効率的らしい。
  2. ポジティブサンプル(検出対象物が写っている画像)を用意する。こちらは7000枚ほどあると効率的とのこと…多いな。
  3. opencv_traincascadeコマンドを使って分類器を作る。

ここで問題になってくるのは学習させるための画像をどうやって集めてくるかだ。
あまりよくないかもしれないが、Google画像検索などを利用すればそれほど苦労しないと思う。
他には、opencv_createsamplesコマンドを利用する方法があるようで、ネガティブサンプルと一つのポジティブサンプル画像を組み合わせることで、ポジティブサンプル画像をたくさん作り出すことができるようだ。

サンプル画像を集める

あまり大きい声では言えないが、画像検索を使ってサンプル画像を集めることにした。
さすがに手作業では面倒なため、簡易的なスクリプトを組んで一気に処理しようと思ってAPIがあるか調べて見ることに。

Google Image Search APIというのを見つけたが、ずいぶん昔に廃止されたようだ。
Custom Search APIというのもあるようだが、こちらは使用用途が違うようだし、一日100リクエスト?までのようでだめっぽい。

Yahooを調べたところ、画像検索用のAPIを公開していた。
検索:画像検索API – Yahoo!デベロッパーネットワーク
通常は一日1000リクエストまでだが、プレミアム会員だと5万リクエストに緩和されるらしい。プレミアム会員であったのでこれを利用することにした。
(他にはFlickrAPIを利用するのもアリだと思う)

“風景 -カレーライス”
こんな感じのクエリを投げれば、カレーライスを含まないネガティブサンプル画像を集めることが出来るはずなので、
作ったスクリプトを回して3000枚ほど用意することができた。

ポジティブサンプルの用意が少しむずかしい。
検出対象物を含んだものを用意するのは同じ手順でいいはずだが、対象物のみを切り出す必要があるはずで、それが非常にめんどくさい…。
こういう時のためにopencv_createsamplesコマンドがあるのか?と思って使ってみることにした。

長くなりそうなので今回はこのへんで終わり。。。次回へつづく。

MT4iのDoS攻撃されるセキュリティホール

MT4iのベータ版である3.1系をのぞく全てのバージョンで、DoS攻撃されうるセキュリティーホールがあるようです。細工されたリクエストを送信するか、もしくは特定の状況下で無限ループしてしまう事が原因で、HTTPプロセスが常駐し続けてしまいます。

原因

記事本文を分割する際に利用する midb_euc 関数の欠陥が原因のようです。分割位置をGETパラメータとして渡すため、「記事本文の文字数より大きい値が渡された時」に分割が終了せずに無限ループに陥ります。なお、記事本文を分割するバイト数として、初期値では「4096」バイトが設定されています。

以下のように sprtbyte に分割位置がカンマ区切りで渡されます。

http://www.example.com/m/mt4i.cgi?mode=individual&eid=1&sprtpage=1&sprtbyte=0,4096

どういう状況でおこりうるか

MT4iはMovableTypeを携帯で見やすいように変換・表示するためのプログラムです。携帯向けに表示するため、記事本文の表示を分割する必要があります。記事本文が分割バイト数ずつ分割表示されるわけですが、それぞれの分割された記事の位置をGETパラメータで制御しているため、後から記事本文を更新した場合に分割位置が一致せずに、無限ループする可能性があります。
クローラーなどが更新前のURLでアクセスしてきて…なんて事もあるかもしれません。

再現方法

sprtbyte パラメータに記事本文より大きい値を渡すことで再現できます。

問題の箇所

midb_euc 関数のコードを抜き出したものです。2系、3.0系ともに同様の関数が使われているようです。
記事本文より大きい開始位置が指定された場合、43行目の while 文内の substr 関数が常に開始位置より小さい値を返すために無限ループし、さらに68行目の while 文内の substr 関数で、開始位置と終了位置が記事本文より大きい場合、常に空文字を返すので無限ループしてしまいます。

sub midb_euc {
my $llen1;
my $llen2;
my $lstr;
my $lstart;
# 先ず正しい開始位置を求めないと
if ($_[1] == 0) {
$lstart = 0;
} else {
$llen1 = $_[1];
$lstr = substr($_[0], 0, $llen1);
$llen2 = MT4i::Func::lenb_euc($lstr);
my $llen3 = $llen1;
while ($_[1] > $llen2) {
$llen3 = $llen1;
$llen3 += $lstr=~s/(\x8E[\xA1-\xDF])/$1/g;                   # 半角カナ数をプラス
$llen3 += ($lstr=~s/(\x8F[\xA1-\xFE][\xA1-\xFE])/$1/g)*2;    # 3バイト文字数*2をプラス
$lstr = substr($_[0], 0, $llen3);
$llen2 = MT4i::Func::lenb_euc($lstr);
}
$llen1 = $llen3;
# 最後の文字が途切れているか判定する
if ($lstr =~ /\x8F$/ || $lstr =~ tr/\x8E\xA1-\xFE// % 2) {
chop $lstr;
$llen1--;
if($lstr =~ /\x8F$/){
$llen1--;
}
}
$lstart = $llen1;
}
# 文字列の切り出し
$llen1 = $_[2];
$lstr = substr($_[0], $lstart, $llen1);
$llen2 = MT4i::Func::lenb_euc($lstr);
my $llen3;
while ($_[2] > $llen2) {
$llen3 = $llen1;
$llen3 += $lstr=~s/(\x8E[\xA1-\xDF])/$1/g;                   # 半角カナ数をプラス
$llen3 += ($lstr=~s/(\x8F[\xA1-\xFE][\xA1-\xFE])/$1/g)*2;    # 3バイト文字数*2をプラス
$lstr = substr($_[0], $lstart, $llen3);
$llen2 = MT4i::Func::lenb_euc($lstr);
}
$llen1 = $llen3;
# 最後の文字が途切れているか判定する
if ($lstr =~ /\x8F$/ || $lstr =~ tr/\x8E\xA1-\xFE// % 2) {
chop $lstr;
if($lstr =~ /\x8F$/){
chop $lstr;
}
}
return $lstr;
}

対応方法

3.1系では問題となっている関数が置き換わっているため、3.1系を利用することで回避できるようですが、まだベータ版なのでそのあたりも考慮する必要がありそうです。

簡単な Func.pl に対するパッチを作ってみました。各 while 文で記事本文より大きい場合と、空文字の場合にループを抜ける処理を追加しただけです。

※2月5日訂正
while 文でループする意味がよく分からなかったので、一度で抜けるように変更しました。

--- Func.pl.org	2011-02-03 16:43:57.000000000 +0900
+++ Func.pl	2011-02-03 16:19:58.000000000 +0900
@@ -46,6 +46,7 @@
$llen3 += ($lstr=~s/(\x8F[\xA1-\xFE][\xA1-\xFE])/$1/g)*2;    # 3バイト文字数*2をプラス
$lstr = substr($_[0], 0, $llen3);
$llen2 = MT4i::Func::lenb_euc($lstr);
+            last;
}
$llen1 = $llen3;
@@ -71,6 +72,7 @@
$llen3 += ($lstr=~s/(\x8F[\xA1-\xFE][\xA1-\xFE])/$1/g)*2;    # 3バイト文字数*2をプラス
$lstr = substr($_[0], $lstart, $llen3);
$llen2 = MT4i::Func::lenb_euc($lstr);
+        last;
}
$llen1 = $llen3;

実はそんなにたいした事じゃないのかも

共有サーバのほとんどは、長時間実行されているプロセスは強制終了されるものかと思われますので、それほど影響はないのかもしれません。

デタラメ書いてるんじゃねーってことがあれば連絡ください。

ひかりoneギガ得プランのHGW(BL190HW)の性能とその後

ひかりoneのギガ得プランを使い始めて1年が経ちました。実質固定IPという噂でしたが、まさにその通りで一度も変わったことはありません。

一つ問題が出てきたのはルータの性能についてです。ルータについては、このブログに記載してあるようにBL190HWというルータが指定されおり、変更することは一切できません。ひかりoneでは、ルータの事をHGW(ホームゲートウェイ)と呼称しています。

  1. 自分で用意したルータに置き換えることはできない。どうしても使いたい場合は、BL190HWのDMZ機能をブリッジのように使うしかない。
  2. グローバルIPアドレスDHCPを使って払い出される。
  3. MACアドレスとIEEE802.1xを使って認証している。

Webサーバを動作させている状態で、同時接続数300で100Mbpsを超えたあたりからレスポンスがかなり悪くなりました。この状態でルータの管理画面にアクセスすると開くのにも時間がかかっており、やはり負荷が原因だと思いました。

## こんな感じで調べてみた
$ netstat -t | grep "ESTABLISHED" | grep http | wc -l

しかし、ルータが認証の役割も担っているため、そう簡単に交換することはできません。

上述した通り、認証にはMACアドレスとIEEE802.1xが使われています。MACアドレスの偽装については比較的簡単なのですが、IEEE802.1x認証をクリアするのは現状厳しいようです。IEEE802.1x認証でシリアル番号も送信している?
IEEE802.1x認証は、24時間に一度確認しているようなので、一度認証されれば他のルータに置き換える事はできますが、24時間後に切断されます。

どうしようかと考えていた所、もう一台ルータをレンタルする方法がある事を発見しました。
電話サービスの追加など、ホームゲートウェイを複数台ご利用になる場合 | auひかり(auの光ファイバーサービス)

合計2台までのようですが、これでとりあえず問題は解決できそうです。ちなみに2台目のルータにもグローバルIPアドレスが割り当てられます。

  1. HGWは合計2台までしかレンタルできない。
  2. 電話サービスを二つ契約する必要がある。
  3. 2台目のルータにもグローバルIPアドレスがもう一つ払い出される。

もう一台レンタルするためには、電話サービスを二つ契約する必要があるため、525円+472円が月額費用に加算される点は注意が必要です。申込手数料が650円くらいかかりますが、サポートセンターに電話経由で申し込み、三日くらいで新しいルータが到着しました。

現在2台のルータで運用していますが、レスポンスも改善したようです。

HyperMacのバッテリーセルを交換してみた

先日バッテリーリフレッシュさんにお願いしたHyperMacのMBP-060が戻ってきました。

15日に注文して、17日には作業完了のメールが着ていました。到着したのが18日という事を考えるとかなり早いです。
かかった金額は11890円と送料の740円だけでしたので、海外から直接購入するよりずいぶん安くなります。

f:id:happytar0:20100918105400j:image
段ボール箱に入って返送されてきました。

f:id:happytar0:20100918105500j:image
しっかり緩衝材で梱包されていました。

f:id:happytar0:20100918105600j:image
二つ入っていると思ったら、見積りをお願いしたMBP-150でした。

交換したバッテリーは無事に使うことができたのか

このバッテリーセル交換サービスは、動作保証までしていないため、本当に動くのかどうかは使ってみるまで分かりません。

f:id:happytar0:20100918105700j:image
端子がある側面にシールを剥がした跡がありましたので、恐らくここから開腹したのでしょう。通常ならまったく気にならないレベルでした。

さっそくACアダプタを通して充電してみる事にしました。

f:id:happytar0:20100919172100j:image
無事に充電のLEDが点灯してほっとしました。

MacBookProに接続した所、充電中のアイコンになり正常に充電されました。うれしい誤算としては、充電中にあれだけ熱くなっていたHyperMacが、ほとんど熱くならなくなった事です。
長時間使ってみた所、交換前と同じように熱くなりました…

バッテリーリフレッシュサービスは使えるのか

今回利用した限りだと、とても満足できる結果だったので今後も利用してみたいと思いました。もちろん、メリット・デメリットがありますので、デメリットが許容できる範囲内であれば、利用してみてもいいのではないでしょうか。

メリット

  1. 純正品を海外から購入するよりも安い
  2. バッテリーセルが日本製になる(純正品は中国製だと思う)
  3. バッテリーの容量がアップする
  4. バッテリーセルの交換作業も国内なので安心できる

デメリット

  1. 動作が保証されていない
  2. 既にHyperMacを持っている必要がある(当たり前)
  3. 交換するHyperMacを送らないといけない

※バッテリーリフレッシュサービスを利用する際は自己責任でお願いします

今回利用させていただいたバッテリーリフレッシュさんはこちらからどうぞ。
http://www.batt.jp/

Mac専用バッテリーHyperMacを復活させる

HyperMacの寿命はどのくらいなのか

1年ほど前に購入したHyperMacですが、フル充電しているにも関わらず1分くらいでバッテリーが無くなってしまうようになりました。
そんなに使用回数は多くなかった気がします。少なくとも1000回は使っていないでしょう。
使用中は、火傷しそうなくらい熱くなったりしたので色々気になる所はありました。

こんな記事もあるようです。
http://blog.goo.ne.jp/portaledge/e/a3b352cf4e20f7d16ad37d548e5b9809

しかし、Macで使用出来る外部バッテリーはほとんどありません。自分が知っている限り、まともに使えるのはHyperMacくらいなのではないでしょうか。
ちなみにMBP-060とMBP-150を同じ時期に購入しましたが、偶然なのかどちらも使えなくなってしまいました。

まともな外部バッテリーが出てこないのはなぜか

どうしてまともな外部バッテリーが出てこないのか、それはMac独自の電源コネクタ「MagSafe」によるものです。Apple社は、この「MagSafe」の特許を取得しており、他社に対してライセンスする気がないようです。

ではなぜ、HyperMacは「MagSafe」を使用できているのか。Apple社からライセンスを受けている訳ではありません。純正品のMagSafe電源アダプタを加工し、流用することで、ライセンスの問題をクリアしているという事のようです。

中国企業らしいアイディアではありますが、ついにApple社から目をつけられたようです。
Apple、外部バッテリーメーカーを訴える|KODAWARISAN

今後、HyperMacはどうなってしまうのか心配ですが、現在持っているHyperMacを大事に使っていきたい所です。純正の外部バッテリーが出てくれれば、それが一番いいような気もしますが・・・。

外部バッテリーの寿命を延ばすには

大事に使っていきたい所ですが、所詮バッテリーは消耗品なので、時間が経てば使えなくなってしまうのは当然です。ではどうすれば、バッテリーの寿命を少しでも延ばす事ができるのでしょうか。

まず自分の反省を踏まえ、どうしてこんなに早くバッテリーがダメになってしまったのかを考えてみました。

  1. 日差しが入ってくるような室温が高い場所に放置していた。
  2. すぐ使えるように充電コネクタを挿し、いつもフル充電状態にしていた。
  3. 実は1000回近く使っていた。
  4. バッテリーセルが中国クオリティ。

さすがに購入して1年程度で毎日使うような事もしませんでしたので、3.はないでしょう。4.については、中国企業なので、中国製のバッテリーセルを使っていてもおかしくありません。中国製と日本製のものでどこまで品質に差があるのかというのも、素人の僕としては分かりません。

バッテリーの保存環境や使い方によって寿命に変化があるのかが一番気になる所ですが、下記のような衝撃的なサイトを発見しました。

リチウムイオンバッテリー考

「この(リチウムイオン電池の)劣化は満充電で保存すると激しくなり、また保存温度が高いほど劣化が早くすすみます。リチウムイオン電池を長期保存するときにはできるだけ冷暗所に保存し、あまり充電しない状態で保存することをお勧めします。」

長期保管
 例えば、iBookラボを学校で管理していて、夏休み中は使わないなど、ノートブックコンピュータをしばらく使う予定がない場合、アップルではバッテリーを50%充電した状態で保管しておくことをお勧めします。

 もし、バッテリーが完全にゼロになった状態でノートブックコンピュータを保管すると、深い放電状態となり、充電ができなくなってしまう可能性があります。

 逆に、フル充電の状態で長期保管すると、バッテリー容量が失われる、つまり、バッテリーの耐用年数が短くなる可能性があるのです。それを防ぐには、PowerBookiBookのバッテリーを取り出して、適温範囲内で保管しましょう。

自分の使っていた環境と合致します。一番衝撃的だったのは「満充電で保存すると劣化が激しくなる」という記述です。外部バッテリーという性質上、常にフル充電状態にして保存している人は多いと思いますので、一番気をつけなくてはいけない所でしょう。

どうすれば少しでも寿命が延ばすことができるかをまとめると

  1. 温度が高い場所で保存せず、涼しい冷暗所で保存する。
  2. フル充電状態にして放置しない。
  3. 使用頻度に間がある場合は、50%程度充電し使用する際にフル充電するようにする。
  4. 過放電を防止するために長期間使用していなかった時は、たまに充電してあげる。

色々気を使わなくてはいけないようで少したいへんそうですね・・・。

バッテリーの寿命を復活させる方法はあるのか

どうすれば寿命を延ばせるかという観点で書いてきましたが、自分のバッテリーは既にお亡くなりになっているので、今さら寿命がどうとか言っても仕方ありません。
円高なのでまた海外から買ってもいいのですが、「バッテリーセルが中国クオリティ」という不安要素を排除することはできません。
そこでバッテリーを復活させる事ができないか調べた所、バッテリーセルを交換することで復活させる事ができるようです。

そもそもバッテリーセルとは何なのか

自分も詳しくないので分かりませんが調べた感じだと、セルというのは電池の事であり、バッテリーとはこのセルの集合体という事のようです。
要するにセルを交換するという事は、バッテリー内部の電池を新しい物に交換するという事になります。
電池なら自分で替えられるのではないか、と思う人もいるかもしれません。バッテリーの内部は若干複雑になっており、リチウムイオンの場合、充電・放電に対して制御を必要とし、制御基板と繋がっているようです。
自己責任で替えておられる方がいるようでしたが、とても怖くて自分で交換する事はできませんでした。

バッテリーセルを購入する場合、数千円程度で購入できるようです。
※自分で交換をする事を推奨しているわけではありません。自分で交換する場合は、必ず自己責任でおこなってください。
ロワジャパン バッテリーバンク  デジカメ携帯パソコンバッテリー・充電器

バッテリーセルを交換してみる事に

そこで今回はバッテリーリフレッシュサービスを利用する事にしました。バッテリーリフレッシュサービスとは、バッテリーのセルを新しい物に交換してくれるサービスで、ベイサンという所が最大手のようです。
ソフマップなどでも受付代行をおこなっているようです。

今回は利用したのはベイサンではなく、バッテリーリフレッシュという別の業者にお願いしてみる事にしました。
バッテリーリフレッシュ・セル交換の専門店

当然HyperMacは一覧に載っておりませんでしたので、見積りをお願いしてみることに。掲載されているバッテリーを見てみると、純正品と大差がない物もありましたので、海外から購入するより安ければいいなという感じでした。その後、ベイサンでも交換可能か問い合わせた所、対応不可との事でした。

実際に見積りをお願いしてみた結果を踏まえてまとめてみました。

直接購入(Cableなし$-30.95) バッテリーリフレッシュ ベイサン
MBP-060 14,365円 11,890円 対応不可
MBP-150 31,365円 29,890円 対応不可

※$1=85円で計算

送料などを考慮するとバッテリーセルを交換した方が安く済むようです。また、バッテリーリフレッシュさんでセルを交換した場合、元の単セルあたり2200mAhに対して、日本製のセルになり2600mAhと容量までアップします。

このサービスで一点注意しないといけないのは、「バッテリーセル」のみを交換するサービスだという事です。動作確認や保証はおこなっておらず、動作を保証してくれるわけではないという事に注意しなくてはいけません。
※リフレッシュサービスにお願いする際も必ず自己責任でおこなってください。

HyperMacのmAhとは

HyperMacのバッテリー容量(mAh)ですが、公式サイトを調べても一切記載されていません。おそらく、充電する端末ごとに使われる電圧が違うため記載されていないのかもしれません。
ここでふと気づいたのですが、外部バッテリーでよく書いてある「8000mAh」などの記載は、電圧ごとに変わるのであって、これはアテにならないと今ごろ気づきました。多くの場合は、3.7V換算での値を表記しているみたいです。

以下のような計算で求められるようです。
Wh x 1000 / V = mAh

とりあえず、3.7V換算で計算してみました。

Energizer XP18000 68Whくらい? 約18000mAh
MBP-060 60Wh 約16000mAh
MBP-100 100Wh 約27000mAh
MBP-150 150Wh 約40000mAh
MBP-222 222Wh 約60000mAh

こうして見てみると、HyperMacはすごいバッテリー容量です。
ちなみにMacBookPro13インチで、システムプロファイラの「電源」から確認した所、12494mV(12Vくらい?)で、4951mAhと表示されていました。MBP-060の公称では、1.0倍長く使えるとの事なので合っているのか計算してみました。

60Wh x 1000 / 12V = 5000mAh

4951mAhと近い数字が出たので計算方法は間違っていないようです。このような感じで、使用する端末ごとに使われる電圧を調べて計算してみると、どのくらいバッテリーが持つのか計算することもできるでしょう。

バッテリーリフレッシュしたバッテリーはちゃんと使うことができるのか

まだ交換したバッテリーが届いていないため、ちゃんと使うことができるかは分かりませんが、届き次第またレポしたいと思います。HyperMacのこれからが分からない状況なので、少しでも寿命を延ばせるように使ったり、使えなくなってしまったらどうすれば良いかを考えるいい機会になりました。
HyperMacを使っている人は参考にしてみてはどうでしょうか。
※バッテリーセルを交換する場合は、くれぐれも自己責任でお願いします。

実際に交換してみた記事はこちらから。
HyperMacのバッテリーセルを交換してみた – フタなしカンヅメ

fon2405EルータにDD-WRTを入れる

iPadを買った時にfonという無線ルータをもらいました。どうせなので、色々いじってみたいと思い、改造ファームウェアを入れてみることにしました。

ファームウェアを書き換えるには、シリアルケーブルで接続する必要があるようで、fonの基盤に直接ケーブルをハンダ付けします。
SSHやtelnetなどの接続も試みましたが、このfonでは無理なようでした。
下記サイトを参考にしました。
unix is mine oyster, which I with code will open!: FON2405Eの中身
FONルータ復旧大作戦 | このいえ
DD-WRT – FoNまとめwiki
[FON] –  
Hot Tuna Labs

今回用意したのは、9-KEというシリアルケーブルと、fon本体のみです。9-KEは、ネット通販で送料込み数百円くらいでした。
ちなみにハンダ付けは今回が初めてで、以前から電子工作に興味があったので、これを機会にやってみようと思ったわけです。

なんとか取り付けることができ、シリアル経由でコンソールに繋いでみた所、起動ログがちゃんと流れてきました。ちょっと感動。

こんなログが流れてきました。

U-Boot 1.1.3 (Jan  6 2010 - 07:10:30)
Board: Fonera
DRAM:  32 MB
relocate_code Pointer at: 81fac000
spi_wait_nsec: 3e
spi deice id: c2 20 15 c2 20 (2015c220)
find flash: mx25l1605d
raspi_read: from:41030000 len:1000
Using default environment
##### The CPU freq = 320 MHZ ####
SDRAM bus set to 16 bit
SDRAM size =32 Mbytes
Please choose the operation:
1: Boot system code via Flash (default).
2: Load system code then write to Flash via TFTP.
3: Entr boot command line interface.
booting from flash
## Booting image at bf020000 ...
raspi_read: from:20000 len:40
Image Name:   MIPS OpenWrt Linux-2.6.21
Created:      2010-02-11   8:09:34 UTC
Image Type:   MIPS Linux Kernel Image (lzma compressed)
Data Size:    613723 Bytes = 599.3 kB
Load Address: 80000000
Entry Point:  80000000
raspi_read: from:20040 len:95d5b
Verifying Checksum ... OK
Uncompressing Kernel Image ... OK
No initrd
## Transferring control to Linux (at address 80000000) ...
## Giving linux memsize in MB, 32
Starting kernel ...
LINUX started...
THIS IS ASIC
Linux version 2.6.21 (blogic@blap4) (gcc version 4.1.2) #143 Thu Feb 11 09:09:18 CET 2010
The CPU feqenuce set to 320 MHz
CPU revision is: 0001964c
Determined physical RAM map:
memory: 02000000 @ 00000000 (usable)
Built 1 zonelists.  Total pages: 8128
Kernel command line: console=ttyS1,57600n8 root=/dev/mtdblock4 init=/sbin/preinit
Primary instruction cache 32kB, physically tagged, 4-way, linesize 32 bytes.
Primary data cache 16kB, 4-way, linesize 32 bytes.
Synthesized TLB refill handler (20 instructions).
Synthesized TLB load handler fastpath (32 instructions).
Synthesized TLB store handler fastpath (32 instructions).
Synthesized TLB modify handler fastpath (31 instructions).
Cache parity protection disabled
cause = 80800000, status = 1100ff00
PID hash table entries: 128 (order: 7, 512 bytes)
calculating r4koff... 0030d400(3200000)
CPU frequency 320.00 MHz
Using 160.000 MHz high precision timer.
Dentry cache hash table entries: 4096 (order: 2, 16384 bytes)
Inode-cache hash table entries: 2048 (order: 1, 8192 bytes)
Memory: 30416k/32768k available (1689k kernel code, 2352k reserved, 186k data, 100k init, 0k highmem)
Mount-cache hash table entries: 512
NET: Registered protocol family 16
Generic PHY: Registered new driver
NET: Registered protocol family 2
Time: MIPS clocksource has been installed.
IP route cache hash table entries: 1024 (order: 0, 4096 bytes)
TCP established hash table entries: 1024 (order: 1, 8192 bytes)
TCP bind hash table entries: 1024 (order: 0, 4096 bytes)
TCP: Hash tables configured (established 1024 bind 1024)
TCP reno registered
was not able to assign mahjor 200 to button chardev
ramips_gpio: done
squashfs: version 3.2-r2 (2007/01/15) Phillip Lougher
squashfs: LZMA suppport for slax.org by jro
io scheduler noop registered
io scheduler deadline registered (default)
ramips_wdt: loaded
Serial: 8250/16550 driver $Revision: 1.3 $ 2 ports, IRQ sharing disabled
serial8250: ttyS0 at I/O 0xb0000500 (irq = 37) is a 16550A
serial8250: ttyS1 at I/O 0xb0000c00 (irq = 12) is a 16550A
PPP generic driver version 2.4.2
NET: Registered protocol family 24
tun: Universal TUN/TAP device driver, 1.6
tun: (C) 1999-2004 Max Krasnyansky <[email protected]>
deice id : c2 20 15 c2 20 (2015c220)
mx25l3205d(c2 20160000) (4096 Kbytes)
mtd .name = raspi, .size = 0x00400000 (4M) .erasesize = 0x00010000 (64K) .numeraseregions = 0
ramips_mtd: kernel size is 613787
padded kernel is 00096000
Creating 6 MTD partitions on "raspi":
0x00000000-0x00010000 : "uboot"
0x00010000-0x00020000 : "boardconfig"
0x00020000-0x00200000 : "image"
0x00020000-0x000b6000 : "linux"
mtd: partition "linux" doesn't end on an erase block -- force read-only
0x000b6000-0x001f0000 : "rootfs"
mtd: partition "rootfs" doesn't start on an erase block boundary -- force read-only
mtd: partition "rootfs" set to be root filesystem
0x001f0000-0x00200000 : "uci_overlay"
Registered led device: power
Registered led device: wps
Registered led device: wlan
nf_conntrack version 0.5.0 (256 buckets, 2048 max)
IPv4 over IPv4 tunneling driver
ip_tables: (C) 2000-2006 Netfilter Core Team, Type=Linux
TCP cubic registered
NET: Registered protocol family 1
NET: Registered protocol family 17
802.1Q VLAN Support v1.8 Ben Greear <[email protected]>
All bugs added by David S. Miller <[email protected]>
ramips: ethernet loaded
ramips_eth: loaded
VFS: Mounted root (squashfs filesystem) readonly.
Freeing unused kernel memory: 100k freed
Warning: unable to open an initial console.
Preinit complete, spawning microd
found uci_overlay -> /dev/mtd5
i found OPENWRT_UCI_OVERLAY inside /dev/mtd5
loading file zepttho 275
loading file wireless 696
loading file user 70
loading file system 72
loading file reg 16
loading file network 473
loading file led 342
loading file lang 33
loading file form 4768
loading file fonsmcd 290
loading file fon 4Algorithmics/MIPS FPU Emulator v1.5
07
loading file firewall 907
loading file passwd 149
loading file chilli.conf 566
loading fon config
Spawning syslogd
registering led wps -> /sys/class/leds/wps/
registering led wlan -> /sys/class/leds/wlan/
registering led power -> /sys/class/leds/power/
loading base firewall ...iptables: Chain already exists
done
adding zone wannet
adding zone lan
adding zone wan
adding zone hotspotwifi
adding zone hotspot
adding forwarding lan->wan
adding forwarding hotspot->wan
interface loopback
proto = static
ipaddr = 127.0.0.1
netmask = 255.0.0.0
ifname = lo
interface lan
proto = static
ipaddr = 192.168.10.1
netmask = 255.255.255.0
ifname = eth0.1
type = bridge
interface wan
proto = dhcp
ifname = eth0.2
interface hotspot
ifname = tun0
interface hotspotwifi
proto = none
ifname = ra1
net event up loopback ...bringing up lo
done
net event up lan ...bringing ueth0.1: dev_set_promiscuity(master, 1)
p eth0.1
device eth0 entered promiscuous mode
device eth0.1 entered promiscuous mode
br-lan: port 1(eth0.1) entering learning state
adding br-lan to zone lan
done
net event up wan ...bringing up eth0.2
Spawning udhcpc for eth0.2
udhcpc (v0.9.9-pre) started
Trying to connect...
sending -> "udhcpc|deconfig|eth0.2|"
adding eth0.2 to zone wan
Sending discover...
done
wifi device rt305x
type = rt305x
channel = 11
mode = 9
wifi interface public
ifname = ra1
ssid = FON_FREE_INTERNET
encryption = none
network = hotspotwifi
device = rt305x
isolate = 1
auto = 1
wifi interface private
ifname = ra0
ssid = MyPlace
encryption = wpa-wpa2
key = 4141414141
password = ueriehaeph
wpa_crypto = mixed
network = lan
device = rt305x
channel = 0
mode = bgn
txpower = 100
br-lan: topology change detected, propagating
br-lan: port 1(eth0.1) entering forwarding state
rt2860v2_ap: module license 'unspecified' taints kernel.
Sending discover...
WSending discover...
0x1300 = 00064380
bringing up rt30device ra0 entered promiscuous mode
5x ...done
br-lan: port 2(ra0) entering learning state
Terminate the task(RtmpCmdQTask) with pid(202)!
Terminate the task(RtmpWscTask) with pid(203)!
br-lan: port 2(ra0) entering disabled state

しかし、入力を一切受け付けてくれません。どうやら制限が入っているようで、こちらの操作を受け付けないようになっているようです。
これではファームウェアの更新はできません…。色々試してみたものの、結局解決には至りませんでした。残念。

f:id:happytar0:20100719003100j:image

配線を4本ハンダ付けしてしまったり、他の線と干渉?しちゃったりで、電子工作はやっぱりたいへんだなという感じでしたが、面白かったのでこれからもいじっていきたいです。

海外VPSや専用サーバの比較

ずいぶん更新していなかったので久しぶりに記事を・・・。
海外のサーバをいくつか借りてみたので詳細をまとめてみます。

金額やスペック

種類 名前 金額 CPU HDD メモリ OS 備考
専用サーバ The Planet $89 Celeron2.0GHz 80GB 512MB Linux全般、FreeBSD、WindowsServer クレジットの審査が厳しい。サポートは早くてすごい。
専用サーバ XLhost $69 DualCoreE2200 80GB 1GB LinuxFreeBSD、Windows。めっちゃ多い 一日で使える。PayPalも使える。
専用サーバ SH3LLS $69 DualCoreAtom 500GB 1GB LinuxFreeBSD、Windows。 サポートが遅い。三日くらいで使える。めっちゃ怪しいがちゃんと使えた。
VPS IX Web Hosting $39.95 XeonE5400 10GB 384MB Linuxちょい少ない Virtuozzo。三日くらいかかる。
VPS Known Host $25 不明 10GB 192MB CentOS5、選択できない? Virtuozzo。即日で使える。
VPS Linode $19.45 不明 16GB 360MB 管理画面から自分で選択 Xen。即日。
VPS PhotonVPS $10.95 8コアCPU 20GB 512MB Linuxのみ Virtuozzo、Xen。即日。サポートが感じ悪い。
VPS VPSLink $7.95 不明 2.5GB 64MB Linuxのみ Virtuozzo、Xen。金を払うと即使える。全てオートメーション化されてる。

海外だと専用サーバもけっこう安いけど、さくらインターネットでも7800円ほどなのでどうだろう?
日本だとまともなところが少ないVPSも海外だといい所がたくさんある印象。VirtuozzoのVPSはカーネルが更新できない?っぽいのでちょっと不便な気がする。

VirtuozzoのVPSはいずれも audit_log_user_command() のようなエラーが出てくる。カーネルを更新すれば直るっぽいけど、出来ないようので直せない・・・問題ないっぽいけど・・・。(再起動すると元に戻る?)
あとはiptablesを使うときにちょっと問題があった。ip_conntrack_ftp モジュールが入ってなくて、入れるにはどうやらカーネルを入れなおす必要があったけど、これも無理っぽかった。この辺りが使っていく上で問題になってくるかも。他にやり方があったら教えてくださいでござる。

各サーバのレスポンス(PING)

名前 応答時間
The Planet 172 ms
XLhost 162 ms
SH3LLS 141 ms
IX Web Hosting 181 ms
Known Host 111 ms
Linode 121 ms
PhotonVPS 110 ms
VPSLink 135 ms

どこもものすごく差があるわけではなさそうです。ちなみに一番長いLinodeのサーバを半年程度使っていますが特にトラブルなく使えてます。

JavaScriptのラジオボタンでのOnChangeイベントの挙動

Railsでは簡単にAjaxを使えるような仕組みとして、RJSなど便利ものがたくさんあるので、最近よく使ってみたりしています。

ちょっとアレ?と思ったのは、ラジオボタンのOnChangeイベントの挙動についてです。IEとFireFoxでどうやら違うみたい。IEの場合は、フォーカスを失った時点でイベントが発生しますが、FireFoxではフォーカスが移った時点でイベントが発生するようです。
正確には、値が変更された後ってことなのかな?

JavaScriptを使ってる人にとっては、かなり周知のことだと思うのですが・・・。OnBlurイベントというのがあるのならば、IEの挙動は微妙な気がしますが、どうなんじゃろか。

とりあえず、動きを統一したいならば、OnClickイベントを使えということらしいです。

参考
HTML ラジオボタンのonchangeイベントのブラウザごとの挙動について
にゃほ~ – IEにおけるラジオボタンのonchange