Пятнадцать лет назад я стал пользователем Evernote. Счастливое время, когда мысли были только о стартапах, Кремниевой долине и технологиях. И хоть сейчас евангелисты Obsidian абсолютно точно уверены, что они впервые создают второй мозг, позволю себе сказать: я создавал second brain в 2010-х с помощью Evernote и Moleskine. А OCR поиск Evernote по картинкам и документам был абсолютно магическим. Не зря, видимо, до этого инженеры работали над решениями OCR для правительства США.
Эта статья о том, как можно собрать локальный поиск по картинкам и фото, который будет работать без больших корпоративных сервисов, без LLM/SaaS и без передачи персональных данных наружу.
Чем был для меня Evernote
Конечно, как и сейчас, за довольно простым, но хорошим продуктом должно стоять что-то большее. Идея или дистрибьюшен. Ведь это же только лишь сервис для написания заметок и ничего больше. Для меня это была идея о том, что за океаном русский бородатый мужик Степан Пачиков, технарь, взял, сделал и заработал на этом какие-то сумасшедшие деньги. Я им пользовался, советовал друзьям, участвовал в его хакатонах и даже там получал призовые. Помню отсыпали довольно много кредитов на AWS.
Из всего набора фичей Evernote самыми ценными были всего три на мой взгляд. Но именно они удерживали меня как пользователя вплоть до 2022 года — года, когда я отказался от SaaS сервисов крупных компаний. Больше десяти лет подписки...
Итак, что это были за якорные фичи:
Поиск по документам, картинкам и фото
Чертовски полезная вещь. Понятно, что при правильной организации меток и папок можно находить любую, даже самую старую заметку или документ, просто двигаясь по какой-то системе. Но если к системе добавить ещё и очень точный поиск, то процесс будет куда приятнее. У меня было много медицинских документов, которые было очень просто находить по ключевым словам, присутствующим на картинке или скане. И мне не надо было руками проводить предварительную индексацию и организацию ради самой организации. Просто добавление одной метки — и OCR-поиск делал своё дело. Да, поиск на тот момент был именно что OCR. Но к этому мы ещё вернёмся.
Будильник на заметки
У меня было несколько удачных применений этой фичи: будильники срабатывали пару раз в год и помогали мне не пропустить нужный момент, возвращая контекст того, что мне надо было сделать. Сейчас я использую для этого почту.
Skitch (аннотатор скриншотов)
В рамках моей профессии нужно коммуницировать с другими людьми, показывая тот или иной элемент продукта и оставляя к нему комментарии. Аналогичные сервисы были и есть. Аналог для Linux до сих пор не умеет делать нормальные стрелочки.
Недавно пробегая по скриншотам в поиске одного нужного на своем пк, подумал, как было бы прикольно если был бы поиск из Evernote. И да я знаю, что подобных решений было и есть множество. Причём речь может идти не только о коммерческих продуктах, но и open source, просто поищите на GitHub. Но все это как это всегда и бывает "не то" "было лучше". Хотя конечно лучше то и не было, поиск на Evernote работал по принципу OCR, очевидно раз компания все еще существует, то смею предположить, что он был улучшен до гибридного поиска. То есть совмещения OCR (поиска по тексту распознаному из картинки) и семантического поиска, то есть поиска по embedding'ам, которые были получены из картинки с помощью какой-то модели. Речь пойдет о небольших моделях, которые можно запустить на обычном пользовательском железе и они имеют очень ограниченный фокус на то что они делают.
Что я хочу от поиска по картинкам?
Если на картинке изображена кошка или металический люк, то я бы мог так и написать свой запрос и получить подобные картинки. Очень хотелось бы искать картинки по цветовой гамме. Скажете зачем, но это невероятно удобно, в моих кейсах когда я точно помню дизайн какого-то сервиса и поиск по запросу это profile form, может и не помочь, а вот фиолетовый цвет + profile form, результат будет найден. Хочется искать используя не только английский текст, но и русский в запросах. Чтобы я мог написать metal grate или металлический люк и получил бы схожий результат. Конечно чтобы поиск учитывал текст на картинке, если текста там много, но и смысл этого текста. К примеру я сделал скриншот с YouTube рецепта теста для пиццы, он был написан маркером на доске. Текст на картинке содержит слова вода, мука и соль. Желательно бы его искать по запросу "рецепт пиццы" или просто "рецепт". Также поиск должен быть быстрым. Ок, я готов ждать долгой индексации и предварительного анализа базы картинок, но сам поиск должен быть около мгновенным. Итак, возможно ли это сделать с использованием современных моделей, не используя внешних сервисов и API.
Шаг 1. Семантический поиск.
Или поиск по embedding'ам картинок и embedding'у полученному из текстового запроса пользователя. Для решения нам нужна мультимодальная модель эмбеддингов и желательно с лицензией MIT/Apache. И такая есть это Nomic Embed Vision, как и любая трендовая модель на основе трансформером и BERT-архитектуры. Прелесть модели для нашей инженерной задачи в том, что текст и картинки на входе, дают вектора из одного пространства. И мы можем искать текст по картинке, картинку по картинке или картинку по тексту. Из минусов, русский язык работает не самым лучшим образом в запросах, это конечно решим тоже, но позже.
Давайте разберемся как я буду проверять качество принятых моделей в пайплайне поиска. А буду качественным методом нравится или не нравится на моей локальной папке со скриншотами. Их всего порядка 500. Глобальная задача скормить всю мою базу знаний, заметок, скриншотов, рефов, но пока ограничимся примером поиска по скриншотам. У меня был список картинок, которые я хотел найти из своей папки скриншотов. И поиск должен их найти. Никаких сложных бенчей и методологии, я просто должен кайфануть от поиска. Итак берем rust, берем onnx модели nomic vision и смотрим что получается.

Первый же результат отработал идеально, это конечно было сопадением, но удачным. У меня был скриншот с YouTube ролика, как можно сварить люк на паркинге, чтобы в нем УК могла бы похоронить насос. И поиск этой картинки был мгновенным, имея на руках лишь Semantic search с использованием одной единственной модели. Сами вектора embeddings можно хранить в любой бд с поддержкой векторов и поиска по ним. У SQLite есть плагин, есть еще LanceDB. Они подходят идеально потому что не имеют избыточной сложности в поддержке и работе с ними. Помним что SQLite установлен вообще на всех смартфонах в мире и абсолютно булетпруфная база данных.
Проблема была только с запросами на русском языке, решать проблему я взялся в лоб. А именно переводом запросов с русского на английский язык и подобрал маленькую модель для пары языков c русского на английский. https://huggingface.co/Teradata/opus-mt_tiny_rus-eng Запросы стали работать значительно лучше. Конечно мы на каждом таком шаге теряем в качестве, если бы модель уже была мультиязычная, было бы лучше, но идем простым путем. Для обучения аналогичной модели нужен не только стек из 16+ H100/H200, но и длительная работа с датасетами, что неприемлемо для моей задачи. Но небольшими силами (увеличением цепочки из моделей) целевой результат вырос и вообще стало возможным искать металлический объект, словом "метал", а не только "metal".
Шаг 2. Поиск по цветам.
Это моя давняя мечта о том, как я бы хотел искать в своей библиотеке референсов с изображениями. И цвета абсолютно идеально укладываются в векторном виде. И чтобы сделать такой поиск надо сделать две вещи. Первое это вытащить доминантные цвета или своего рода палитру из картинки. Второе уложить палитру цветов в виде вектора. И дальше можно просто искать близкие вектора, абсолютно как я сделал до этого с embeddings от nomic vision модели. Чтобы вытащить палитру можно использовать разные алгоритмы, но как я люблю подходить к таким задачам, будет брать самое простое решение. Главное чтобы оно могло дать значимо хороший результат. Есть k-means clustering, его и возьмем. Существует несколько хороших библиотек для Rust, я взял https://crates.io/crates/kmeans_colors Для каждого изображения k-means извлекает 5 доминирующих цветов в цветовом пространстве Lab, которые склеиваются в один плоский вектор и индексируются в LanceDB. Когда пользователь ищет одновременно по тексту и цвету, система сначала выполняет семантический поиск (векторный или гибридный), а затем переранжирует результаты с учетом поиска по цвету. В этой оценке семантическая релевантность первична, а цветовая близость — уточняющий фактор.
Бам! Это работает конечно же в совокупности с семантическим поиском. К примеру я хочу поискать login UX скриншоты. И помню что нужный мне сайт оранжевый или красный.
Пушка булочка. Ровно то, что я и хотел. В моем следующем youtube видео я расскажу, что еще можно добавить в этот пайплайн, чтобы сделать еще лучше. Напомню, что поиск уже работает абсолютно волшебно и при этом мы даже не сделали OCR, мы еще не распознали текст на картинках и не добавили его в так называемый гибридный поиск.
С момента того, как над Evernote начали работать прошло уже больше 20 лет. И если в начале пути этого SaaS технология поиска по картинкам была действительно заградительным оружием против конкурентов, то со временем технология стала очень дешевой в разработке и внедрении. Да, конечно, Tesseract OCR стал open source уже тоже десятилетия назад. Но это абсолютная земля и небо, что мы можем запускать сейчас и тогда локально на машине пользователя и с каким уровнем качества.
Мы вроде бы должны войти в эпоху тысяч и тысяч новых приложений и сервисов, которые могут решать старые проблемы значительно проще, быстрее и лучше. LLM вроде как решили проблему софта и оставили лишь проблему идеи. Но где же эти тысячи новых стартапов?
P.S.
Результат работы можно попробовать скачав приложение по ссылке. Приложение доступно только для macOS, Linux и Windows. Протестирован пока только на Linux. Я продолжаю над ним работу прямо сейчас и не вижу еще то, что из него получится. Пока появилась рабочая идея, сделать свой Skitch. Только абсолютно с локальными моделями для поиска и для обработки изображений. Если интересно можете подписаться на https://zatsepin.dev/subscribe или на YouTube https://www.youtube.com/@yurizatsepin