Skip to content

Latest commit

 

History

History
123 lines (99 loc) · 12.4 KB

task.ru.md

File metadata and controls

123 lines (99 loc) · 12.4 KB

BSA-PERCEPT

В этом задании вы будете создавать приложение с возможностями Computer Vision. Вам предстоит создать веб сервис для поиска похожих изображений.

Concept

Для распознавания изображений мы будем использовать хеширование. Популярными алгоритмами для данной задачи являются aHash, pHash и dHash. Мы будем использовать dHash, данный алгоритм основан на подсчете направления градиента изображения.
Хеширование осуществляется в 3 этапа: 0) Инициализировать значение хеша 0

  1. Обесцветить изображение, превратив его в полутона(grayscale)
  2. уменьшить изображение до определенного размера(9x8, 8x9, 9x9)
  3. Подсчет хеша. Для его подсчета можно использовать 3 варианта: вертикальный, горизонтальный и диагональный.
    Мы рассмотрим диагональный алгоритм - для этого нам понадобится изображение 9х9. Начиная с позиции (1,1) мы обходим изображение и выставляем текущий бит хеша в единицу, если значение яркости текущего пикселя больше, чем значение яркости в пикселе по диагонали сверху-слева. При этом на каждой итерации мы сдвигаем значение хеша на 1 влево.

Более детально про алгоритм можно почитать тут

Словесное описание алгоритма звучит довольно сложно, однако псевдокод показывает, что алгоритм очень простой:

long dHash(Image image){
  long hash = 0;
  Image colorless = grayScale(image);
  Image scaled = resizeImage(image, 9, 9); //resize(Image target, int width, int height)
  int colorArray = scaled.toColorArray();
  for(int i = 1; i < 9; i++){
    for(int j = 1; j < 9; j++){
      if(colorArray[i][j] > colorArray[i - 1][j - 1]){
        hash |= 1;
      }
      hash = hash << 1;
    }
  }

  return hash;  
}

После того, как мы посчитали 2 хеша, мы можем сравнить их. Для этого используется расстояние Хемминга - колличество бит, которые различаются между двумя хешами. Для его подсчета необходимо использовать побитовое исключающие или(XOR) и посчитать колличество единиц в полученном числе и поделить их на 64. Это позволит узнать насколько два хеша отличаются друг от друга. Для того, чтобы узнать насколько они похожи необходимо от 1 отнять полученое число

double diffPercent = count_set_bits(hash1 ^ hash2) / 64;
double matchPercent = 1 - diffPercent

Требования к приложению

Сервис должен поддреживать 4 операции:

  1. загрузить несколько изображений в базу для распознавания
  2. Загрузить 1 изображение для поиска. Если оно найдено небыло, то добавить его в базу
  3. удалить изображение из базы по id
  4. удалить все изображения из базы

Загруженные изображения сохраняются на жестком диске. Подсчитаный хеш с id и путем к изображению сохраняется в "пресистентное хранилище". Это может быть как просто коллекция в памяти, так и база данных(в этом случае используй PostgreSQL).
Операции с файловой системой следует вынести в сервис, который будет предоставлять асинхронный API.

При обработке батч запроса запись на жесткий диск и подсчет хеша должны происходить паралельно, после того как обе операции закончатся необходимо создать запись с информацией об изображении в персистентном хранилище. После сохранения всех изображений в массиве необходимо вернуть ответ клиенту:

                                   Изображение 1 -----> подсчет хеша --------------> запись в персистентное хранилище--\ 
                                 /               \                               /                                      \                   
                                /                 --- запись на жесткий диск ---/                                        \
                               /                                                                                          \
- Получение массива изображений                                                                                           -----> отправка ответа клиенту
                               \                                                                                         /
                                \                                                                                       /
                                 \                                                                                     /
                                  Изображение 2 -----> подсчет хеша --------------> запись в персистентное хранилище--/ 
                                                \                               /
                                                 --- запись на жесткий диск ---/

В случае поиска изображения, если небыло найдено ни одного совпадения, необходимо также как и при батч загрузке записать файл на диск и создать запись в персистентном хранилище, однако ответ отправляется не дожидаясь окончания данных операций:

 Изображение --> подсчет хеша ----> поиск совпадений в персистентном хранилище ------> отправка ответа клиенту ---------------------------------------------->
                                                                              \
                                                                               \                                                                                                                                                         
                                                                                \---запись на жесткий диск --------> запись в персистентное хранилище ------->

Удаление сущностей реализуйте на ваше усмотрение.

Приложение должно предоставлять 4 эндпоинта:

POST /image/batch
content-type: multipart/form-data
images: MultipartFile[]

Загружает несколько файлов, считает хеш, сохраняет изображения на диске и создает запись в персистентном хранилище.

POST /image/search?threshold=?
content-type: multipart/form-data
image: MultipartFile

Response: [
    {
        id: "92c73b0f-77d6-41b9-be87-1e0ebf20be31",
        image: "http://127.0.0.1:8080/files/92c73b0f-77d6-41b9-be87-1e0ebf20be31.jpg",
        match: 96.1265
    }
]

Загружает файл, поиск которого необходимо выполнить в персистентном хранилище с указаным минимальным процентом совпадения(threshold). Threshold должен находится в пределах (0, 1], при его отсутствии в качестве значения по умолчанию использовать 0.9. Если похожие изображения не найдены, то необходимо сохранить его на диск и добавить запись в персистентное хранилище.

DELETE /image/{id}

Удаляет указанное изображение из персистентного хранилища и с жесткого диска

DELETE /image/purge

Удаляет все изображения с жесткого диска и из персистентного хранилища

Также приложение должно предоставлять доступ к сохраненным изображениям по ссылке.

Tips

  1. Со многими элементами домашки вы уже знакомы по предыдущим домашкам. Для упрощения задачи мы подготовили репозиторий с боийлерплейтом.
  2. Начни работу с реализации хеша. Можешь выбрать любой вариант(горизонтальный, вертикальный, диагональный). Если застрял, то мы подготовили пример реализации алгоритма в этом гисте(никакие зависимости подключать не надо)
  3. Поиск совпадений в базе данных будет довольно сложно реализовать, но за это мы добавим дополнительные баллы. Если хочешь попробывать его реализовать, то тебе прийдется писать нативный запрос в базу данных. Перформанс такого запроса мы оценивать не будем, важен сам факт его наличия ;)
  4. Не забудь ограничить конкаренси в сервисах, которые выполняют паралельные вычисления. Для этого можешь создать свой ExecutorService или использовать любые другие средства.
  5. Используй современные возможности Java - CompletableFuture и паралельные стримы. Избегай создания нативных потоков вручную. Также не стоит забывать про возможность использовать @Async аннотацию.
  6. Будет плюсом, если вы настроите ограничение наразмер тела multipart/form-data. Только не выставляйте слишком маленькое значение.
  7. Логирование всех входящих запросов будет плюсом :)
  8. Для тестирования загрузки файлов на сервер вам скорее всего понадобится сторонний HTTP клиент. Мы рекомендуем Postman или Insomnia, но вы можете использовать любой удобный вам клиент.
  9. Прочитайте дополнительный гайд в ConcurrencyApplication, он подскажет в каком порядке лучше выполнять работу.

Удачи с выполнением домашки ;)