@PostConstruct
public void initOcrEngine() {
tesseract = new Tesseract();
//语言包路径和支持语言
tesseract.setDatapath(tessDataPath);
tesseract.setLanguage("eng+chi_sim");
tesseract.setPageSegMode(6); //自动页面分割
tesseract.setOcrEngineMode(3); //LSTM引擎
}
/**
* OCR识别图片内容
*/
private String extractImageText(MultipartFile file) {
try (InputStream is = file.getInputStream()) {
BufferedImage image = ImageIO.read(is);
if (image == null) {
return MessageUtils.message("Image.parsing.failed");
}
image = binarizeImage(image, 128); // 二值化
image = breakCharacterConnections(image); // 断开粘连
List<Rectangle> regions = findCharacterRegions(image); // 字符级检测
image = addSpacingBetweenCharacters(image, regions, 10); // 插入间距
saveDebugImage(image, "3_final.png"); // 保存最终处理结果
String result = tesseract.doOCR(image); //OCR识别
result = postProcess(result);
result = result.replaceAll("\\s+", " ").trim();
System.out.println("图片内容:\n" + result);
return result;
} catch (Exception e) {
e.printStackTrace();
return MessageUtils.message("file.read.picture.error");
}
}
private void saveDebugImage(BufferedImage img, String filename) throws IOException {
// 保存到项目目录下的debug文件夹
String basePath = "D:/pdf/debug/";
File outputDir = new File(basePath);
if (!outputDir.exists()) outputDir.mkdirs();
String fullPath = basePath + filename;
ImageIO.write(img, "png", new File(fullPath));
}
private static BufferedImage binarizeImage(BufferedImage image, int threshold) {
int width = image.getWidth();
int height = image.getHeight();
BufferedImage binaryImage = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int argb = image.getRGB(x, y);
int gray = (argb >> 16) & 0xff;
if (gray < threshold) {
binaryImage.setRGB(x, y, Color.BLACK.getRGB());
} else {
binaryImage.setRGB(x, y, Color.WHITE.getRGB());
}
}
}
return binaryImage;
}
public static List<Rectangle> findCharacterRegions(BufferedImage binaryImage) {
int width = binaryImage.getWidth();
int height = binaryImage.getHeight();
boolean[][] visited = new boolean[width][height];
List<Rectangle> regions = new ArrayList<>();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (isBlack(binaryImage.getRGB(x, y)) && !visited[x][y]) {
// BFS查找连通域
Queue<Point> queue = new LinkedList<>();
queue.add(new Point(x, y));
visited[x][y] = true;
int minX = x, maxX = x, minY = y, maxY = y;
while (!queue.isEmpty()) {
Point p = queue.poll();
for (int dx = -1; dx <= 1; dx++) {
for (int dy = -1; dy <= 1; dy++) {
int nx = p.x + dx, ny = p.y + dy;
if (nx >= 0 && nx < width && ny >= 0 && ny < height
&& isBlack(binaryImage.getRGB(nx, ny)) && !visited[nx][ny]) {
visited[nx][ny] = true;
queue.add(new Point(nx, ny));
minX = Math.min(minX, nx);
maxX = Math.max(maxX, nx);
minY = Math.min(minY, ny);
maxY = Math.max(maxY, ny);
}
}
}
}
// 过滤小面积噪点
if ((maxX - minX + 1) * (maxY - minY + 1) > 20) {
regions.add(new Rectangle(minX, minY, maxX - minX + 1, maxY - minY + 1));
}
}
}
}
return regions;
}
private static boolean isBlack(int rgb) {
return (rgb & 0xFFFFFF) == 0; // 纯黑色
}
public static BufferedImage addSpacingBetweenCharacters(BufferedImage image, List<Rectangle> regions, int spacing) {
int width = image.getWidth();
int height = image.getHeight();
BufferedImage newImage = new BufferedImage(width + spacing * regions.size(), height, BufferedImage.TYPE_BYTE_BINARY);
Graphics2D g = newImage.createGraphics();
g.setColor(Color.WHITE);
g.fillRect(0, 0, newImage.getWidth(), newImage.getHeight());
int offsetX = 0;
for (Rectangle r : regions) {
// 截取字符区域
BufferedImage charImg = image.getSubimage(r.x, r.y, r.width, r.height);
g.drawImage(charImg, offsetX, 0, null);
offsetX += r.width + spacing; // 插入间距
}
g.dispose();
return newImage;
}
public static BufferedImage breakCharacterConnections(BufferedImage image) {
int width = image.getWidth();
int height = image.getHeight();
BufferedImage result = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
// 定义3x3结构元素
int[][] kernel = {
{1, 1, 1},
{1, 1, 1},
{1, 1, 1}
};
// 腐蚀(缩小边缘)
BufferedImage eroded = new BufferedImage(width, height, BufferedImage.TYPE_BYTE_BINARY);
for (int y = 1; y < height - 1; y++) {
for (int x = 1; x < width - 1; x++) {
boolean allBlack = true;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if ((image.getRGB(x + dx, y + dy) & 0xFF) > 0x7F) {
allBlack = false;
break;
}
}
}
eroded.setRGB(x, y, allBlack ? Color.BLACK.getRGB() : Color.WHITE.getRGB());
}
}
// 膨胀(恢复字符主体)
for (int y = 1; y < height - 1; y++) {
for (int x = 1; x < width - 1; x++) {
boolean anyBlack = false;
for (int dy = -1; dy <= 1; dy++) {
for (int dx = -1; dx <= 1; dx++) {
if ((eroded.getRGB(x + dx, y + dy) & 0xFF) == 0) {
anyBlack = true;
break;
}
}
}
result.setRGB(x, y, anyBlack ? Color.BLACK.getRGB() : Color.WHITE.getRGB());
}
}
return result;
}对嘛