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

Продолжу свою тему про разгадывание капчи , теперь как я и обещал возьмем капчу посложнее, например капчу с rewbux.com

rewbuxcaptha1

Капча особенная тем что, имеет шум на фоне, разные цвета чисел, и самое противное, поворачивает цифры пор разный угол.

Первое что нeжно сделать это очистить капчу от шума и превратить её в чёрно белую картинку.

Цвет идет от 0.0.0 — чёрный до 255.20.250 — белый, мы возьмем середину где-то 150,150,150 , далее все пиксели что меньше нашей золотой середины мы сделаем чёрными, а всё остальные белыми, получится следующие

black

Вынесу очистку в отдельную функцию.

imgClear( QImage &img);


void imgClear( QImage &img){
 QRect rc;
 QRgb rgbMiddle;
 QRgb rgbBlack;
 QRgb rgbWhite;
 QColor color;

 color.setRgb(150,150,150);
 rgbMiddle = color.rgb();
 color.setRgb(255, 250,250);
 rgbWhite = color.rgb();
 color.setRgb(0, 0, 0);
 rgbBlack = color.rgb();

rc = img.rect();
 for(int width = 0;width<rc.width();width++){
 for(int height = 0;height<rc.height();height++){
 if(img.pixel(width,height) > rgbMiddle ){
 img.setPixel(width,height,rgbWhite);
 }else{
 img.setPixel(width,height,rgbBlack);
 }

}
 }

}

Дальше, цифры нужно отделить и обрезать по краям

black2

Обрезку по краям тоже вынесу в отдельную функцию

imgCrop( QImage &img );


void imgCrop( QImage &img ){

QRect rc;
 rc = img.rect();
 bool FindBlack = 0,NowBlack = 0;
 int First = 0,Second = rc.width();
 QRgb rgbBlack;
 QColor color;
 color.setRgb(0, 0,0);
 rgbBlack = color.rgb();
 color.setRgb(255, 0,0);

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

if( img.pixel(width,height) == rgbBlack){
 FindBlack = 1;
 }
 }
 if(FindBlack){
 if(!NowBlack){
 First = width;
 NowBlack = 1;
 }
 }else{
 if(NowBlack){
 Second = width;
 break;
 }
 }
 }
 img = img.copy(First,0,Second-First,rc.height());
 //---------------------------------------------------------
 FindBlack = 0;
 NowBlack = 0;
 First = 0;
 rc = img.rect();
 for(int height = 0;height<rc.height();height++){
 FindBlack = 0;
 for(int width = 0;width<rc.width();width++){

if( img.pixel(width,height) == rgbBlack){
 FindBlack = 1;
 }
 }
 if(FindBlack){
 if(!NowBlack){
 First = height;
 NowBlack = 1;
 }
 }else{
 if(NowBlack){
 Second = height;
 break;
 }
 }
 }
 img = img.copy(0,First,img.rect().width(),Second-First);
 //------------------------------------------------------
}

Следующий шаг это выравнивание изображения, делается оно так, мы поворачиваем картинку от —90 до 90 градусов, при этом каждый раз замеряем ее ширину, и результатом будет градус при котором картинка имеет меньшую ширину, тоесть практически ровная))

Для этого мне нужно 2 функции

void CopyImage(QImage& img1,int width,int height,const QImage& img2); — нужно для того чтобы скопировать картинку на холст в 3 раза больше ее самой, чтобы функция для которая будет ее поворачивать могла свободно ее вращать без утраты качества.

void CopyImage(QImage& img1,int width,int height,const QImage& img2){
for(int x = 0;x<img2.rect().width();x++){
for(int y = 0;y<img2.rect().height();y++){
if(img1.rect().width() >= x+width && img1.rect().height() >= y+height){
img1.setPixel(x+width,y+height,img2.pixel(x,y));
}
}
}
}

Ну и сама функция поворота

QImage imgRotate(const int& degreese , const QImage& img){

//-----------------------------подготовка холста-----------------------
int Big = 0;
Big = img.rect().height();
if(img.rect().width() > Big){
Big = img.rect().width();
}
QImage canvas(Big*5,Big*5,img.format());
canvas.fill(Qt::white);
CopyImage(canvas,Big*2,Big*2,img);
QPixmap shipPixels;
shipPixels = QPixmap::fromImage(canvas);
//-----------------------------подготовка холста-----------------------
QPixmap rotatePixmap(shipPixels.size());
rotatePixmap.fill(Qt::white);

QPainter p(&rotatePixmap);
p.setRenderHint(QPainter::Antialiasing);
p.setRenderHint(QPainter::SmoothPixmapTransform);
p.setRenderHint(QPainter::HighQualityAntialiasing);
p.translate(rotatePixmap.size().width() / 2, rotatePixmap.size().height() / 2);
p.rotate(degreese);
p.translate(-rotatePixmap.size().width() / 2, -rotatePixmap.size().height() / 2);
p.drawPixmap(0, 0, shipPixels);
p.end();
canvas = rotatePixmap.toImage();
imgClear(canvas);
imgCrop(canvas);
return canvas;

}

В итоге получаем функцию CropCaptcha(); которая подготовит нам шаблоны изображений

void MainWindow::CropCaptcha(){

 QImage img("captcha.png");
 QImage img1,img2,img3,tempImg;

 imgClear(img);
 QRgb rgbWhite;
 QColor color;
 color.setRgb(255, 250,250);
 rgbWhite = color.rgb();

 bool FindBlack = 0,NowBlack = 0;
 int First = 0;
 int count = 0;

 //========================== Делим картинки ===========================================
 for(int width = 0;width<img.rect().width();width++){
 FindBlack = 0;
 for(int height = 0;height<img.rect().height();height++){

 if( img.pixel(width,height) != rgbWhite){
 FindBlack = 1;
 }
 }
 if(FindBlack){
 if(!NowBlack){
 First = width;
 NowBlack = 1;
 }
 }else{
 if(NowBlack){
 if(count == 0){
 img1 = img.copy(First,0,width-First,img.rect().height());
 }else if( count == 1){
 img2 = img.copy(First,0,width-First,img.rect().height());
 }else{
 img3 = img.copy(First,0,width-First,img.rect().height());
 }
 count++;
 NowBlack = 0;
 }
 }
 }
 //========================== Делим картинки ===========================================

 //========================== Выравниваем картинки ===========================================
 int MinWidth = 1000;
 QImage GoodImage;
 //----1-----//========================== Выравниваем первую ===========================================

 for(int degrece =-90;degrece<90;degrece++){
 tempImg = imgRotate(degrece,img1);
 if( tempImg.rect().width() < MinWidth ){
 MinWidth = tempImg.rect().width();
 GoodImage = tempImg;
 }
 }
 GoodImage.save("img4/"+QString::number(num)+".png");
 num++;
 //----1-----//========================== Выравниваем первую ===========================================

 //----2-----//========================== Выравниваем вторую ===========================================
 MinWidth = 1000;
 for(int degrece =-90;degrece<90;degrece++){
 tempImg = imgRotate(degrece,img2);
 if( tempImg.rect().width() < MinWidth ){
 MinWidth = tempImg.rect().width();
 GoodImage = tempImg;
 }
 }
 GoodImage.save("img4/"+QString::number(num)+".png");
 num++;
 //----2-----//========================== Выравниваем вторую ===========================================

 //----3-----//========================== Выравниваем третию ===========================================
 MinWidth = 1000;
 for(int degrece =-90;degrece<90;degrece++){
 tempImg = imgRotate(degrece,img3);
 if( tempImg.rect().width() < MinWidth ){
 MinWidth = tempImg.rect().width();
 GoodImage = tempImg;
 }
 }
 GoodImage.save("img4/"+QString::number(num)+".png");
 num++;
 //----3-----//========================== Выравниваем третию ===========================================


 //========================== Выравниваем картинки ===========================================

}

black3

из шаблонов выбираем наилучшие и получаем

black4

Как видите на скрине, 7 и 4 я выбрал кривые, так как именно в таком положении они занимают наименьшую ширину.

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


int 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 tempImg,tImg1,tImg2,tImg3;
QImage * lp_tempImg;

imgClear(img);
QRgb rgbWhite;
QColor color;
color.setRgb(255, 250,250);
rgbWhite = color.rgb();

bool FindBlack = 0,NowBlack = 0;
int First = 0;
int count = 0;

//========================== Делим картинки ===========================================
for(int width = 0;width<img.rect().width();width++){
FindBlack = 0;
for(int height = 0;height<img.rect().height();height++){

if( img.pixel(width,height) != rgbWhite){
FindBlack = 1;
}
}
if(FindBlack){
if(!NowBlack){
First = width;
NowBlack = 1;
}
}else{
if(NowBlack){
if(count == 0){
tImg1 = img.copy(First,0,width-First,img.rect().height());
}else if( count == 1){
tImg2 = img.copy(First,0,width-First,img.rect().height());
}else{
tImg3 = img.copy(First,0,width-First,img.rect().height());
}
count++;
NowBlack = 0;
}
}
}
//========================== Делим картинки ===========================================

//========================== Выравниваем картинки ===========================================
int MinWidth = 1000;
QImage GoodImage;
//----1-----//========================== Выравниваем первую ===========================================

for(int degrece =-90;degrece<90;degrece++){
tempImg = imgRotate(degrece,tImg1);
if( tempImg.rect().width() < MinWidth ){
MinWidth = tempImg.rect().width();
GoodImage = tempImg;
}
}
tImg1 = GoodImage;
//----1-----//========================== Выравниваем первую ===========================================

//----2-----//========================== Выравниваем вторую ===========================================
MinWidth = 1000;
for(int degrece =-90;degrece<90;degrece++){
tempImg = imgRotate(degrece,tImg2);
if( tempImg.rect().width() < MinWidth ){
MinWidth = tempImg.rect().width();
GoodImage = tempImg;
}
}
tImg2 = GoodImage;
//----2-----//========================== Выравниваем вторую ===========================================

//----3-----//========================== Выравниваем третию ===========================================
MinWidth = 1000;
for(int degrece =-90;degrece<90;degrece++){
tempImg = imgRotate(degrece,tImg3);
if( tempImg.rect().width() < MinWidth ){
MinWidth = tempImg.rect().width();
GoodImage = tempImg;
}
}
tImg3 = GoodImage;
//----3-----//========================== Выравниваем третию ===========================================
//========================== Выравниваем картинки ===========================================

//=========================== сверяем результат ==============================================

QMap<int,int> ResultData;
QByteArray EndREsult;

for(int nImg = 0;nImg < 3;nImg++){
if(nImg == 0){
lp_tempImg = &tImg1;
}else if(nImg == 1){
lp_tempImg = &tImg2;
}else if(nImg == 2){
lp_tempImg = &tImg3;
}
ResultData.clear();
for(int i = 0;i<10;i++){

if( i == 0){
tempImg = img0;
}else if( i == 1){
tempImg = img1;
}else if( i == 2){
tempImg = img2;
}else if( i == 3){
tempImg = img3;
}else if( i == 4){
tempImg = img4;
}else if( i == 5){
tempImg = img5;
}else if( i == 6){
tempImg = img6;
}else if( i == 7){
tempImg = img7;
}else if( i == 8){
tempImg = img8;
}else if( i == 9){
tempImg = img9;
}

count = 0;
for(int x = 0;x<tempImg.rect().width();x++){
for(int y = 0;y<tempImg.rect().height();y++){
if( lp_tempImg->rect().width() > x && lp_tempImg->rect().height() > y){
if( tempImg.pixel(x,y) == lp_tempImg->pixel(x,y)){
count++;
}
}
}
}
ResultData.insert(count,i);
}
EndREsult += QByteArray::number(ResultData.last());
}

//=========================== сверяем результат ==============================================

img.save("img4/"+QString::number(num)+"__"+EndREsult+".png");
num++;

}

 

Результат такой

black5

Всего 5 ошибок из 60, что в принципе не плохо.

 

 

Проголосовать за статью

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

  1. Djoser, а ты tesseract принципиально не используешь? Все-таки алгоритмы распознавания на нейронных сетях показывают более высокую точность. Я понимаю, что свои велосипеды незаменимы для лучшего понимания работы алгоритмов распознавания, но развитие нейронных сетей — это принципиально новый скачок и переломный момент в программировании. Очень интересно раскрывает эту тему на ютубе Анатолий Левенчук.

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

  2. а почему белый 255 250 250 ?
    он же 255 255 255
    зачем писать
    color.setRgb(255, 250,250);
    rgbWhite = color.rgb();

    проще = rgb(255,255,255);
    и определить ф-ю

    int rgb (r,g,b){
    QColor color;
    color.setRgb(r, g, b);
    return color.rgb();
    }

    Эти алгоритмы фигня вобщем, сверточные нейронные сети давно делают это лучше.
    Перепиши МП на питон лучше, там куча библиотек и возможностей, С+ уже морально устарел. Свой язык тоже не удобно, лучше в виде библиотек а шаблон это просто скрипт-прога на питоне. на java/php/js тоже можно, но они хуже.

    • 1) Эти алгоритмы я разрабатывал больше года назад, когда понятия не имел о том что такое нейронные сети и тд, по этому, как бы они не выглядели, фигней их называть не надо.
      2) с++? может то питон морально устарел?)

  3. «и самое противное, поворачивает цифры пор разный угол» 🙂
    самое противное это искажение, а еще противнее — наложение символов друг на друга
    а тут просто нужно продумать алгоритм, не боящийся поворота

Добавить комментарий для Djoser Отменить ответ

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