• 先对验证码进行简单的处理噪点和纯色 例未曾处理的图片 public static void main(String[] args) throws Exception { //源文件 String picName = "C:\\Users\\syxy101\\Desktop\\6.jpg"; File file...

     先对验证码进行简单的处理噪点和纯色

    例未曾处理的图片

    public static void main(String[] args) throws Exception {
       //源文件
      String picName = "C:\\Users\\syxy101\\Desktop\\6.jpg";
    
     File filepic=new File(picName);
    //去噪点 并处理为纯色
      BufferedImage bufferedImage = removeBackgroud(picName);
    //处理后图片地址
    File w2 = new File("C:\\Users\\syxy101\\Desktop\\5.png");//可以是jpg,png格式
    ImageIO.write(bufferedImage, "jpg", w2);//不管输出什么格式图片,此处不需改动
    
    
    }
    
    //去除图片噪点  并将彩色转为纯色(黑色)
      public static BufferedImage removeBackgroud(String picFile)
                throws Exception {
            BufferedImage img = ImageIO.read(new File(picFile));
            int width = img.getWidth();
            int height = img.getHeight();
            for (int x = 0; x < width; ++x) {
                for (int y = 0; y < height; ++y) {
                    if (isWhite(img.getRGB(x, y)) == 1) {
                        img.setRGB(x, y, Color.WHITE.getRGB());
                    } else {
                        img.setRGB(x, y, Color.BLACK.getRGB());
                    }
                }
            }
            return img;
        }
    //设置颜色阀值
     public static int isWhite(int colorInt) {
            Color color = new Color(colorInt);
            if (color.getRed() + color.getGreen() + color.getBlue() > 200 && color.getRed() + color.getGreen() + color.getBlue()<500) {
                return 0;
            }
            return 1;
        }

    处理后

    可以看到已经清晰了不少  当然现在还是无法识别的

    一下步 强加黑色 处理掉横线斜线 噪点

    package com.adc.da.sync.web;
    
    import org.apache.commons.httpclient.HttpClient;
    import org.apache.commons.httpclient.HttpStatus;
    import org.apache.commons.httpclient.methods.GetMethod;
    import org.apache.commons.io.IOUtils;
    
    import javax.imageio.ImageIO;
    import java.awt.Color;
    import java.awt.image.BufferedImage;
    import java.io.File;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.OutputStream;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * //TODO 添加类/接口功能描述
     *
     * @author zhangsj
     * @date 2018-12-13
     */
    public class ImagePreProcess4 {
    
    
    
            public static void main(String[] args) throws IOException
            {
                //源文件
                File testDataDir = new File("C:\\Users\\syxy101\\Desktop\\5.png");
                //处理后路径
                final String destDir = "C:\\Users\\syxy101\\Desktop\\ss";
                    cleanLinesInImage(testDataDir, destDir);
            }
    
            /**
             *
             * @param sfile
             *            需要去噪的图像
             * @param destDir
             *            去噪后的图像保存地址
             * @throws IOException
             */
            public static void cleanLinesInImage(File sfile, String destDir)  throws IOException{
                File destF = new File(destDir);
                if (!destF.exists())
                {
                    destF.mkdirs();
                }
    
                BufferedImage bufferedImage = ImageIO.read(sfile);
                int h = bufferedImage.getHeight();
                int w = bufferedImage.getWidth();
    
                // 灰度化
                int[][] gray = new int[w][h];
                for (int x = 0; x < w; x++)
                {
                    for (int y = 0; y < h; y++)
                    {
                        int argb = bufferedImage.getRGB(x, y);
                        // 图像加亮(调整亮度识别率非常高)
                        int r = (int) (((argb >> 16) & 0xFF) * 1.1 + 30);
                        int g = (int) (((argb >> 8) & 0xFF) * 1.1 + 30);
                        int b = (int) (((argb >> 0) & 0xFF) * 1.1 + 30);
                        if (r >= 255)
                        {
                            r = 255;
                        }
                        if (g >= 255)
                        {
                            g = 255;
                        }
                        if (b >= 255)
                        {
                            b = 255;
                        }
                        gray[x][y] = (int) Math
                                .pow((Math.pow(r, 2.2) * 0.2973 + Math.pow(g, 2.2)
                                        * 0.6274 + Math.pow(b, 2.2) * 0.0753), 1 / 2.2);
                    }
                }
    
                // 二值化
                int threshold = ostu(gray, w, h);
                BufferedImage binaryBufferedImage = new BufferedImage(w, h, BufferedImage.TYPE_BYTE_BINARY);
                for (int x = 0; x < w; x++)
                {
                    for (int y = 0; y < h; y++)
                    {
                        if (gray[x][y] > threshold)
                        {
                            gray[x][y] |= 0x00FFFF;
                        } else
                        {
                            gray[x][y] &= 0xFF0000;
                        }
                        binaryBufferedImage.setRGB(x, y, gray[x][y]);
                    }
                }
    
                //去除干扰线条
                for(int y = 1; y < h-1; y++){
                    for(int x = 1; x < w-1; x++){
                        boolean flag = false ;
                        if(isBlack(binaryBufferedImage.getRGB(x, y))){
                            //左右均为空时,去掉此点
                            if(isWhite(binaryBufferedImage.getRGB(x-1, y)) && isWhite(binaryBufferedImage.getRGB(x+1, y))){
                                flag = true;
                            }
                            //上下均为空时,去掉此点
                            if(isWhite(binaryBufferedImage.getRGB(x, y+1)) && isWhite(binaryBufferedImage.getRGB(x, y-1))){
                                flag = true;
                            }
                            //斜上下为空时,去掉此点
                            if(isWhite(binaryBufferedImage.getRGB(x-1, y+1)) && isWhite(binaryBufferedImage.getRGB(x+1, y-1))){
                                flag = true;
                            }
                            if(isWhite(binaryBufferedImage.getRGB(x+1, y+1)) && isWhite(binaryBufferedImage.getRGB(x-1, y-1))){
                                flag = true;
                            }
                            if(flag){
                                binaryBufferedImage.setRGB(x,y,-1);
                            }
                        }
                    }
                }
    
    
                // 矩阵打印
                for (int y = 0; y < h; y++)
                {
                    for (int x = 0; x < w; x++)
                    {
                        if (isBlack(binaryBufferedImage.getRGB(x, y)))
                        {
                            System.out.print("*");
                        } else
                        {
                            System.out.print(" ");
                        }
                    }
                    System.out.println();
                }
    
                ImageIO.write(binaryBufferedImage, "jpg", new File(destDir, sfile
                        .getName()));
            }
    
            public static boolean isBlack(int colorInt)
            {
                Color color = new Color(colorInt);
                if (color.getRed() + color.getGreen() + color.getBlue() <= 300)
                {
                    return true;
                }
                return false;
            }
    
            public static boolean isWhite(int colorInt)
            {
                Color color = new Color(colorInt);
                if (color.getRed() + color.getGreen() + color.getBlue() > 300)
                {
                    return true;
                }
                return false;
            }
    
            public static int isBlackOrWhite(int colorInt)
            {
                if (getColorBright(colorInt) < 30 || getColorBright(colorInt) > 730)
                {
                    return 1;
                }
                return 0;
            }
    
            public static int getColorBright(int colorInt)
            {
                Color color = new Color(colorInt);
                return color.getRed() + color.getGreen() + color.getBlue();
            }
    
            public static int ostu(int[][] gray, int w, int h)
            {
                int[] histData = new int[w * h];
                // Calculate histogram
                for (int x = 0; x < w; x++)
                {
                    for (int y = 0; y < h; y++)
                    {
                        int red = 0xFF & gray[x][y];
                        histData[red]++;
                    }
                }
    
                // Total number of pixels
                int total = w * h;
    
                float sum = 0;
                for (int t = 0; t < 256; t++)
                    sum += t * histData[t];
    
                float sumB = 0;
                int wB = 0;
                int wF = 0;
    
                float varMax = 0;
                int threshold = 0;
    
                for (int t = 0; t < 256; t++)
                {
                    wB += histData[t]; // Weight Background
                    if (wB == 0)
                        continue;
    
                    wF = total - wB; // Weight Foreground
                    if (wF == 0)
                        break;
    
                    sumB += (float) (t * histData[t]);
    
                    float mB = sumB / wB; // Mean Background
                    float mF = (sum - sumB) / wF; // Mean Foreground
    
                    // Calculate Between Class Variance
                    float varBetween = (float) wB * (float) wF * (mB - mF) * (mB - mF);
    
                    // Check if new maximum found
                    if (varBetween > varMax)
                    {
                        varMax = varBetween;
                        threshold = t;
                    }
                }
    
                return threshold;
            }
        }
    
    

     

     

    已经去掉多余的横线和噪点了 

    这时候识别度已经很高了 

    为了达到识别度百分之80

    需要把每个字母隔开 分开识别

      public static void main(String[] args) throws Exception {
    
       int x=6;
          for(int i=0;i<4;i++){
                if(i==0){
                    x=6;
                }else if(i==1){
                    x=22;
                }else if(i==2){
                    x=36;
                }else if(i==3){
                    x=52;
                }
                imageCut(x,0,15,30,picName,"D:\\ccccc"+i+".jpg");
    
            }
    
    }
       /**
         * 图片剪裁
         * @param x 距离左上角的x轴距离
         * @param y 距离左上角的y轴距离
         * @param width 宽度
         * @param height 高度
         * @param sourcePath 图片源
         * @param descpath 目标位置
         */
        public static void imageCut(int x, int y, int width, int height, String sourcePath, String descpath) {
            FileInputStream is = null;
            ImageInputStream iis = null;
            try {
                is = new FileInputStream(sourcePath);
                String fileSuffix = sourcePath.substring(sourcePath.lastIndexOf(".") + 1);
                Iterator<ImageReader> it = ImageIO.getImageReadersByFormatName(fileSuffix);
                ImageReader reader = it.next();
                iis = ImageIO.createImageInputStream(is);
                reader.setInput(iis, true);
                ImageReadParam param = reader.getDefaultReadParam();
                Rectangle rect = new Rectangle(x, y, width, height);
                param.setSourceRegion(rect);
                BufferedImage bi = reader.read(0, param);
                ImageIO.write(bi, fileSuffix, new File(descpath));
            } catch (Exception ex) {
                ex.printStackTrace();
            } finally {
                if (is != null) {
                    try {
                        is.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    is = null;
                }
                if (iis != null) {
                    try {
                        iis.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                    iis = null;
                }
            }
        }

     

    换了个验证码剪辑

    这时候识别度已经到百分之90了

    展开全文
  • 本文将算法第一步的图像预处理,这里有... 由于要进行图像处理,所以文件头首先要写上:  from PIL import Image,ImageEnhance,ImageFilter,ImageGrab   #定义图像预处理的整体函数 def Change_Image(Do

    本文将算法第一步的图像预处理,这里有对图像的格式转换,灰度化,对比度改变,锐化,转为黑白图像,去除噪点等一系列操作。

          由于要进行图像处理,所以文件头首先要写上:

          from PIL import Image,ImageEnhance,ImageFilter,ImageGrab

     

    #定义图像预处理的整体函数

    def Change_Image(Docu_Name,Dist):
        im = Handle_Image(Docu_Name,Dist)
        X_Value=Cut_X(im)
        Y_Value=Cut_Y(im)
        
        ims = []
        Image_Value=[]
        Image_Values=[]
        Image_Value_Row=[]
        for k in range(4):
            im1= im.crop((X_Value[(2*k)],Y_Value[(2*k)],(X_Value[(2*k 1)] 1),(Y_Value[(2*k 1)] 1))) #切割图像为4个子图像
            ims.append(im1)    
            for j in range(Y_Value[(2*k)],(Y_Value[(2*k 1)] 1)):
                for i in range(X_Value[(2*k)],(X_Value[(2*k 1)] 1)):
                    if im.getpixel((i,j))==0:#黑色像素的值是0
                        Image_Value_Row.append(1)
                    else:
                        Image_Value_Row.append(0)

                Image_Value.append(Image_Value_Row)#
                Image_Value_Row=[]#
               
            Image_Values.append(Image_Value)
            Image_Value=[]
        
        return Image_Values #返回切割后各个图像对应的黑白像素的0-1值所存储在其中的三维数组。

       
    #处理图片以便后续的0-1二值化
    def Handle_Image(Docu_Name,Dist):
        im = Image.open('%s'%(Dist Docu_Name) '.png') #打开对应目录的png格式的验证码图片
        im=im.convert('RGB')

        for j in range(im.size[1]):
            for i in range(im.size[0]):            
                Gray = Change_Gray(im.getpixel((i,j)))  #灰度化
                im.putpixel([i,j],(Gray,Gray,Gray))
                if i==0 or i==(im.size[0]-1): #将图片的第一行和最后一行设为白色。
                    im.putpixel([i,j],(255,255,255))
                if j==0 or j==(im.size[1]-1):#将图片的第一列和最后一列设为白色。
                    im.putpixel([i,j],(255,255,255))
        enhancer = ImageEnhance.Contrast(im) #增加对比对
        im = enhancer.enhance(2)
        enhancer = ImageEnhance.Sharpness(im) #锐化
        im = enhancer.enhance(2)
        enhancer = ImageEnhance.Brightness(im) #增加亮度
        im = enhancer.enhance(2)
        #im=im.convert('L').filter(ImageFilter.DETAIL) #滤镜效果
        im = im.convert('1') #转为黑白图片
        
        im = Clear_Point(im) #清除周围8个像素都是白色的孤立噪点
        im = Clear_Point_Twice(im) #清除两个孤立的噪点:周围8个像素中有7个是白色,而唯一的黑色像素对应的他的邻域(他周围的8个像素)中唯一的黑色像素是自身。

        im = Clear_Point_Third(im) #清除第三种噪点:左右都是3个(含)以上的空白列,自身相邻的3个列上的X值投影不大于3.
       
        return im


    #改变灰度,查文献后发现据说按照下面的R,G,B数值的比例进行调整,图像的灰度最合适。
    def Change_Gray(RGB_Value):
        Gray = int((RGB_Value[0]*299 RGB_Value[1]*587 RGB_Value[2]*114)/1000)
        return Gray

     

    图像处理的关键是后续的清楚噪点,也就是所谓的孤立点

     

    #清除单个孤立点
    def Clear_Point(im):
        for j in range(1,(im.size[1]-1)):
            for i in range(1,(im.size[0]-1)):
                if im.getpixel((i,j))==0 and im.getpixel(((i-1),(j-1)))==255  and im.getpixel((i,(j-1)))==255  and im.getpixel(((i 1),(j-1)))==255  and im.getpixel(((i-1),j))==255  and im.getpixel(((i 1),j))==255  and im.getpixel(((i-1),(j 1)))==255  and im.getpixel((i,(j 1)))==255  and im.getpixel(((i 1),(j 1)))==255:
                    im.putpixel([i,j],255)
        return im

     

    #清除只有2个的孤立点
    def Clear_Point_Twice(im):
        for j in range(1,(im.size[1]-1)):
            for i in range(1,(im.size[0]-1)):
                if im.getpixel((i,j))==0 and ( im.getpixel(((i-1),(j-1))) im.getpixel((i,(j-1))) im.getpixel(((i 1),(j-1))) im.getpixel(((i-1),j)) im.getpixel(((i 1),j)) im.getpixel(((i-1),(j 1))) im.getpixel((i,(j 1))) im.getpixel(((i 1),(j 1)))) == 255*7:
                    if im.getpixel(((i 1),j))==0: #因为扫描的顺序是从上到下,从左到右,噪点只能是在自身像素的后面和下面,也就是只有4个可能性而已,而不是8个,可以减少一半的代码。
                        m=i 1
                        n=j
                        if ( im.getpixel(((m-1),(n-1))) im.getpixel((m,(n-1))) im.getpixel(((m 1),(n-1))) im.getpixel(((m-1),n))   im.getpixel(((m 1),n)) im.getpixel(((m-1),(n 1))) im.getpixel((m,(n 1))) im.getpixel(((m 1),(n 1)))) == 255*7:
                           im.putpixel([i,j],255)
                           im.putpixel([m,n],255)
                    elif im.getpixel(((i-1),(j 1)))==0:
                        m=i-1
                        n=j 1
                        if ( im.getpixel(((m-1),(n-1))) im.getpixel((m,(n-1))) im.getpixel(((m 1),(n-1))) im.getpixel(((m-1),n))    im.getpixel(((m 1),n)) im.getpixel(((m-1),(n 1))) im.getpixel((m,(n 1))) im.getpixel(((m 1),(n 1)))) == 255*7:
                           im.putpixel([i,j],255)
                     

    展开全文
  • 本篇讲的主要是对验证码图片的二值去边去线降噪。 最近天热了。人也有点疲惫,不打太多字。 首先安装 opencv :(点击链接查看) https://blog.csdn.net/weixin_43582101/article/details/88660570 我自己画了个图...

    本篇讲的主要是对验证码图片的二值去边去线降噪。

    最近天热了。人也有点疲惫,不打太多字。

    首先安装 opencv :(点击链接查看)
    https://blog.csdn.net/weixin_43582101/article/details/88660570

    我自己画了个图(下文图片数据根据这张图写的)
    图片名:1234567.png:
    在这里插入图片描述
    读入图片1234567.png

    import cv2
    im = cv2.imread('1234567.png')
    

    使用cvtColor方法进行颜色空间转换,转成黑白的

    im = cv2.cvtColor(im, cv2.COLOR_BGR2GRAY)
    

    可以保存下看看:

     cv2.imwrite('33333.png',im)
    

    在这里插入图片描述
    然后我们先去除边框:
    啥原理呢,就是把这个边框范围的 所有坐标的像素 都变成 255 白色的。这个具体要看你的图片的边框值是多少,需要你看情况。

    def clear_border(img):
      h, w = img.shape[:2]                          # h高、w宽
      for y in range(0, w):
        for x in range(0, h):
          if y < 50 or y > w - 61:     # 把在50以内的像素坐标[0,0]到[高,50],[0,宽-50]到[高,宽],都变白色
            img[x, y] = 255
          if x < 60 or x > h - 60:    #  如上
            img[x, y] = 255
      cv2.imwrite('22222.png',img)
      return img
    

    看下 22222.png:
    在这里插入图片描述
    框没了。

    现在我们来去除下线:也就是干扰线降噪

    这个原理呢,就是来判断这个像素点旁边是不是白的,如果是说明他是干扰线,就把他也变成白的。

    def interference_line(img):
      h, w = img.shape[:2]
      # opencv矩阵点是反的
      # img[1,2] 1:图片的高度,2:图片的宽度
      for y in range(1, w - 1):
        for x in range(1, h - 1):
          count = 0
          if img[x, y - 1] > 245:
            count = count + 1
          if img[x, y + 1] > 245:
            count = count + 1
          if img[x - 1, y] > 245:
            count = count + 1
          if img[x + 1, y] > 245:
            count = count + 1
          if count > 2:
            img[x, y] = 255         #判断一圈有多少白的,超过2,就转成白的。
      cv2.imwrite('44444.png',img)
      return img
    

    看下44444.png:
    在这里插入图片描述
    线去的差不多了。但是还有些干扰点了什么的。

    点降噪:

    点降噪的话可以自己看情况来写,也可以利用已经封装好的方法。

    中值滤波medianBlur:

    这个中值滤波 基本思想是用像素点邻域 灰度值的中值 来代替该像素点的灰度值,让周围的像素值接近真实的值从而消除孤立的噪声点。

    import numpy
    image = cv2.imread('44444.png')
    result = numpy.array(image)
    ss = cv2.medianBlur(result,5)
    cv2.imwrite('66666.png',ss)
    

    看下66666.png
    在这里插入图片描述
    是不是好多了。你还可以再次调用这个方法。

    高斯滤波:GaussianBlur函数:

    高斯滤波 是需要对一个像素的周围的像素给予更多的重视。因此,可通过分配权重来重新计算这些周围点的值。这可通过高斯函数(钟形函数,即喇叭形数)的权重方案来解决

    # 将每个像素替换为该像素周围像素的均值
    image1 = cv2.imread('66666.png')
    result = cv2.blur(image1,(5,5))
    gaussianResult = cv2.GaussianBlur(result,(5,5),1.5)
    cv2.imwrite('77777.png',gaussianResult)
    

    再看77777.png
    在这里插入图片描述

    好了,先到这吧。其实没多少东西,如果想研究下降噪的原理话,可以与我联系一起再深入学习下。

    完整流程代码:

    import cv2
    import numpy
    
    
    def cvt(img):
      img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
      return img
    
    def clear_border(img):
      h, w = img.shape[:2]                          # h高、w宽
      for y in range(0, w):
        for x in range(0, h):
          if y < 50 or y > w - 61:     # 把在50以内的像素坐标[0,0]到[高,50],[0,宽-50]到[高,宽],都变白色
            img[x, y] = 255
          if x < 60 or x > h - 60:    #  如上
            img[x, y] = 255
      cv2.imwrite('22222.png',img)
      return img
    
    def interference_line(img):
      h, w = img.shape[:2]
      # opencv矩阵点是反的
      # img[1,2] 1:图片的高度,2:图片的宽度
      for y in range(1, w - 1):
        for x in range(1, h - 1):
          count = 0
          if img[x, y - 1] > 245:
            count = count + 1
          if img[x, y + 1] > 245:
            count = count + 1
          if img[x - 1, y] > 245:
            count = count + 1
          if img[x + 1, y] > 245:
            count = count + 1
          if count > 2:
            img[x, y] = 255         #判断一圈都是白的,就转成白的。
      return img
    
    def dian_medianBlur(image):
      result = numpy.array(image)
      image = cv2.medianBlur(result,5)
      return image
    
    def dian_GaussianBlur(image):
      result = cv2.blur(image,(5,5))
      gaussianResult = cv2.GaussianBlur(result,(5,5),1.5)
      return gaussianResult
    
    if __name__ == '__main__':
      img = cv2.imread('1234567.png')
      image = dian_GaussianBlur(dian_medianBlur(interference_line(clear_border(cvt(img)))))
      cv2.imwrite('88888.png',image)
    
    展开全文
  •   在上一篇博客Python图像处理之图片文字识别(OCR)中我们介绍了在Python中如何利用Tesseract软件来识别图片中的英文与中文,本文将具体介绍如何在Python中利用Tesseract软件来识别验证码(数字加字母)。...

      在上一篇博客Python图像处理之图片文字识别(OCR)中我们介绍了在Python中如何利用Tesseract软件来识别图片中的英文与中文,本文将具体介绍如何在Python中利用Tesseract软件来识别验证码(数字加字母)。
      我们在网上浏览网页或注册账号时,会经常遇到验证码(CAPTCHA),如下图:




    本文将具体介绍如何利用Python的图像处理模块pillow和OCR模块pytesseract来识别上述验证码(数字加字母)。
      我们识别上述验证码的算法过程如下:

    1. 将原图像进行灰度处理,转化为灰度图像;
    2. 获取图片中像素点数量最多的像素(此为图片背景),将该像素作为阈值进行二值化处理,将灰度图像转化为黑白图像(用来提高识别的准确率);
    3. 去掉黑白图像中的噪声,噪声定义为:以该点为中心的九宫格的黑点的数量小于等于4;
    4. 利用pytesseract模块识别,去掉识别结果中的特殊字符,获得识别结果。

      我们的图片如下(共66张图片):


    这里写图片描述

      完整的Python代码如下:

    import os
    import pytesseract
    from PIL import Image
    from collections import defaultdict
    
    # tesseract.exe所在的文件路径
    pytesseract.pytesseract.tesseract_cmd = 'C://Program Files (x86)/Tesseract-OCR/tesseract.exe'
    
    # 获取图片中像素点数量最多的像素
    def get_threshold(image):
        pixel_dict = defaultdict(int)
    
        # 像素及该像素出现次数的字典
        rows, cols = image.size
        for i in range(rows):
            for j in range(cols):
                pixel = image.getpixel((i, j))
                pixel_dict[pixel] += 1
    
        count_max = max(pixel_dict.values()) # 获取像素出现出多的次数
        pixel_dict_reverse = {v:k for k,v in pixel_dict.items()}
        threshold = pixel_dict_reverse[count_max] # 获取出现次数最多的像素点
    
        return threshold
    
    # 按照阈值进行二值化处理
    # threshold: 像素阈值
    def get_bin_table(threshold):
        # 获取灰度转二值的映射table
        table = []
        for i in range(256):
            rate = 0.1 # 在threshold的适当范围内进行处理
            if threshold*(1-rate)<= i <= threshold*(1+rate):
                table.append(1)
            else:
                table.append(0)
        return table
    
    # 去掉二值化处理后的图片中的噪声点
    def cut_noise(image):
    
        rows, cols = image.size # 图片的宽度和高度
        change_pos = [] # 记录噪声点位置
    
        # 遍历图片中的每个点,除掉边缘
        for i in range(1, rows-1):
            for j in range(1, cols-1):
                # pixel_set用来记录该店附近的黑色像素的数量
                pixel_set = []
                # 取该点的邻域为以该点为中心的九宫格
                for m in range(i-1, i+2):
                    for n in range(j-1, j+2):
                        if image.getpixel((m, n)) != 1: # 1为白色,0位黑色
                            pixel_set.append(image.getpixel((m, n)))
    
                # 如果该位置的九宫内的黑色数量小于等于4,则判断为噪声
                if len(pixel_set) <= 4:
                    change_pos.append((i,j))
    
        # 对相应位置进行像素修改,将噪声处的像素置为1(白色)
        for pos in change_pos:
            image.putpixel(pos, 1)
    
        return image # 返回修改后的图片
    
    # 识别图片中的数字加字母
    # 传入参数为图片路径,返回结果为:识别结果
    def OCR_lmj(img_path):
    
        image = Image.open(img_path) # 打开图片文件
        imgry = image.convert('L')  # 转化为灰度图
    
        # 获取图片中的出现次数最多的像素,即为该图片的背景
        max_pixel = get_threshold(imgry)
    
        # 将图片进行二值化处理
        table = get_bin_table(threshold=max_pixel)
        out = imgry.point(table, '1')
    
        # 去掉图片中的噪声(孤立点)
        out = cut_noise(out)
    
        #保存图片
        # out.save('E://figures/img_gray.jpg')
    
        # 仅识别图片中的数字
        #text = pytesseract.image_to_string(out, config='digits')
        # 识别图片中的数字和字母
        text = pytesseract.image_to_string(out)
    
        # 去掉识别结果中的特殊字符
        exclude_char_list = ' .:\\|\'\"?![],()~@#$%^&*_+-={};<>/¥'
        text = ''.join([x for x in text if x not in exclude_char_list])
        #print(text)
    
        return text
    
    def main():
    
        # 识别指定文件目录下的图片
        # 图片存放目录figures
        dir = 'E://figures'
    
        correct_count = 0  # 图片总数
        total_count = 0    # 识别正确的图片数量
    
        # 遍历figures下的png,jpg文件
        for file in os.listdir(dir):
            if file.endswith('.png') or file.endswith('.jpg'):
                # print(file)
                image_path = '%s/%s'%(dir,file) # 图片路径
    
                answer = file.split('.')[0]  # 图片名称,即图片中的正确文字
                recognizition = OCR_lmj(image_path) # 图片识别的文字结果
    
                print((answer, recognizition))
                if recognizition == answer: # 如果识别结果正确,则total_count加1
                    correct_count += 1
    
                total_count += 1
    
        print('Total count: %d, correct: %d.'%(total_count, correct_count))
        '''
        # 单张图片识别
        image_path = 'E://figures/code (1).jpg'
        OCR_lmj(image_path)
        '''
    
    main()

    运行结果如下:

    ('101659', '101659')
    ('111073', '111073')
    ('114510', '114510')
    ('118235', '118235')
    ('124677', '124677')
    ('147291', '147291')
    ('169147', '169147')
    ('185302', '185302')
    ('23YB', '23YB')
    ('262051', '262051')
    ('2HED', '2MED')
    ('315386', '315386')
    ('3D7K', '3D7K')
    ('3DYH', '3DYH')
    ('3QG8', '30G8')
    ('3XNR', 'EXNR')
    ('44G5', '44G5')
    ('470259', '470259')
    ('515413', '515413')
    ('522351', '522351')
    ('539824', '539824')
    ('5CVL', 'SCVL')
    ('642689', '642689')
    ('671991', '671991')
    ('672838', '672838')
    ('6F5Y', '6F5Y')
    ('6USB', 'GUSB')
    ('703167', '703167')
    ('765120', '765120')
    ('779931', '779931')
    ('8UEF', '8SUEF')
    ('905857', '905857')
    ('9H4H', '9H4H')
    ('9SK1', 'OSK1')
    ('BDP4', 'BDP4')
    ('DXV3', 'DXV3')
    ('E78Y', 'E78Y')
    ('EAHR', 'EAHR')
    ('F585', 'Fss§')
    ('FBV8', 'FBV8')
    ('FJKK', 'FJKK')
    ('GXKQ', 'GXKQ')
    ('H7Y9', 'H7Y9')
    ('J4LJ', 'J4LJ')
    ('J8YH', 'J8YH')
    ('JCDL', 'JCDL')
    ('JTX2', 'JTX2')
    ('JYLH', 'JYLH')
    ('KFYA', 'KFYA')
    ('L3VZ', 'L3VZ')
    ('LCGV', 'LCGV')
    ('LKEK', 'LKEK')
    ('N3FJ', 'N3FJ')
    ('PJZN', 'PJZN')
    ('PNDQ', 'PNDQ')
    ('Q7HP', 'Q7HP')
    ('QSHU', 'QSHU')
    ('R1RN', 'RLRN')
    ('RPNX', 'RPNX')
    ('TUKG', 'TUKG')
    ('U9G3', 'U9G3')
    ('UZAH', 'UZAH')
    ('V6P9', 'very')
    ('Y18D', '18D')
    ('Y237', 'Y237')
    ('ZZT5', '2215')
    Total count: 66, correct: 54.

    我们可以看到图片识别的正确率为80%以上,其中数字类图片的识别正确率为100%.
      我们可以在图片识别方面的算法再加改进,以提高图片识别的正确率。当然,以上算法并不是对所有验证码都适用,不同的验证码需要用不同的图片处理算法。

    注意:本人现已开通两个微信公众号: 因为Python(微信号为:python_math)以及轻松学会Python爬虫(微信号为:easy_web_scrape), 欢迎大家关注哦~~

    展开全文
  • 这里老样子先说几句无关的话,去年毕业的时候帮同学做了一个验证码识别系统,按部就班的参考了大神的操作,对于二值化这些操作都没有深入了解,最近做项目遇到了要实现机器登录的操作,验证码自然绕不过去所以就又翻...

    阅读本文可能会解决的问题:

    ① Java识别验证码
    ② Tess4J的使用和OCR识别
    ③ JavaCV的使用二值化和灰度测试
    ④ Java裁剪和缩放图片
    ⑤ 如何生成数字&字母验证码
    ⑥ ...

    这里老样子先说几句无关的话,去年毕业的时候帮同学做了一个验证码识别系统,按部就班的参考了大神的操作,对于二值化这些操作都没有深入了解,最近做项目遇到了要实现机器登录的操作,验证码自然绕不过去所以就又翻出来参考总结一下,方便自己也分享给大家。同时我也发现现在除了一些政府网站外,基本很少有采用下面我列出的这种数字&字母验证码了,所以针对项目需求做的这个识别可能目前并没有什么参考价值,但是在学习的过程中也了解了很多比如Tess4J和JavaCV这些技术,图像处理比如缩放和裁剪等,话不多说,下面就搞起吧。

    一、常见的验证码识别

    1.常见验证码

         

    这里我主要针对第四种验证码进行识别,其实试验后我发现前面三个都是可以识别的,只是裁剪和二值化的阈值需要调整。

    2.识别思路

    输入原始验证码之后,先对图片进行干扰像素去除,再裁剪边角,最后利用Tess4J进行识别。

    3.主要代码

    main方法调用具体实现方法

        public static void main(String[] args){
    
            //原始验证码地址
            String OriginalImg = "C:\\mysoftware\\images\\upload\\OcrImg\\oi.jpg";
            //识别样本输出地址
            String ocrResult = "C:\\mysoftware\\images\\upload\\OcrResult\\or.jpg";
            //去噪点
            ImgUtils.removeBackground(OriginalImg, ocrResult);
            //裁剪边角
            ImgUtils.cuttingImg(ocrResult);
            //OCR识别
            String code = Tess4J.executeTess4J(ocrResult);
            //输出识别结果
            System.out.println("Ocr识别结果: \n" + code);
    
        }
    其中removeBackground方法去除验证码噪点,首先我定义了一个临界阈值,这个值代表像素点的亮度,我们在实际扫描验证码的每一个像素块时通过判断该像素块的亮度(获取该像素块的三原色)是否超过该自定义值,从而判断出是否删除或保留该像素块,因此针对不同的验证码我们可以调整这个阈值,比如像我上面列出的几种验证码波动一般都在100~600之间,通过测试就得出一个比较适中的阈值可以大大提高验证码的提纯度。
    public static void removeBackground(String imgUrl, String resUrl){
            //定义一个临界阈值
            int threshold = 300;
            try{
                BufferedImage img = ImageIO.read(new File(imgUrl));
                int width = img.getWidth();
                int height = img.getHeight();
                for(int i = 1;i < width;i++){
                    for (int x = 0; x < width; x++){
                        for (int y = 0; y < height; y++){
                            Color color = new Color(img.getRGB(x, y));
                            System.out.println("red:"+color.getRed()+" | green:"+color.getGreen()+" | blue:"+color.getBlue());
                            int num = color.getRed()+color.getGreen()+color.getBlue();
                            if(num >= threshold){
                                img.setRGB(x, y, Color.WHITE.getRGB());
                            }
                        }
                    }
                }
                for(int i = 1;i<width;i++){
                    Color color1 = new Color(img.getRGB(i, 1));
                    int num1 = color1.getRed()+color1.getGreen()+color1.getBlue();
                    for (int x = 0; x < width; x++)
                    {
                        for (int y = 0; y < height; y++)
                        {
                            Color color = new Color(img.getRGB(x, y));
    
                            int num = color.getRed()+color.getGreen()+color.getBlue();
                            if(num==num1){
                                img.setRGB(x, y, Color.BLACK.getRGB());
                            }else{
                                img.setRGB(x, y, Color.WHITE.getRGB());
                            }
                        }
                    }
                }
                File file = new File(resUrl);
                if (!file.exists())
                {
                    File dir = file.getParentFile();
                    if (!dir.exists())
                    {
                        dir.mkdirs();
                    }
                    try
                    {
                        file.createNewFile();
                    }
                    catch (IOException e)
                    {
                        e.printStackTrace();
                    }
                }
                ImageIO.write(img, "jpg", file);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
    

    这样就能得到一个降噪之后的验证码,再后续通过颜色反转将验证码的内容显示出来就完成了干扰像素的去除工作,接下来非必要的一项工作就是将验证码进行裁边。为什么要裁边呢,通过观察我们发现验证码的边角部分没有干扰素涂抹,在我们的去除干扰素过程中可能会影响到这部分像素块导致最后边角有噪点,这时将边角裁掉几个像素就可以了。

    public static void cuttingImg(String imgUrl){
            try{
                File newfile=new File(imgUrl);
                BufferedImage bufferedimage=ImageIO.read(newfile);
                int width = bufferedimage.getWidth();
                int height = bufferedimage.getHeight();
                if (width > 52) {
                    bufferedimage=ImgUtils.cropImage(bufferedimage,(int) ((width - 52) / 2),0,(int) (width - (width-52) / 2),(int) (height));
                    if (height > 16) {
                        bufferedimage=ImgUtils.cropImage(bufferedimage,0,(int) ((height - 16) / 2),52,(int) (height - (height - 16) / 2));
                    }
                }else{
                    if (height > 16) {
                        bufferedimage=ImgUtils.cropImage(bufferedimage,0,(int) ((height - 16) / 2),(int) (width),(int) (height - (height - 16) / 2));
                    }
                }
                ImageIO.write(bufferedimage, "jpg", new File(imgUrl));
            }catch (IOException e){
                e.printStackTrace();
            }
        }
    其中裁剪图片我们需要预先读取宽高度,确认需要裁剪的像素宽度,我这里将60*20的图像宽度两边各裁剪4像素,高度上下各裁剪2像素,输出就得到了一个清晰的图像。到这里基本完成了对验证码的简单处理,不出意外的话验证码由

      变为了 

    到这里就很简单了,我们可以利用裁切将图片平均剪成四份,再通过简单的训练比对完成匹配,详细操作可以看这里,我就不再赘述了。我这里提供另一个思路,就是要说的Tess4J文本识别工具,通过这个工具我可以直接对当前生成的验证码进行OCR识别,准确率接近100%,注意我项目里是采用Tess4J进行最终的验证码图片内容识别的。

    二、Tess4J的使用

    1.首先需要到官网下载Tess4J的压缩包,下载后先将解压出来的包内tessdata文件拷入项目根目录,然后引入Tess4J\lib包,最后还要单独引入Tess4J\dist\tess4j-3.4.8.jar包

    2.编写简单的识别代码

    public static String executeTess4J(String imgUrl){
            String ocrResult = "";
            try{
                ITesseract instance = new Tesseract();
                //instance.setLanguage("chi_sim");
                File imgDir = new File(imgUrl);
                //long startTime = System.currentTimeMillis();
                ocrResult = instance.doOCR(imgDir);
            }catch (TesseractException e){
                e.printStackTrace();
            }
            return ocrResult;
        }

    ocrResult 即为最终是别的结果,代码相当简单,其中我注释掉的 instance.setLanguage("chi_sim"); 这部分是指定需要识别文本的语言,如果是识别中文按照我注释的chi_sim填写即可,当然这是一个中文包,需要先在这里找到中文包下载后放到Tess4J\tessdata目录下即可使用。

    到这里基本就完成了对这个验证码的简单识别,在这个过程中基本没遇到什么有难度的工作,毕竟是站在大佬们的肩膀上操作,其实还是学到了很多东西的,需求这东西说不定哪天就遇到了呢。接下来我在研究验证码识别的过程中了解到了JavaCV,比较感兴趣就记录下来,方便之后使用吧。

    三、JavaCV的使用

    1. 首先就是去官网下载包啦,点这里,引入很简单,解压后将javacv-bin包引入项目即可(可以看到封装了OpenCV)

    2. 基本操作:获取灰度图像、获取二值化处理图像

    public static void main(String[] args) {
            //图片地址
            String imgUrl = "C:\\mysoftware\\images\\upload\\OcrImg\\20180607004153.png";
            //得到灰度图像
            getHuidu(imgUrl);
            //得到二值化处理图像
            getErzhihua(imgUrl);
        }
    
        //得到灰度图像
        public static void getHuidu(String imgUrl){
            Mat image=imread(imgUrl,CV_LOAD_IMAGE_GRAYSCALE);
            //读入一个图像文件并转换为灰度图像(由无符号字节构成)
            Mat image1=imread(imgUrl,CV_LOAD_IMAGE_COLOR);
            //读取图像,并转换为三通道彩色图像,这里创建的图像中每个像素有3字节
            //如果输入图像为灰度图像,这三个通道的值就是相同的
            System.out.println("image has "+image1.channels()+" channel(s)");
            //channels方法可用来检查图像的通道数
            flip(image,image,1);//就地处理,参数1表示输入图像,参数2表示输出图像
            //在一窗口显示结果
            namedWindow("输入图片显示窗口");//定义窗口
            imshow("输入图片显示窗口",image);//显示窗口
            waitKey(0);//因为他是控制台窗口,会在mian函数结束时关闭;0表示永远的等待按键,正数表示等待指定的毫秒数
        }
    
        //得到二值化处理图像
        public static void getErzhihua(String imgUrl){
            // TODO Auto-generated method stub
            Mat image=imread(imgUrl);	//加载图像
            if(image.empty())
            {
                System.out.println("图像加载错误,请检查图片路径!");
                return ;
            }
            imshow("原始图像",image);
            Mat gray=new Mat();
            cvtColor(image,gray,COLOR_RGB2GRAY);		//彩色图像转为灰度图像
            imshow("灰度图像",gray);
            Mat bin=new Mat();
            threshold(gray,bin,120,255,THRESH_TOZERO); 	//图像二值化
            imshow("二值图像",bin);
            waitKey(0);
        }

    看一下效果:

    四、生成验证码

    生成验证码大致思路:
    加载登录界面时向后台请求一个随机验证码code存入session,并在页面以图形显示出来,用户输入登录信息后点击登录,后台读出session的验证码code比对是否一致,不一致返回错误信息,一致则进行登录账号密码校验。
    1. 首先在login的页面添加一个img
    <div class="form-group">
                       <label class="col-sm-2 control-label">验证</label>
                       <div class="col-sm-10">
                           <input id = "code" type="text" name="vcode" value="请输入验证码" maxlength="4" tabindex="3" style="width:123px;padding: 6px 12px;border: 1px solid #ccc;font-size: 14px;line-height: 1.42857143;color: #555;background-color: #fff;border-radius: 4px;box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075);transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;" οnfοcus="if(this.value=='请输入验证码'){this.value='';}"  οnblur="if(this.value==''){this.value='请输入验证码';}"/>
                           <a href="#" id="js-get_mobile_vcode" class="button btn-disabled">
                               <img src="../recordHome/getRand" id="randImg"  οnclick="changeValidateCode(this)"/>
                           </a>
                       </div>
                   </div>
    其中为了方便用户使用,需要做点击图片自动获取新的验证码,所以changeValidateCode()方法是动态去后台请求验证码并设置到页面上
    function changeValidateCode() {
        var timestamp = new Date().getTime();
        $("#randImg").attr('src','../recordHome/getRand?flag='+timestamp);
    }
    请求时添加时间戳是为了防止浏览器缓存导致获取失败
    2. 后台生成验证码
    /**
    	 * 随机生成4位验证码
    	 * @param httpSession
    	 * @param request
    	 * @param response
    	 */
    	@RequestMapping(value="/getRand")
    	public void rand(HttpSession httpSession, HttpServletRequest request, HttpServletResponse response){
    		// 在内存中创建图象
    		int width = 65, height = 20;
    		BufferedImage image = new BufferedImage(width, height,BufferedImage.TYPE_INT_RGB);
    		// 获取图形上下文
    		Graphics g = image.getGraphics();
    		// 生成随机类
    		Random random = new Random();
    		// 设定背景色
    		g.setColor(getRandColor(200, 250));
    		g.fillRect(0, 0, width, height);
    		// 设定字体
    		g.setFont(new Font("Times New Roman", Font.PLAIN, 18));
    		// 随机产生155条干扰线,使图象中的认证码不易被其它程序探测到
    		g.setColor(getRandColor(160, 200));
    		for (int i = 0; i < 155; i++) {
    			int x = random.nextInt(width);
    			int y = random.nextInt(height);
    			int xl = random.nextInt(12);
    			int yl = random.nextInt(12);
    			g.drawLine(x, y, x + xl, y + yl);
    		}
    		// 取随机产生的认证码(6位数字)
    		String sRand = "";
    		for (int i = 0; i < 4; i++) {
    			String rand = String.valueOf(random.nextInt(10));
    			sRand += rand;
    			// 将认证码显示到图象中
    			g.setColor(new Color(20 + random.nextInt(110), 20 + random
    					.nextInt(110), 20 + random.nextInt(110)));
    			// 调用函数出来的颜色相同,可能是因为种子太接近,所以只能直接生成
    			g.drawString(rand, 13 * i + 6, 16);
    		}
    		// 将认证码存入SESSION
    		ContextHolderUtils.getSession().setAttribute("rand", sRand);
    		//httpSession.setAttribute("rand", sRand);
    		// 图象生效
    		g.dispose();
    		try{
    			ImageIO.write(image, "JPEG", response.getOutputStream());
    			response.getOutputStream().flush();
    		}catch (Exception e){
    		}
    	}
    
    	/**
    	 * 给定范围获得随机颜色
    	 * @param fc
    	 * @param bc
    	 * @return
    	 */
    	private Color getRandColor(int fc, int bc) {
    		Random random = new Random();
    		if (fc > 255)
    			fc = 255;
    		if (bc > 255)
    			bc = 255;
    		int r = fc + random.nextInt(bc - fc);
    		int g = fc + random.nextInt(bc - fc);
    		int b = fc + random.nextInt(bc - fc);
    		return new Color(r, g, b);
    	}
    使用ContextHolderUtils.getSession().setAttribute("rand", sRand);将验证码存入session中
    @RequestMapping("/login")
    	public String login(HttpServletRequest request, HttpServletResponse response, @RequestParam String username, @RequestParam String password, @RequestParam String vcode) {
    		if(!vcode.equals(ContextHolderUtils.getSession().getAttribute("rand"))){
    			request.setAttribute("failMsg", "验证码不正确!");
    			return "/base/login";
    		}
    		logger.info("用户登录用户:" + username );
    		...
    	}
    登录时进行校验,就酱so easy


    最后是全部代码,这里说到的我都集成到了项目里,直接下载就可以运行,欢迎大家指正。

    有用的话可以☆star一下哦

    https://github.com/jwwam/OcrImage.git


    本文完 2018-6-7

    有问题欢迎大家留言指正大笑

    来自 https://blog.csdn.net/zhulier1124/article/details/80606647

    转载请注明,谢谢。

    展开全文
  • Tesseracr-OCR 验证码识别 1、预处理——去除干扰线与点 2、不同的结构元素中选择 3、Image与numpy array相互转化 4、识别与输出 OCR(Optional Character Recognition,光学字符识别)是通过扫描字符,然后通过其形状...
  • paip 验证码识别---图像处理类库
  • 验证码识别源代码

    2020-07-07 17:26:05
    该资源支持验证码识别,识别率是100%,解压密码在公众号:herok
  • 验证码识别过程详解。 先上几个图: 第一个图: 原始的验证码图 <br />第二个图:灰度直方图 <br />第三个图:红色直方图 <br />  第四个图:绿色直方图 <br />  第五个...
  • 图像处理验证码识别)程序中常用算法:灰度,二值化,去噪(1*1像素或者3*3像素等)欢迎拍砖!看原始图像: 图像灰度化效果图:代码:view plaincopy to clipboardprint?//灰度 private void btnGray_Click...
  • 传统验证码识别:传统方法通常是先对验证码图像进行字符分割,再进行特征提取、最后通过分类器得到结果。一些验证码加入噪声或线条,字符位置不固定及粘连时,字符分割效果不好,也会影响后续字符识别。除了只包含...
  • 简单的验证码识别

    2018-03-11 21:15:11
    所以本文主要提供验证码识别的一个简单的思路,代码实现的部分还望各位大佬指点。 看了好几篇验证码图片识别的博文,不难归纳出验证码识别的大概思路是收集训练集——&gt;图像处理——&gt;得到图片特征值...
  • 一键安装过后可在箭头处添加语言包,但是由于语言包增加多了的话对之后的验证码识别会有影响,(比如本来只有 62个字符 26+26+10,但是增加了语言包会识别出其他的字符),所以我只下了中文包。然后一键安装完毕。 ...
  • 图像识别验证码篇   本文介绍了图像识别领域应用到验证码破解中的第一课,以一种极简的,学习成本极低的方式,带你初识AI,初识图像识别。   如果您对图像识别类的内容或者课程感兴趣,敬请关注我们的订阅号...
  • 一、准备工作与代码实例 1、PIL、pytesser、tesseract (1)安装PIL:下载地址:http://www.pythonware.com/products/pil/... 下载后是一个exe,直接双击安装,它会自动安装到C:\Python27\Lib\site-packages中去, ...
  • 在matlab下,验证码识别部分,字符分割处理的代码。学习matlab软件下图像处理,及字符分割的实例
  • 基于BP神经网络的验证码识别 个人作业,数据集太大没放进去 仅用于个人学习,不可商用
  • 基本识别原理概述:1、每一幅图像在构成上,都是由一个个像素组成的矩阵,每一个像素为单元格。2、 彩色图像的像素的由三原色(红,绿,蓝)构成元组,灰度图像的像素是一个单值,每个像素的值范围为(0,255)。问题...
  • matlab数字验证码识别

    2019-10-10 10:41:49
    matlab数字验证码识别
1 2 3 4 5 ... 20
收藏数 6,384
精华内容 2,553