Delphi. Розпізнаємо капчу

begin
І знову привіт. У цій статті ми розглянемо так зване розпізнавання "КАПЧІ". І так трохи поринемо в історію. Давним-давно вже у багатьох галузях техніки використовуються різні автомати та пристрої, більш-менш вдало вирішують завдання розпізнавання (це автомат для сортування поштових конвертів за індексом, і зенітна ракета, що захоплює гаряче сопло літакового двигуна, але ігнорує сонце, і різні системи аналізу супутникових знімків, і голосовий виклик вашого мобільника, і багато іншого), тим не менш, людей, впевнених у непереборній складності алгоритмів, дуже багато. Розглянемо алгоритм, який можна навчати.

Особливу пікантність алгоритму надає той факт, що його математичне обґрунтування було запропоновано радянськими математиками на початку 60-х років (тобто в той час, коли комп'ютер не завжди містився в середньостатистичній квартирі), а ще через 15 років була доведена його вельми глибока аналогія з дуже популярним нині нейромережевим методом.

Давайте спочатку відкриємо делфі і накидаємо на форму компоненти. 2 Image (Перше назвав Kapcha, друге kap1), 1 buuton, 1 edit та 1 memo. Мою форму ви зможете побачити на малюнку 1

begin
Мал. 1 Далі нам треба знайти капчу та проаналізувати його. Я вибрав не дуже складну, простеньку капчу і збережи його у форматі *.bmp (Рис.2). Мал. 2 Трохи подумавши приходимо до висновку, що нам легше буде працювати, якщо зображення зробити чорно-білим, тобто фон білим, а наші циферки чорними при чорними. Як це можна реалізувати? Дивимося далі. Підвантажуємо нашу капчу в Image (Kapcha). Зображення на канві є ні що інше, як матрицю чисел, тобто кольорів. Ми будемо звертатися до кожного пікселя та змінювати його колір на потрібний нам (фон-білий, цифри-чорні). // Визначаємо колір тла Fon:=Kapcha.Canvas.Pixels[0,0]; // Перефарбовуємо всі не фонові пікселі в білі інші в чорні For i:=0 to Kapcha.Picture.Width do For j:=0 to Capcha. .Canvas.Pixels[i,j]=Fon then Kapcha.Canvas.Pixels[i,j]:=RGB(255,255,255) else Kapcha.Canvas.Pixels[i,j]:= 0; Тут ми організували 2 вкладені цикли і змінили колір фону та цифер. Далі нам слід вирізати кожну цифру та поміщати у другий image (kap1). Щоб це зробити нам треба знайти координати початку та кінця цифри це реалізовано у процедурі Find_Digit. //Здійснюємо пошук цифр procedure TForm1.Find_Koordinati(var L: info); var i,j,i1,j1:integer; Find_Digit:info; proverka:boolean; begin Find_Digit.X1:=0; Find_Digit.Y1: = 0; Find_Digit.X2:=0; Find_Digit.Y2:=0;

Для j: = 0 до Kapcha>begin Find_Digit.Y1:=j; break; end; if Find_Digit.Y1<>0 then break; end;

Для i: = 0 до Kapcha>begin Find_Digit.X1:=i; break; end; if Find_Digit.X1<>0 then break; end;

proverka: = true; Для i:=Find_Digit.X1 на Kapcha.Picture.Width до початок На j:=Find_Digit.Y1 на Kapcha.Picture.Height do If Kapcha.Canvas.Pixels[i, j]=0 then begin proverka:=false; break; end; якщо не (proverka) then proverka:=true else begin Find_Digit.X2:=i; break; end; end;

proverka: = true; For j:=Find_Digit.Y1 to Kapcha.Picture.Height do begin For i:=Find_Digit.X1 to Kapcha.Picture.Width do IfKapcha.Canvas.Pixels[i,j]=0 then begin proverka:=false; break; end; якщо не (proverka) then proverka:=true else begin Find_Digit.Y2:=j; break; end; end;

kap1.Canvas.FillRect(kap1.Canvas.ClipRect); i1:=0; j1:=0; For i:=Find_Digit.X1 to Find_Digit.X2 do begin For j:=Find_Digit.Y1 to Find_Digit.Y2 do begin kap1.Canvas.Pixels[i1,j1 ]:=Kapcha.Canvas.Pixels[i,j]; Kapcha. Canvas. Pixels [i, j]: = RGB (255,255,255); inc(j1); end; j1:=0; inc(i1); end; end; У даній процедурі також використовуються вкладені цикли в яких перевіряються пікселі якщо вони чорні, то це початок, збережемо (X1,Y2) їх потім проводимо пошук білих писклей не спочатку зображення, а зі збережених координат (X1,Y2) якщо знайшли білі пікслі на весь стовпець значить знайшли кінець, зберігаємо в (X2,Y2). Далі копіюємо цифру в кар1 і стираємо його в kapcha (щоб він не заважав для подальшого пошуку). Всі ми вже знаємо, як виділяти цифри, тепер залишилося розпізнати їх. Робиться це так. 1) Підвантажуємо зразки 2) Порівнюємо нашу цифру з зразками і знаходимо потенціал 3) Знаходимо максимальний потенціал і ось індекс максимального потенціалу і буде наша розпізнана цифра. Тепер докладно розглянемо кожний пункт. 1. Підвантажуємо зразки Для початку ми створюємо самі зразки (вони знаходяться у мене в папці "atalon") і зберігаємо їх у форматі *.bmp. Розміри еталонів повинні збігатися з розмірами kap1 (і розмірами цифр на капчі). Процедура LoadData подружить зразки: procedure TForm1.LoadData; var j:integer; path:string; begin path := ExtractFilePath(Application.ExeName)+'\atalon\'; for j := 0 to 3 do begin Data[j] := TBitmap.Create; Data[j].LoadFromFile(path + IntToStr( + '.bmp'); end; end; Процедура підвантажує їх і записує до масиву Data. 2) Порівнюємо нашу цифру з еталонами і знаходимо потенціал За порівняння відповідає функція Compare function TForm1.Compare(b1, b2: TBitmap): integer; var i,j,count:integer; begin count := 0; for i := 0 to 17 do for j := 0 to 28 do if b1.Canvas.Pixels[i,j] <> b2.Canvas.Pixels[i,j] then inc(count); Result: = count; end; А код знаходження потенціалу наведено нижче: for i := 0 to 3 do p[i] := 0; for j := 0 to 3 do begin r := Compare(kap1.Picture.Bitmap,Data[j]); p[j] := p[j] + 1000000/(1+r*r); end;

memo1.lines.clear; max := 0; maxNO := 0; for i := 0 to 3 do if p[i] > max then begin max := p[i]; maxNO := i; end;

for i := 0 to 9 do memo1.lines.add(floattostr(p[i]));

case MaxNO of 0: chislo:=0; 1: chislo:=3; 2: chislo:=5; 3: chislo:=7; end;

Edit1.Text := Edit1.Text + IntToStr(chislo); Тут ми обчислюємо потенціал за такою формулою: І завантажуємо потенціали в мемо. Знаходимо максимум з них і зберігаємо його індекс в зміну MaxNO. І в кінці по ньому знаходимо нашу цифру та виводимо в Edit. Особливо складного тут немає, просто треба трохи подумати і все. Якщо що не зрозуміло дивіться вихідник.

Запропонований метод дає дуже непогані результати як на машинописних, і на рукописних цифрах. Алгоритм легко може бути перенавчений для розрізнення інших символів (латинських/українських букв, розділових знаків тощо). Для підвищення надійності розпізнавання можна передбачити еталони, що сильно розрізняються між собою (для того, щоб якомога сильніше розкидати еталонні точки в межах компактної множини).