У меня есть приложение, которое принимает галерею изображений (все в Jpeg) и дает оценки подобия между каждыми возможными парами. В каждый момент времени может быть выбрана только одна пара и отображается ее оценка сходства.Дешевый/быстрый способ хэш-растровых изображений?
Алгоритм, который сравнивает два изображения, имеет определенную производительность, так что для сравнения пары требуется несколько секунд.
Когда выбраны две картины:
- Если пара никогда не сравнивали, оценка показывает «еще не набрал.». Пользователь может нажать кнопку «Оценка», и пара будет отправлена в поток, который ставит в очередь оценки баллов. Пример: http://db.tt/gb1Yk6yx
- Если пара в настоящее время находится в очереди для вычисления, в поле оценки отображается «Computing ...». Пример: http://db.tt/OvS1qGP3
- Если сравнивать пару, отображается оценка, привязанная к паре. Пример: http://db.tt/m2OQGybW
Пример (при выполнении партии): http://db.tt/iD67SdCp
Если оценка никогда не была вычислена, и пользователем, нажмите «Score», поле переключится на «Computing ...», а затем будет отображаться оценка, когда вычисление будет завершено.
Прежде чем отображать что-либо в поле оценки, когда выбраны две пары, их прикрепленный битмап отправляется на HashMap, которые проверяют, имеют ли эти два растровых изображения уже прикрепленный балл, и в этом случае он просто возвращает его. Если нет оценки, то задание отправляется в очередь.
Чтобы узнать, существует ли оценка в кеше, мне нужно найти способ хэша пары, чтобы я мог использовать полученный ключ для поиска кеша. Вот где моя проблема. Чтобы иметь смысл, хеширование двух растровых изображений должно быть быстрым. В противном случае я просто добавляю еще один уровень вычислений. Но, как я делаю до сих пор хэш, два 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;
}
Однако этот метод, в соответствии с профилированием я сделал, стоит около 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 лет.
Не могли бы вы использовать Bitmap.getPixels? Он возвращает массив ints (ну, на самом деле, он заполняет массив ints, который вы проходите, но что это такое между друзьями?). – Iain
Почему вы не используете имя файла во время использования файлов для хранения растровых изображений и первичный ключ строки (или флаг в самой базе данных) после того, как вы используете базу данных для хранения растровых изображений? –
Посмотрите на метод 'copyPixelsToBuffer', который принимает' ByteBuffer'. Кроме того, JB находится на месте; по какой причине вы не хотите использовать имена файлов? –