将旧书籍的文字照片进行二值化处理,便于存储。
就是简单的图片二值化,多了个分割的步骤
缺点:
模糊的文字会丢失部分
图片切割后的残余部分会丢失
无法进一步压缩图片体积,一本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;
}
}
效果图
没有找到现成的深度学习识别文字的模型,后面找到会继续更新