Что нового

Алгоритм распознования капчи. С чего начинать.

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Никогда не работал с графикой, не знаю с чего начинать. Хочу распознать капчу. (пример во вложении).
Картинку не планирую хранить на жестком диске, она лежит в буфере обмена и еще можно представить в виде $hBitmap или $hImage через
Код:
_GDIPlus_BitmapCreateFromHBITMAP($hBitmap)


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

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
inververs
Вряд ли получится без временного сохранения на диск.
А так, например, такие картинки Tesseract вроде нормально распознает, только увеличить нужно.
Установить Tesseract, скрипт положить в папку к нему, и картинки туда же.
Как-то так:

Код:
#include <GDIPlus.au3>

_GDIPlus_Startup()

$sImage1 = 'capcha.png'
$sImage2 = 'capcha0.png'

$hSource = _GDIPlus_ImageLoadFromFile($sImage1)

$Size = _GDIPlus_GetImageDimension($hSource)
$hDestination = _GDIPlus_CreateBitmapFromScan0($Size[0] * 3, $Size[1] * 3, 0, _GDIPlus_GetImagePixelFormat($hSource)) ; увеличиваем картинку
$hGraphics = _GDIPlus_ImageGetGraphicsContext($hDestination)
_GDIPlus_GraphicsDrawImageRect($hGraphics, $hSource, 0, 0, $Size[0] * 3, $Size[1] * 3)
_GDIPlus_ImageSaveToFile($hDestination, $sImage2)
_GDIPlus_GraphicsDispose($hGraphics)
_GDIPlus_ImageDispose($hDestination)
_GDIPlus_ImageDispose($hSource)
_GDIPlus_Shutdown()

$ocr_filename = 'capcha' ; текстовый файл

ShellExecuteWait('D:\ocr\tesseract.exe', $sImage2 & ' ' & $ocr_filename & ' -l', @SW_MINIMIZE)
$sText = FileRead($ocr_filename & '.txt')

MsgBox(0, 'Заголовок', $sText)


Func _GDIPlus_CreateBitmapFromScan0($iWidth, $iHeight, $iStride = 0, $iPixelFormat = 0x0026200A, $pScan0 = 0)
    Local $Ret = DllCall($ghGDIPDll, 'uint', 'GdipCreateBitmapFromScan0', 'int', $iWidth, 'int', $iHeight, 'int', $iStride, 'int', $iPixelFormat, 'ptr', $pScan0, 'ptr*', 0)
    If (@error) Or ($Ret[0]) Then
        Return SetError(1, 0, 0)
    EndIf
    Return $Ret[6]
EndFunc   ;==>_GDIPlus_CreateBitmapFromScan0

Func _GDIPlus_GetImageDimension($hImage)
    Local $Ret = DllCall($ghGDIPDll, 'uint', 'GdipGetImageDimension', 'ptr', $hImage, 'float*', 0, 'float*', 0)
    If (@error) Or ($Ret[0]) Then
        Return SetError(1, 0, 0)
    EndIf
    Local $Result[2]
    $Result[0] = $Ret[2]
    $Result[1] = $Ret[3]
    Return $Result
EndFunc   ;==>_GDIPlus_GetImageDimension

Func _GDIPlus_GetImagePixelFormat($hImage)
    Local $Ret = DllCall($ghGDIPDll, 'uint', 'GdipGetImagePixelFormat', 'ptr', $hImage, 'uint*', 0)
    If (@error) Or ($Ret[0]) Then
        Return SetError(1, 0, 0)
    EndIf
    Return $Ret[2]
EndFunc   ;==>_GDIPlus_GetImagePixelFormat
 

sngr

AutoIT Гуру
Сообщения
1,010
Репутация
408
Вот пример с получением координат и цвета пикселя. Зная их, распознать цифры дело техники.
<a href="http://autoit-script.ru/index.php?action=downloads;sa=downfile&id=298" />drawingscript.au3</a>
 

Belfigor

Модератор
Локальный модератор
Сообщения
3,608
Репутация
941
Начни с освоения нейронных сетей и генного алгоритма, если тебе действительно обойти надо именно капчу, а не то что у тебя сверху там приведено.
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Belfigor [?]
Начни с освоения нейронных сетей и генного алгоритма, если тебе действительно обойти надо именно капчу, а не то что у тебя сверху там приведено.
тем не менее это и есть капча.
 

sidoroff.john

Новичок
Сообщения
4
Репутация
0
Если цель именно обойти капчу, то лучше заюзать специальные сервисы. Где индусы в реальном времени за доллар разгадают тебе тыщу капч.
Т.е. твой софт отдает им картинку, а получает уже цифры готовые.

Если задача - поупражняться в матане, то тут да, кури научные труды )
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Нужно распознать именно такую капчу как на рисунке.


Добавлено:
Сообщение автоматически объединено:

не используя antigate.com
 

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
inververs
Там только цифры? И всегда одинаковые по форме и т.д.?
И цвет фона один?
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
WSWR [?]
Там только цифры? И всегда одинаковые по форме и т.д.?
И цвет фона один?
Да, всегда 4 цифры, размер картинки одинаковый, на одинаковом фоне
 

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
inververs

Ну, тогда проще
Можно сделать образцы картинок (в архиве) цифр от 0 до 9, и сравнивать их
с капчей с помощью функций обработки текста (образцы картинок можно заранее перевести в строки текста и хранить в скрипте). Проблема - картинка должна быть повернута по часовой стрелке на 90 градусов, как и образцы цифр. С обычной картинкой на диске проблем нет, но вот чтобы прямо памяти битмап повернуть... я знаю сложный способ, но что-то мне подсказывает, что можно это как-то проще сделать.

Код:
#include <GDIPlus.au3>
#include <Array.au3>

$countstring1 = _SegmentMatrix(@ScriptDir & '\1.bmp') ; образец "1"
$countstring5 = _SegmentMatrix(@ScriptDir & '\5.bmp') ; образец "5"
$countstring7 = _SegmentMatrix(@ScriptDir & '\7.bmp') ; образец "7"

$bData = _SegmentMatrix('capcha.png') ; нужно повернуть по часовой

$a = StringRegExp($bData, $countstring1 & '|' & $countstring5 & '|' & $countstring7, 3)
$sTring1 = _ArrayToString($a, '')
;_ArrayDisplay($a)
$sTring1 = StringReplace($sTring1, $countstring1, '1')
$sTring1 = StringReplace($sTring1, $countstring5, '5')
$sTring1 = StringReplace($sTring1, $countstring7, '7')

MsgBox(0, 'Заголовок', $sTring1)

Func _SegmentMatrix($image)
	Local $tMap1, $hBitmap1, $bData, $Width1, $Height1
	_GDIPlus_Startup()
	$hBitmap1 = _GDIPlus_BitmapCreateFromFile($image)		
	$Width1 = _GDIPlus_ImageGetWidth($hBitmap1)
	$Height1 = _GDIPlus_ImageGetHeight($hBitmap1)
	$tMap1 = _GDIPlus_BitmapLockBits($hBitmap1, 0, 0, $Width1, $Height1, $GDIP_ILMREAD, $GDIP_PXF32ARGB)
	$bData1 = DllStructGetData(DllStructCreate('byte[' & ($Width1 * $Height1 * 4) & ']', DllStructGetData($tMap1, 'Scan0')), 1)
	_GDIPlus_BitmapUnlockBits($hBitmap1, $tMap1)
	_GDIPlus_BitmapDispose($hBitmap1)
	_GDIPlus_Shutdown()
	Return StringTrimLeft($bData1, 2)
EndFunc   ;==>_SegmentMatrix
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
Все это как то сложно, хранить образцы цифер на диске и сравнивать, мне кажется нужно всего лишь найти пару уникальных пикселей в определенных позициях, которые будут идентифицировать каждую цифру. Вот например, есть скрипт на питоне, автор не я, делает распознование именно для этой капчи.
Код:
from PIL import Image, ImageDraw
import sys

db = {
  289: "0",
  175: "1",
  228: "2",
  257: "3",
  297: "4",
  251: "5",
  244: "6",
  182: "7",
  272: "8",
  283: "9",
}

if len(sys.argv) < 2:
  sys.exit(1)  
image = Image.open(sys.argv[1])
  
width, height = image.size

pix = image.load()

fl = open("out.txt", "w")

f = False
out = ""
for lx in range(0, width):
  s = 0
  for ly in range(0, height):
    if pix[lx, ly][0] == 252:
      s += 1
 
  if not f and s > 0:
    f = True
    n = 1
    tot = 0
    
  if s > 0 and f:
    tot += s*n
    n += 3 #magic
    
  if s == 0:
    if f:
      out += db[tot]
    f = False

fl.write(out + "\n")
fl.close()


Добавлено:
Сообщение автоматически объединено:

Для меня работа с изображениями, тема новая и незнакомая, вот и прошу помощи.
 

madmasles

Модератор
Глобальный модератор
Сообщения
7,790
Репутация
2,322
inververs,
А Вы не можете прикрепить картинки с остальными цифрами?
 

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
inververs
Образцы хранятся не на диске, а в коде, т.к. это текст
 

WSWR

AutoIT Гуру
Сообщения
941
Репутация
363
inververs

Хотел сделать с поворотом всей картинки, но какие-то глюки с этим вариантом, поэтому взял пример от C2H5OH с разрезанием картинки на части с сильной разницой в цвете http://autoit-script.ru/index.php?topic=11556.0
Скрипт "режет" капчу и сравнивает каждую часть с текстовым образцом цифры.
Вроде работает. Только нужно изменить получение картинки с диска на получение из буфера.

Код:
#include <GDIP.au3>
#include <Color.au3>
#include <Crypt.au3>
_Glob()

Global $delta = 10 ; допустимое отклонение цвета фона от образца
$sImage = @ScriptDir & '\1327.png'

_GDIPlus_Startup()
$hImage = _GDIPlus_ImageLoadFromFile($sImage)
Global $iWidth = _GDIPlus_ImageGetWidth($hImage)
Global $iHeight = _GDIPlus_ImageGetHeight($hImage)
$iBitmap = _GDIPlus_BitmapCreateHBITMAPFromBitmap($hImage)
Global $hBitmap = _GDIPlus_BitmapCreateFromHBITMAP($iBitmap)

Global $Ground = '0x' & Hex(_GDIPlus_BitmapGetPixel($hBitmap, 1, 1), 6) ; получаем образец цвета фона

$xLeft = 0
$xRight = 0
$x = 0
$sTring1 = ''

While $x < $iWidth - 1
	While _BlackLine($x) And $x < $iWidth - 1
		$x += 1
	WEnd
	$xLeft = $x
	While Not _BlackLine($x) And $x < $iWidth
		$x += 1
	WEnd
	$xRight = $x - 1
	$hClone = _GDIPlus_BitmapCloneArea($hBitmap, $xLeft, 0, $xRight - $xLeft + 1, $iHeight, $GDIP_PXF24RGB)
	$countstring = _SegmentMatrix($hClone)	
	Switch _Crypt_HashData($countstring, $CALG_MD5)
		Case $countstring1			
			$sTring1 &= '1'
		Case $countstring2
			$sTring1 &= '2'
		Case $countstring3
			$sTring1 &= '3'
		Case $countstring4
			$sTring1 &= '4'
		Case $countstring5
			$sTring1 &= '5'
		Case $countstring6
			$sTring1 &= '6'
		Case $countstring7
			$sTring1 &= '7'
		Case $countstring8
			$sTring1 &= '8'
		Case $countstring9
			$sTring1 &= '9'
		Case $countstring0
			$sTring1 &= '0'
	EndSwitch
	_GDIPlus_ImageDispose($hClone)
WEnd

_GDIPlus_ImageDispose($hImage)
_WinAPI_DeleteObject($iBitmap)
_WinAPI_DeleteObject($hBitmap)
_GDIPlus_Shutdown()

MsgBox(0, 'Заголовок', $sTring1)

Func _SegmentMatrix($hBitmap1)
	Local $tMap1, $bData, $Width1, $Height1
	_GDIPlus_Startup()
	$Width1 = _GDIPlus_ImageGetWidth($hBitmap1)
	$Height1 = _GDIPlus_ImageGetHeight($hBitmap1)
	$tMap1 = _GDIPlus_BitmapLockBits($hBitmap1, 0, 0, $Width1, $Height1, $GDIP_ILMREAD, $GDIP_PXF32ARGB)
	$bData1 = DllStructGetData(DllStructCreate('byte[' & ($Width1 * $Height1 * 4) & ']', DllStructGetData($tMap1, 'Scan0')), 1)
	_GDIPlus_BitmapUnlockBits($hBitmap1, $tMap1)
	_GDIPlus_BitmapDispose($hBitmap1)
	Return StringTrimLeft($bData1, 2)
EndFunc   ;==>_SegmentMatrix

Func _BlackLine($stolb)
	Local $color
	For $i = 0 To $iHeight - 1
		$color = '0x' & Hex(_GDIPlus_BitmapGetPixel($hBitmap, $stolb, $i), 6)
		If Abs(_ColorGetRed($color) - _ColorGetRed($Ground)) > $delta Or _
				Abs(_ColorGetGreen($color) - _ColorGetGreen($Ground)) > $delta Or _
				Abs(_ColorGetBlue($color) - _ColorGetBlue($Ground)) > $delta Then Return False
	Next
	Return True
EndFunc   ;==>_BlackLine

Func _Glob()
	Global $countstring1 = '0xE1AFE94DC29AF51804FC8C75E952BB00'
	Global $countstring2 = '0x08224666CF1D9524B7A6E323AC9C8424'
	Global $countstring3 = '0x2AFE694C5E3FC663315FB6D65983F86E'
	Global $countstring4 = '0xEA0C6F46E4B6BFFEC67E23F89AAA55EF'
	Global $countstring5 = '0x4C4FA1B0B5CAFB68E86FF8ECD87CC490'
	Global $countstring6 = '0xFBD76FFAA30C61E29D0ABB74C43F5C8E'
	Global $countstring7 = '0x3AB9ED7A8DE0A26AEA2056954567B6C2'
	Global $countstring8 = '0xD4A7A881A0E00E656DF05F7FDC857E47'
	Global $countstring9 = '0x82A5549839B3602EF5CDEBA21FEA6061'
	Global $countstring0 = '0x8E99DA0D20F02A3CA3781326B45A98B5'
EndFunc   ;==>_Glob


GDIP.au3 здесь http://autoit-script.ru/index.php?topic=1384.0
 

Black_Hole

Знающий
Сообщения
126
Репутация
11
inververs

создаем массив 43х20, получаем пиксели с картинки и отправляем в массив, все что фон 0, остальное 1, делаем алгоритм сравнения 8х8 бит для 10 цифр, сравниваем, выводим результат или (отправляем в буфер и издаем сигнал), все просто ;)

Ps. С репутацией >100 решить задачу минут 10
 
Автор
inververs

inververs

AutoIT Гуру
Сообщения
2,135
Репутация
465
WSWR
Отлично, работает! Правда осталось неясным что такое Scan0 в структуре, а так принцип ясен. Думаю можно будет упростить, убрав определение границ цифр, они всегда будут на одном и том же месте, и убрать определения цвета подложки, он тоже постоянный.
Спасибо!


Добавлено:
Сообщение автоматически объединено:

Black_Hole
Ну если просто, покажите код 8)
 
Верх