私はギャラリーの写真(すべてJPEGで)をとり、可能な各ペアの間に類似性スコアを与えるアプリケーションを持っています。どの時点においても、1つの対のみが選択され、その類似度スコアが表示される。ビットマップをハッシュするための安価な方法ですか?
2つの画像を比較するアルゴリズムには、パフォーマンスのコストがあり、ペアを比較するのに数秒かかることがあります。
ときに二つの絵が選択されています:
- をペアで比較されていない場合、スコアは「まだ決めていません。」と表示されます。ユーザは「スコア」ボタンをクリックすることができ、計算されるべきスコアをキューに入れるスレッドにそのペアが送られる。例:http://db.tt/gb1Yk6yx
- 現在、ペアが計算待ちの場合、スコアフィールドには「コンピューティング...」と表示されます。例:http://db.tt/OvS1qGP3
- ペアを比較すると、そのペアに付けられたスコアが表示されます。例:http://db.tt/m2OQGybW
例(バッチ実行):スコアが計算されていない場合はhttp://db.tt/iD67SdCp
、および「スコア」ユーザーのクリックは、フィールドがに切り替わります「コンピューティング...」その後、計算が完了するとスコアが表示されます。
スコアのフィールドに何かを表示する前に、2つのペアが選択されている場合、その2つのビットマップに既にスコアが添付されているかどうかを確認するHashMapに送信されます。スコアがない場合、ジョブはキューに送られます。
スコアがキャッシュに存在するかどうかを知るには、結果のキーを使用してキャッシュを参照できるように、ペアをハッシュする方法を見つける必要があります。それが私の問題です。意味をなさないためには、2つのビットマップのハッシングが速くなければなりません。それ以外の場合は、別の計算レイヤーを追加するだけです。しかし、これまで2つのBitmapをハッシュする方法は、それらをバイト配列で送信し、MD5チェックサムを取得することです。このように:
private Long getHashKey(Bitmap first, Bitmap second){
// TODO this IS costly, it render useless the cache optimization.
// also, it doesn't detect that comp(A,B) is the same as comp(B,A).
// much work to do here.
if(D) Profiling.start(TAG, "getHashKey");
ByteArrayOutputStream stream = new ByteArrayOutputStream();
first.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] firstArray = stream.toByteArray();
second.compress(Bitmap.CompressFormat.JPEG, 100, stream);
byte[] secondArray = stream.toByteArray();
byte[] bitmapBuffer = new byte[firstArray.length + secondArray.length];
System.arraycopy(firstArray, 0, bitmapBuffer, 0, firstArray.length);
System.arraycopy(secondArray, 0, bitmapBuffer,
firstArray.length, secondArray.length);
Adler32 md5Hash = new Adler32();
md5Hash.update(bitmapBuffer);
long hashKey = md5Hash.getValue();
if(D) Profiling.stop();
return hashKey;
}
しかし、この方法では、私がやったプロファイリングによると、非常に不快であるUIの遅れの原因となる、実行するのに約53ミリ秒を要しました。より詳細なプロファイリングでは、コンピューティング時間の約95%がcompress
の方法で行われていることがわかりました。しかし、私はビットマップをバックアップする別の方法を発見していません。
05-26 17:56:13.220: D/Profiling(9458): Profile for ImageCompareActivity.getHashKey:
05-26 17:56:13.220: D/Profiling(9458): > Count : 1996 calls
05-26 17:56:13.220: D/Profiling(9458): > Total runtime : 105765140 us
05-26 17:56:13.220: D/Profiling(9458): > Avg runtime : 52988 us
ビットマップをハッシュする方法はわかっています。しかし、私は関数のハッシュと、ビットマップのどの部分を使ってファイルを一意に識別できるかについてはあまり知らない。私は最終的にそれらのビットマップをデータベースに送りたいので、ファイル名などを使用したくありません。
[更新1] 私はObject.hashCode()について知りませんでした。今度は、このような方法を変更しました:
private Integer getHashKey(Bitmap first, Bitmap second){
if(D) Profiling.start(TAG, "getHashKey");
Integer hashKey = new Integer(
1013 * (first.hashCode())^1009 * (second.hashCode()));
if(D) Profiling.stop();
return hashKey;
}
平均約18 usで実行されます。
Bitmap.getPixelsを使用できますか? intの配列を返します(実際には渡すintの配列に値を設定しますが、それは何ですか?)。 – Iain
データベースを使用してビットマップを格納した後で、ファイルを使用してビットマップを格納するときにファイル名を使用したり、行のプライマリキー(またはデータベース自体のフラグ)を使用しないのはなぜですか? –
'ByteBuffer'を受け入れる' copyPixelsToBuffer'メソッドを調べてください。また、JBは現場にいる。あなたがファイル名を使用したくない理由は何ですか? –