Разгадываем капчу в qt

Сегодня писал функцию для разгадывания капчи на очередном буксе, и решил описать сам процесс в блоге, может кому в будущем пригодится)) Разгадывание будет реализовано на qt, но данные методы можно применять где угодно, в почти в любом языке есть функции для роботы с картинками.

Нашей жертвой будет букс onlinx.ru.

Капча простая, обычные циферки.

onlinxcaptcha

Я специально выбрал простую капчу для статьи, чтобы тем кто в этом деле не имеют никакого опыта, было проще уловить суть процесса.  Думаю в следующих статьях я приведу пример разгадывания капчи по сложнее.

Первое что нужно сделать, это подготовить шаблон цифр, по которым мы буде сравнивать изображение. Так как цифры абсолютно идентичны – не наклоняются и не искривляются, то будем копировать их прямо с капчи.

Для начала скачаем капчу и сохраним в файл, метод DownloadCaptcha(); Тут всё просто. комментировать думаю не стоит.


void MainWindow::DownloadCaptcha(){
QTcpSocket sSoket;
QByteArray Header,data;
int f1= 0;

Header += "GET /captcha.php HTTP/1.0\r\n";
Header += "Host: onlinx.ru\r\n";
Header += "User-Agent: Opera/9.80 (Windows NT 5.1; U; ru) Presto/2.8.131 Version/11.00\r\n";
Header += "Connection: close\r\n\r\n";
sSoket.connectToHost("onlinx.ru" ,80);
sSoket.write(Header);
sSoket.waitForBytesWritten();
while(sSoket.waitForReadyRead()){
data.append(sSoket.readAll());
}
sSoket.disconnect();
f1 = data.indexOf("\r\n\r\n");
f1+=4;
data = data.mid(f1);

QFile file;
file.setFileName("captcha.png");
if(file.open(QIODevice::WriteOnly|QIODevice::Truncate)){
file.write(data);
file.close();
}
}

 

Следующий шаг, нужно взять каждую цифру и отдельно сохранить.

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

onlinxcaptcha2

Копируем цифры и сохраняем.

void MainWindow::CropCaptcha(){

 QImage img("captcha.png");
 QImage img2;

 QRect rc;
 QRgb rgb;
 QRgb rgbWhite;
 rc = img.rect();

 int First = 0;
 bool FindBlack = 0,NowBlack=0;
 rgbWhite = img.pixel(0,0);

 for(int width = 0;width<rc.width();width++){
 FindBlack = 0;
 for(int height = 0;height<rc.height();height++){

 rgb = img.pixel(width,height);

 if( rgb != rgbWhite){
 FindBlack = 1;
 }
 }
 if(FindBlack){
 if(!NowBlack){
 First = width;
 }
 NowBlack = 1;

 }else{
 if(NowBlack){
 img2 = img.copy(First,0,width-First-1,rc.height());
 img2.save("img/"+QString::number(num)+"img.png");
 num++;
 NowBlack = 0;
 }
 }
 }
}

Вызываем наши 2 функции раз 30(в цикле), чтобы нам попались все варианты чисел

 for(int i =0;i<30;i++){
 DownloadCaptcha();
 CropCaptcha();
 }

Идем в папку img в которой сохранились наши картинки
onlinxcaptcha3
убираем повторы, а картинкам даем имена исходя из цифр которые на них изображены, получится следующие
onlinxcaptcha4
Всё, шаблоны готовы,осталось написать функцию которая будет по ним искать картинки на капче, это не сложно, логика поиска будет следующая, берем шаблон изображения и подставляем к оригиналу, при этом каждый раз смещаясь на 1 пиксель в право.
onlinxcaptcha5

Метод ReadCaptcha();

QByteArray MainWindow::ReadCaptcha(){

 QImage img("captcha.png");

 QImage img1("img/1.png");
 QImage img2("img/2.png");
 QImage img3("img/3.png");
 QImage img4("img/4.png");
 QImage img5("img/5.png");
 QImage img6("img/6.png");
 QImage img7("img/7.png");
 QImage img8("img/8.png");
 QImage img9("img/9.png");
 QImage img0("img/0.png");

 QImage nImg;

 QRect rc = img.rect();
 QRect rc2;
 bool FindPict = 0;
 QMap<int,QChar> FindList;
 QByteArray Result;
 QChar cNum;



 for(int i = 0;i<=9;i++){

 if( i == 1){
 rc2 = img1.rect();
 nImg = img1;
 cNum = '1';
 }else if( i == 2){
 rc2 = img2.rect();
 nImg = img2;
 cNum = '2';
 }else if( i == 3){
 rc2 = img3.rect();
 nImg = img3;
 cNum = '3';
 }else if( i == 4){
 rc2 = img4.rect();
 nImg = img4;
 cNum = '4';
 }else if( NowPict == 5){
 rc2 = img5.rect();
 nImg = img5;
 cNum = '5';
 }else if( i == 6){
 rc2 = img6.rect();
 nImg = img6;
 cNum = '6';
 }else if( i == 7){
 rc2 = img7.rect();
 nImg = img7;
 cNum = '7';
 }else if( i == 8){
 rc2 = img8.rect();
 nImg = img8;
 cNum = '8';
 }else if( i == 9){
 rc2 = img9.rect();
 nImg = img9;
 cNum = '9';
 }else if( i == 0){
 rc2 = img0.rect();
 nImg = img0;
 cNum = '0';
 }



 for(int x = 0;x<rc.width()-rc2.width();x++){
 FindPict = 1;

 for(int nx = 0;nx<rc2.width();nx++){
 if(!FindPict){
 break;
 }
 for(int ny = 0;ny<rc2.height();ny++){
 if(img.pixel(x+nx,ny) != nImg.pixel(nx,ny)){
 FindPict = 0;
 break;
 }
 }
 }

 if(FindPict){
 FindList.insert(x,cNum);
 }

 }
 }


 QMapIterator<int,QChar> it(FindList);
 while(it.hasNext()){
 Result += it.next().value();
 }

 return Result;


}

 

В код статьи вникать ненужно, он лишь для наглядного примера, сразу понять что там происходит будет сложно, поэтому лучше поэтапно попробовать самому периодически в него подглядывая.
На этом всё.

 

2 thoughts on “Разгадываем капчу в qt

  1. можно просто посчитать кол-во точек в непрерывной площади и контуру — и оно разное для всех цифр. Еще лучше разбить на 2 части-нижнее и верхнее и посчитать.

    Алгоритм сравнения по точкам очень медленный когда их много. Сначала лучше сравнить 3-5 точек в ключевых позициях, а если равны, то сравнить все остальные.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *