基于Spring Boot的文字识别系统

前端使用html+css+js,后端使用Spring Boot,数据库使用mysql,识别算法有两个,一个是使用百度OCR接口,一个是自己写一个python,用flask包装。
其中百度OCR接口可以去免费申请,然后把appid、apikey、secretKey填入application.properties即可

在这里插入图片描述
在这里插入图片描述

application.properties

spring.application.name=demo# baidu_ocr_config
baidu.ocr.appid=your
baidu.ocr.apiKey=your
baidu.ocr.secretKey=your# myself_ocr_model_config
myself.model.url=your flaskspring.thymeleaf.cache= false# database
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver#JPA
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=update
spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.MySQL8Dialect# application.properties
spring.web.resources.static-locations=classpath:/static/,classpath:/demo/static/

数据库

在这里插入图片描述

0. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.3.2</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><name>demo</name><description>demo</description><url/><licenses><license/></licenses><developers><developer/></developers><scm><connection/><developerConnection/><tag/><url/></scm><properties><java.version>17</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- okhttp --><dependency><groupId>com.squareup.okhttp3</groupId><artifactId>okhttp</artifactId><version>4.12.0</version></dependency><!-- 引入Lombok依赖 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><!-- 百度人工智能依赖 --><!-- https://mvnrepository.com/artifact/com.baidu.aip/java-sdk --><dependency><groupId>com.baidu.aip</groupId><artifactId>java-sdk</artifactId><version>4.11.3</version></dependency><!-- thymeleaf模板引擎 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId></dependency><!-- MySQL驱动 --><dependency><groupId>com.mysql</groupId><artifactId>mysql-connector-j</artifactId><scope>runtime</scope></dependency><!-- Spring Data JPA 依赖 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-jpa</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

1. 项目结构

在这里插入图片描述

  • config为一些配置设置
  • controller为控制层
  • dao为数据库实体层
  • service为服务层
  • css保存样式文件
  • js保存js文件
  • ocrImg是本地保存图片的位置,数据库里指存放图片在本地的地址

2. css代码

2.1 base.css

/* 去除常见标签默认的 margin 和 padding */
* {margin: 0;padding: 0;box-sizing: border-box;
}/* 设置网页统一的字体大小、行高、字体系列相关属性 */
body {font: 16px/1.5  "Microsoft Yahei","Hiragino Sans GB", "Heiti SC", "WenQuanYi Micro Hei", sans-serif;color: #333;background-color: #f5f7f8;
}/* 去除列表默认样式 */
ul,
ol {list-style: none;
}/* 去除默认的倾斜效果 */
em,
i {font-style: normal;
}/* 去除a标签默认下划线,并设置默认文字颜色 */
a {text-decoration: none;color: #333;
}/* 设置img的垂直对齐方式为居中对齐,去除img默认下间隙 */
img {width: 100%;height: 100%;vertical-align: middle;
}/* 去除input默认样式 */
input {border: none;outline: none;color: #333;
}h1,
h2,
h3,
h4,
h5,
h6 {font-weight: 400;
}

2.2 index.css

.container {width: 10rem;height: auto;
}/* 顶部 */
.header {display: flex;padding: 0 .1563rem;width: 10rem;height: .3385rem;align-items: center;background-color: #fff;
}
.header img {width: 30px;height: 30px;
}
.header span {margin-left: .026rem;font-size: .1042rem;
}
/* 功能部分 */
.main {width: 10rem;height: auto;padding: 0 .1563rem;
}
.operation {display: flex;margin: .1563rem 0;
}
.operation input {display: none;
}
.operation button {display: none;
}
.operation label {display: flex;margin-right: .1042rem;width: .7813rem;height: .2604rem;border: .0052rem solid #cbc2c2;border-radius: .0417rem;align-items: center;cursor: pointer;
}
.operation label:hover {background-color: #d1f6ff;
}
.operation label .left {margin-right: .0417rem;padding: .0781rem 0;width: .2083rem;height: .2604rem;text-align: center;
}
.operation label .left svg {width: .1042rem;height: .1042rem;
}
.operation label .right h2 {font-size: .0833rem;font-weight: bold;color: #1296db;
}
.operation label .right h3 {font-size: .0625rem;color: #cbc2c2;
}
/* 主体 */
.function {width: 9.70rem;height: 3.125rem;border-radius: .0938rem;background-color: #fff;overflow: hidden;border: .0052rem solid #cbc2c2;
}
.function .title {display: flex;width: 100%;height: .2604rem;border-bottom: .0052rem solid #cbc2c2;align-items: center;
}
.function .title h2 {width: 50%;padding-left: .1042rem;font-size: .0833rem;font-weight: bold;color: #1f4d77;
}
.function .action {display: flex;width: 100%;height: 2.8646rem;
}
.function .action .source {width: 50%;height: 2.8646rem;border-right: .0052rem solid #cbc2c2;
}
.function .action .source img {width: 100%;height: 100%;object-fit: contain;margin: auto;
}
.function .action .result {width: 50%;height: 2.8646rem;
}
.function .action .result textarea{width: 100%;height: 100%;font-size: .0833rem;border: none;
}/* 历史记录按钮 */
.circle-button {position: absolute;top : 75.5%;right: 0.18rem;bottom: 0.1064rem;width: 0.1583rem;height: 0.1583rem;border: 1px solid black;border-radius: 50%;display: flex;align-items: center;justify-content: center;cursor: pointer;background: transparent;
}.circle-button::before {content: "";display: block;width: 0.1063rem; /* 内部圆的直径 */height: 0.1063rem;border: 1px solid black; /* 细细的黑色边框 */border-radius: 50%; /* 创建圆形效果 */background: transparent; /* 设置背景为透明 */
}.circle-button:hover {border-color: #000; /* 鼠标悬停时保持黑色边框 */
}#modelSelect {background: transparent; /* 可以设置背景颜色 */border: none; /* 移除边框 */padding: 0; /* 移除内边距 */margin: 0; /* 移除外边距 */font-size: .0833rem;font-weight: bold;color: #1f4d77;cursor: pointer; /* 改变鼠标指针 */position: relative; /* 为了定位伪元素 */display: inline-block; /* 使其与周围的元素在同一行显示 *//* 增加宽度以适应文本和箭头 */width: 100px; /* 或者使用您喜欢的宽度 *//* 设置高度,以确保箭头在垂直方向上居中 */height: 2em; /* 或者使用您喜欢的高度 */line-height: 2em; /* 文本垂直居中 */
}/* 加载动画 */
.loading {border: 4px solid rgba(0, 0, 0, 0.1);border-top-color: #1296db;border-radius: 50%;width: 24px;height: 24px;animation: spin 1s linear infinite;position: absolute;top: 50%;left: 74%;transform: translate(-50%, -50%);display: none;
}@keyframes spin {0% { transform: rotate(0deg); }100% { transform: rotate(360deg); }
}

2.3 re.css

.container {display: flex;flex-direction: column;width: 100vw;height: 100vh;
}/* 顶部 */
.header {display: flex;padding: 0 1.563vw;width: 100vw;height: 3.385vw;align-items: center;background-color: #fff;
}
.header img {width: 30px;height: 30px;
}
.header span {margin-left: .26vw;font-size: 1.042vw;
}
/* 功能部分 */
.main {display: flex;flex: 1;width: 100vw;
}
.main .left {width: 10%;background-color: #fff;
}
.main .left .logs {width: 100%;height: 3vw;line-height: 3vw;text-align: center;background-color: #bdd7ee;
}
.main .left .logs span {font-weight: bold;color: #2e75b6;
}
.main .right {width: 90%;height: 100%;overflow-y: scroll;
}
.right .table {width: 100%;height: auto;
}
table
{border-collapse:collapse;
}
table, th, td
{border: 1px solid black;
}
.table .thead {width: 100%;height: 3vw;text-align: center;line-height: 3vw;
}
.table td {position: relative;text-align: center;
}
.table tbody tr {width: 100%;height: 10vw;
}
.table tbody tr:nth-child(2n-1){background-color: #fff;
}
.table tbody td:nth-child(1) {width: 5%;
}
.table tbody tr td:nth-child(2){width: 40%;
}
.table tbody tr td:nth-child(3){width: 30%;
}
.table tbody tr td:nth-child(4){width: 15%;
}
.table tbody tr td:nth-child(5){width: 10%;
}.image-container {position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%); /* 将 image-container 移动到 td 的中心 */width: 470px; /* 或者指定一个宽度 */height: 130px; /* 或者指定一个高度 */overflow: hidden;display: flex;align-items: center;justify-content: center;
}.image-container img {max-width: 100%; /* 图片的最大宽度为容器宽度 */max-height: 100%; /* 图片的最大高度为容器高度 */object-fit: contain; /* 保持图片的比例 */
}

3. JS 代码

3.1 doData.js

document.addEventListener('DOMContentLoaded', function() {const tableBody = document.getElementById('table-body');if (tableBody === null) {console.error('The element with id "table-body" is not found.');} else {fetch('http://localhost:8080/history').then(response => response.json()).then(data => {//逆序data.sort((a,b) =>b.id-a.id);data.forEach(item => {// 创建一个新的表格行const row = document.createElement('tr');// 创建序号单元格const idCell = document.createElement('td');idCell.textContent = item.id;row.appendChild(idCell);// 创建图片单元格const imageCell = document.createElement('td');const imgElement = document.createElement('img');// 使用 item.imagedata 作为图片的 srcimgElement.src = item.imagedata;imgElement.alt = "Image";// 包裹在 div 中const imageContainer = document.createElement('div');imageContainer.className = 'image-container'; // 添加类名imageContainer.appendChild(imgElement);imageCell.appendChild(imageContainer);row.appendChild(imageCell);// 创建识别结果单元格const textCell = document.createElement('td');textCell.textContent = item.text;row.appendChild(textCell);// 创建识别时间单元格const datetimeCell = document.createElement('td');datetimeCell.textContent = item.datetime;row.appendChild(datetimeCell);// 创建识别模型单元格const modelCell = document.createElement('td');modelCell.textContent = item.modelname;row.appendChild(modelCell);// 将新行添加到表格中tableBody.appendChild(row);});}).catch(error => console.error('Error fetching data:', error));}console.log(tableBody);
});

3.2 flexible.js

(function flexible (window, document) {var docEl = document.documentElementvar dpr = window.devicePixelRatio || 1// adjust body font sizefunction setBodyFontSize () {if (document.body) {document.body.style.fontSize = (12 * dpr) + 'px'}else {document.addEventListener('DOMContentLoaded', setBodyFontSize)}}setBodyFontSize();// set 1rem = viewWidth / 10function setRemUnit () {var rem = docEl.clientWidth / 10docEl.style.fontSize = rem + 'px'}setRemUnit()// reset rem unit on page resizewindow.addEventListener('resize', setRemUnit)window.addEventListener('pageshow', function (e) {if (e.persisted) {setRemUnit()}})// detect 0.5px supportsif (dpr >= 2) {var fakeBody = document.createElement('body')var testElement = document.createElement('div')testElement.style.border = '.5px solid transparent'fakeBody.appendChild(testElement)docEl.appendChild(fakeBody)if (testElement.offsetHeight === 1) {docEl.classList.add('hairlines')}docEl.removeChild(fakeBody)}}(window, document))

3.3 index.js

// 预览上传图片
document.getElementById('imageInput').addEventListener('change', function (e) {let file = e.target.files[0];if (file) {let reader = new FileReader();reader.onload = function (e) {let image = new Image();image.src = e.target.result;image.onload = function () {document.getElementById('uploadedImage').src = image.src;document.getElementById('uploadedImage').style.display = 'block';};};reader.readAsDataURL(file);}
});// 当点击识别按钮
document.getElementById('sumbutt').addEventListener('click', function () {let fileInput = document.getElementById('imageInput');if (fileInput.files.length > 0) {let formData = new FormData();formData.append('file', fileInput.files[0]);// 获取用户选择的OCR模型let modelSelect = document.getElementById('modelSelect');let selectedModel = modelSelect.value;showLoadingAnimation();// 调用函数发送文件到服务器进行OCR识别sendFileToServer(formData, selectedModel);} else {alert('请选择一个图片文件!');}
});// 发送文件到服务器进行OCR识别
function sendFileToServer(formData, model) {let url;if (model === 'baidu') {url = 'http://localhost:8080/baiduOcr';} else if (model === 'myModel') {url = 'http://localhost:8080/myModel';}fetch(url, {method: 'POST',body: formData}).then(function (response) {if (!response.ok) {throw new Error('Network response was not ok');}return response.text();}).then(function (text) {hideLoadingAnimation();console.log('OCR Result:', text);displayOcrResult(splitAndProcessText(text));}).catch(function (error) {hideLoadingAnimation();alert('识别失败,请稍后重试');console.error('There has been a problem with your fetch operation:', error);});
}// 将OCR结果显示在页面上
function displayOcrResult(ocrData) {let resultMsgElement = document.getElementById('resultMsg');resultMsgElement.value = ocrData.join('\n');
}// 将从后端收到的字符串分割和处理成数组
function splitAndProcessText(text) {return text.split(/\r?\n/);
}// 显示加载动画
function showLoadingAnimation() {let loading = document.getElementById('loading');loading.style.display = 'block';
}// 隐藏加载动画
function hideLoadingAnimation() {let loading = document.getElementById('loading');loading.style.display = 'none';
}// 点击打开历史记录页面
let circleButton = document.querySelector('.circle-button');
circleButton.addEventListener('click', function() {window.open('re.html', '_blank');
});

4. 前端页面

4.1识别页面

在这里插入图片描述

4.1.1 index.html

<!DOCTYPE html>
<html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><link rel="stylesheet" href="../static/css/base.css"><link rel="stylesheet" href="../static/css/index.css"><title>识别页面</title>
</head><body><div class="container"><div class="header"><a href="index.html"><img src="../static/ocrImg/logo.jpg" alt=""><span>OCR文字识别</span></a></div><div class="main"><!-- 操作 --><div class="operation"><label for="imageInput"><div class="left"><svg t="1722243087697" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"p-id="2417" width="200" height="200"><pathd="M839.647708 1016.974416H184.352292a98.358181 98.358181 0 0 1-97.719492-98.358182V107.480583A98.358181 98.358181 0 0 1 184.352292 9.122402h383.213694a97.080802 97.080802 0 0 1 63.868948 24.90889l275.27517 243.979385a95.803423 95.803423 0 0 1 32.573164 72.810602v567.794955a98.358181 98.358181 0 0 1-99.63556 98.358182zM184.352292 74.907419a32.573164 32.573164 0 0 0-32.573164 32.573164v811.135651a32.573164 32.573164 0 0 0 32.573164 32.573164h655.295416a32.573164 32.573164 0 0 0 32.573164-32.573164V350.821279a30.657095 30.657095 0 0 0-10.857722-23.631512l-274.63648-243.979384a35.127922 35.127922 0 0 0-21.715442-8.302964z"fill="#1296db" p-id="2418"></path><pathd="M448.131051 354.653416H288.458679a33.211853 33.211853 0 0 1 0-63.868949H448.131051a33.211853 33.211853 0 1 1 0 63.868949zM667.840235 547.537641H288.458679a32.573164 32.573164 0 0 1 0-63.868949h379.381556a32.573164 32.573164 0 0 1 0 63.868949zM667.840235 741.060556H288.458679a33.211853 33.211853 0 0 1 0-63.868949h379.381556a33.211853 33.211853 0 0 1 0 63.868949zM883.078593 359.124242h-319.344744a32.573164 32.573164 0 0 1-33.211854-32.573164V42.334255a33.211853 33.211853 0 1 1 63.868949 0v251.643659h285.494202a32.573164 32.573164 0 1 1 0 63.868949z"fill="#1296db" p-id="2419"></path></svg></div><div class="right"><h2>上传图片</h2><h3>JPG,PNG,JPEG</h3></div></label><input type="file" id="imageInput" accept="image/png,image/jpeg,image/jpg" onchange="uploadImage(this)"><label for="sumbutt"><div class="left"><svg t="1722300478940" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"p-id="2849" width="200" height="200"><pathd="M974.72 224L798.08 47.36a52.928 52.928 0 0 0-75.52 0l-603.52 603.52c-6.4 6.4-10.88 14.72-13.44 23.04L35.84 924.16c-5.12 18.56 0 39.04 14.08 52.48 10.24 10.24 23.68 15.36 37.12 15.36 5.12 0 10.24-0.64 15.36-1.92l246.4-73.6c8.32-2.56 16-7.04 22.4-13.44l603.52-603.52c21.12-20.48 21.12-54.4 0-75.52z m-113.28 37.76l-34.56 34.56-101.12-101.12 34.56-34.56 101.12 101.12z m-517.76 518.4L242.56 678.4l407.68-407.68 101.12 101.12-407.68 408.32z m-155.52-5.12l60.16 60.16-83.84 24.96 23.68-85.12z"fill="#1296db" p-id="2850"></path></svg></div><div class="right"><h2>开始识别</h2></div></label><button id="sumbutt" type="submit">识别</button></div><!-- 功能部分 --><div class="function"><div class="title"><h2>识别图片</h2><select id="modelSelect"><option value="baidu">百度OCR模型</option><option value="myModel">自建OCR模型</option></select></div><div class="action"><!-- 上传图片展示 --><div class="source"><img id="uploadedImage"></div><!-- 识别结果展示 --><div class="result"><textarea name="" id="resultMsg" cols="30" rows="10"></textarea><div class="circle-button"></div><div class="loading" id="loading"></div></div></div></div></div></div>
</body><script src="../static/js/index.js"></script>
<script src="../static/js/flexible.js"></script></html>

4.2 历史记录页面

在这里插入图片描述

4.2.1 re.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>历史记录</title><link rel="stylesheet" href="../static/css/base.css"><link rel="stylesheet" href="../static/css/re.css"><script src="../static/js/doData.js"></script>
</head>
<body>
<div class="container"><div class="header"><a href="index.html"><img src="../static/ocrImg/logo.jpg" alt=""><span>OCR文字识别</span></a></div><div class="main"><div class="left"><div class="logs"><span>识别记录</span></div></div><div class="right"><table class="table"><thead class="thead"><tr><th>序号</th><th>图片</th><th>识别结果</th><th>识别时间</th><th>识别模型</th></tr></thead><tbody id="table-body"></tbody></table></div></div>
</div></body>
</html>

5. config文件夹

在这里插入图片描述

5.1 baiduOcrProperties

package com.example.demo.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;@Data
@Configuration
@ConfigurationProperties(prefix = "baidu.ocr")
public class baiduOcrProperties {// 百度OCR的App IDprivate String appId;// 百度OCR的API Keyprivate String apiKey;// 百度OCR的Secret Keyprivate String secretKey;
}

5.2 CorsConfiguration

package com.example.demo.config;import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/*** 配置跨域请求*/
@Configuration
public class CorsConfiguration implements WebMvcConfigurer {@Overridepublic void addCorsMappings(CorsRegistry registry) {registry.addMapping("/**").allowedOriginPatterns("*").allowCredentials(true).allowedMethods("GET", "POST", "DELETE", "PUT").maxAge(3600);}
}

5.3 myselfModelProperties

package com.example.demo.config;import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;@Data
@Configuration
@ConfigurationProperties(prefix = "myself.model")
public class myselfModelProperties {// 识别模型地址private String url;
}

6.controller文件夹

在这里插入图片描述

6.1 ocrController

package com.example.demo.controller;import com.example.demo.dao.imageTable;
import com.example.demo.service.imageService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.List;@RestController
public class ocrController {private final imageService imageService;@Autowiredpublic ocrController(imageService imageService) {this.imageService = imageService;}/*** 获取所有OCR识别记录。** @return 所有OCR识别记录。*/@GetMapping("/history")public List<imageTable> getAllImages() {return imageService.getAllImages();}/*** 使用自建OCR模型识别图片** @param file 用户上传的文件* @return OCR识别结果。* @throws IOException 读取文件时发生错误*/@PostMapping("/myModel")public ResponseEntity<String> myModel(MultipartFile file) throws IOException {String saveImgName= imageService.loadAndSaveImage(file);try {List<String> ocrResult = imageService.performMyselfOcrModel(file);//执行自建OCR识别// 将 List<String> 转换为一个单一的字符串StringBuilder resultString = new StringBuilder();for (String line : ocrResult) {resultString.append(line).append("\n");}String modelName="OcrNetV1";imageService.saveOcrData(modelName,saveImgName, resultString.toString());return ResponseEntity.ok(resultString.toString());} catch (Exception e) {e.printStackTrace();return ResponseEntity.status(500).body("Doing ocr is error:" + e.getMessage());// 返回一个带有错误信息的失败响应}}/*** 使用百度ocr处理图片文字识别请求。** @param file  用户上传的文件* @return 识别结果。* @throws IOException 如果读取文件时发生错误。*/@PostMapping(value = "/baiduOcr")public ResponseEntity<String> ocr(MultipartFile file) throws IOException {String saveImgName=imageService.loadAndSaveImage(file);try {List<String> ocrResult = imageService.performBaiduOcr(file);//执行百度OCR识别// 将 List<String> 转换为一个单一的字符串StringBuilder resultString = new StringBuilder();for (String line : ocrResult) {resultString.append(line).append("\n");}System.out.println(saveImgName);String modelName="BaiDuOcr";imageService.saveOcrData(modelName,saveImgName, resultString.toString());return ResponseEntity.ok(resultString.toString());} catch (Exception e) {e.printStackTrace();return ResponseEntity.status(500).body("Doing ocr is error:" + e.getMessage());// 返回一个带有错误信息的失败响应}}
}

7. dao文件夹

在这里插入图片描述

7.1 imageTable

package com.example.demo.dao;import jakarta.persistence.*;
import lombok.Getter;
import lombok.Setter;import static jakarta.persistence.GenerationType.IDENTITY;@Getter
@Setter
@Entity
@Table(name = "OCRHISTORY")
public class imageTable {@Id@GeneratedValue(strategy = IDENTITY)@Column(name = "id", nullable = false)private Integer id;@Column(name = "modelname")private String modelname;@Column(name = "imagedata")private String imagedata;@Column(name = "datetime")private String datetime;@Column(name = "text")private String text;}

7.2 imageTableRepository

package com.example.demo.dao;import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;@Repository
public interface imageTableRepository extends JpaRepository<imageTable, Integer> {}

8.service文件夹

在这里插入图片描述

8.1 imageService

package com.example.demo.service;import com.example.demo.dao.imageTable;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.util.List;public interface imageService {List<imageTable> getAllImages();//获取所有图片void saveOcrData(String modelName,String imagePath, String ocrResult);//保存图片识别结果String getFileContentAsBase64(MultipartFile file, boolean urlEncode) throws IOException;//将文件转换为Base64编码List<String> performBaiduOcr(MultipartFile file) throws Exception;//执行百度OCR识别List<String> performMyselfOcrModel(MultipartFile file) throws Exception;//执行自定义OCR识别String loadAndSaveImage(MultipartFile file) throws IOException;//加载并保存图片}

8.2 imageServiceImpl

package com.example.demo.service;import com.example.demo.dao.imageTableRepository;
import com.example.demo.dao.imageTable;
import com.example.demo.config.baiduOcrProperties;
import com.example.demo.config.myselfModelProperties;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import okhttp3.*;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Date;
import java.util.List;@Service
public class imageServiceImpl implements imageService{private final baiduOcrProperties baiduOcrProperties;static final OkHttpClient HTTP_CLIENT = new OkHttpClient().newBuilder().build();private final imageTableRepository imageTableRepository;private final myselfModelProperties myselfModelProperties;// 初始化@Autowiredpublic imageServiceImpl(baiduOcrProperties baiduOcrProperties, imageTableRepository imageTableRepository, com.example.demo.config.myselfModelProperties myselfModelProperties) {this.baiduOcrProperties = baiduOcrProperties;this.imageTableRepository = imageTableRepository;this.myselfModelProperties = myselfModelProperties;}/*** 导入图片、保存图片到本地** @param file  用户上传的文件* @return 图片保存后的文件名* @throws IOException 读取文件时发生错误*/public String loadAndSaveImage(MultipartFile file) throws IOException {System.out.println("<-------------->");System.out.println("Image upload ok!");System.out.println("<-------------->");System.out.println(" ");String contentType = file.getContentType();String[] parts = null;//格式为:image/pngif (contentType != null) {parts = contentType.split("/");}else{System.out.println("<-------------->");System.out.println("Parts is null!");System.out.println("<-------------->");System.out.println(" ");}String fileExtension = null;//文件后缀if (parts != null) {fileExtension = parts[parts.length - 1];}else{System.out.println("<-------------->");System.out.println("Parts is null!");System.out.println("<-------------->");System.out.println(" ");}// 保存图片到本地Path path = Paths.get("D:/code/java/demo/src/main/resources/static/ocrImg");Files.createDirectories(path);String fileName = "image-" + System.currentTimeMillis() + '.'+fileExtension;String saveImgName="/demo/static/ocrImg/"+fileName;Files.copy(file.getInputStream(), path.resolve(fileName));Path imagePath = path.resolve(fileName);// 图片的本地路径// 检查文件是否存在if (Files.exists(imagePath)) {// 如果文件大小为0,说明文件可能有问题long fileSize = Files.size(imagePath);if (fileSize == 0) {System.err.println("The saved file is empty.");}} else {System.err.println("The file was not saved.");}return saveImgName;}/*** 获取所有图片的识别记录** @return 所有图片的识别记录*/@Overridepublic List<imageTable> getAllImages() {return imageTableRepository.findAll();}/*** 将识别记录(路径、识别时间、识别结果)保存到数据库** @param imagePath 图片在本地的存储路径* @param ocrResult 图片的识别记录*/public void saveOcrData(String modelName,String imagePath, String ocrResult) {Date now = new Date();SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");// 创建 imagesTable 实例imageTable ocrhistory = new imageTable();ocrhistory.setModelname(modelName);ocrhistory.setDatetime(dateFormat.format(now));ocrhistory.setImagedata(imagePath);ocrhistory.setText(ocrResult);// 保存到数据库imageTableRepository.save(ocrhistory);}/*** 获取文件base64编码** @param file      文件* @param urlEncode 如果Content-Type是application/x-www-form-urlencoded时,传true* @return base64编码信息,不带文件头* @throws IOException IO异常*/public String getFileContentAsBase64(MultipartFile file, boolean urlEncode) throws IOException {if (file == null){return "";}else{byte[] buf = file.getBytes();String base64 = Base64.getEncoder().encodeToString(buf);if (urlEncode) {base64 = URLEncoder.encode(base64, StandardCharsets.UTF_8);}return base64;}}/*** 连接自建OCR模型识别系统** @param  fileBase64 base64编码信息,不带文件头* @return 识别结果*/public String MyselfOcrModel(String fileBase64) {String re="";String modelUrl = myselfModelProperties.getUrl();HttpHeaders headers = new HttpHeaders();headers.setContentType(org.springframework.http.MediaType.APPLICATION_JSON);String requestBody = "{\"image\": \"" + fileBase64 + "\"}";HttpEntity<String> entity = new HttpEntity<>(requestBody, headers);RestTemplate restTemplate = new RestTemplate();ResponseEntity<String> response = restTemplate.postForEntity(modelUrl, entity, String.class);if(response.getBody() != null){re= response.getBody();}return re;}/*** 执行自建OCR模型识别** @param file 需要识别的图片* @return 识别结果* @throws Exception 异常*/@Overridepublic List<String> performMyselfOcrModel(MultipartFile file) throws Exception {List<String> wordsList = new ArrayList<>();String fileBase64;fileBase64 = getFileContentAsBase64(file, false);if (fileBase64.isEmpty()){wordsList.add("File is NULL");return wordsList;}String res_json=MyselfOcrModel(fileBase64);return JsonToWordsList(wordsList, res_json);}/*** 从百度OCR服务获取Access Token。** @return Access Token,用于身份验证。* @throws IOException 如果在获取Access Token过程中出现IO错误。*/public String getAccessToken() throws IOException {String acc="";String apiKey=baiduOcrProperties.getApiKey();String secretKey=baiduOcrProperties.getSecretKey();MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");RequestBody body = RequestBody.create(mediaType, "grant_type=client_credentials&client_id=" + apiKey+ "&client_secret=" + secretKey);Request request = new Request.Builder().url("https://aip.baidubce.com/oauth/2.0/token").method("POST", body).addHeader("Content-Type", "application/x-www-form-urlencoded").build();Response response = HTTP_CLIENT.newCall(request).execute();if (response.body() != null) {acc=response.body().string();}return new JSONObject(acc).getString("access_token");}/*** 连接百度OCR识别系统** @param fileBase64 base64编码信息,不带文件头* @return 百度OCR识别结果* @throws IOException IO异常*/public String baiduOcr(String fileBase64) throws IOException{String res = "";System.out.println(fileBase64);MediaType mediaType = MediaType.parse("application/x-www-form-urlencoded");RequestBody body = RequestBody.create(mediaType, "image="+fileBase64+"&detect_direction=false&paragraph=false&probability=false");Request request = new Request.Builder().url("https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic?access_token=" + getAccessToken()).method("POST", body).addHeader("Content-Type", "application/x-www-form-urlencoded").addHeader("Accept", "application/json").build();Response response = HTTP_CLIENT.newCall(request).execute();if (response.body() != null) {res=response.body().string();}return res;}/*** 执行百度OCR识别操作。** @param file 需要进行OCR识别的文件。* @return 识别到的文本列表。* @throws Exception 如果识别过程中出现错误,则抛出异常。*/public List<String> performBaiduOcr(MultipartFile file) throws Exception {List<String> wordsList = new ArrayList<>();String fileBase64;fileBase64 = getFileContentAsBase64(file, true);if (fileBase64.isEmpty()){wordsList.add("File is NULL");return wordsList;}String res_json=baiduOcr(fileBase64);return JsonToWordsList(wordsList, res_json);}/**** 将json数据转换为wordsList** @param wordsList 存储识别结果的列表* @param res_json 识别模型回传的Json数据* @return wordsList*/private List<String> JsonToWordsList(List<String> wordsList, String res_json) {try {ObjectMapper mapper = new ObjectMapper();JsonNode rootNode = mapper.readTree(res_json);JsonNode wordsResultNode = rootNode.get("words_result");for (JsonNode wordNode : wordsResultNode) {wordsList.add(wordNode.get("words").asText());}// 输出结果System.out.println("------wordsList------");for (String word : wordsList) {System.out.println(word);}System.out.println("------wordsList------");} catch (IOException e) {e.printStackTrace();}return wordsList;}}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/412050.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Java Web_00001

目录 Web项目介绍网页的组成部分 HTMLHTML简介HTML示例HTML文件的书写规范HTML标签标签介绍标签的语法&#xff1a;常用标签font特殊字符标题标签超链接列表标签img标签表格标签跨行跨列表格iframe框架标签(内嵌窗口)表单标签表单的显示表单格式化表单提交细节 其他标签 CSSCSS…

OpenHarmony轻松玩转GIF数据渲染

OpenAtom OpenHarmony&#xff08;以下简称“OpenHarmony”&#xff09;提供了Image组件支持GIF动图的播放&#xff0c;但是缺乏扩展能力&#xff0c;不支持播放控制等。今天介绍一款三方库——ohos-gif-drawable三方组件&#xff0c;带大家一起玩转GIF的数据渲染&#xff0c;搞…

CI/CD实践(五)Jenkins Docker 自动化构建部署Node服务

微服务CI/CD实践系列&#xff1a; 微服务CI/CD实践&#xff08;一&#xff09;环境准备及虚拟机创建 微服务CI/CD实践&#xff08;二&#xff09;服务器先决准备 微服务CI/CD实践&#xff08;三&#xff09;gitlab部署及nexus3部署 微服务CI/CD实践&#xff08;四&#xff09…

GraphPad Prism下载安装教程怎样中文汉化

GraphPad Prism下载安装教程怎样中文汉化&#xff1a; GraphPad Prism 是一款集生物统计、曲线拟合和科技绘图于一体的软件&#xff0c;主要用于医学和生物科学领域的数据分析和绘图&#xff0c;具有高效、简便、多功能和高质量的特点&#xff0c;被广泛应用于科研、教育和业界…

湖南的智榜样网络安全公司开的培训学校参加学习成为网络安全工程师

学习网络安全可以通过以下步骤进行&#xff1a; 获取基础知识&#xff1a;开始学习网络安全之前&#xff0c;建议先获取一些计算机基础知识&#xff0c;包括计算机网络、操作系统、编程语言等方面的知识。这些基础知识将为你理解和学习网络安全提供必要的背景。 学习网络安全基…

安卓13去掉权限动态申请,默认授权,不用动态申请权限

总纲 android13 rom 开发总纲说明 1、前言 2、问题分析 3.代码处理 4.代码修改 5.编译 6.彩蛋 1、前言

day44——C++对C的扩充

八、C对函数的扩充 8.1 函数重载&#xff08;overload&#xff09; 1> 概念 函数重载就是能够实现"一名多用"&#xff0c;是实现泛型编程的一种 泛型编程&#xff1a;试图以不变的代码&#xff0c;来实现可变的功能 2> 引入背景 程序员在写函数时&#x…

C++语法基础(二)

C复合类型 结构体 1. C的结构&#xff0c;定义结构体类型的变量时&#xff0c;可以省略struct关键字 2. 可以定义成员函数&#xff0c;在结构体中的成员函数内部可以直接访问本结构体的成员&#xff0c;无需通过“.”或“->” 联合 1. C的联合,定义联合体类型的变…

Linux系统ubuntu20.04 无人机PX4 开发环境搭建(失败率很低)

PX4固件下载 PX4的源码处于GitHub&#xff0c;因为众所周知的原因git clone经常失败&#xff0c;此处从Gitee获取PX4源码和依赖模块。 git clone https://gitee.com/voima/PX4-Autopilot.git 正克隆到 ‘PX4-Autopilot’… remote: Enumerating objects: 454209, done. remot…

Apache CloudStack Official Document 翻译节选(十二)

快速部署一朵 Apache CloudStack 云 &#xff08;一&#xff09; 部署前的准备工作 Apache CloudStack快速部署指南 我们究竟在构建什么&#xff1f; 构建IAAS云是一件很复杂的事项&#xff0c;根据相关定义&#xff0c;构建IAAS云的可选项有很多。这些纷繁复杂的概念通常给…

WLAN原理实验简述——AP上线

一、需求&#xff1a; AP通过AC上线。 AC通过控制VLAN管理AP,创建VLAN100和放行。 AP同AC建立CAPWAP关系。 二、实验拓扑图&#xff1a; 三、实验步骤&#xff1a; LSW1: sys Enter system view, return user view with CtrlZ. [Huawei]Sysname lsw1 [lsw1]undo info enable I…

扩散模型(Diffusion Model)

扩散模型&#xff08;diffusion model&#xff09;是一种运用了物理热力学扩散思想的生成模型。扩散模型有很多不同的变形&#xff0c;本文主要介绍最知名的去噪扩散概率模型&#xff08;Denoising Diffusion Probabilistic Model&#xff0c;DDPM&#xff09;。如今比较成功的…

Notepad++回车不自动补全

问题 使用Notepad时&#xff0c;按回车经常自动补全&#xff0c;但我们希望回车进行换行&#xff0c;而不是自动补全&#xff0c;而且自动补全使用Tab进行补全足够了。下文介绍设置方法。 设置方法 打开Notepad&#xff0c;进入设置 - 首选项 - 自动完成&#xff0c;在插入选…

Windows上MSYS2的安装和使用

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言一、下载二、安装三、使用1.打开命令行2.搜索软件3.安装软件4.卸载软件5.更新环境6.其他四、MSYS2和Cygwin的差别总结前言 MSYS2这个工具我是越用越喜欢,很多东西放在Linux上如鱼得水但是放在…

禁止文件外发 | 如何禁止员工外发文件?严守企业机密,禁止员工外发敏感文件!

近期&#xff0c;我们注意到一些敏感项目资料有外泄的风险&#xff0c;这对公司的核心竞争力构成了严重威胁&#xff01; 我们必须立即采取行动&#xff0c;严守企业机密&#xff0c;确保每一份文件都安全无虞。 从今天起&#xff0c;我们要全面升级信息安全措施&#xff0c;…

2024世界机器人大会盛大开幕,卓翼飞思携无人智能领域产品集中亮相 !

开放创新 聚享未来&#xff01;万众瞩目的2024世界机器人大会暨博览会于8月21日在北京亦创国际会展中心盛大开幕。大会聚焦机器人技术与产业前沿趋势&#xff0c;展示机器人创新应用赋能千行百业的多元场景&#xff0c;全球顶尖的机器人科学家、行业领袖、创新精英汇聚一堂&…

React学习day04-useEffect、自定义Hook函数

11、useEffect&#xff08;一个React Hook函数&#xff09; &#xff08;1&#xff09;作用&#xff1a;用于在React组件中创建不是由事件引起而是由渲染本身引起的操作&#xff0c;比如发送AJAX请求&#xff0c;更改DOM等&#xff08;即&#xff1a;视图渲染完后会触发一些事…

前端开发,太难啦!

声明&#xff1a;此篇为 ai123.cn 原创文章&#xff0c;转载请标明出处链接&#xff1a;https://ai123.cn/#1 《黑神话&#xff1a;悟空》作为一款国产3A游戏&#xff0c;其前端开发的游戏界面和交互设计复杂度极高&#xff0c;这要求开发团队与设计师之间进行紧密合作&#xf…

嵌入式Qt移植之tslib部署到Busybox根文件-思维导图-学习笔记-基于正点原子阿尔法开发板

嵌入式Qt移植之tslib部署到Busybox根文件 烧写Busybox根文件系统到开发板 准备好一个固化系统 以TF卡为例子 TF 卡用读卡器插到 Ubuntu 虚拟机 会出现两个分区 boot分区是存放内核和设备树这些 rootfs分区是存放文件系统的 eMMC、NADA FLASH或者其他方式挂载也可&#xf…

中国严肃游戏开发的最佳实践

严肃游戏产业在中国迅速发展&#xff0c;将娱乐与教育、培训和宣传活动融为一体。旨在实现特定学习成果或行为改变的严肃游戏在从企业培训到医疗保健和教育的各个领域越来越受欢迎。然而&#xff0c;为中国市场开发成功的严肃游戏需要深入了解当地文化、用户偏好和技术趋势。以…