使用opencv+tesseract识别图片中的表格

本文详细介绍了在Java环境中,如何通过OpenCV和Tesseract库处理图像表格,包括图像预处理、识别水平和垂直线、过滤重叠线、构建单元格以及单元格文本识别的过程,同时提及了Docker部署SpringBoot服务的背景。
摘要由CSDN通过智能技术生成

描述

在java环境中使用opencv和tesserac识别一个图片表格

环境:opencv和tesseract安装在linux环境下,docker将运行springboot服务

opencv和tesseract的安装和docker加载可参考之前的文章

过程

将图片进行预处理,过滤掉颜色等干扰元素

提取图片的水平线和垂直线,并进行重叠过滤

得到水平线和垂直线的交点,根据交点构建单元格

对每个单元格进行识别

1.转换

将image转换成mat

private  Mat bufferedImageToMat(BufferedImage bufferedImage) {
      Mat mat = new Mat();
      try {
            // Convert BufferedImage to byte array
            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
         
            ImageIO.write(bufferedImage, "png", byteArrayOutputStream);
      
            byteArrayOutputStream.flush();

            byte[] imageInByte = byteArrayOutputStream.toByteArray();

            byteArrayOutputStream.close();

            // Convert byte array to Mat
            MatOfByte matOfByte = new MatOfByte(imageInByte);

            mat = Imgcodecs.imdecode(matOfByte, Imgcodecs.IMREAD_UNCHANGED);
  
        } catch (IOException e) {
            e.printStackTrace();
        }
    return mat;
}

2.图片预处理

原图:

将图片灰度化,并进行边缘检测

灰度化

 //image为加载的图片
 Mat imread = bufferedImageToMat(image);
 Mat gray = new Mat();
 Imgproc.cvtColor(imread, gray,Imgproc.COLOR_BGR2GRAY);

边缘检测

Mat edges = new Mat();
Imgproc.Canny(gray, edges, 50, 150);

3.检测水平线和垂直线

识别水平线和垂直线

            List<MatOfPoint> verticalLines = new ArrayList<>();
            List<MatOfPoint> horizontalLines = new ArrayList<>();

            for (int i = 0; i < lines.rows(); i++) {
                double[] val = lines.get(i, 0);
                if (isVertical(val)) {
                    verticalLines.add(new MatOfPoint(new Point(val[0], val[1]), new Point(val[2], val[3])));

                } else if (isHorizontal(val)) {
                    horizontalLines.add(new MatOfPoint(new Point(val[0], val[1]), new Point(val[2], val[3])));
                }

            }

水平线和垂直线的阈值可根据实际情况调节 

    private  boolean isVertical(double[] line) {
        // 实现判断线是否垂直的逻辑
        return Math.abs(line[0] - line[2]) < 1.0; // 这里的阈值需要根据实际情况调整
    }

    private  boolean isHorizontal(double[] line) {
        // 实现判断线是否水平的逻辑
        return Math.abs(line[1] - line[3]) < 1.0; // 这里的阈值需要根据实际情况调整
    }

4.重叠过滤

过滤掉相邻太近,应该为同一条线的线段

    private  List<MatOfPoint> overlappingFilter(List<MatOfPoint> lines, int sortingIndex) {
        List<MatOfPoint> uniqueLines = new ArrayList<>();

        // 按照 sortingIndex 进行排序
        if(sortingIndex == 0){
            //行,检查y坐标
            lines.sort(Comparator.comparingDouble(line -> calculateLineCenter(line).y));
        }else{
            //列检查x坐标
            lines.sort(Comparator.comparingDouble(line -> calculateLineCenter(line).x));
        }


        double distanceThreshold = 5;
        for (int i = 0; i < lines.size(); i++) {
            MatOfPoint line1 = lines.get(i);
            Point[] pts1 = line1.toArray();

            // 如果 uniqueLines 为空或当前线与最后一条线不重复,则添加到 uniqueLines 中
            if (uniqueLines.isEmpty() || !isDuplicate(pts1, uniqueLines.get(uniqueLines.size() - 1).toArray(), distanceThreshold)) {
                uniqueLines.add(line1);
            }
        }

        return uniqueLines;
    }

    private  Point calculateLineCenter(MatOfPoint line) {
        Point[] pts = line.toArray();
        return new Point((pts[0].x + pts[1].x) / 2, (pts[0].y + pts[1].y) / 2);
    }

5.水平线和垂直线的焦点

得到水平线和垂直线的焦点

            List<List<Point>> intersectionList = new ArrayList<>();//交点列表
                for (MatOfPoint hLine : horizontalLines) {
                List<Point> intersectionRow = new ArrayList<>();
                for (MatOfPoint vLine : verticalLines) {
                    Point intersection = getIntersection(hLine, vLine);
                    intersectionRow.add(intersection);
                }
                intersectionList.add(intersectionRow);
            }

获取两条线的焦点

    private Point getIntersection(MatOfPoint line1, MatOfPoint line2) {
        Point[] points1 = line1.toArray();
        Point[] points2 = line2.toArray();

        double x1 = points1[0].x, y1 = points1[0].y, x2 = points1[1].x, y2 = points1[1].y;
        double x3 = points2[0].x, y3 = points2[0].y, x4 = points2[1].x, y4 = points2[1].y;

        double det = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4);
        double x = ((x1 * y2 - y1 * x2) * (x3 - x4) - (x1 - x2) * (x3 * y4 - y3 * x4)) / det;
        double y = ((x1 * y2 - y1 * x2) * (y3 - y4) - (y1 - y2) * (x3 * y4 - y3 * x4)) / det;

        return new Point(x, y);
    }

6.构建单元格

             List<List<Rect>> cells = new ArrayList<>();

            // 构建单元格
            for (int i = 0; i < intersectionList.size() - 1; i++) {
                List<Rect> rowCells = new ArrayList<>();
                for (int j = 0; j < intersectionList.get(i).size() - 1; j++) {
                    Point p1 = intersectionList.get(i).get(j);
                    Point p2 = intersectionList.get(i).get(j + 1);
                    Point p3 = intersectionList.get(i + 1).get(j);
                    Rect cell = new Rect((int) p1.x, (int) p1.y, (int) (p2.x - p1.x), (int) (p3.y - p1.y));
                    rowCells.add(cell);
                }
                cells.add(rowCells);
            }

7.对每个单元格进行识别

           for(int i=0;i<cells.size();i++){
                List<String> row = new ArrayList<>();
                for(int j=0;j<cells.get(i).size();j++){
                    Rect cell = cells.get(i).get(j);
                    Mat cellImage = new Mat(gray, cell);
                    BufferedImage bufferedImage = matToBufferedImage(cellImage);
                    if(bufferedImage == null)continue;
                    String text = tess.doOCR(bufferedImage);
                    row.add(text);
                }
            }
    private  BufferedImage matToBufferedImage(Mat mat) {
        int type = BufferedImage.TYPE_BYTE_GRAY;
        if (mat.channels() > 1) {
            type = BufferedImage.TYPE_3BYTE_BGR;
        }
        int bufferSize = mat.channels() * mat.cols() * mat.rows();
        byte[] buffer = new byte[bufferSize];
        mat.get(0, 0, buffer); // 获取所有像素值
        BufferedImage image = new BufferedImage(mat.cols(), mat.rows(), type);
        final byte[] targetPixels = ((DataBufferByte) image.getRaster().getDataBuffer()).getData();
        System.arraycopy(buffer, 0, targetPixels, 0, buffer.length);
        return image;
    }

使用Java进行OpenCV图像处理需要借助JavaCV库,这是一个JavaOpenCV的交互库。本文将介绍如何使用JavaCV和OpenCV来实现表格文本识别。 首先需要安装JavaCV和OpenCV: 1. 下载JavaCV库:https://github.com/bytedeco/javacv/releases 2. 下载OpenCV:https://opencv.org/releases/ 接下来,我们可以按照以下步骤来实现表格文本识别: 1. 加载图像 ```java Mat image = imread("path/to/image.jpg"); ``` 2. 图像预处理 图像预处理包括二值化、去噪等操作,可以提高后续的文字检测效果。 ```java Mat gray = new Mat(); cvtColor(image, gray, COLOR_BGR2GRAY); // 转换成灰度图像 Mat binaryImage = new Mat(); threshold(gray, binaryImage, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); // 二值化 ``` 3. 文本检测 使用OpenCV提供的文本检测算法(如MSER、EAST等)来检测图像的文本区域。 ```java MatOfRect textRegions = new MatOfRect(); MatOfFloat confidenceScores = new MatOfFloat(); textDetector.detect(binaryImage, textRegions, confidenceScores); ``` 其,`textDetector`是文本检测器,可以根据需求选择不同的检测算法。 4. 文本识别 使用OCR技术来识别文本区域内的文字。 ```java Tesseract tesseract = new Tesseract(); // 初始化OCR引擎 tesseract.setLanguage("chi_sim"); // 设置语言 tesseract.setDatapath("path/to/tessdata"); // 设置tessdata路径 for(Rect rect : textRegions.toArray()) { Mat roi = new Mat(binaryImage, rect); String text = tesseract.doOCR(roi); System.out.println(text); } ``` 其,`Tesseract`是OCR引擎,需要下载安装,并设置tessdata路径。 完整代码示例: ```java import org.bytedeco.javacpp.Loader; import org.bytedeco.opencv.global.opencv_imgcodecs; import org.bytedeco.opencv.global.opencv_imgproc; import org.bytedeco.opencv.opencv_core.*; import static org.bytedeco.opencv.global.opencv_core.*; import static org.bytedeco.opencv.global.opencv_imgproc.*; public class TableTextRecognition { public static void main(String[] args) { Loader.load(opencv_imgcodecs.class); Loader.load(opencv_imgproc.class); Mat image = imread("path/to/image.jpg"); Mat gray = new Mat(); cvtColor(image, gray, COLOR_BGR2GRAY); Mat binaryImage = new Mat(); threshold(gray, binaryImage, 0, 255, THRESH_BINARY_INV | THRESH_OTSU); TextDetector textDetector = TextDetectorCNN.create("path/to/text_detection.pb"); MatOfRect textRegions = new MatOfRect(); MatOfFloat confidenceScores = new MatOfFloat(); textDetector.detect(binaryImage, textRegions, confidenceScores); Tesseract tesseract = new Tesseract(); tesseract.setLanguage("chi_sim"); tesseract.setDatapath("path/to/tessdata"); for(Rect rect : textRegions.toArray()) { Mat roi = new Mat(binaryImage, rect); String text = tesseract.doOCR(roi); System.out.println(text); } } } ``` 需要注意的是,使用OCR识别文本区域内的文字可能会存在识别准确率不高的问题,可以考虑使用深度学习模型来提高识别准确率。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值