- Dünyada eskiden beri kullanılan, Türkiye'de de son zamanlarda hızla yaygınlaşan bir sistemle hepimiz bir şekilde tanıştık.
- Türkiye'deki örnekleri ÖSYM Bilgi Sistemi, T.C.Kimlik bilgi sorgulama ve SSK Pirim ödemeleri sorgulama gibi sitelerde biz bilgisayar programcılarının otomasyonunu engellemeyi amaçlayan ama bireysel kullanıcıların bu bilgiden faydalanmasını sağlayan bir sistemi ele alıyoruz.
- Bu makalemizde gerek forumlarda gerekse news grouplarda bana sıkça sorulan bu kodların bilgisayara nasıl okutulabileceği sorusuna açıklık getirmek için, uygulanabilecek tekniklerden OCR (optik karakter tanımlama'nın İngilizcesi Optical Character Recognition) tekniğini irdeliycez.
- Makalede, sadece Captcha kod çözümlemeyi değil, Canvas operasyonlarının nasıl daha etkin kullanılacağını da göreceğiz.

- Bu sistem için oluşturulan dinamik resimlere CAPTCHA deniyor..
- Şimdi başlayalım...TR Wikipedia yazdı:CAPTCHA (Completely Automated Public Turing test to tell Computers and Humans Apart) Carnegie Mellon School of Computer Science tarafından geliştirilen bir projedir. Projenin amacı bilgisayar ile insanların davranışlarının ayırt edilmesidir ve daha çok bu ayrımı yapmanın en zor olduğu web ortamında kullanılmaktadır.
CAPTCHA projesinin bazı uygulamalarına çoğu web sayfalarında rastlamak mümkündür. Üyelik formlarında rasgele resim gösterilerek formu dolduran kişiden bu resmin üzerinde yazan kelimeyi girmesi istenir. Buradaki basit mantık o resimde sadece insan tarafından okunabilecek bir program tarafından okunması zor olan bir kelime oluşturmak. Eğer forma girilen kelime resimdeki ile aynı değilse ya formu dolduran kişi yanlış yaptı ya da formu dolduran bir program önermesini yapabilir.

- Bir resimdeki karakteri tanımak için aşamalı bazı işlemlerden geçmesi gerekiyor.
- Örnek olarak Türk Telekom ADSL Kota Kullanım Bilgileri sitesi linki olan http://adslkota.ttnet.net.tr/adslkota/login.jsp linkindeki resimli kodları üreten http://adslkota.ttnet.net.tr/adslkota/jcaptcha linkini ele alalım.

şeklinde kod üretilir. Bunu almanın yollarını forumda irdelemiştik. Ref: viewtopic.php?t=5531#106731
- Bu gördüğünüz kod farklı renklerde ve karakterden oluşan, aynı zamanda karakter sayısı ve yeri sabit olmayan dinamik bir kod resmi.
- Bu resmi işlemek için native olarak tanınan Bitmap formatında alıyoruz.
- İşimizi kolaylaştırmak için ilk olarak bu resmi siyah beyaz formata dönüştürüyoruz. Nasıl mı ? Tabii ki tüm makale boyunca OCR işlemini yapacağımız Canvas operasyonlarıyla...
Kod: Tümünü seç
Procedure SiyahBeyazYap( Image: TImage );
Var
i,j : Integer;
begin
With Image.Picture.Bitmap do begin
PixelFormat := pf4Bit;
For i := 0 to Width-1 do
For j := 0 to Height-1 do
If Canvas.Pixels[i,j] < clWhite
then Canvas.Pixels[i,j] := clBlack;
end;
end;

- Resmimiz bu hale geldi. Orjinal resme yakından bakacak olursanız, karakterlerin etrafında bozulmalar vardı. Fonksiyonumuzda siyah beyaza dönüştürürken < clWhite diyerek net bir siyat ve net bir beyaz elde ettik.

- Sıradaki işlem yeri yurdu belli olmayan karakterleri tespit etmek, her birini sınırlarından alıp müstakil basamak resimcikler oluşturmak. Bunun için soldan sağa, yukarıdan aşağıya ve aşağıdan yukarıya döngüler kurarak siyaha rastlaya kadar kırpma işlemi yapacak bir PROCEDURE daha yazıcaz.
Kod: Tümünü seç
Procedure DilimlereAyir( Image:TImage; HedefDizin:String );
Var
Harf : TBitmap;
Hedef : TFileName;
i, j,
Sayac : Integer;
oncekison,
ybas, yson,
xbas, xson : Integer;
xGordum : Boolean;
begin
Harf := TBitmap.Create;
Sayac := 0;
xBas := 1;
xSon := 1;
oncekiSon := 0;
While (xBas > 0) AND ( xSon > 0 ) do begin
xBas := 0;
xSon := 0;
For i := oncekison to Image.Picture.Bitmap.Width do begin
xGordum := False;
For j := 0 to Image.Picture.Bitmap.Height do begin
If Image.Picture.Bitmap.Canvas.Pixels[i,j] = clBlack
then xGordum := True;
end;
If xGordum then begin
If (xBas = 0) then xBas := i;
end else begin
If (xSon = 0) AND (xBas > 0) then xSon := i;
end;
If (xBas > 0) AND (xSon > 0) then break;
end;
If (xBas > 0) AND (xSon > 0) then begin
// Üstten Bulalım...
yBas := 0;
For j := 0 to Image.Picture.Bitmap.Height do begin
For i := xBas to xSon do begin
If Image.Picture.Bitmap.Canvas.Pixels[i,j] = clBlack then
begin
yBas := j;
BreaK;
end;
end;
If yBas > 0 then break;
end;
// Alttan Bulalım...
ySon := 0;
For j := Image.Picture.Bitmap.Height downto 0 do begin
For i := xBas to xSon do begin
If Image.Picture.Bitmap.Canvas.Pixels[i,j] = clBlack then
begin
ySon := j;
BreaK;
end;
end;
If ySon > 0 then break;
end;
Harf.Height := ySon-yBas+1;
Harf.Width := xSon-xBas+1;
Harf.Canvas.CopyRect( Harf.Canvas.ClipRect, Image.Picture.Bitmap.Canvas, Rect(xBas, yBas, xSon, ySon) );
Inc(Sayac);
Hedef := Format('%sHarf_%.5d.BMP', [HedefDizin, Sayac]);
Harf.SaveToFile(Hedef);
OncekiSon := xSon;
end;
end;
Harf.Free;
end;





- Şimdi elimizde her harfin siyah beyaz hali mevcut. OCR işlemi henüz başlıyor...

- OCR işlemi yapılacak sitenin iyi etüd edilmesi gerekmektedir. Ne tip karakterler kullanılıyor, büyük küçük harf kullanımı vb. kriterler üretilmesi sağlanan birden fazla captcha kod incelenerek öğrenilebilir. Bu bize ne sağlar ? Aşağıdaki sorulara cevap bulmamızı ve zamandan tasarruf sağlamamızı sağlar.

1. Hangi Font tipleri kullanılıyor ? Arial, Times New Roman, vb.
2. Hangi Font stilleri kullanılıyor ? Bold, Italic, her ikisi, vb.
3. BüyükHarf / KüçükHarf Kullanımı ne ölçüde
- Bu soruların cevabına göre fonksiyon döngümüze karar vericez..
Method 1 : ( kendi ürettiğimiz karakterleri karşılaştırma methodu )
- Sorduğumuz sorulara karşılıkilgilendiğimiz ADSL kota sayfası için cevaplar :
1. Font Adı : "Arial" kullanılıyor.
2. Font Stili : [], [Bold], [Italic], [Bold + Italic] yani her dört ihtimalde de bulunabiliyor.
3. Sadece küçük harf kullanılmış.
- Karakterleri bileşenlerine ayırmıştık. Biz program içerisinden Canvas.TextOut ile benzer karakteri üretmeye çalışıcaz. 26 tane ingilizce küçük harfi sırayla canvas üzerine 4 farklı şekil ve boyutta basıcaz.
- Elimizde her karakter için fiziksel büyüklük Width, Height olarak mevcut olduğuna göre her harfi bu sınırlara kadar büyük olacak şekilde (yani Canvas.TextHeight( cHarf ) aynı genişliği bulana kadar Canvas.Font.Size ile oynayarak) deneme yanılma ile karşılaştırmaya hazırlık yapıcaz. Yapılan karşılaştırma sonucu uyumluluk gözlenirse cHarf'dir diyebilicez.
- Elimizdeki ürettiğimiz karakteri de aynı şartlara dönüştürmek için kenar boşluklarını kırpmamız lazım...
- Aşağıdaki fonksiyon, dilimlere ayırmaya benzer ama sadece bir harf için olanıdır. Canvas.Textout ile bastığımız harfin kenarındaki boşlukları kesmek için kullanıcaz...
Kod: Tümünü seç
Procedure BosluklariKirp( Bitmap:TBitmap );
Var
Harf : TBitmap;
i, j : Integer;
ybas, yson,
xbas, xson : Integer;
xGordum : Boolean;
begin
Harf := TBitmap.Create;
xBas := 0;
xSon := 0;
// Soldan -> Sağa
For i := 0 to Bitmap.Width do begin
xGordum := False;
For j := 0 to Bitmap.Height do begin
If Bitmap.Canvas.Pixels[i,j] = clBlack
then xGordum := True;
end; // For j
If xGordum then begin
If (xBas = 0) then xBas := i;
end else begin
If (xSon = 0) AND (xBas > 0) then xSon := i;
end; // If xGordum
If (xBas > 0) AND (xSon > 0) then break;
end; // For i
If (xBas > 0) AND (xSon > 0) then begin
// Yulardan -> Aşağı
yBas := 0;
For j := 0 to Bitmap.Height do begin
For i := xBas to xSon do begin
If Bitmap.Canvas.Pixels[i,j] = clBlack then
begin
yBas := j;
BreaK;
end;
end;
If yBas > 0 then break;
end;
// Alttan -> Yukarı
ySon := 0;
For j := Bitmap.Height downto 0 do begin
For i := xBas to xSon do begin
If Bitmap.Canvas.Pixels[i,j] = clBlack then
begin
ySon := j;
BreaK;
end;
end;
If ySon > 0 then break;
end;
Harf.Height := ySon-yBas+1;
Harf.Width := xSon-xBas+1;
Harf.Canvas.CopyRect( Harf.Canvas.ClipRect, Bitmap.Canvas, Rect(xBas, yBas, xSon, ySon) );
Bitmap.Assign(Harf);
end;
Harf.Free;
end;
- Karşılaştırma Procedure
Kod: Tümünü seç
Procedure Karsilastir(BitmapA, BitmapB: TBitmap;
var Alan, Benzer, Fark: Integer);
Type
TRGB = Array [WORD] of TRGBTriple;
pRGBTripleArray = ^TRGB;
Var
i, j : Integer;
rowA, rowB : pRGBTripleArray;
begin
If (BitmapA.width = BitmapB.width) and
(BitmapA.height = BitmapB.height) then begin
BitmapA.PixelFormat := pf24bit;
BitmapB.PixelFormat := pf24bit;
Alan := BitmapA.width * BitmapA.height;
Benzer := 0;
Fark := 0;
for j := 0 to BitmapA.height-1 do
begin
rowA := BitmapA.Scanline[j];
rowB := BitmapB.Scanline[j];
for i := 0 to BitmapA.width-1 do
begin
if (rowA[i].rgbtRed = rowB[i].rgbtRed) and
(rowA[i].rgbtGreen = rowB[i].rgbtGreen) and
(rowA[i].rgbtBlue = rowB[i].rgbtBlue)
then INC(Benzer)
else INC(Fark)
end
end
end else begin
Alan := BitmapA.width * BitmapA.height;
Benzer := 0;
Fark := Alan;
end;
end;
Kod: Tümünü seç
procedure TForm1.Button2Click(Sender: TObject);
Var
iFontS : Integer;
Alan, Benzer, Fark : Integer;
Sayac : Integer;
begin
Image1.Picture.LoadFromFile('D:\Programcilik\Delphi\Pasif06\Captcha_Tespit\MAKALE\Harf_00002.BMP');
With Image2.Picture.Bitmap.Canvas do begin
For Sayac := Ord('a') to Ord('z') do begin
iFontS := 10;
Font.Name := 'Arial';
Font.Style := [];
Font.Size := iFontS;
While (TextHeight(chr(Sayac))-18) < Image1.Picture.Height do
begin
Font.Size := iFontS;
Application.ProcessMessages;
Inc(iFontS);
end;
Image2.Picture.Bitmap.Width := TextWidth ( Chr(Sayac));
Image2.Picture.Bitmap.Height := TextHeight( Chr(Sayac));
TextOut(0, 0, chr(Sayac));
SiyahBeyazYap( Image2 );
BosluklariKirp(Image2.Picture.Bitmap);
Image2.Picture.Bitmap.Width := Image1.Picture.Bitmap.Width;
Image2.Picture.Bitmap.Height := Image1.Picture.Bitmap.Height;
Karsilastir( Image1.Picture.Bitmap, Image2.Picture.Bitmap, Alan, Benzer, Fark );
// iki resim arasındaki fark, toplam alanın %10'undan küçük ise Bulduk (eurika) !!!
If Fark < Trunc(Alan*10/100)
then Memo1.Lines.Add( Format('%s (*): %d / %d', [Chr(Sayac), Benzer, Fark]) )
else Memo1.Lines.Add( Format('%s ( ): %d / %d', [Chr(Sayac), Benzer, Fark]) );
end;
end;
end;

Sonuç tablo : ( g yanındaki (*) dikkatinizi çekerim. )
Kod: Tümünü seç
a ( ): 572 / 279
b ( ): 418 / 433
c ( ): 622 / 229
d ( ): 474 / 377
e ( ): 589 / 262
f ( ): 421 / 430
g (*): 833 / 18
h ( ): 428 / 423
i ( ): 399 / 452
j ( ): 401 / 450
k ( ): 369 / 482
l ( ): 414 / 437
m ( ): 502 / 349
n ( ): 566 / 285
o ( ): 655 / 196
p ( ): 616 / 235
q ( ): 727 / 124
r ( ): 451 / 400
s ( ): 484 / 367
t ( ): 407 / 444
u ( ): 561 / 290
v ( ): 337 / 514
w ( ): 411 / 440
x ( ): 325 / 526
y ( ): 313 / 538
z ( ): 437 / 414
- Bu method ise dlimlere ayırıp incelediğimiz karakter tekleri, eğer tutarlılık arz ediyorsa bunları bir klasörde veya Resource olarak EXE içine gömerek yedekte bulundurur, her birini karşılaştırıp uyumlu olanın karşılığı olan cHarf'dir diyebiliriz.
- Yukardaki verdiğimiz karşılaştırma fonksiyonu tek başına yeterli olmaktadır.
Sonuc :
- Web Sitesinlerinde Resimli Kodları ( CAPTCHA kodlar ) günden güne iyice yaygınlaşıyor ve kusursuzlaşıyor. Biz programcılara ter döktüren CAPTCHA kodlar hakkında çözümleme teknikleri de her geçen gün daha da gelişiyor.
- Bu makalemizde dilim / klavyem döndüğünce bu işin ABC'si olan Canvas operasyonlarını anlatmaya çalıştım.
- Yapay zeka vb. teknikler için de altyapı teşkil ettiğinden ilgilenenlere önemli ve faydalı bir başlangıç olacaktır. Bu örneklediğim karşılaştıma teknikleri, tek başına bir çok sitede yeterli olduğunu defalarca kanıtlamıştır. Şahsen kullanmaktayım.

- Lütfen anlatılanları kavramaya çalışın, biliyorum ki hemen akabinde benden anahtar teslimi kod talepleri illa ki olacaktır. (tecrübe ile sabittir) Zaten balık tutumasını bırakın kodları ardarda koyduğunuzda proje kendiliğinden oluşacak şekilde örnekler sundum. Gördüğünüz üzere Balık kovaya kadar kondu, pazara götürmek için bir zahmet kendiniz biraz çaba gösterin.


- Çalışmalarınızda başarılar dilerim...
