java图像识别

今年软件杯有一个项目是工商图片文字提取。大致的要求就是将天猫给的50张样例图片中的企业名称和注册号提取出来。速度和准确率是这个项目的关键。
我觉得软件杯历年的项目都挺有难度的,很有挑战性。近几年由于人工智能和大数据的飞速发展,项目命题也偏向这方面而不是传统项目了。所以对于程序员来说,除了了解传统项目知识(比如java web三大框架),还要与时俱进,自主学习大数据和人工智能的前沿知识,我认为现在及时转型对个人发展是非常重要的。这可能决定未来的走向,因为在不远的将来,AI和大数据将是主流。
工商图片文字提取涉及到计算机视觉处理,也就是图像识别。本来是想用python写的,会方便点,但是那时候看项目的语言要求,好像不能使用python,只能用java写咯。java图像处理这方面还真不如python。由于底层涉及太多的像素处理等等,我用的是Tesseract-OCR,tess4j这个谷歌的开源框架,识别文字挺好的。能识别一些常用的文字,底层代码都是C++写的,然后用java封装起来,写好方法供上层调用。
这个框架自行百度下载然后配置好就ok了,下面看看我的maven项目结构。
项目结构
有个特别重要的是tessdata文件夹中要放一些词库,比如英文词库eng.traineddata,中文词库chi_sim.traineddata(要自行下载)等等。如果你想提高这套框架识别的速度和准确率的话,可以去训练对应词库,然后加入到tessdata文件夹下,这样对特定的文字群将会有更好的识别效果。以前是接近0.9秒一张,训练词库以后可以达到0.1秒左右。
另外在识别图片之前,还需要对一些难点图片进行特殊处理,比如翻转,放大缩小,去水印(二值化),灰度处理,图片截取等等,这样预处理对后面的识别有很大的帮助。
总代码:

package Test;

import com.recognition.software.jdeskew.ImageDeskew;
import net.sourceforge.tess4j.ITessAPI;
import net.sourceforge.tess4j.ITesseract;
import net.sourceforge.tess4j.Tesseract;
import net.sourceforge.tess4j.Word;
import net.sourceforge.tess4j.util.ImageHelper;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.util.Date;
import java.util.List;

    public class Test {
    static final double MINIMUM_DESKEW_THRESHOLD = 0.05d;
    static ITesseract instance;
    public static void main(String[] args) throws Exception{
        Date data1=new Date();
          //testEn();
         testZh();
        //Wordbyword_extraction();
        Date data2=new Date();
        System.out.println((data2.getTime()-data1.getTime())/1000);
    }

    //使用英文字库 - 识别图片
    public static void testEn() throws Exception {
        File imageFile = new File("pictures/10.png");
        BufferedImage image = ImageIO.read(imageFile);
        //对图片进行处理
        image = flipImage(image);
        image = convertImage(image);
        instance = new Tesseract();//JNA Interface Mapping
        //划定区域
        // x,y是以左上角为原点,width和height是以x,y为基础
        //Rectangle rect = new Rectangle(20, 20, 1500, 200);
        String result = instance.doOCR(image);
        System.out.println(result);
    }

    //使用中文字库 - 识别图片
    public static void testZh() throws Exception {
        File imageFile = new File("pictures/10.png");
        BufferedImage image = ImageIO.read(imageFile);
        //对图片进行处理
        image = flipImage(image);
        image = convertImage(image);
        instance = new Tesseract();//JNA Interface Mapping
        instance.setLanguage("chi_sim");//使用中文字库
        //划定区域
        // x,y是以左上角为原点,width和height是以x,y为基础
        //Rectangle rect = new Rectangle(20, 20, 800, 200);
        String result = instance.doOCR(image);
        System.out.println(result);
    }

    //逐词提取
    public static void Wordbyword_extraction() throws Exception {
        //按照每个字取词
        int pageIteratorLevel = ITessAPI.TessPageIteratorLevel.RIL_SYMBOL;
        File imageFile = new File("pictures/10.png");
        BufferedImage image = ImageIO.read(imageFile);
        //对图片进行处理
        image = flipImage(image);
        image = convertImage(image);
        instance = new Tesseract();//JNA Interface Mapping
        instance.setLanguage("chi_sim");//使用中文字库
        List<Word> result = instance.getWords(image, pageIteratorLevel);
        for (Word word : result) {
            System.out.print(word.toString());
        }
    }

    //对图片进行处理 - 提高识别度
    public static BufferedImage convertImage(BufferedImage image) throws Exception {
        //按指定宽高创建一个图像副本
        image = ImageHelper.getSubImage(image, 0, 0, image.getWidth(), image.getHeight());
        //图像转换成灰度的简单方法 - 黑白处理
        image = ImageHelper.convertImageToGrayscale(image);
        //图像缩放 - 放大n倍图像
        image = ImageHelper.getScaledInstance(image, image.getWidth() * 3,   image.getHeight() * 3);
        return image;
    }

    //处理倾斜
    public static BufferedImage flipImage(BufferedImage image) throws Exception {
        //按指定宽高创建一个图像副本
        image = ImageHelper.getSubImage(image, 0, 0, image.getWidth(), image.getHeight());
             ImageDeskew id = new ImageDeskew(image);
             double imageSkewAngle = id.getSkewAngle(); //获取倾斜角度
               if ((imageSkewAngle > MINIMUM_DESKEW_THRESHOLD || imageSkewAngle < -(MINIMUM_DESKEW_THRESHOLD))) {
                   image = ImageHelper.rotateImage(image, -imageSkewAngle); //纠偏图像
        }
        return image;
        }
        }

去水印

package Test;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.awt.Color;

public class RemoveImageWatermark {
    public static void binaryImage() throws IOException {
        String frompath = "pictures/1.png";
        String topath = "pictures/1.png";
        File file1 = new File(frompath);
        BufferedImage image = ImageIO.read(file1);
        int w=image.getWidth();
        int h=image.getHeight();
        double[][] zuobiao = new double[w][h];
        for(int i=0;i<w;i++)
        {
            for(int j=0;j<h;j++)
            {
                int pixel = image.getRGB(i, j);
                int red = (pixel & 0xff0000) >> 16;
                int green = (pixel & 0xff00) >> 8;
                int blue = (pixel & 0xff);
                //System.out.println(red+" "+green+" "+blue);
                zuobiao[i][j]=(red+green+blue)/3.0;
                if(red>=229&&green>=229&&blue>=229)
                {
                    int white=new Color(255,255,255).getRGB();
                    image.setRGB(i,j,white);
                }
//                if(red>=10&&red<=20&&green>=10&&green<=20&&blue>=10&&blue<=20)
//                {
//                    int cover=new Color(red,green,blue).getRGB();
//                    image.setRGB(i,j,cover);
//                }
//                else
//                {
//                    int white=new Color(255,255,255).getRGB();
//                    image.setRGB(i,j,white);
//                }
            }
        }
        File file2=new File(topath);
        ImageIO.write(image,"png",file2);
    }
    public static void main(String[] args) throws IOException
    {
        new RemoveImageWatermark().binaryImage();
    }
}

图片截取:

package Test;

import javax.imageio.ImageIO;
import javax.imageio.ImageReadParam;
import javax.imageio.ImageReader;
import javax.imageio.stream.ImageInputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Iterator;

public class Screenshots {
    private String srcpath ;
    private String subpath ;
    private   int x ;
    private   int y ;
    private   int width ;
    private   int height ;
    public  Screenshots() {

    }
    public  Screenshots( int x, int y, int width, int height) {
        this.x = x ;
        this.y = y ;
        this.width = width ;
        this.height = height ;
    }

    public void cut()throws IOException {
        FileInputStream is =   null ;
        ImageInputStream iis = null ;
        try {
            is =new FileInputStream(srcpath);
            Iterator < ImageReader > it=ImageIO.getImageReadersByFormatName("png");
            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,"png",new File(subpath));
        } finally {
            if (is != null )
                is.close() ;
            if (iis != null )
                iis.close();
        }
    }

    public static void main(String[] args) {
        Screenshots screenshots = new Screenshots(0, 0, 493, 80);
        screenshots.srcpath = "pictures/1.png";
        screenshots.subpath = "pictures/1.png";
        try {
            screenshots.cut();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

加入训练词库:
先将训练好的exercise.traineddata加入tessdata,然后在代码中这样修改就好了

instance.setLanguage("chi_sim+exercise");

训练词库的流程比较复杂,花了我挺多时间的,在这里不多说了,可以去看看训练词库的博客,有一个jTessBoxEditor软件,按照流程一步一步来就好了。
如果有好的图像识别的算法(可以是自己写的),欢迎交流。能理解图像识别原理并能自己写出算法或者运用经典算法的话,是很厉害的。

猜你喜欢

转载自blog.csdn.net/CowBoySoBusy/article/details/80579627