Что нового

"Изобретаем" алгоритм кодирования и декодирования шрифта

Insari

Осваивающий
Сообщения
34
Репутация
35
В ожидании изменений шрифтов в EVE задался вопросом, как бы улучшить используемый мною алгоритм декодирования, этих самых шрифтов.
Учитывая, что после введения ССР новых "некапсовых" шрифтов, всё равно придется обновлять таблицы их распознавания, обращаюсь за помощью в вопросе приведения той "халтуры", что у меня есть к более правильному виду.
Что не нравится:
[list type=decimal]
[*]Алгоритм делает два прохода по одному месту (для не пробельных символов), что как-то расточительно.
[*]Хотя точность распознавания при нормально подобранном пороге очень высока, но напрягает сам подбор порога в ручную.
[*]В дополнение ко второму пункту, хотелось бы уменьшить привязку к элементам на которых мы распознаем (SI, оверью и т.п.), чтобы не использовать отдельные таблицы для каждого элемента (хотя стоит ли это делать вопрос философский :smile: )
[*]Может как попроще сделать?
[/list]

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

Функции кодирования и декодирования
Код:
Func Decode_OCR()
	Local $decodeTimer = TimerInit()
	Local $sum = ''
	Local $hPic = GUICtrlGetHandle($PicOCR)
	Local $pos = WinGetPos($hPic)
	Local $countChar = 0, $code = 0, $j = 0		; j-счетчик непустоты столбца
	Local $iEmpty = 0							; счетчик пустых столбцов
	Local $bI = True							; Если True, то разделитель первый, False = разделитель был и в предыдущем столбце
	Local $Xp = $pos[0], $Yp = $pos[1], $Wp = $pos[2], $Hp = $pos[3]
	Local $x1 = 0, $y1 = $Wp
	For $x = 0 To $Wp
		Local $codeY = 0
		Local $i = 0									; счетчик пройденности порога анализа
		For $y = 0 To $Hp
			Local $color = TBitsGetColorR($x, $y, $Wp)
			If $color > $limit Then						; Порог анализа пройден
				If $i = 0 Then $i = 1				
				$codeY += $i
				If $y < $y1 Then $y1 = $y
			EndIf
			$i *= 2										; Положение точки
		Next
		If $i == 0 And $bI And $code <> 0 Then							; Если это разделитель и он первый
			$bI = False	
			If $iEmpty > 3 And $countChar > 0 Then							; ВНИМАНИЕ! Считаем, что четыре столбца следующие последовательно дают символ пробела
				$sum &= ' '
			EndIf
			$iEmpty = 1
			Local $YV = CodingV($x1, $y1, $x, $Hp, $Wp)
			Local $value = IniRead('Code - 0x'& Hex($limit,2) &'.Txt', $code, $YV, '*')
			$sum &= $value
			$j = 0
			$countChar += 1						
			$code = 0
			$x1 = $x			; следуем за иксом пока нам попадаются разделители, чтобы потом знать на каком иксе они закончились			
			ContinueLoop
		EndIf
		If $i == 0 Then
			$iEmpty += 1
			$x1 = $x			; следуем за иксом пока нам попадаются разделители, чтобы потом знать на каком иксе они закончились			
			ContinueLoop
		EndIf
		$j += 1											; Пройденный столбец был чем-то наполнен
		$code += $codeY*$j
		$bI = True
	Next
	If $code <> 0 Then									; На случай, если последним столбцом был не пустой (хотя и нежелательно)
		ConsoleWrite('Код с последним столбцом: '& $code & @CR)
	EndIf	
	ConsoleWrite('Строка = <'& $sum &'>' & @CR)
	ConsoleWrite('Время выполнения: '& TimerDiff($decodeTimer) & @CR)	
	GUICtrlSetData($lbOCRDecodeString, '<'& $sum &'>')	
	Return $sum
EndFunc	
	
Func Code_OCR()
	; ============== Создаем красную рамочку для выделения кодируемого символа ==============
	Local $GUI = GUICreate("", 0, 0, 1, 1, $WS_POPUP)
	Local $Top = GUICreate("Top Line", 0, 0, 1, 1, $WS_POPUP, -1, $GUI)
	GUISetBkColor(0xFF0000)
	GUISetState()
	Local $Left = GUICreate("Left Line", 0, 0, 1, 1, $WS_POPUP, -1, $GUI)
	GUISetBkColor(0xFF0000)
	GUISetState()
	Local $Right = GUICreate("Right Line", 0, 0, 1, 1, $WS_POPUP, -1, $GUI)
	GUISetBkColor(0xFF0000)
	GUISetState()
	Local $Bottom = GUICreate("Bottom Line", 0, 0, 1, 1, $WS_POPUP, -1, $GUI)
	GUISetBkColor(0xFF0000)
	GUISetState()			
	WinSetOnTop($Top,"",1)
	WinSetOnTop($Left,"",1)
	WinSetOnTop($Right,"",1)
	WinSetOnTop($Bottom,"",1)
	;============== Основной блок функции ==============
	Local $decodeTimer = TimerInit()
	Local $hPic = GUICtrlGetHandle($PicOCR)
	Local $pos = WinGetPos($hPic)
	Local $countChar = 0, $code = 0, $j = 0					; j-счетчик непустоты столбца, jP - позиция со степенью 2
	Local $bI = True										; Если True, то разделитель первый, False = разделитель был и в предыдущем столбце
	Local $Xp = $pos[0], $Yp = $pos[1], $Wp = $pos[2], $Hp = $pos[3]
	Local $x1 = 0, $y1 = $Wp
	Local $xG = 0					; координата x для рамочки
	For $x = 0 To $Wp
		Local $codeY = 0
		Local $i = 0									; счетчик пройденности порога анализа		
		For $y = 0 To $Hp
			Local $color = TBitsGetColorR($x, $y, $Wp)
			If $color > $limit Then						; Порог анализа пройден
				If $i = 0 Then $i = 1				
				$codeY += $i
				If $y < $y1 Then $y1 = $y
			EndIf
			$i *= 2										; Положение точки
		Next
		If $i == 0 And $bI And $code <> 0 Then							; Если это разделитель и он первый
			$bI = False	
			;============== Перемещаем рамочку вслед за кодируемым символом ==============
			WinMove($Top, "", $Xp+$x-1-$j, $Yp-1, $j+1, 1)			
			WinMove($Left, "", $Xp+$x-1-$j, $Yp-1, 1, $Hp+1)
			WinMove($Right, "", $Xp+$x, $Yp-1, 1, $Hp+1)
			WinMove($Bottom, "", $Xp+$x-1-$j, $Yp+$Hp, $j+1, 1)

			;============== Узнаем вертикальный код и запрашиваем ввод значения ==============
			Local $YV = CodingV($x1+1, $y1, $x-1, $Hp, $Wp)
			Local $valueRead = IniRead('Code - 0x'& Hex($limit,2) &'.Txt', $code, $YV, 'Символ не задан')
			Local $value
			Do
				If $valueRead = 'Символ не задан' Then
					$value = InputBox("Значение кода", "Введите символ, который означает код <"& $code &">:<"& $YV &">", "", "", -1, -1, 0, 0)				
				Else
					$value = InputBox("Значение кода", "Подтвердите символ, который означает код <"& $code &">:<"& $YV &">"& @CR & "Значится символ: ["& $valueRead &"]", $valueRead, "", -1, -1, 0, 0)
					If $value <> $valueRead Then  MsgBox(16, 'Дубликат кода!', 'Перезаписан символ <'& $valueRead &'>')
				EndIf
				if @error Then
					$code = 0
					ExitLoop 2
				EndIf
			Until $value <> '' 
			ConsoleWrite('№'& $countChar &' Код: '& $code & ', YV='& $YV &', Значение: '& $value & @CR)			
			IniWrite('Code - 0x'& Hex($limit,2) &'.Txt', $code, $YV, $value)		; Записываем код в файл только в том случае, если его там еще не было
			$j = 0
			$countChar += 1						
			$code = 0
			$x1 = $x
			ContinueLoop
		EndIf
		If $i == 0 Then
			$x1 = $x			; следуем за иксом пока нам попадаются разделители, чтобы потом знать на каком иксе они закончились
			ContinueLoop
		EndIf
		$j += 1											; Пройденный столбец был чем-то наполнен
		$code += $codeY*$j
		$bI = True
		$xG += 2
	Next
	If $code <> 0 Then									; На случай, если последним столбцом был не пустой (хотя и нежелательно)
		ConsoleWrite('Код с последним столбцом: '& $code & @CR)
	EndIf	
	GUIDelete($Bottom)
	GUIDelete($Right)
	GUIDelete($Left)
	GUIDelete($Top)
	GUIDelete($GUI)	
EndFunc

Func CodingV($x1, $y1, $x2, $y2, $width)
	Local $code = 0
	For $y = $y1 To $y2
		Local $codeX = 0
		Local $i = 0				
		For $x = $x1 To $x2
			Local $color = TBitsGetColorR($x, $y, $width)
			If $color > $limit Then
				If $i = 0 Then $i = 1
				$codeX += $i					
			EndIf
			$i *= 2		
		Next
		$code += ($y-$y1)*$codeX
	Next	
	Return $code
EndFunc

;	В оригинальной функции возвращалась красная компонента цвета
;	Для более адекватной по времени работы функции необходимо вставить "правильное" определение цвета точки
Func TBitsGetColorR($x, $y, $width=1024)
	  $color = PixelGetColor($x, $y)
   Return TrimColor($color)
EndFunc

Функции несколько "засорены" излишним кодом, потому что выдраны из ZoomInfo v0.44 http://autoit-script.ru/index.php/topic,3636.0.html.
 

Lexx98

Продвинутый
Сообщения
272
Репутация
73
Стоп-стоп-стоп.
Если ты помнишь, то тема распознавания символов у нас считается табу для выкладывания в общий доступ.
В личке или аське готов посмотреть и возможно обсудить.
 

Belfigor

Модератор
Локальный модератор
Сообщения
3,608
Репутация
941
Да собственно народ шагнул уже намного дальше, уже появились окр для евы и тд и тп. Мб пора снять табу? :smile:
 

DJ_Tommy

Продвинутый
Сообщения
236
Репутация
57
Кстати, да, я голосую за снятие табу - тем более что в ивку все реже захожу, а боты чисто на шипы и проплату карибасят.
 
Автор
I

Insari

Осваивающий
Сообщения
34
Репутация
35
Табу в данном случае, продолжает распространяться на скорость работы с экраном клиента (которую каждый, немного почитав данный форум, в частности в разделах не касающихся EVE, решает для себя сам).
Распространять его на алгоритмы, как верно замечено, несколько неактуально и уже вредно.
 

Belfigor

Модератор
Локальный модератор
Сообщения
3,608
Репутация
941
Поддерживаю.
 

bugaj

Знающий
Сообщения
140
Репутация
11
Че то я ничо не понял чо написал ТС ) че у него за алгоритм такой )
1. Сначала идем сверху вниз (т.е. внешний цикл по Y, внутренний по X), в каждой строке смотрим сколько есть пикселей цвета соответствующего буквам (чтобы это хорошо работало, очень полезно фильтрануть изображение перед этим). По оси Y определяем область в которой встречаются эти пиксели - это строка.
2. По найденой строке теперь идем по оси Х(Т.е. внешний цикл по X, внутренний по Y) - принцип тот же что и в первом пункте, ищем области с пикселями соотвествующими буквам. Каждое поле разделенное областями без этих пикселей есть буква.
3. Получившийся квадратег сравниваем с шаблоном. Чтобы не сравнивать каждый раз картинки, можно обсчитать например центр масс или площадь символа и сравнивать только с теми шаблонами у которых аналогичные данные параметры. Можно поискать в инете и другие возможные параметры.
4. Полезно после выделения символа еще раз вписать его в прямоугольник удалив внешние области не относящиеся к символу, а то например центр масс для букв в разных строках может быть разным. Т.к. символы в строке разной высоты и высота символа может быть меньше высоты строки.

Ну вот как то так.

П.С: Все это круто, но мало применимо для Автоита из за скорости чтения пикселей. Лично я забил на идею сделать ОЦР на автоите практически сразу.

П.С. Конечно, специально для тех кто в автоите, можно в шаблоне хранить не всю матрицу, а например только значащие пиксели, или только ключевые(в массиве) и при сравнении идти по шаблону в котором написаны координаты пикселя в квадратиге. А если по данным параметрам находится всего одна запись шаблона, то его можно и вовсе не сравнивать. Но т.к. я не в автоите, то не стал заморачиваться, т.к. итак все за доли секунд делается.


Особенно продвинутым предлагаю, поразрабатывать где нибудь алгоритм сегментации образов надежный, где нибудь, т.к. автоит тут вообще не потянет. Ничего путного по этой теме похоже еще никто не придумал (. А то боты бы были знатные если бы такой был ))
 

Lexx98

Продвинутый
Сообщения
272
Репутация
73
А мне никогда и не требуется распознавать все видимые на экране буквы-цифры. Чаще всего это всего-лишь маленькая область, строка длиной эдак в 4-12 символов. И поверь, для распознавания этого мощностей и скоростей автоита при правильном подходе хватает с головой.

3. Насколько я понял глянув мельком код, тут, как и у меня, не сравниваются "картинки", а сравниваются шаблоны - строка из единичек и нулей. Более того, тут просто тупо берётся и смотрится в ини-файле, а задано ли соответствие этому набору нулей-единичек соответствие? Кстати, когда я делал ОЦР, я до этого не дошёл, может потому что с инишниками ещё не работал. Надо внедрять, это проще, чем у меня...
 

DJ_Tommy

Продвинутый
Сообщения
236
Репутация
57
Я когда начинал писать своего распознователя для трейд бота делал след образом. Задавал точную область поиска цифр, проверял всю область попиксельно на наличие точек ярче заданного порога. Вертикальная линия из пустых пикселей означала конец зоны определения знака. Далее делал проверку массива из 0 и 1 на сходство с массивами всех 10 цифр. При совпадении более 90% символ считался распознанным. Потом немного увеличил скорость за счет сканирования и распознания не всей области знака, а всего лишь 15 контрольных точек. Если описать процесс словами, то получается следущее:
1. задаем координаты левой верхней точки окна маркета (или любого другого окна нужного)
2. ищем первую горизонтальную линию в ордерах (обычно она могла сползти +-3 пикселя)
3. ищем первую вертикальную линию в первом ордере
4. определяем количество символов в нужной нам строке по пробелам между знаками
5. определяем координаты левой нижней точки первого знака
6. создаем массив из данных по координатам 15 контрольных точек
7. методом перебора ищем совпадение по контрольным точкам. Если совпадает менее 12 точек проверяем следущую цифру.
8. при совпадении более 12 знаков цифра считается обозначенной и аналогично определяем остальные знаки.

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

Ну вот на этом я и забил на это дело.
 

arabidopsis

Новичок
Сообщения
11
Репутация
3
Новые шрифты отлично распознаются по маске тени (используем только пикселы выше порогового значения) за вычетом цвета фона.

Моя распознавалка работает со строчками, в стандартном наборе символов ошибок не делает (раньше были ошибки между D O, & 6 и т.д.). Базу символов храню в БД, написал конечно сразу скрипт автоматической генерации (тупо вводит по одному символу в любое ингейм поле и заносит его маску в БД).
 

running-frag

why me?
Сообщения
441
Репутация
60
arabidopsis [?]
Моя распознавалка работает со строчками, в стандартном наборе символов ошибок не делает (раньше были ошибки между D O, & 6 и т.д.). Базу символов храню в БД, написал конечно сразу скрипт автоматической генерации (тупо вводит по одному символу в любое ингейм поле и заносит его маску в БД).
всё гениально - просто, спс за мыслю (родил мыслю как сделать для себя окр)
 

EVEGamer

Знающий
Сообщения
27
Репутация
12
Если кто с Tesseract`ом разобрался, огромная просьба пример выложить.
 

running-frag

why me?
Сообщения
441
Репутация
60
Развиваем мысль. Допустим есть шрифты. Все что используются в еве (любые окна). Имея .ttf как тогда быть?
 
Верх