Проектирование интерфейсов — ремесло очень сложное. Увы, не очень понятно, как этому ремеслу учиться. Разумеется, на сегодняшний день существует огромное количество разных хороших книжек про разные хорошие подходы к этому самому проектированию. Однако практика подсказывает, что даже если вы их все вдумчиво прочитаете, то это не будет означать, что вы будете уметь в любой ситуации создавать идеальный UI. А всё дело в том, что подобная деятельность скорее является своего рода искусством, нежели следованием набору правил. Но как же этим искусством овладевать, если хорошие книжки не дадут необходимых навыков? Нам кажется, что ключевым фактором в этом деле всё-таки является опыт. Но учиться только на своём опыте — занятие долговременное, лучше бы и на других людей поглядывать (и это далеко не только к интерфейсам относится).
Давайте немного поговорим о том, как правильно перенимать чужой опыт. У нас есть много знакомых команд, которые разрабатывают действительно крутые приложения. И при показе нам своих интерфейсов они начинают рассказывать, что и как они сделали. А мы всегда спрашиваем, почему они так сделали, почему они пришли именно к такому решению. И нам кажется, что это самый правильный вопрос. Да, бывают гениальные люди, которым без всякой матчасти во сне привидится идеальный интерфейс без разного рода тяжёлых размышлений о том, как делать хорошо, а как делать плохо. Но таких людей мало, а на подобном опыте многому не научишься. Образное
В этом посте мы хотели бы рассказать историю одной формочки, которую мы сделали при разработке нашего продукта PassportVision (о котором уже рассказывалось на Хабре). Это одна-единственная небольшая формочка, но мы её делали целый год. Насколько хорошо у нас получилось — судить вам, но пользователи весьма довольны (а разве не это критерий удачного юзабилити?). Мы многократно переделывали разные штуки, горячо спорили о разных мелочах, но в конце концов получилось то, чем удобно пользоваться. Впрочем, нам думается, что через год мы взглянем на этот пост, взглянем на наш к тому времени уже старый UI и скажем: «О Господи! Мы что, действительно отдавали это пользователям? Ох, стыдно, как же стыдно». Но всё правильно — интерфейс постоянно должен развиваться, эволюционировать, становиться лучше. А сегодня мы поговорим о том, что имеется на сегодняшний день и как же мы к этому пришли.
Постановка задачи
Имеется следующая ситуация: клиент приходит в какое-нибудь замечательное место, а у него спрашивают паспортные данные. Клиент передаёт свой паспорт оператору, который, в свою очередь, размещает документ в сканере и нажимает специальную кнопочку для запуска PassportVision. И через несколько секунд на выходе у нашей программы имеются распознанные данные клиента. И можно было бы сразу отправить эти данные в вордовский документ, базу данных, веб-форму, другую специализированную программу (нужное подчеркнуть), но сперва неплохо было бы показать результаты распознавания оператору для проверки. Не подумайте, что PassportVision всё так плохо распознаёт, что за ним постоянно приходится править ошибки. Но, увы, приходится мириться с тем фактом, что современные компьютерные технологии не в состоянии обеспечить стопроцентную распознаваемость совершенно любого текста. А учитывая всю ту любовь, с которой в нашей стране подходят к проектированию индивидуального дизайна паспорта для каждого человека (уникальная комбинация шрифта, его начертания и цвета, расположения полей и угла наклона — пусть твой паспорт будет не похож на остальные!), ошибки будут появляться. Возможно, они будут только на каждом n-ом паспорте, возможно, они будут совсем мелкие, но они всё равно будут. Поэтому этап проверки пропускать не очень хочется.
Строгая постановка технического задания: сделать так, чтобы верификация происходила максимально просто и быстро. Чтобы прям «вжух!» — и правильность результатов была проверена.
Первое приближение
Мы хорошо помним наш самый первый вариант интерфейса: слева — картинка паспорта, справа — значения распознанных полей. Мол, смотри на результаты да с оригиналом сравнивай.
И вроде бы всё хорошо, но только проверка занимает много времени (порой это время сравнимо с ручным вводом данных паспорта), а многие ошибки всё равно остаются.
Работа с зонами
Подрисовываем зоны
Процесс работы над интерфейсом происходил очень просто: мы каждый день пользовались своей программой и неудобные вещи находились сами собой. Самым раздражающим обстоятельством было следующее: приходилось тратить слишком много времени на то, чтобы после чтения значения очередного поля перевести взгляд на паспорт и найти на нём соответствую зону. Выход простой: давайте подсказывать пользователю, где находится на картинке та зона, с которой он сейчас работает (т. е. поле с курсором, мы его также отдельно дополнительно подсвечиваем). Можно, например, обвести нужное место:
Однако перескакивающий с места на место прямоугольник создавал не очень приятный визуальный шум, многих это раздражало. Поэтому мы решили рисовать все прямоугольники, а нужный выделять цветом:
Связка «поле-зона» работает в обе стороны: при клике на поле подсвечивается зона, а при клике на зону — активируется поле (подсветка + постановка курсора + фокус). При наведении мышкой на некоторую зону появляется всплывающая подсказка с распознанными данными.
Всплывающие подсказки
С этими цветным прямоугольниками стало, конечно, несколько лучше, но не особо. Всё равно тратилось много времени на то, чтобы гонять взгляд влево-вправо, чтобы сверять распознанные данные с оригинальным изображением. Непорядок, давайте это расстояние сокращать. Мы начали выводить рядом с текущим полем всплывающую подсказку, на которой отображали соответствующую зону:
Возможно, решение покажется несколько странным, но опыт показал, что это действительно удобно. Немного мороки добавили поля, которые размещены вертикально (например, серия и номер паспорта). Разумеется, для удобства чтения их пришлось повернуть:
Таким образом, перемещение взгляда получилось минимальным. В каждый момент времени вы находитесь в контексте одного поля: вот вам распознанные данные, а вот вам картинка с оригиналом. Не нужно отвлекаться, чтобы смотреть куда-то ещё. Это значительно ускорило процесс проверки.
Подсветка отдельных символов
Давайте ещё упростим поисковые задачи. Не всегда и не все могут проверить всё поле целиком. Особенно если это какое-нибудь большое поле (например, место выдачи из трёх строк). Вскоре мы поговорим про навигацию и подозрительные символы и узнаем, что в ходе работы с программой проблемные места зачастую оказываются выделенными. Возникает задача найти выделенный текстовый фрагмент на картинке. Вроде бы простая задача, но если на всплывающей подсказке обвести выделенную область в рамочку, то всё становится ещё проще:
Некоторые могут сказать, что улучшения такого уровня не нужны, что, мол, ни на что они не повлияют. Но у нас другой подход: если мы можем как-нибудь сэкономить несколько десятков миллисекунд пользователю, то лучше бы так и сделать. (В этом месте нужно с умным видом привести расчёты вроде тех, что если 100 операторов будут с помощью нашей программы обрабатывать по 100 клиентов в день каждый, то за 100 дней сэкономленные 10 миллисекунд на одном паспорте дадут индустрии экономию почти в три человеко-часа. Но мы не будем так делать.) В любом случае, фича вроде как не мешает, но зато выглядит прикольно. =)
Несколько зон для одного поля
Дополнительная проблема возникает после того, как приходит осознание, что в некоторых документах некоторые поля могут дублироваться. Скажем, в паспорте гражданина РФ номер и серия проставлены как на верхней странице, так и на нижней. А хуже всего то, что каждое вхождение может распознаться по-своему (например, из-за того, что верхняя страница паспорта лишена какой-либо внешней защиты и сильнее подвержена механическим повреждениям; мы по этому поводу много грустим). По умолчанию мы показываем пользователю тот вариант, в котором сами больше уверены, но ведь нужно предоставить выбор. Поэтому лёгким движением руки парой строчек кода мы превращаем наш TextBox в ComboBox, который можно раскрыть с помощью горячей клавиши, после чего клавишами вверх/вниз выбрать нужный вариант. Для упрощения выбора во время путешествия по выпадающему списку в области всплывающей подсказки мы показываем именно ту зону, которая соответствует выбранному варианту:
На основном изображении паспорта альтернативные варианты также особым образом подсвечиваются:
К некоторым полям мы подходим особым образом. Например, пол всегда представлен ComboBox-ом с двумя вариантами, вне зависимости от результатов распознавания (они влияют только на начальное значение).
Машиночитаемая зона
И всё у нас было хорошо, пока мы не стали делать поддержку машиночитаемой зоны. Визуально её проверять несколько, кхм, проблематично. Если серию и номер проверить ещё легко, то вот среднестатистический человек с улицы вряд ли справится с фамилией. Дело в том, что 33 буквы великого и могучего отображаются в 26 букв английского алфавита и 7 цифр (например, 'Ч'->'3', 'Ю'->'7'). Показывать подобное на всплывающей подсказке опасно — можно напугать неподготовленного человека. Поэтому некоторые зоны мы обрабатываем особым образом: даже если мы взяли фамилию из машиночитаемой зоны (а чаще всего мы в ней как-то более уверены), то на всплывающей подсказке мы показываем основную зону с фамилией, чтобы проверка была попроще. В этом случае возникают проблемы с восстановлением исходных контуров символов (изображение-то нам другое подсунули!), но мы стараемся по мере сил их решать.
Навигация по ошибкам
Подозрительные символы
Всяких подсказок мы навыводили, но пока что не избавили человека от потребности внимательно прочитать весь текст. Непорядок! К счастью для наших пользователей, мы научились классифицировать некоторые символы как «подозрительные». В интерфейсе мы красим такие символы красным цветом:
Изначально мы базируемся на confidence каждого символа (степень уверенности в правильности распознавания), который нам с заботой сообщает Tesseract. С помощью волшебных эвристик мы в каждом конкретном случае определяем пороговый confidence, ниже которого все символы считаются подозрительными. Затем мы запускаем несколько дополнительных алгоритмов (больше эвристик для бога эвристик!), которые помогают нам улучшить критерии подозрительности. Например, если в дате «01.01.2014» точки плохо пропечатались и Tesseract в них не особо уверен, то мы их всё же не будем считать подозрительными, ведь мы знаем что-то, чего не знает алгоритм распознавания.
Основы навигации
Наши старания не пропали даром: практически все ошибки у нас окрашиваются в красный. Да, мы захватываем и верные символы, но лучше уж перестраховаться, чем недосмотреть какие-нибудь недочёты. В подавляющем большинстве случаев достаточно пробежаться по подозрительным символам и при необходимости внести правки. Для ускорения процесса мы добавили возможность переходить к следующему и предыдущему подозрительному символу с клавиатуры. При этом соответствующая потенциальная ошибка сразу выделяется в тексте и подсвечивается на всплывающей подсказке. За счёт выделения пользователь сразу может начинать вводить правильный вариант данных, если результаты распознавания действительно оказались ошибочны.
Улучшаем навигацию
Но беда не приходит одна. Мы заметили, что подозрительные символы чаще всего идут группами. Так происходит из-за того, что на изображении паспорта в каком-то месте возникает некоторая неприятность, которая портит не один конкретный символ, а сразу некоторую область. Поэтому если несколько подозрительных символов идут подряд, то мы объединяем их в подозрительную группу. Однако бывает так, что внутри слова, которое бы по логике вещей всё должно быть одной подозрительной группой, встречается буква, в которой мы вопреки всему уверены. Такая буква останется чёрной, но слово, скорее всего, придётся переписывать полностью. Поэтому мы ввели концепт подозрительных слов — это такие слова, в которых слишком много подозрительных символов. По аналогии мы ввели концепт подозрительного поля: если в поле слишком много подозрительных символов (например, нехорошие люди перепачкали весь паспорт), то, скорее всего, всё поле придётся переписать, а чудо в виде нескольких верно распознанных символов нам тут не особо поможет.
Как же теперь организовать навигацию по ошибкам в терминах «предыдущая» и «следующая»? Мы долго думали и остановились на следующем решении: мы построили дерево из подозрительных символов, групп, слов и полей, после чего прошлись по нему поиском в глубину. Воспользовавшись практикой как критерием истины, поняли, что это действительно удобно.
Разумеется, есть дополнительные плюшки. Есть режим навигации, в котором совмещены переход по ошибкам и переход по полям. Т. е. мы в любом случае побываем в каждом поле, даже если в нём нет «подозрительных символов» (всё равно не помешает на него глянуть, чтобы совесть была чиста). Есть горячие клавиши для перехода к каждому конкретному полю.
Редактирование
Undo/Redo
Наблюдения за пользователями дали нам интересную информацию для размышлений. Оказывается, большинство людей воспринимает нашу формочку не как набор не особо связанных между собой полей ввода, а как единый документ. А раз это один документ, то и Undo/Redo-стек должен быть у всех полей общий. Другими словами, если пользователь что-то исправил, перешёл к следующему полю, а затем понял, что как-то криво он внёс исправления, то по нажатию Ctrl+Z он ожидает, что будет произведён откат изменений именно в предыдущем поле. Не то чтобы это было очевидно с самого начала, но удобство пользователей превыше всего: мы сделали для всех полей единый Undo/Redo-стек.
Следим за символами
При редактировании нужно было учитывать ещё один момент, который относится скорее к программной реализации, нежели к юзабилити-решению. У каждого символа есть специальные метаданные (его confidence и контур на исходной картинке), которые нужно таскать при редактировании. Более того, при Undo/Redo-операциях все метаданные нужно корректным образом восстанавливать. Бывают более интересные случаи. Например, фамилия распозналась неверно («Ивонов»), а у пользователя в буфере обмена как раз завалялось правильное написание фамилии («Иванов»). Что же должно произойти после нажатия Ctrl+V с метаданными? Правильный ответ: для начала и конца слова данные должны сохраниться полностью (и confidence, и контур буквы), а превращение буквы «o» в «a» должно быть понято программой как исправление ошибки, ввиду чего контур буквы должен остаться в метаданных новоиспечённой буквы «а».
Хочется сказать, что особую боль доставляло не исправление букв, а разъединение и соединение слов, ведь у пробелов-то метаданных нету (мы их достраиваем на лету по контурам соседних букв). Внутренняя структура данных значительно меняется при подобных операциях, что нужно было аккуратно учитывать при реализации того же Undo/Redo.
Корректировка регистра
Не все изменения нужно обрабатывать одинаково. Скажем, если пользователь поправил регистр буквы (например, «иванов» заменил на «Иванов»), то с точки зрения метаданных ничего поменяться не должно: все confidence-ы и контуры букв остаются на месте. А вот в Undo/Redo-стек такую операцию всё равно нужно занести. Кроме того, мы поддерживаем возможность автоматической корректировки регистра. У некоторых наших клиентов есть стандарты на то, как должны быть представлены паспортные данные: кому-то нужны все буквы заглавные, а кому-то нужно только первые буквы делать заглавными (особое веселье начинается в месте выдачи, когда мы пытаемся понять, какие же слова всё-таки нужно написать с большой буквы, а какие — нет). Если поставить соответствующую галочку, то PassportVision возьмёт все проблемы в регистром на себя. Например, если вы посреди «городамосквы» поставите пробел, то в результате должны получить «города Москвы». Подобные капитализации не должны смущать логику работы Undo/Redo-стека и следилки за метаданными: всё должно работать чётко и максимально ожидаемо для пользователя.
Кастомизация
Мы уже поговорили о многих мелочах и нюансах, но как бы детально ни был продуман интерфейс, всегда найдутся люди, которым он не понравится. У нас всё ещё остаётся куча моментов, из-за которых ведутся горячие споры. Например, некоторые считают, что изображение паспорта должно быть справа, некоторые — что слева, некоторым оно не нужно вовсе. Поле «Код подразделения» в результатах распознавания называется «Номер» из-за того, что у многих наших корпоративных клиентов в их программном обеспечении в соответствующем месте написано именно «Номер», но другие клиенты хотят именно «Код подразделения». Некоторым наш порядок полей нравится, а некоторые хотят, чтобы порядок полей был как в паспорте: сперва информация о выдаче, а только потом ФИО и т. д. А некоторым информация о выдаче не нужна вовсе, они хотят её убрать. Кому-то так и не пришлась по нраву наша система навигации и всплывающие подсказки. Нужно ли в самом начале работы с формой выделять первое подозрительное место (чтобы сэкономить одно нажатие клавиш) или же просто ставить курсор в начало первого поля. Споры ведутся и о дополнительных фичах. Например, у нас есть функция проверки паспорта на просроченность. Некоторым она не нужна вовсе, а некоторые хотят заблокировать обработку результатов в случае просроченного паспорта. Некоторые хотят фичу с автоматической транслитерацией результатов. Список можно продолжать долго.
И вот если бы все эти замечания исходили только от каких-то сторонних наблюдателей, то на них можно было бы и не обращать внимания. Но когда приходит клиент и говорит: «Я вам дам денег только если всё будет так, так и так», то игнорировать замечания становится сложнее. Поэтому сейчас мы активно прорабатываем настройки приложения, чтобы, протыкав несколько галочек, мы могли бы для каждого собрать тот интерфейс, который его по максимуму устроит. Но это уже совсем другая история.
Вместо заключения
Повторюсь: мы не претендуем на то, что у нас сейчас идеальный интерфейс для нашей задачи. Но это интерфейс, с которым некоторые люди работают ежедневно, и он им нравится. Мы планируем и дальше активно его дорабатывать, совершенствовать и делать всё более и более удобным и приятным в использовании. А в этом посте мы лишь пытались показать, как же шла наша мысль, когда мы принимали те или иные решения, на что обращали внимание, как прорабатывали разные мелочи. Искренне надеемся, что вы смогли почерпнуть для себя из нашей истории какие-нибудь полезные вещи.
Автор: DreamWalker