Search

2012年11月16日 星期五

Visual CAPTCHA Detection

    幾個禮拜前上課寫個去除背景的demo給人看後沒有在碰了,所以就放上來等哪天想改就有,對於圖形驗證碼感覺上如果背景夠複雜,去除背景的演算法複雜度就很高,至於雜訊一些去除線條的演算法或者濾波可以容易去除常見的雜訊,文字本身扭曲的話可以讓Tesseract之類的OCR Library辨識率降低(即使背景、雜訊完整去除),而一些方法的閾值也是要看情況調整,總結來說個人感覺是背景複雜求平均值不容易,而且可能得將背景分區快去除,但針對設計應該是還是可以去除大部分只是複雜度的問題,但如果文字扭曲變形到一定程度似乎常見OCR Library辨識也不易,所以感覺上文字扭曲是必須的,當然最好還是不要用同系列工具生產的驗證圖片,最好混和多種系列使用,不過沒有試過用ML訓練萬筆樣本測試是否能有效提昇辨識度就是了,這裡這裡有對驗證碼類型的分析。

extractor.h


#ifndef __EXTRACTOR__
#define __EXTRACTOR__

#include<cv.h>
#include<cxcore.h>
#include<highgui.h>

#include <iostream>
#include <map>

class Extractor
{
   public:
      Extractor(char*);
      long Average(); //It depends
      void BackgroundErase(); //It depends
      void ColorFull();
      void NoiseClear(); //It depends
      void Cut();
   private:
      long avg;
      IplImage *image;
};

#endif


extractor.cpp


#include "extractor.h"

Extractor::Extractor(char *name)
{
   image = cvLoadImage(name,1);
}

long Extractor::Average()
{
   cvSmooth(image,image,CV_BLUR);

   avg = 0;

   for(int y = 0;y < 3;++y){
      for(int x = 0;x < 3;++x){
long temp = 0;
temp += image->imageData[y * image->widthStep + (x * 3)] + image->imageData[y * image->widthStep + (x * 3) + 1] + image->imageData[y * image->widthStep + (x * 3) + 2];
avg += temp / 3;
      }
   }


   for(int y = image->height - 1;y > image->height - 4;--y){
      for(int x = 0;x < 3;++x){
long temp = 0;
temp += image->imageData[y * image->widthStep + (x * 3)] + image->imageData[y * image->widthStep + (x * 3) + 1] + image->imageData[y * image->widthStep + (x * 3) + 2];
avg += temp / 3;
      }
   }

   for(int y = 0;y < 3;++y){
      for(int x = image->width - 1;x > image->width - 4;--x){
long temp = 0;
temp += image->imageData[y * image->widthStep + (x * 3)] + image->imageData[y * image->widthStep + (x * 3) + 1] + image->imageData[y * image->widthStep + (x * 3) + 2];
avg += temp / 3;
      }
   }

   for(int y = image->height - 1;y > image->height - 4;--y){
      for(int x = image->width - 1;x > image->width - 4;--x){
long temp = 0;
temp += image->imageData[y * image->widthStep + (x * 3)] + image->imageData[y * image->widthStep + (x * 3) + 1] + image->imageData[y * image->widthStep + (x * 3) + 2];
avg += temp / 3;
      }
   }

   avg /= 36;


   return avg ;
}

void Extractor::BackgroundErase()
{
   long range = 20;

   for(int y = 0;y < image->height;++y){
      for(int x = 0;x < image->widthStep;++x){
long temp = (image->imageData[y * image->widthStep + x] +
      image->imageData[y * image->widthStep + x + 1] +
      image->imageData[y * image->widthStep + x + 2]) / 3;
if(temp  < (avg + range) && temp > (avg - range)){
   image->imageData[y * image->widthStep + x] = image->imageData[y * image->widthStep + x + 1] = image->imageData[y * image->widthStep + x + 2] = 255;
}
      }
   }
}

void Extractor::ColorFull()
{
   for(int y = 0;y < image->height;++y){
      for(int x = 0;x < image->widthStep - 3;x += 3){
long temp = (image->imageData[y * image->widthStep + x] +
      image->imageData[y * image->widthStep + x + 1] +
      image->imageData[y * image->widthStep + x + 2]) / 3;
if(temp > 0){
   image->imageData[y * image->widthStep + x] = image->imageData[y * image->widthStep + x + 1] = image->imageData[y * image->widthStep + x + 2] = 0;
}
      }
   }
}

void Extractor::NoiseClear()
{
   int range = 20;
   IplImage *itemp = cvCreateImage(cvGetSize(image),IPL_DEPTH_8U,3);
   cvCopy(image,itemp);

   for(int y = 0;y < itemp->height;++y){
      for(int x = 0;x < itemp->widthStep - 3;x += 3){
long temp = (itemp->imageData[y * itemp->widthStep + x] +
      itemp->imageData[y * itemp->widthStep + x + 1] +
      itemp->imageData[y * itemp->widthStep + x + 2]) / 3;

long ref =  (itemp->imageData[(y - 1) * itemp->widthStep + x] +
      itemp->imageData[(y - 1) * itemp->widthStep + x + 1] +
      itemp->imageData[(y - 1) * itemp->widthStep + x + 2] / 3)
   +
   (itemp->imageData[(y + 1) * itemp->widthStep + x]  +
    itemp->imageData[(y + 1) * itemp->widthStep + x + 1] +
    itemp->imageData[(y + 1) * itemp->widthStep + x + 1] / 3)
   +
   (itemp->imageData[y * itemp->widthStep + (x + 3)] +
    itemp->imageData[y * itemp->widthStep + (x + 3) + 1] +
    itemp->imageData[y * itemp->widthStep + (x + 3) + 2] / 3)
   +
   (itemp->imageData[y * itemp->widthStep + (x - 3)] +
    itemp->imageData[y * itemp->widthStep + (x - 3) + 1] +
    itemp->imageData[y * itemp->widthStep + (x - 3) + 2] / 3)
   +
   (itemp->imageData[(y + 1) * itemp->widthStep + (x - 3)]  +
    itemp->imageData[(y + 1) * itemp->widthStep + (x - 3) + 1] +
    itemp->imageData[(y + 1) * itemp->widthStep + (x - 3) + 1] / 3)
   +
   (itemp->imageData[(y + 1) * itemp->widthStep + (x + 3)]  +
    itemp->imageData[(y + 1) * itemp->widthStep + (x + 3) + 1] +
    itemp->imageData[(y + 1) * itemp->widthStep + (x + 3) + 1] / 3)
   +
   (itemp->imageData[(y - 1) * itemp->widthStep + (x + 3)] +
    itemp->imageData[(y - 1) * itemp->widthStep + (x + 3) + 1] +
    itemp->imageData[(y - 1) * itemp->widthStep + (x + 3) + 2] / 3)
   +
   (itemp->imageData[(y - 1) * itemp->widthStep + (x - 3)] +
    itemp->imageData[(y - 1) * itemp->widthStep + (x - 3) + 1] +
    itemp->imageData[(y - 1) * itemp->widthStep + (x - 3) + 2] / 3)
   ;

ref /= 8;

if((temp + range) < ref){
   image->imageData[y * image->widthStep + x] = image->imageData[y * image->widthStep + x + 1] = image->imageData[y * image->widthStep + x + 2] = 255;
}
      }
   }
}

void Extractor::Cut()
{
   IplImage *gray = cvCreateImage(cvSize(image->width,image->height),IPL_DEPTH_8U ,1);
   cvCvtColor(image,gray,CV_RGB2GRAY);

   cvCanny(gray,gray,30,90);
   cvSmooth(gray,gray,CV_BLUR);
   cvSmooth(gray,gray,CV_BLUR);
   cvSmooth(gray,gray,CV_BLUR);

   std::multimap<int,CvRect> list;

   CvMemStorage* storage = cvCreateMemStorage( 0 );
   CvSeq* contours = NULL;
   cvFindContours(gray, storage, &contours, sizeof( CvContour ), CV_RETR_LIST,CV_CHAIN_APPROX_NONE);
   for( ; contours != NULL; contours = contours->h_next ){
      CvRect rect = cvBoundingRect( contours, 0 );
      //cvRectangle(image, cvPoint( rect.x, rect.y ),cvPoint( rect.x + rect.width, rect.y + rect.height ), cvScalar(0,0,255), 0 );
      list.insert(std::make_pair(rect.width * rect.height,rect));
   }

   //cvSaveImage("find.jpg",image);

 
   int captcha_numbers = 5;
   std::multimap<int,CvRect> result;
   std::multimap<int,CvRect>::reverse_iterator rit = list.rbegin();
   for(int i = 0;rit != list.rend() && i < captcha_numbers;++rit,++i){
      result.insert(std::make_pair(rit->second.x,rit->second));
   }

   char name[] = "1.jpg";
   std::multimap<int,CvRect>::iterator it = result.begin();
   for(;it != result.end();++it){
      cvSetImageROI(image,it->second);
      cvSaveImage(name,image);
      name[0] += 1;
      //std::cout << it->first << std::endl;
   }
}


main.cpp


#include "extractor.h"

int main(int argc,char**argv)
{
   char *name = argv[1];

   if(name){

      Extractor extractor(name);

      /*************pipeline*************/

      extractor.Average();

      extractor.BackgroundErase();

      extractor.ColorFull();

      extractor.NoiseClear();

      extractor.Cut();
   }
   return 0;
}




針對的樣本:

(這組失敗,7和8被當成同範圍)










2012年11月6日 星期二

累人...

  感覺最近又再重複做去年同樣這個時間在做的事,一直覺得太規律的事情不適合我做,工作也好學校也好太規律就渾身不自在啊啊...前些日子把很久以前寫的繪圖引擎改寫之後先試著做2D ACT遊戲引擎驗證,如果把怪物AI跟物理部分獨立出來用LUA之類的腳本個別設計就可以做任何ACT,等有時間再慢慢來寫另一個3D RPG引擎驗證。



  在沒屋頂買了一個很有趣的東西叫VFD管,整個很漂亮等找人弄到Driver IC再來設計PCB弄個時鐘。



  還有很多東西要學要研究偏偏時間很難把握,其實我自己對時間管理不是很擅長希望到過年前能完成大部分。