Функциональное тестирование интерфейса (GUI) приложений — задача очень важная, нужная, но не всегда тривиальная. Основной вопрос тут: как сэмулировать работу пользователя? Простого, рядового пользователя, которому придется непосредственно изо дня в день работать с вашим софтом.

Казалось бы, причем здесь распознавание текстов?

Как и чем обычно тестируют и автоматизируют


В общем, тут скучный текст, основанный большей частью на опыте
Обычно отталкиваются от того софта, который надо автоматизировать. Например, для Win приложений можно посмотреть в сторону MSAA и его развития UI Automation. Этот фреймворк неплохо распознает элементы управления и экранный текст посредством API. Web приложения можно «потыкать» Selenium-ом или WatiN-ом и т. п.

Подобные способы не всегда подходят, например когда пользователь работает с приложением через удаленный рабочий стол, или Web приложение «нашпигованно» сторонними контроллами (ActiveX, Java applets и т. п.).

Очень хорошо данная проблематика описана здесь, а список софта — здесь.

Идея


мы пойдем другим путем ©Маяковский

А что, если сделать снимок экрана, запомнить некоторую область и в качестве проверки распознать текст в этой области. Если в заданной области есть ожидаемый текст, то можно туда кликнуть.

Звучит неплохо, надо пробовать.

И тернистый путь реализации


Для начала необходимо выбрать движок для распознавания текстов или написать его самому. Писать самому — задача нетривиальная, поэтому возьмем что-то готовое и желательно Open Source. Ну что ж, для начала почитаем мануалы.

Есть такое чудо под названием Tesseract OCR. И к нему есть API-обертка на .NET! Звучит просто, но не все так просто: фоткаем, распознаем и… ничего — Tesseract не увидел в нашем снимке текста! Если быть точнее, не увидел на куске снимка.

Как сделать снимок прямоугольной области экрана
public static Bitmap GetAreaFromScreen(Rectangle area)
{
	var rect = new Rectangle(area.X, area.Y, area.Width, area.Height);
	var bmp = new Bitmap(rect.Width, rect.Height, PixelFormat.Format24bppRgb);
	using (var g = Graphics.FromImage(bmp))
		g.CopyFromScreen(rect.Left, rect.Top, 0, 0, bmp.Size, CopyPixelOperation.SourceCopy);
	return bmp;
}


Кусок 'плохого' кода, который ничего не распознал
public string RecognizeText(Bitmap source)
{
	try
	{
		using (var page = engine.Process(source, PageSegMode.SingleLine))
		{
			var text = page.GetText();
			var conf = page.GetMeanConfidence();

			threshold = conf;
			return text;
		}

	}
	catch (Exception e)
	{
		Trace.TraceError(e.ToString());
		return "";
	}
}


Покопавшись немного в тонкостях и механизмах распознавания в интернете, я нашел это: stackoverflow.com/questions/9480013/image-processing-to-improve-tesseract-ocr-accuracy.

Получается, надо сначала сделать:

1. fix DPI (if needed) 300 DPI is minimum
2. fix text size (e.g. 12 pt should be ok)
3. try to fix text lines (deskew and dewarp text)
4. try to fix illumination of image (e.g. no dark part of image
5. binarize and de-noise image

А уже потом пробовать что-то там распознавать!

Тут нам на помощь пришла библиотека AForge.NET. Она, кстати, Open Source, как и Tesseract.

кусок 'хорошего' кода с магией AForge
public string RecognizeText(Bitmap source)
{
	try
	{
		var seq = new FiltersSequence();
		seq.Add(new ResizeBilinear(source.Width * 2, source.Height * 2));

		seq.Add(new Grayscale(0.2126, 0.7152, 0.0722));
		seq.Add(new OtsuThreshold());
		seq.Add(new Threshold(100));
		temp = seq.Apply(source);

		using (var page = engine.Process(temp, PageSegMode.SingleLine))
		{
			var text = page.GetText();
			var conf = page.GetMeanConfidence();

			threshold = conf;
			return text;
		}

	}
	catch (Exception e)
	{
		Trace.TraceError(e.ToString());
		return "";
	}
}


Поясню по пунктам:

  1. DPI — у скриншота DPI норм, нам хватит;
  2. fix text size — тут зависит от размера шрифта, иногда надо увеличить, иногда сделать потолще или повыше. ResizeBilinear в помощь;
  3. try to fix text lines — тут зависит от точности выделенной области экрана. Выделяем текст прицельно, стараясь не зацепить «артефакты» — ручная работа;
  4. try to fix illumination of image — Grayscale да и все тут!
  5. binarize and de-noise image — бинаризуем и дешумизуем (!) OtsuThreshold и Threshold(100).

Теперь гораздо лучше.

Ядро программки уже готово, осталось совсем чуть-чуть и все будет круто написать приложение с неким подобием тест дизайнера, где можно разметить области экрана и сохранить в некое подобие тест плана. И написать проигрыватель тестов. Делов-то, на 15 минутЗдесь конечно пришлось повозиться, но это тема другой статьи.

В итоге, соединяя все вместе, получился и простенький дизайнер тестов и простенький проигрыватель.

А теперь скриншот из которого ничего непонятно и видео без звука


Это скриншот той части программки, которую я называю Тест дизайнер!



А это видео работы программки — записываем простенький тест и запускаем его!



Вот собственно и всё!

Ах да!

Исходники, ТТХ и прочее


Исходники проекта: github.com/skaeff/miranda-tester
Собранная версия с английским и русским словарями: github.com/skaeff/miranda-tester/blob/master/miranda-tester-net40-compiled.7z

Краткое резюме:

  1. Tesseract OCR github.com/tesseract-ocr/tesseract — библиотека оптического распознавания текстов;
  2. AForge.Net www.aforgenet.com — библиотека обработки изображений;
  3. Немного логики, написанной на C#.

И да, для скриншота нажимаем Shift+Esc.

Комментарии (13)


  1. xtraroman
    05.04.2016 14:44
    -2

    Может где то и нужно оптическое распозравание текста, но не в том примере, который показан в статье. Сайт хабра можно потестить и без этого. Распознавать капчу пробовали?


    1. skaeff
      05.04.2016 14:48

      Такой задачи не стояло. Задача именно в автоматизации рутинных действий с GUI приложения со сбором статистики.


    1. mantyr
      06.04.2016 08:06

      Tesseract OCR очень плохо распознаёт искажённые тексты, коими чаще всего капча и является. Конечно можно сделать поверх изображения кучу фильтров, выровнять некоторые символы, но попадание будет очень низким. Для таких целей подойдёт что-то более специализированное.

      У себя распознаём мульти-язычные pdf с в целом вменяемым качеством снимков страниц и то приходится делать несколько попыток что бы найти в тексте то что нужно на произвольном документе. Чуть что отличное от прямого текста — разметка (порядок) символов превращается в кашу с кучей пробелов. А так в целом хорошая библиотека.


  1. redmanmale
    05.04.2016 15:03

    Selenium и для десктопа есть, от 2gis.


    1. skaeff
      05.04.2016 15:14

      Это и хорошо. Вот только в софте от 2gis используется UIAutomation Framework, который кстати говоря, не всегда применим, даже к Desktop приложениям.
      В статье же я попытался описать совсем другой подход к автоматизации тестирования — не нужно никаких фреймворков, просто PrintScreen + OCR


  1. kmikeru
    05.04.2016 17:41
    +2

    Но ведь уже давно есть Sikuli, что с ним не так?


    1. skaeff
      05.04.2016 17:47

      Да, Sikuli это классный фреймворк судя по описанию, но там используется другой механизм — матчинг паттернов без распознавания текста. Т. е. «если эта картинка выглядит как шаблон, то кликаем».

      Описанный механизм OCR на мой взгляд дает больше свободы — можно брать информацию с экранной формы.
      Кстати, template matching неплохо реализован и в AForge.


      1. leorush
        06.04.2016 08:21
        +1

        У Sikuli есть распознавание ) Причём именно при помощи Tesseract OCR )


        1. skaeff
          06.04.2016 10:42

          Да, не приметил :)
          Там используется OpenCV + Tesseract.
          Получается yet another tool, только на .Net и используя AForge.NET + Tesseract!


  1. VanKrock
    06.04.2016 07:53

    В подходе я вижу проблему в том, что элемент UI может поменять свое положение и не всегда понятно, как предсказать, где должен быть элемент, простой пример, вы ищете хаб «Программирование» и он всегда у вас на первом месте, и тут вдруг добавили хаб «Администрирование» и он стал на первом месте, а «Программирование» ушло на второе место и как это обработать, это ошибка интерфейса или придется переделать тест? Сам пользуюсь Coded UI Test для автоматизации тестирования GUI.


    1. skaeff
      06.04.2016 08:06

      Определенно, элементы могут менять положение. В данном случае это лишь добавить шагов проверки «если не здесь, то здесь». В тест придется добавить проверку всех положений или обрабатывать данную ситуацию как-то особенно (используя тот же UIAutomation если это возможно).

      Ошибка или не ошибка интерфейса — это вопрос скорее по usability и дизайну. На мой взгляд каждый раз произвольное положение каких-то контроллов — это плохая usability.


      1. VanKrock
        06.04.2016 19:53

        Ну допустим вы добавили пост на Хабрахабр и хотите проверить, что он отобразился, но сразу после вашего поста другой тест, который запущен на другом агенте добавил еще один пост, то есть ваш пост стал вторым, вопрос, как вы определите положение вашего поста, ведь высота поста из второго теста может быть любой?


  1. TheGodfather
    06.04.2016 19:55
    +1

    Вообще для тестирования GUI приложений куда более логично использовать Accessebility технологии, как уже вышеупомянутые MSAA или UI Automation. Т.е. процесс выглядит как «опросить систему и получить список диалогов -> зная ожидаемые контролы на диалоге, определить, какой диалог из этих нам нужен (без всякого распознавания, простой обход деревьев) -> из полученной информации получить информацию о конкретном контроле „Next“ -> послать эвент по нажатию этой кнопки». Да, с таким подходом на линуксе и маке немного посложнее, чем на винде, но все равно применимо. И меня уже долгое время мучает вопрос — почему все пытаются написать свой велосипед, использующий графическое распознавание, нежели использовать Accessebility?