zoukankan      html  css  js  c++  java
  • 如何识别高级的验证码

     

    来源:转载 作者:佚名 时间:2008-09-15 09:31:25

    分享到:

        ==Ph4nt0m Security Team==
                          Issue 0x02, Phile #0x09 of 0x0A
    |=---------------------------------------------------------------------------=|
    |=-----------------------=[  如何识别高级的验证码  ]=------------------------=|
    |=---------------------------------------------------------------------------=|
    |=---------------------------------------------------------------------------=|
    |=----------------------=[      By moonblue333    ]=------------------------=|
    |=-------------------=[  <moonblue333_at_hotmail.com>  ]=--------------------=|
    |=---------------------------------------------------------------------------=|
    一、验证码的基本知识
        1. 验证码的主要目的是强制人机交互来抵御机器自动化攻击的。
        2. 大部分的验证码设计者并不得要领,不了解图像处理,机器视觉,模式识别,人工智能
    的基本概念。
        3. 利用验证码,可以发财,当然要犯罪:比如招商银行密码只有6位,验证码形同虚设,计
    算机很快就能破解一个有钱的账户,很多帐户是可以网上交易的。
        4. 也有设计的比较好的,比如Yahoo,Google,Microsoft等。而国内Tencent的中文验证
    码虽然难,但算不上好。
    二、人工智能,模式识别,机器视觉,图像处理的基本知识
        1)主要流程:
        比如我们要从一副图片中,识别出验证码;比如我们要从一副图片中,检测并识别出一张
    人脸。 大概有哪些步骤呢?
        1.图像采集:验证码呢,就直接通过HTTP抓HTML,然后分析出图片的url,然后下载保存就
    可以了。 如果是人脸检测识别,一般要通过视屏采集设备,采集回来,通过A/D转操作,存为
    数字图片或者视频频。
        2.预处理:检测是正确的图像格式,转换到合适的格式,压缩,剪切出ROI,去除噪音,灰度
    化,转换色彩空间这些。
        3.检测:车牌检测识别系统要先找到车牌的大概位置,人脸检测系统要找出图片中所有
    的人脸(包括疑似人脸);验证码识别呢,主要是找出文字所在的主要区域。
        4.前处理:人脸检测和识别,会对人脸在识别前作一些校正,比如面内面外的旋转,扭曲
    等。我这里的验证码识别,“一般”要做文字的切割
        5.训练:通过各种模式识别,机器学习算法,来挑选和训练合适数量的训练集。不是训练
    的样本越多越好。过学习,泛化能力差的问题可能在这里出现。这一步不是必须的,有些识
    别算法是不需要训练的。
        6.识别:输入待识别的处理后的图片,转换成分类器需要的输入格式,然后通过输出的类
    和置信度,来判断大概可能是哪个字母。识别本质上就是分类。
        2)关键概念:
        图像处理:一般指针对数字图像的某种数学处理。比如投影,钝化,锐化,细化,边缘检测,
    二值化,压缩,各种数据变换等等。
        1.二值化:一般图片都是彩色的,按照逼真程度,可能很多级别。为了降低计算复杂度,
    方便后续的处理,如果在不损失关键信息的情况下,能将图片处理成黑白两种颜色,那就最好
    不过了。
        2.细化:找出图像的骨架,图像线条可能是很宽的,通过细化将宽度将为1,某些地方可能
    大于1。不同的细化算法,可能有不同的差异,比如是否更靠近线条中间,比如是否保持联通
    行等。
        3.边缘检测:主要是理解边缘的概念。边缘实际上是图像中图像像素属性变化剧烈的地
    方。可能通过一个固定的门限值来判断,也可能是自适应的。门限可能是图像全局的,也可
    能是局部的。不能说那个就一定好,不过大部分时候,自适应的局部的门限可能要好点。被
    分析的,可能是颜色,也可能是灰度图像的灰度。
        机器视觉:利用计算机来模式实现人的视觉。 比如物体检测,定位,识别。按照对图像
    理解的层次的差别,分高阶和低阶的理解。
        模式识别:对事物或者现象的某种表示方式(数值,文字,我们这里主要想说的是数值),
    通过一些处理和分析,来描述,归类,理解,解释这些事物,现象及其某种抽象。
        人工智能:这种概念比较宽,上面这些都属于人工智能这个大的方向。简单点不要过分
    学院派的理解就是,把人类的很“智能”的东西给模拟出来协助生物的人来处理问题,特别是
    在计算机里面。
    三、常见的验证码的破解分析
        以http://libcaca.zoy.org/wiki/PWNtcha这里PWNtcha项目中的资料为例分析,各种验
    证码的破解。(方法很多,仅仅从我个人乍看之下觉得可行的方法来分析)
        1)Authimage


        使用的反破解技巧:
        1.不连续的点组成字符
        2.有一定程度的倾斜
        设计不好的地方:
        1.通过纵横的直方图投影,可以找到字幕区域
        2.通过Hough变换,适当的参数,可以找到近似的横线,可以做倾斜矫正
        3.字符串的倾斜式面内的,没有太多的破解难度
        4.字母宽度一定,大小一定
        2)Clubic


        使用的反破解技巧:
        1.字符是手写体
        设计不好的地方:
        1.检测切割阶段没有任何技术含量,属于设计的比较丑的
        2.只有数字,而且手写体变化不大
        3.表面看起来对识别阶段有难度,仔细分析,发现几乎不用任何高级的训练识别算法,就
    固定的招某些像素点是否有色彩就够了
        3)linuxfr.org


        使用的反破解技巧:
        1.背景颜色块
        2.前景的横线或矩形
        设计不好的地方:
        1.背景色是单一色块,有形状,通过Region-Growth区域增长来很容易把背景给去掉
        2.前景色是标准的线条,色彩单一
        3.字母无粘连
        4.都是印刷体
        4)Ourcolony


        使用的反破解技巧:
        1.设计的太低级,不屑于去评价
        设计不好的地方:
        1.这种验证码,设计的最丑,但还是能把菜鸟搞定,毕竟学计算机的少,搞这个破解的更
    少,正所谓隔行如隔山
        5)LiveJournal


        使用的反破解技巧:
        1.这个设计略微好点,使用个随机噪音,而且作为前景
        2.字母位置粗细都有变化
        设计不好的地方:
        1.字母没有粘连
        2.噪音类型单一
        3.通过在X轴的直方图投影,能准确分割字幕
        4.然后在Y周作直方图投影,能准确定位高度
        5.识别阶段,都是印刷体,简单地很
    四、网上的一些高级验证码
        1)ICQ


        2)IMDb


        3)MS MVPS


        4)MVN Forum


        这些类型是被很多人认为比较难得类型,分析一下可以发现,字符检测,定位和分割都不
    是难。 唯一影响识别率的是IMDBb和MVPS这两类,字体变形略大。
        总体来说,这些类型的破解也不难,很容易做到50%以上的识别率。
    五、高级验证码的破解分析
        时间关系,我简单介绍如何利用图像处理和模式识别技术,自动识别比较高级的验证码。
    (以风头正劲的Google为例)


        1)至少从目前的AI的发展程度看,没有简单的做法能自动处理各种不同的验证码,即使
    能力很强,那么系统自然也十分复杂强大。所以,要想在很简单的算法实现比较高级的验证
    码破解,必须分析不同验证码算法的特点:
        作为一般的图像处理和计算机视觉,会考虑色彩,纹理,形状等直接的特征,同时也考虑
    直方图,灰度等统计特征,还考虑FFT,Wavelet等各种变换后的特征。但最终目标都是
    Dimension Reduction(降维)然后利于识别,不仅仅是速度的考虑。从图像的角度看,很多系
    统都考虑转换为灰度级甚者黑白图片。
        Google的图片可以看出,颜色变化是虚晃一枪,不存在任何处理难度。难度是字体变形
    和字符粘连。
        如果能成功的分割字符,那么后期识别无论是用SVM等分类算法,还是分析笔顺比划走向
    来硬识别,都相对好做。
        2)图像处理和粘连分割
        代码中的part1目录主要完成图像预处理和粘连字符分割
        001:将图像从jpg等格式转换为位图便于处理
        002:采用Fix/Adaptive的Threshold门限算法,将图片Bin-Value二值化。
        (可用003算法)
        003:采用OSTU分水岭算法,将图片Bin-Value二值化。
        (更通用,大部分时候效果更好)
        005:获取ROI感兴趣的区域。
        006:Edge Trace边缘跟踪。
        007:Edge Detection边界检测。
        008:Thin细化去骨架。
        009:做了一些Tidy整理。
    (这个一般要根据特定的Captcha算法调整)
        010:做切割,注意图片中红色的交叉点。
        011:将边缘检测和骨干交叉点监测的图像合并。
    (合并过程可以做分析: 比如X坐标偏移门限分析,交叉点区域纹理分析,线条走势分析,
    等等各种方法,找出更可能的切分点和分离后部件的组合管理。)


        代码:(代码质量不高,从其他项目拷贝过来,简单修改的。)
        查看代码(./pstzine_09_01.txt)

    Copy code
    //SuperImage.h
    #ifndef _SUPER_IMAGE_
    #define _SUPER_IMAGE_
    typedef short Pixel;
    //
    template <class T>
    class SuperImage
    {
    public:
        SuperImage(const int w, const int h);
        ~SuperImage();
    public:
        T* data;
        int width;
        int height;
    };
    //
    class EnhancedImage
    {
    public:
        SuperImage<Pixel>* superImage;
    public:
        EnhancedImage();
        ~EnhancedImage();
        int load(const char* name);
        int save(const char* name);
        int copy(EnhancedImage* copy);
        int film(EnhancedImage* film);
        int binv();
        int otsu();
        int line();
        int roii();
        int trac();
        int edge();
        int thin();
        int tidy();   
        int kerf();   
        int join();
    };
    #endif
    //SuperImage.cpp
    #include "superImage.h"
    #include "ximage.h"
    template <class T>
    SuperImage<T>::SuperImage(const int w, const int h)
    {
        width = w;
        height = h;
        data = new T[w * h];
    };
    template <class T>
    SuperImage<T>::~SuperImage()
    {
        if(data)
        {
            delete[] data;
        }
    };
    EnhancedImage::EnhancedImage()
    {
        superImage=NULL;
    };
    EnhancedImage::~EnhancedImage()
    {
        if(superImage)
        {
            delete superImage;
        }
    };
    int EnhancedImage::load(const char* name)
    {
        CxImage input;
        input.Load(name, CXIMAGE_SUPPORT_J2K);   
        if(!input.IsValid())
        {
            return 1;
        }
        else
        {
            int h=input.GetHeight();
            int w=input.GetWidth();       
            superImage = new SuperImage<Pixel>(w,h);
            for(int y=0;y<h;y++)
            {
                for(int x=0;x<w;x++)
                {
                    RGBQUAD rgbQUAD = CxImage::RGBtoXYZ(input.GetPixelColor(x,y));
                    Pixel pixel = 255;
                    if(true)
                    {
                        pixel = (rgbQUAD.rgbRed + rgbQUAD.rgbGreen + rgbQUAD.rgbBlue) / 3.0;
                    }
                    else
                    {
                        double yy = (0.299 * rgbQUAD.rgbRed + 0.587 * rgbQUAD.rgbGreen + 0.114 * rgbQUAD.rgbBlue) / 256.0;
                       pixel = (int) (219.0 * yy + 16.5);
                    }
                    superImage->data[y*w+x]=pixel;
                }
            }
        }
        return 0;
    };
    int EnhancedImage::save(const char* name)
    {
        CxImage output;
        output.Create(superImage->width,superImage->height,24,CXIMAGE_SUPPORT_BMP);  //1 4 8 24
        for(int y=0;y<superImage->height;y++)
        {
            for(int x=0;x<superImage->width;x++)
            {           
                Pixel pixel = superImage->data[y*superImage->width + x];
                RGBQUAD rgbQUAD;
                if(pixel == -1)
                {
                    rgbQUAD.rgbRed = 255;
                    rgbQUAD.rgbGreen = 0;
                    rgbQUAD.rgbBlue = 0;
                }
                else if(pixel == -2)
                {
                    rgbQUAD.rgbRed = 0;
                    rgbQUAD.rgbGreen = 255;
                    rgbQUAD.rgbBlue = 0;
                }
                else if(pixel == -3)
                {
                    rgbQUAD.rgbRed = 0;
                    rgbQUAD.rgbGreen = 0;
                    rgbQUAD.rgbBlue = 255;
                }
                else
                {
                    rgbQUAD.rgbRed = pixel;
                    rgbQUAD.rgbGreen = pixel;
                    rgbQUAD.rgbBlue =pixel;
                }
                output.SetPixelColor(x,y,rgbQUAD);
            }
        }
        output.Save(name,CXIMAGE_SUPPORT_BMP);
        return 0;
    };
    int EnhancedImage::copy(EnhancedImage* copy)
    {
        int w = copy->superImage->width;
        int h = copy->superImage->height;   
        short* cpy = copy->superImage->data;
        //
        this->superImage = new SuperImage<Pixel>(w,h);
        short* tgt = this->superImage->data;
        //
        for (int y=0; y<h; y++)
        {
            for (int x=0; x<w; x++)
            {
                tgt[w*(y)+(x)] = cpy[w*(y)+(x)];
            }
        }
        return 0;
    }
    int EnhancedImage::film(EnhancedImage* film)
    {
        int h = film->superImage->height;
        int w = film->superImage->width;
        short* flm = film->superImage->data;
        short* tgt = this->superImage->data;
        //
        for (int y=0; y<h; y++)
        {
            for (int x=0; x<w; x++)
            {
                unsigned char source = flm[w*(y)+(x)];
                unsigned char target = tgt[w*(y)+(x)];   
                if(source < 0 || target < 0)
                {
                    printf("x=%d  y=%d    src=%d  tgt=%d\n",x,y,source,target);
                }
                if(source==0 && target == 255)
                {
                    tgt[w*(y)+(x)] = source;
                }
            }
        }
        return 0;
    }
    int EnhancedImage::binv()
    {
        int threshold = 256 / 8 * 7;
        for(int y2=0;y2<superImage->height;y2++)
        {
            for(int x2=0;x2<superImage->width;x2++)
            {           
                Pixel pixel = superImage->data[y2*superImage->width+x2];
                if(pixel>=threshold)
                {
                    superImage->data[y2*superImage->width+x2] = 255;
                }
                else
                {
                    superImage->data[y2*superImage->width+x2] = 0;
                }
            }
        }
        return 0;
    };
    int EnhancedImage::otsu()
    {
        int ihist[256];
        memset(ihist, 0, sizeof(ihist));   
        int gmin=255;
        int gmax=0;
        for (int y=0; y<superImage->height; y++)
        {
            for (int x=0; x<superImage->width; x++)
            {
                unsigned char point = superImage->data[y*superImage->width+x];
                ihist[point]++;
                if(point > gmax)
                {
                    gmax=point;
                }
                if(point < gmin)
                {
                    gmin=point;
                }
            }
        }   
        double sum = 0.0;
        int n = 0;   
        for (int k = 0; k <= 255; k++)
        {
            sum += (double) k * (double) ihist[k];    // x*f(x) 质量矩
            n  += ihist[k];                          // f(x) 质量
        }   
        // do the otsu global thresholding method
        int threshold = 127;
        double fmax = -1.0;
        int n1 = 0;
        double csum = 0.0;
        for (k = 0; k < 255; k++)
        {
            n1 += ihist[k];
            if (!n1)
            {
                continue;
            }
            int n2 = n - n1;
            if (n2 == 0)
            {
                break;
            }
            csum += (double) k *ihist[k];
            int m1 = csum / n1;
            int m2 = (sum - csum) / n2;
            int sb = (double) n1 *(double) n2 *(m1 - m2) * (m1 - m2);
            //note: can be optimized.
            if (sb > fmax)
            {
                fmax = sb;
                threshold = k;
            }
        }
        //
        for(int y2=0;y2<superImage->height;y2++)
        {
            for(int x2=0;x2<superImage->width;x2++)
            {           
                Pixel pixel = superImage->data[y2*superImage->width+x2];
                if(pixel>threshold)
                {
                    superImage->data[y2*superImage->width+x2] = 255;
                }
                else
                {
                    superImage->data[y2*superImage->width+x2] = 0;
                }
            }
        }
        return 0;
    };
    /*************************************************************************
    *
    * 函数名称:
    *  Hough()
    *
    * 参数:
    *  LPSTR lpDIBBits    - 指向源DIB图像指针
    *  LONG  lWidth      - 源图像宽度(象素数,必须是4的倍数)
    *  LONG  lHeight      - 源图像高度(象素数)
    * 返回值:
    *  BOOL              - 运算成功返回TRUE,否则返回FALSE。
    *
    * 说明:
    * 该函数用于对检测图像中的平行直线。如果图像中有两条平行的直线,则将这两条平行直线
    * 提取出来。
    *
    * 要求目标图像为只有0和255两个灰度值的灰度图像。
    ************************************************************************/
    // 在计算图像大小时,采用公式:biSizeImage = biWidth' × biHeight。
    // 是biWidth',而不是biWidth,这里的biWidth'必须是4的整倍数,表示
    // 大于或等于biWidth的,离4最近的整倍数。WIDTHBYTES就是用来计算
    // biWidth'
    #define WIDTHBYTES(bits)    (((bits) + 31) / 32 * 4)
    #define pi 3.1415927
    typedef struct{
        int Value;
        int Dist;
        int AngleNumber;
    }MaxValue;
    int EnhancedImage::line()
    {
        //
        LONG lWidth = superImage->width;
        LONG lHeight = superImage->height;
        //
        LPSTR lpDIBBits = (char *)LocalAlloc(LHND, lWidth * lHeight);
        if (lpDIBBits == NULL)
        {
            return false ;
        }   
        lpDIBBits = (char *)LocalLock(lpDIBBits);
        //
        for (int y=0; y<superImage->height; y++)
        {
            for (int x=0; x<superImage->width; x++)
            {
                unsigned char point = superImage->data[y*superImage->width+x];           
                lpDIBBits[lWidth * y + x] = point;
            }
        }
        //       
        //
        // 指向源图像的指针
        LPSTR    lpSrc;
        // 指向缓存图像的指针
        LPSTR    lpDst;
        // 指向变换域的指针
        LPSTR  lpTrans;
        // 图像每行的字节数
        LONG lLineBytes;
        // 指向缓存DIB图像的指针
        LPSTR    lpNewDIBBits;
        HLOCAL    hNewDIBBits;
        //指向变换域的指针
        LPSTR    lpTransArea;
        HLOCAL    hTransArea;
        //变换域的尺寸
        int iMaxDist;
        int iMaxAngleNumber;
        //变换域的坐标
        int iDist;
        int iAngleNumber;
        //循环变量
        long i;
        long j;
        //像素值
        unsigned char pixel;
        //存储变换域中的两个最大值
        MaxValue MaxValue1;
        MaxValue MaxValue2;
        // 暂时分配内存,以保存新图像
        hNewDIBBits = LocalAlloc(LHND, lWidth * lHeight);
        if (hNewDIBBits == NULL)
        {
            // 分配内存失败
            return FALSE;
        }
        // 锁定内存
        lpNewDIBBits = (char * )LocalLock(hNewDIBBits);
        // 初始化新分配的内存,设定初始值为255
        lpDst = (char *)lpNewDIBBits;
        memset(lpDst, (BYTE)255, lWidth * lHeight);
        //计算变换域的尺寸
        //最大距离
        iMaxDist = (int) sqrt(lWidth*lWidth + lHeight*lHeight);
        //角度从0-180,每格2度
        iMaxAngleNumber = 90;
        //为变换域分配内存
        hTransArea = LocalAlloc(LHND, lWidth * lHeight * sizeof(int));
        if (hNewDIBBits == NULL)
        {
            // 分配内存失败
            return FALSE;
        }
        // 锁定内存
        lpTransArea = (char * )LocalLock(hTransArea);
        // 初始化新分配的内存,设定初始值为0
        lpTrans = (char *)lpTransArea;
        memset(lpTrans, 0, lWidth * lHeight * sizeof(int));
        // 计算图像每行的字节数
        for(j = 0; j <lHeight; j++)
        {
            for(i = 0;i <lWidth; i++)
            {
                // 指向源图像倒数第j行,第i个象素的指针           
                lpSrc = (char *)lpDIBBits + lWidth * j + i;
                //取得当前指针处的像素值,注意要转换为unsigned char型
                pixel = (unsigned char)*lpSrc;
                //目标图像中含有0和255外的其它灰度值
                if(pixel != 255 && *lpSrc != 0)
                    return FALSE;
                //如果是黑点,则在变换域的对应各点上加1
                if(pixel == 0)
                {
                    //注意步长是2度
                    for(iAngleNumber=0; iAngleNumber<iMaxAngleNumber; iAngleNumber++)
                    {
                        iDist = (int) fabs(i*cos(iAngleNumber*2*pi/180.0) + j*sin(iAngleNumber*2*pi/180.0));
                        //变换域的对应点上加1
                        *(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber) = *(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber) +1;
                    }
                }
            }
        }
        //找到变换域中的两个最大值点
        MaxValue1.Value=0;
        MaxValue2.Value=0;
        //找到第一个最大值点
        for (iDist=0; iDist<iMaxDist;iDist++)
        {
            for(iAngleNumber=0; iAngleNumber<iMaxAngleNumber; iAngleNumber++)
            {
                if((int)*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber)>MaxValue1.Value)
                {
                    MaxValue1.Value = (int)*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber);
                    MaxValue1.Dist = iDist;
                    MaxValue1.AngleNumber = iAngleNumber;
                }
            }
        }
        //将第一个最大值点附近清零
        for (iDist = -9;iDist < 10;iDist++)
        {
            for(iAngleNumber=-1; iAngleNumber<2; iAngleNumber++)
            {
                if(iDist+MaxValue1.Dist>=0 && iDist+MaxValue1.Dist<iMaxDist && iAngleNumber+MaxValue1.AngleNumber>=0 && iAngleNumber+MaxValue1.AngleNumber<=iMaxAngleNumber)
                {
                    *(lpTransArea+(iDist+MaxValue1.Dist)*iMaxAngleNumber+(iAngleNumber+MaxValue1.AngleNumber))=0;
                }
            }
        }
        //找到第二个最大值点
        for (iDist=0; iDist<iMaxDist;iDist++)
        {
            for(iAngleNumber=0; iAngleNumber<iMaxAngleNumber; iAngleNumber++)
            {
                if((int)*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber)>MaxValue2.Value)
                {
                    MaxValue2.Value = (int)*(lpTransArea+iDist*iMaxAngleNumber+iAngleNumber);
                    MaxValue2.Dist = iDist;
                    MaxValue2.AngleNumber = iAngleNumber;
                }
            }
        }
        //判断两直线是否平行
        if(abs(MaxValue1.AngleNumber-MaxValue2.AngleNumber)<=2)
        {
            //两直线平行,在缓存图像中重绘这两条直线
            for(j = 0; j <lHeight; j++)
            {
                for(i = 0;i <lWidth; i++)
                {   
                    // 指向缓存图像倒数第j行,第i个象素的指针           
                    lpDst = (char *)lpNewDIBBits + lLineBytes * j + i;   
                    //如果该点在某一条平行直线上,则在缓存图像上将该点赋为黑
                    //在第一条直线上
                    iDist = (int) fabs(i*cos(MaxValue1.AngleNumber*2*pi/180.0) + j*sin(MaxValue1.AngleNumber*2*pi/180.0));
                    if (iDist == MaxValue1.Dist)
                        *lpDst = (unsigned char)0;
                    //在第二条直线上
                    iDist = (int) fabs(i*cos(MaxValue2.AngleNumber*2*pi/180.0) + j*sin(MaxValue2.AngleNumber*2*pi/180.0));
                    if (iDist == MaxValue2.Dist)
                        *lpDst = (unsigned char)0;
                }
            }
        }
        // 复制腐蚀后的图像
        memcpy(lpDIBBits, lpNewDIBBits, lWidth * lHeight);
        // 释放内存
        LocalUnlock(hNewDIBBits);
        LocalFree(hNewDIBBits);
        // 释放内存
        LocalUnlock(hTransArea);
        LocalFree(hTransArea);
        //
        //
        for (int yy=0; yy<superImage->height; yy++)
        {
            for (int xx=0; xx<superImage->width; xx++)
            {
                unsigned char point = lpDIBBits[lWidth * yy + xx];           
                superImage->data[yy*superImage->width+xx] = point;
            }
        }
        //
        LocalUnlock(lpDIBBits);
        LocalFree(lpDIBBits);
        // 返回
        return TRUE;
    }
    int EnhancedImage::roii()
    {
        int minX = superImage->width;
        int minY = superImage->height;
        int maxX = 0;
        int maxY = 0;
        //
        for (int y=0; y<superImage->height; y++)
        {
            for (int x=0; x<superImage->width; x++)
            {
                unsigned char point = superImage->data[y*superImage->width+x];
                if(point==0)
                {
                    if(x<minX)
                    {
                        minX = x;                   
                    }
                    if(y<minY)
                    {
                        minY = y;
                    }
                    //
                    if(x>maxX)
                    {
                        maxX = x;
                    }
                    if(y>maxY)
                    {
                        maxY = y;
                    }
                }
            }
        }
        //
        if(minX>maxX)
        {
            minX=maxX=0;
        }
        if(minY>maxY)
        {
            minY=maxY=0;
        }
        //
        if(minX>0)
            minX = minX -1;
        if(minY>0)
            minY = minY -1;
        if(maxX<superImage->width-1)
            maxX= maxX+1;
        if(maxY<superImage->height-1)
            maxY = maxY+1;
        //
        int tmpW = maxX - minX+1;
        int tmpH = maxY - minY+1;
        SuperImage<Pixel>* tmpD = new SuperImage<Pixel>(tmpW,tmpH);
        //
        for(int y2=minY,y3=0;y3<tmpH;y2++,y3++)
        {
            for(int x2=minX,x3=0;x3<tmpW;x2++,x3++)
            {           
                Pixel pixel = superImage->data[y2*superImage->width+x2];
                tmpD->data[y3*tmpW+x3] = pixel;
            }
        }
        //
        if(superImage)
        {
            delete superImage;
            superImage = NULL;
        }
        //
        superImage = tmpD;
        //
        return 0;
    };
    /*************************************************************************
    *
    * 函数名称:
    *  TraceDIB()
    *
    * 参数:
    *  LPSTR lpDIBBits    - 指向源DIB图像指针
    *  LONG  lWidth      - 源图像宽度(象素数,必须是4的倍数)
    *  LONG  lHeight      - 源图像高度(象素数)
    * 返回值:
    *  BOOL              - 运算成功返回TRUE,否则返回FALSE。
    *
    * 说明:
    * 该函数用于对图像进行轮廓跟踪运算。
    *
    * 要求目标图像为只有0和255两个灰度值的灰度图像。
    ************************************************************************/
    int EnhancedImage::trac()
    {
        typedef struct
        {
            int Height;
            int Width;
        }Point;
        //
        LONG lWidth = superImage->width;
        LONG lHeight = superImage->height;
        //
        LPSTR lpDIBBits = (char *)LocalAlloc(LHND, lWidth * lHeight);
        if (lpDIBBits == NULL)
        {
            return false ;
        }   
        lpDIBBits = (char *)LocalLock(lpDIBBits);
        //
        for (int y=0; y<superImage->height; y++)
        {
            for (int x=0; x<superImage->width; x++)
            {
                unsigned char point = superImage->data[y*superImage->width+x];           
                lpDIBBits[lWidth * y + x] = point;
            }
        }
        //       
        //
        // 指向源图像的指针
        LPSTR    lpSrc;
        // 指向缓存图像的指针
        LPSTR    lpDst;
        // 指向缓存DIB图像的指针
        LPSTR    lpNewDIBBits;
        HLOCAL    hNewDIBBits;
        //循环变量
        long i;
        long j;
        //像素值
        unsigned char pixel;
        //是否找到起始点及回到起始点
        bool bFindStartPoint;
        //是否扫描到一个边界点
        bool bFindPoint;
        //起始边界点与当前边界点
        Point StartPoint,CurrentPoint;
        //八个方向和起始扫描方向
        int Direction[8][2]={{-1,1},{0,1},{1,1},{1,0},{1,-1},{0,-1},{-1,-1},{-1,0}};
        int BeginDirect;
        // 图像每行的字节数
        //LONG lLineBytes;
        // 计算图像每行的字节数
        //lLineBytes = WIDTHBYTES(lWidth * 8);
        // 暂时分配内存,以保存新图像
        hNewDIBBits = LocalAlloc(LHND, lWidth * lHeight);
        if (hNewDIBBits == NULL)
        {
            // 分配内存失败
            return FALSE;
        }
        // 锁定内存
        lpNewDIBBits = (char * )LocalLock(hNewDIBBits);
        // 初始化新分配的内存,设定初始值为255
        lpDst = (char *)lpNewDIBBits;
        memset(lpDst, (BYTE)255, lWidth * lHeight);
        //先找到最左上方的边界点
        bFindStartPoint = false;
        for (j = 0;j < lHeight && !bFindStartPoint;j++)
        {
            for(i = 0;i < lWidth && !bFindStartPoint;i++)
            {
                // 指向源图像倒数第j行,第i个象素的指针           
                lpSrc = (char *)lpDIBBits + lWidth * j + i;
                //取得当前指针处的像素值,注意要转换为unsigned char型
                pixel = (unsigned char)*lpSrc;
                if(pixel == 0)
                {
                    bFindStartPoint = true;
                    StartPoint.Height = j;
                    StartPoint.Width = i;
                    // 指向目标图像倒数第j行,第i个象素的指针           
                    lpDst = (char *)lpNewDIBBits + lWidth * j + i;   
                    *lpDst = (unsigned char)0;
                }       
            }
        }
        //由于起始点是在左下方,故起始扫描沿左上方向
        BeginDirect = 0;
        //跟踪边界
        bFindStartPoint = false;
        //从初始点开始扫描
        CurrentPoint.Height = StartPoint.Height;
        CurrentPoint.Width = StartPoint.Width;
        while(!bFindStartPoint)
        {
            bFindPoint = false;
            while(!bFindPoint)
            {
                //沿扫描方向查看一个像素
                lpSrc = (char *)lpDIBBits + lWidth * ( CurrentPoint.Height + Direction[BeginDirect][1]) + (CurrentPoint.Width + Direction[BeginDirect][0]);
                pixel = (unsigned char)*lpSrc;
                if(pixel == 0)
                {
                    bFindPoint = true;
                    CurrentPoint.Height = CurrentPoint.Height + Direction[BeginDirect][1];
                    CurrentPoint.Width = CurrentPoint.Width + Direction[BeginDirect][0];
                    if(CurrentPoint.Height == StartPoint.Height && CurrentPoint.Width == StartPoint.Width)
                    {
                        bFindStartPoint = true;
                    }
                    lpDst = (char *)lpNewDIBBits + lWidth * CurrentPoint.Height + CurrentPoint.Width;
                    *lpDst = (unsigned char)0;
                    //扫描的方向逆时针旋转两格
                    BeginDirect--;
                    if(BeginDirect == -1)
                        BeginDirect = 7;
                    BeginDirect--;
                    if(BeginDirect == -1)
                        BeginDirect = 7;
                }
                else
                {
                    //扫描方向顺时针旋转一格
                    BeginDirect++;
                    if(BeginDirect == 8)
                        BeginDirect = 0;
                }
            }
        }
        // 复制腐蚀后的图像
        memcpy(lpDIBBits, lpNewDIBBits, lWidth * lHeight);
        // 释放内存
        LocalUnlock(hNewDIBBits);
        LocalFree(hNewDIBBits);
        //
        //
        for (int yy=0; yy<superImage->height; yy++)
        {
            for (int xx=0; xx<superImage->width; xx++)
            {
                unsigned char point = lpDIBBits[lWidth * yy + xx];           
                superImage->data[yy*superImage->width+xx] = point;
            }
        }
        //
        LocalUnlock(lpDIBBits);
        LocalFree(lpDIBBits);
        // 返回
        return TRUE;
    }
    int EnhancedImage::edge()
    {
        //
        LONG lWidth = superImage->width;
        LONG lHeight = superImage->height;
        //
        LPSTR lpDIBBits = (char *)LocalAlloc(LHND, lWidth * lHeight);
        if (lpDIBBits == NULL)
        {
            return false ;
        }   
        lpDIBBits = (char *)LocalLock(lpDIBBits);
        //
        for (int y=0; y<superImage->height; y++)
        {
            for (int x=0; x<superImage->width; x++)
            {
                unsigned char point = superImage->data[y*superImage->width+x];           
                lpDIBBits[lWidth * y + x] = point;
            }
        }
        //       
        //
        // 指向源图像的指针
        LPSTR    lpSrc;
        // 指向缓存图像的指针
        LPSTR    lpDst;
        // 指向缓存DIB图像的指针
        LPSTR    lpNewDIBBits;
        HLOCAL    hNewDIBBits;
        //循环变量
        long i;
        long j;
        unsigned char n,e,s,w,ne,se,nw,sw;
        //像素值
        unsigned char pixel;
        // 暂时分配内存,以保存新图像
        hNewDIBBits = LocalAlloc(LHND, lWidth * lHeight);
        if (hNewDIBBits == NULL)
        {
            // 分配内存失败
            return FALSE;
        }
        // 锁定内存
        lpNewDIBBits = (char * )LocalLock(hNewDIBBits);
        // 初始化新分配的内存,设定初始值为255
        lpDst = (char *)lpNewDIBBits;
        memset(lpDst, (BYTE)255, lWidth * lHeight);
        for(j = 1; j <lHeight-1; j++)
        {
            for(i = 1;i <lWidth-1; i++)
            {
                // 指向源图像倒数第j行,第i个象素的指针   
                lpSrc = (char *)lpDIBBits + lWidth * j + i;
                // 指向目标图像倒数第j行,第i个象素的指针
                lpDst = (char *)lpNewDIBBits + lWidth * j + i;
                //取得当前指针处的像素值,注意要转换为unsigned char型
                pixel = (unsigned char)*lpSrc;
                if(pixel == 0)
                {
                    *lpDst = (unsigned char)0;
                    nw = (unsigned char)*(lpSrc + lWidth -1);
                    n  = (unsigned char)*(lpSrc + lWidth );
                    ne = (unsigned char)*(lpSrc + lWidth +1);
                    w = (unsigned char)*(lpSrc -1);
                    e = (unsigned char)*(lpSrc +1);
                    sw = (unsigned char)*(lpSrc - lWidth -1);
                    s  = (unsigned char)*(lpSrc - lWidth );
                    se = (unsigned char)*(lpSrc - lWidth +1);
                    //如果相邻的八个点都是黑点
                    if(nw+n+ne+w+e+sw+s+se==0)
                    {
                        *lpDst = (unsigned char)255;
                    }
                }
            }
        }
        // 复制腐蚀后的图像
        memcpy(lpDIBBits, lpNewDIBBits, lWidth * lHeight);
        // 释放内存
        LocalUnlock(hNewDIBBits);
        LocalFree(hNewDIBBits);
        //
        //
        for (int yy=0; yy<superImage->height; yy++)
        {
            for (int xx=0; xx<superImage->width; xx++)
            {
                unsigned char point = lpDIBBits[lWidth * yy + xx];           
                superImage->data[yy*superImage->width+xx] = point;
            }
        }
        //
        LocalUnlock(lpDIBBits);
        LocalFree(lpDIBBits);
        // 返回
        return TRUE;
    }
    int EnhancedImage::thin()
    {
        //
        LONG lWidth = superImage->width;
        LONG lHeight = superImage->height;
        //
        LPSTR lpDIBBits = (char *)LocalAlloc(LHND, lWidth * lHeight);
        if (lpDIBBits == NULL)
        {
            return false ;
        }   
        lpDIBBits = (char *)LocalLock(lpDIBBits);
        //
        for (int y=0; y<superImage->height; y++)
        {
            for (int x=0; x<superImage->width; x++)
            {
                unsigned char point = superImage->data[y*superImage->width+x];           
                lpDIBBits[lWidth * y + x] = point;
            }
        }
        //
        //
        LPSTR    lpSrc;               
        LPSTR    lpDst;   
        LPSTR    lpNewDIBBits;   
        HLOCAL    hNewDIBBits;   
        BOOL bModified;               
        long i,j,m,n;       
        BOOL con1;
        BOOL con2;
        BOOL con3;
        BOOL con4;
        unsigned char nCount;       
        unsigned char pixel;       
        unsigned char ne[5][5];   
        hNewDIBBits = LocalAlloc(LHND, lWidth * lHeight);
        if (hNewDIBBits == NULL)
        {
            return false ;
        }
        lpNewDIBBits = (char * )LocalLock(hNewDIBBits);
        // 初始化新分配的内存,设定初始值为255
        lpDst = (char *)lpNewDIBBits;
        memset(lpDst, (BYTE)255, lWidth * lHeight);
        bModified = TRUE;
        while(bModified)
        {
            bModified = FALSE;
            lpDst = (char *)lpNewDIBBits;
            memset(lpDst, (BYTE)255, lWidth * lHeight);
            for(j = 2; j <lHeight-2; j++)
            {
                for(i = 2;i <lWidth-2; i++)
                {
                    con1 = FALSE;
                    con2 = FALSE;
                    con3 = FALSE;
                    con4 = FALSE;
                    lpSrc = (char *)lpDIBBits + lWidth * j + i;       
                    lpDst = (char *)lpNewDIBBits + lWidth * j + i;
                    pixel = (unsigned char)*lpSrc;
                    if(pixel != 255 && *lpSrc != 0)
                    {
                        continue;
                    }
                    else if(pixel == 255)
                    {
                        continue;
                    }
                    //白色用0代表,黑色用1代表
                    for (m = 0;m < 5;m++ )
                    {
                        for (n = 0;n < 5;n++)
                        {
                            ne[m][n] =(255 - (unsigned char)*(lpSrc + ((4 - m) - 2)*lWidth + n - 2 )) / 255;
                        }
                    }
                    //判断2<=NZ(P1)<=6
                    nCount =  ne[1][1] + ne[1][2] + ne[1][3]
                        + ne[2][1]            + ne[2][3]
                        + ne[3][1] + ne[3][2] + ne[3][3];
                    if ( nCount >= 2 && nCount <=6)
                    {
                        con1 = TRUE;
                    }
                    //判断Z0(P1)=1
                    nCount = 0;
                    if (ne[1][2] == 0 && ne[1][1] == 1)
                        nCount++;
                    if (ne[1][1] == 0 && ne[2][1] == 1)
                        nCount++;
                    if (ne[2][1] == 0 && ne[3][1] == 1)
                        nCount++;
                    if (ne[3][1] == 0 && ne[3][2] == 1)
                        nCount++;
                    if (ne[3][2] == 0 && ne[3][3] == 1)
                        nCount++;
                    if (ne[3][3] == 0 && ne[2][3] == 1)
                        nCount++;
                    if (ne[2][3] == 0 && ne[1][3] == 1)
                        nCount++;
                    if (ne[1][3] == 0 && ne[1][2] == 1)
                        nCount++;
                    if (nCount == 1)
                        con2 = TRUE;
                    //判断P2*P4*P6=0 or Z0(p4)!=1
                    if (ne[1][2]*ne[2][1]*ne[3][2] == 0)
                    {
                        con3 = TRUE;
                    }
                    else
                    {
                        nCount = 0;
                        if (ne[1][1] == 0 && ne[1][0] == 1)
                            nCount++;
                        if (ne[1][0] == 0 && ne[2][0] == 1)
                            nCount++;
                        if (ne[2][0] == 0 && ne[3][0] == 1)
                            nCount++;
                        if (ne[3][0] == 0 && ne[3][1] == 1)
                            nCount++;
                        if (ne[3][1] == 0 && ne[3][2] == 1)
                            nCount++;
                        if (ne[3][2] == 0 && ne[2][2] == 1)
                            nCount++;
                        if (ne[2][2] == 0 && ne[1][2] == 1)
                            nCount++;
                        if (ne[1][2] == 0 && ne[1][1] == 1)
                            nCount++;
                        if (nCount != 1)
                            con3 = TRUE;
                    }
                    //判断P2*P4*P8=0 or Z0(p2)!=1
                    if (ne[1][2]*ne[2][1]*ne[2][3] == 0)
                    {
                        con4 = TRUE;
                    }
                    else
                    {
                        nCount = 0;
                        if (ne[0][2] == 0 && ne[0][1] == 1)
                            nCount++;
                        if (ne[0][1] == 0 && ne[1][1] == 1)
                            nCount++;
                        if (ne[1][1] == 0 && ne[2][1] == 1)
                            nCount++;
                        if (ne[2][1] == 0 && ne[2][2] == 1)
                            nCount++;
                        if (ne[2][2] == 0 && ne[2][3] == 1)
                            nCount++;
                        if (ne[2][3] == 0 && ne[1][3] == 1)
                            nCount++;
                        if (ne[1][3] == 0 && ne[0][3] == 1)
                            nCount++;
                        if (ne[0][3] == 0 && ne[0][2] == 1)
                            nCount++;
                        if (nCount != 1)
                            con4 = TRUE;
                    }
                    if(con1 && con2 && con3 && con4)
                    {
                        *lpDst = (unsigned char)255;
                        bModified = TRUE;
                    }
                    else
                    {
                        *lpDst = (unsigned char)0;
                    }
                }
            }       
            memcpy(lpDIBBits, lpNewDIBBits, lWidth * lHeight);
        }
        memcpy(lpDIBBits, lpNewDIBBits, lWidth * lHeight);
        LocalUnlock(hNewDIBBits);
        LocalFree(hNewDIBBits);
        //
        //
        for (int yy=0; yy<superImage->height; yy++)
        {
            for (int xx=0; xx<superImage->width; xx++)
            {
                unsigned char point = lpDIBBits[lWidth * yy + xx];           
                superImage->data[yy*superImage->width+xx] = point;
            }
        }
        //
        LocalUnlock(lpDIBBits);
        LocalFree(lpDIBBits);
    }
    int EnhancedImage::tidy()
    {
        //一个更好的算法,是要求整理之后,还保证原来的联通性,现在还不够好
        int h = this->superImage->height;
        int w = this->superImage->width;
        short* src = this->superImage->data;
        //
        for (int y=0; y<h; y++)
        {
            for (int x=0; x<w; x++)
            {
                unsigned char o = src[w*(y)+(x)];
                //
                unsigned char u = -1;                   
                unsigned char ur = -1;                   
                unsigned char r = -1;                   
                unsigned char br = -1;                   
                unsigned char b = -1;                   
                unsigned char bl = -1;                   
                unsigned char l = -1;                   
                unsigned char ul = -1;   
                //
                if(y<h)
                {
                    u = src[w*(y+1)+(x)];
                }
                if(y<h && x< w)
                {
                    ur =src[w*(y+1)+(x+1)];
                }
                if(x<w)
                {
                    r=src[w*(y)+(x+1)];
                }
                if(y>0 && x<w)
                {
                    br = src[w*(y-1)+(x+1)];
                }
                if(y>0)
                {
                    b = src[w*(y-1)+(x)];
                }
                if(y>0 && x>0)
                {
                    bl = src[w*(y-1)+(x-1)];
                }
                if(x>0)
                {
                    l = src[w*(y)+(x-1)];
                }
                if(y<h && x>0)
                {
                    ul = src[w*(y+1)+(x-1)];
                }       
                //
                if(o==0)
                {
                    if(r==0 && u==0 &&  br==0)
                    {
                        src[w*(y)+(x+1)] = 255;
                    }
                    if(r==0 && ur==0 &&  r==0)
                    {
                        src[w*(y)+(x+1)] = 255;
                    }
                    if(r==0 && ul==0 &&  br==0)
                    {
                        src[w*(y)+(x+1)] = 255;
                    }
                    //
                    if(l==0 && u==0 &&  bl==0)
                    {
                        src[w*(y)+(x-1)] = 255;
                    }
                    if(l==0 && u==0 &&  ur==0)
                    {
                        src[w*(y)+(x-1)] = 255;
                    }
                    if(l==0 && bl==0 &&  ur==0)
                    {
                        src[w*(y)+(x-1)] = 255;
                    }
                    if(l==0 && bl==0 &&  r==0)
                    {
                        src[w*(y)+(x-1)] = 255;
                    }
                    if(l==0 && ul==0 &&  r==0)
                    {
                        src[w*(y)+(x-1)] = 255;
                    }
                    if(l==0 && ul==0 &&  br==0)
                    {
                        src[w*(y)+(x-1)] = 255;
                    }
                }
            }
        }
        return 0;
    }
    int EnhancedImage::kerf()
    {
        int w = this->superImage->width;
        int h = this->superImage->height;   
        short* src = this->superImage->data;
        //
        SuperImage<Pixel>* tmpD = new SuperImage<Pixel>(w,h);
        short* tgt = tmpD->data;
        //
        for (int y=0; y<h; y++)
        {
            for (int x=0; x<w; x++)
            {
                int count = 0;
                unsigned char p = src[y*w+x];
                tgt[y*w+x] = p;
                //           
                if(y<h && src[w*(y+1)+(x)]  == 0)
                {
                    count = count + 1;
                }
                if(y<h && x< w && src[w*(y+1)+(x+1)]  == 0)
                {
                    count = count + 1;
                }
                if(x<w && src[w*(y)+(x+1)]  == 0)
                {
                    count = count + 1;
                }
                if(y>0 && x<w && src[w*(y-1)+(x+1)]  == 0)
                {
                    count = count + 1;
                }
                if(y>0 && src[w*(y-1)+(x)]  == 0)
                {
                    count = count + 1;
                }
                if(y>0 && x>0 && src[w*(y-1)+(x-1)]  == 0)
                {
                    count = count + 1;
                }
                if(x>0 && src[w*(y)+(x-1)]  == 0)
                {
                    count = count + 1;
                }
                if(y<h && x>0 && src[w*(y+1)+(x-1)]  == 0)
                {
                    count = count + 1;
                }
                //
                if(p == 0 && count > 2)
                {
                    tgt[y*w+x] = -1;
                }
            }
        }
        //
        if(superImage)
        {
            delete superImage;
            superImage = NULL;
        }
        //
        superImage = tmpD;
        //
        return 0;
    }
    int EnhancedImage::join()
    {
        return 0;
    }
    //AntiCaptchaPart1.cpp
    #include <stdio.h>
    #include <string>
    #include <windows.h>
    #include "common.h"
    //
    void process(char* loadPathName,char* tempPathName)
    {
        EnhancedImage otsuImage;
        otsuImage.load(loadPathName);
        if(1)
        {
            char convPathName[MAX_PATH];
            sprintf(convPathName,"%s%s",tempPathName,".001.conv.bmp");
            otsuImage.save(convPathName);
        }
        if(1)
        {
            char binvPathName[MAX_PATH];
            sprintf(binvPathName,"%s%s",tempPathName,".002.binv.bmp");
            otsuImage.binv();
            otsuImage.save(binvPathName);
        }
        if(1)
        {
            char otsuPathName[MAX_PATH];
            sprintf(otsuPathName,"%s%s",tempPathName,".003.otsu.bmp");
            otsuImage.otsu();
            otsuImage.save(otsuPathName);
        }
        if(0)
        {
            char linePathName[MAX_PATH];
            sprintf(linePathName,"%s%s",tempPathName,".004.line.bmp");
            otsuImage.line();
            otsuImage.save(linePathName);
        }
        if(1)
        {
            char roiiPathName[MAX_PATH];
            sprintf(roiiPathName,"%s%s",tempPathName,".005.roii.bmp");
            otsuImage.roii();
            otsuImage.save(roiiPathName);
        }
        EnhancedImage tracImage;
        tracImage.copy(&otsuImage);
        if(1)
        {
            char tracPathName[MAX_PATH];
            sprintf(tracPathName,"%s%s",tempPathName,".006.trac.bmp");
            tracImage.trac();
            tracImage.save(tracPathName);
        }
        //
        EnhancedImage edgeImage;
        edgeImage.copy(&otsuImage);
        if(1)
        {
            char edgePathName[MAX_PATH];
            sprintf(edgePathName,"%s%s",tempPathName,".007.edge.bmp");
            edgeImage.edge();
            edgeImage.save(edgePathName);
        }
        //
        EnhancedImage thinImage;
        thinImage.copy(&otsuImage);
        if(1)
        {
            char thinPathName[MAX_PATH];
            sprintf(thinPathName,"%s%s",tempPathName,".008.thin.bmp");
            thinImage.thin();
            thinImage.save(thinPathName);
        }
        if(1)
        {
            char tidyPathName[MAX_PATH];
            sprintf(tidyPathName,"%s%s",tempPathName,".009.tidy.bmp");
            thinImage.tidy();
            thinImage.save(tidyPathName);
        }
        EnhancedImage kerfImage;
        kerfImage.copy(&thinImage);
        if(1)
        {
            char kerfPathName[MAX_PATH];
            sprintf(kerfPathName,"%s%s",tempPathName,".010.kerf.bmp");
            kerfImage.kerf();
            kerfImage.save(kerfPathName);
        }
        EnhancedImage filmImage;
        filmImage.copy(&kerfImage);
        if(1)
        {   
            char filmPathName[MAX_PATH];
            sprintf(filmPathName,"%s%s",tempPathName,".011.film.bmp");
            filmImage.film(&edgeImage);
           filmImage.save(filmPathName);
        }
    }
    //
    void travel()
    {
        char homePathName[MAX_PATH];
        GetCurrentDirectory(MAX_PATH,homePathName);
        //
        SetCurrentDirectory(homePathName);
        SetCurrentDirectory(".\\output");
        WIN32_FIND_DATA    findDataDelete;
        HANDLE    hHandleDelete  =  FindFirstFile("*.*", &findDataDelete);
        int hasNextFileDelete = (hHandleDelete !=  INVALID_HANDLE_VALUE);
        while (hasNextFileDelete !=  0)
        {
            char deletePathName[MAX_PATH];
            GetFullPathName(findDataDelete.cFileName, MAX_PATH, deletePathName, NULL);
            printf("Delete %s\n",deletePathName);
            DeleteFile(deletePathName);
            hasNextFileDelete = FindNextFile(hHandleDelete, &findDataDelete);
        }
        if(hHandleDelete !=  INVALID_HANDLE_VALUE)
        {
            FindClose(hHandleDelete);
        }
        //
        //
        SetCurrentDirectory(homePathName);
        SetCurrentDirectory(".\\sample");
        WIN32_FIND_DATA    findData;
        HANDLE    hFindHandle  =  FindFirstFile("*.jpg", &findData);
        int hasNextFile = (hFindHandle !=  INVALID_HANDLE_VALUE);
        while (hasNextFile !=  0)
        {
            char loadPathName[MAX_PATH];
            GetFullPathName(findData.cFileName, MAX_PATH, loadPathName, NULL);       
            //
            char tempPathName[MAX_PATH];
            sprintf(tempPathName,"%s",loadPathName);
            for(int i=strlen(tempPathName);i>=0;i--)
            {
                if(tempPathName[i]=='\\')
                {
                    break;
                }
                else
                {
                    tempPathName[i]='\0';
                }
            }
            strcat(tempPathName,"..\\output\\");
            strcat(tempPathName,findData.cFileName);
            //
            printf("Process %s to %s\n",loadPathName,tempPathName);
            process(loadPathName,tempPathName);
            //
            //
            hasNextFile = FindNextFile(hFindHandle, &findData);
        }
        if(hFindHandle !=  INVALID_HANDLE_VALUE)
        {
            FindClose(hFindHandle);
        }
    }
    //
    void main(int argc, char* argv[])
    {
        printf("Anti Captcha ...\n");
        travel();
        printf("Anti Captcha !!!\n");
    }

        注: 在这里,我们可以看到,基本的部件(字母是分割开了,但可以造成统一字母的被切
    割成多个Component。 一种做法是:利用先验知识,做分割; 另外一种做法是,和第二部分的
    识别结合起来。 比如按照从左至右,尝试增加component来识别,如果不能识别而且
    component的总宽度,总面积还比较小,继续增加。 当然不排除拒识的可能性。 )
        3)字符部件组合和识别。
        part2的代码展示了切割后的字母组合,和基于svm的字符识别的训练和识别过程。
    Detection.cpp中展示了ImageSpam检测过程中的一些字符分割和组合,layout的分析和利用
    的简单技术。 而Google的验证码的识别,完全可以不用到,仅做参考。
        SVM及使用:
        本质上,SVM是一个分类器,原始的SVM是一个两类分类的分类器。可以通过1:1或者1:n
    的方式来组合成一个多类分类的分类器。 天生通过核函数的使用支持高维数据的分类。从
    几何意义上讲,就是找到最能表示类别特征的那些向量(支持向量SV),然后找到一条线,能最
    大化分类的Margin。
        libSVM是一个不错的实现。
        训练间断和识别阶段的数据整理和归一化是一样的。 这里的简单做法是:
        首先:
        #define SVM_MAX  +0.999
        #define SVM_MIN  +0.001
        其次:
        扫描黑白待识别字幕图片的每个像素,如果为0(黑色,是字母上的像素),那么svm中该位
    置就SVM_MAX,反之则反。
        最后:
        训练阶段,在svm的input的前面,为该类打上标记,即是那一个字母。
        识别阶段,当然这个类别标记是SVM分类出来。
        注意:
        如果是SVM菜鸟,最好找一个在SVM外边做了包装的工具,比如样本选择,交叉验证,核函
    数选择这些,让程序自动选择和分析。
        代码:通过ReginGrowth来提取单个单个的字符,然后开始识别。
        查看代码(./pstzine_09_02.txt)
    六、对验证码设计的一些建议
        1.在噪音等类型的使用上,尽力让字符和用来混淆的前景和背景不容易区分。尽力让坏
    人(噪音)长得和好人(字母)一样。
        2.特别好的验证码的设计,要尽力发挥人类擅长而AI算法不擅长的。 比如粘连字符的
    分割和手写体(通过印刷体做特别的变形也可以)。 而不要一味的去加一些看起来比较复杂
    的噪音或者其他的花哨的东西。即使你做的足够复杂,但如果人也难识别,显然别人认为你
    是没事找抽型的。
        3. 从专业的机器视觉的角度说,验证码的设计,一定要让破解者在识别阶段,反复在低
    阶视觉和高阶视觉之间多反复几次才能识别出来。 这样可以大大降低破解难度和破解的准
    确率。
    七、个人郑重申明
        1.这个问题,本身是人工智能,计算机视觉,模式识别领域的一个难题。我是虾米,菜得
    不能再菜的那种。作为破解者来说,是出于劣势地位。要做的很好,是很难得。总体来说,我
    走的是比较学院派的线路,能真正的破解难度比较高的验证码,不同于网上很多不太入流的
    破解方法。我能做的只有利用有限的知识,抛砖引玉而已。 很多OCR的技术,特别是离线手
    写体中文等文字识别的技术,个人了解有限的很,都不敢在这里乱写。
        2.希望不要把这种技术用于非法用途。
  • 相关阅读:
    城市的划入划出效果
    文本溢出省略解决笔记css
    长串英文数字强制折行解决办法css
    Poj 2352 Star
    树状数组(Binary Indexed Trees,二分索引树)
    二叉树的层次遍历
    Uva 107 The Cat in the Hat
    Uva 10336 Rank the Languages
    Uva 536 Tree Recovery
    Uva10701 Pre, in and post
  • 原文地址:https://www.cnblogs.com/hsapphire/p/1929732.html
Copyright © 2011-2022 走看看