书籍照片二值化处理

将旧书籍的文字照片进行二值化处理,便于存储。
就是简单的图片二值化,多了个分割的步骤

缺点:
模糊的文字会丢失部分
图片切割后的残余部分会丢失
无法进一步压缩图片体积,一本800页的书籍,大概50M左右的体积
如果以后还是要进行文字识别,那还不如直接使用微信的提取文字功能,一步到位

import net.coobird.thumbnailator.Thumbnails;
import org.junit.Test;

import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class Test20220810 {

    // 横向切分块数
    private static final int widthSplitNum = 12;

    // 纵向切分块数
    private static final int heightSplitNum = 14;

    /**
     * 批量二值化图片
     *
     * @throws IOException
     */
    @Test
    public void test0811() throws IOException {
        // 指定文件夹
        String fromPic = "D:\\temp\\20220726";
        File directory = new File(fromPic);

        if (!directory.isDirectory()) {
            return;
        }

        for (File file : directory.listFiles()) {

            BufferedImage bufferedImage = ImageIO.read(file);

            // 压缩图片
            BufferedImage compactImage = Thumbnails.of(bufferedImage).size(2000, 2000).asBufferedImage();

            // 切割图片,分别处理,防止出现大片黑斑
            Map<Integer, BufferedImage> map = splitImage(compactImage);

            // 二值化
            for (Integer key : map.keySet()) {
                BufferedImage binaryImage = binaryImage(map.get(key));
                map.put(key, binaryImage);
            }

            // 合并图片
            BufferedImage destImage = mergeImage(map);

            File newFile = new File("d:\\temp\\20220811\\" + file.getName().replace(".jpg", "") + ".png");
            ImageIO.write(destImage, "png", newFile);
        }
    }

    /**
     * 切分图片
     *
     * @param bufferedImage
     * @return
     */
    private static Map<Integer, BufferedImage> splitImage(BufferedImage bufferedImage) {
        Map<Integer, BufferedImage> map = new HashMap<>();

        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        int type = bufferedImage.getType();

        int blockWidth = w / widthSplitNum;
        int blockHeight = h / heightSplitNum;

        for (int i = 0; i < widthSplitNum; i++) {
            for (int j = 0; j < heightSplitNum; j++) {
                BufferedImage grayImage = new BufferedImage(blockWidth, blockHeight, type);

                int mStart = i * blockWidth;
                int mEnd = (i + 1) * blockWidth;
                int nStart = j * blockHeight;
                int nEnd = (j + 1) * blockHeight;

                for (int m = mStart; m < mEnd; m++) {
                    for (int n = nStart; n < nEnd; n++) {
                        grayImage.setRGB(m - mStart, n - nStart, bufferedImage.getRGB(m, n));
                    }
                }

                map.put(i * heightSplitNum + j, grayImage);
            }
        }

        return map;
    }

    public BufferedImage mergeImage(Map<Integer, BufferedImage> map) {
        if (map == null || map.get(0) == null) {
            return null;
        }

        int blockWidth = map.get(0).getWidth();
        int blockHeight = map.get(0).getHeight();

        BufferedImage destImage = new BufferedImage(blockWidth * widthSplitNum, blockHeight * heightSplitNum, BufferedImage.TYPE_BYTE_BINARY);


        for (int i = 0; i < widthSplitNum; i++) {
            for (int j = 0; j < heightSplitNum; j++) {
                BufferedImage grayImage = map.get(i * heightSplitNum + j);

                for (int m = 0; m < grayImage.getWidth(); m++) {
                    for (int n = 0; n < grayImage.getHeight(); n++) {
                        destImage.setRGB(i * blockWidth + m, j * blockHeight + n, grayImage.getRGB(m, n));
                    }
                }
            }
        }

        return destImage;
    }

    /**
     * 二值化图片
     *
     * @param bufferedImage 原图片
     * @return 二值化后的图片
     */
    private static BufferedImage binaryImage(BufferedImage bufferedImage) {
        BufferedImage grayImage = new BufferedImage(bufferedImage.getWidth(), bufferedImage.getHeight(), bufferedImage.getType());

        int threshold = getMeanThreshold(bufferedImage);

        for (int i = 0; i < bufferedImage.getWidth(); i++) {
            for (int j = 0; j < bufferedImage.getHeight(); j++) {
                // getRGB()方法,根据手册,其返回的int型数据(32位)为ARGB格式,其中ARGB各占8bit
                int color = bufferedImage.getRGB(i, j);
                int r = (color >> 16) & 0xff;
                int g = (color >> 8) & 0xff;
                int b = color & 0xff;
                int gray = (int) (0.2126 * r + 0.7152 * g + 0.0722 * b);
                if (gray > threshold) {
                    // 白色
                    grayImage.setRGB(i, j, 0xFFFFFF);
                } else {
                    // 黑色
                    grayImage.setRGB(i, j, 0);
                }
            }
        }

        return grayImage;
    }

    /**
     * 获取图片的阀值,采用基于灰度平均值的阈值
     *
     * @param bufferedImage 原图片
     * @return 二值化的阈值
     */
    private static int getMeanThreshold(BufferedImage bufferedImage) {
        double aa = 0.83;
        int w = bufferedImage.getWidth();
        int h = bufferedImage.getHeight();
        int num = 0;
        long sum = 0;
        for (int i = 0; i < w; i++) {
            for (int j = 0; j < h; j++) {
                int color = bufferedImage.getRGB(i, j);
                int r = (color >> 16) & 0xff;
                int g = (color >> 8) & 0xff;
                int b = color & 0xff;
                int gray = (int) (0.2126 * r + 0.7152 * g + 0.0722 * b);
                sum += gray;
                num += 1;
            }
        }

        // 测试表明,阀值取平均值的1.2倍效果最好。
        // 越大越黑
        int threshold = (int) (sum / num);
        if (threshold * aa < 255) {
            threshold = (int) (aa * sum / num);
        }

        return threshold;
    }

}

效果图
在这里插入图片描述
没有找到现成的深度学习识别文字的模型,后面找到会继续更新

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值