Продолжу свою тему про разгадывание капчи , теперь как я и обещал возьмем капчу посложнее, например капчу с rewbux.com
Капча особенная тем что, имеет шум на фоне, разные цвета чисел, и самое противное, поворачивает цифры пор разный угол.
Первое что нeжно сделать это очистить капчу от шума и превратить её в чёрно белую картинку.
Цвет идет от 0.0.0 — чёрный до 255.20.250 — белый, мы возьмем середину где-то 150,150,150 , далее все пиксели что меньше нашей золотой середины мы сделаем чёрными, а всё остальные белыми, получится следующие
Вынесу очистку в отдельную функцию.
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); } } } }
Дальше, цифры нужно отделить и обрезать по краям
Обрезку по краям тоже вынесу в отдельную функцию
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-----//========================== Выравниваем третию =========================================== //========================== Выравниваем картинки =========================================== }
из шаблонов выбираем наилучшие и получаем
Как видите на скрине, 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++; }
Результат такой
Всего 5 ошибок из 60, что в принципе не плохо.
да,хороший результат! больше всего понравилась задумка с выравниванием )
10 ошибок, не 5
Djoser, а ты tesseract принципиально не используешь? Все-таки алгоритмы распознавания на нейронных сетях показывают более высокую точность. Я понимаю, что свои велосипеды незаменимы для лучшего понимания работы алгоритмов распознавания, но развитие нейронных сетей — это принципиально новый скачок и переломный момент в программировании. Очень интересно раскрывает эту тему на ютубе Анатолий Левенчук.
ну как сказать, вообще я не любитель пользоваться чужим умом и чужой работой, поэтому использовать тессеракт не буду, как говорят, хочешь чтобы было что-то сделано хорошо — сделай это сам, на а по поводу, качества, да тессеракт лучше моего кода, но это лишь вопрос времени.
а почему белый 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) с++? может то питон морально устарел?)
«и самое противное, поворачивает цифры пор разный угол» 🙂
самое противное это искажение, а еще противнее — наложение символов друг на друга
а тут просто нужно продумать алгоритм, не боящийся поворота