話不多說,向上效果圖
1)先說框架版本
為什么要先說框架版本呢,因為我在各種版本中嘗試了兩天,總算確定了如下版本適合我,至于其他的版本,各位自己去嘗試
? ? ? ? python 3.9.7
? ? ? ? EasyOCR 1.7.2
? ? ? ? flask 3.0.3
2)執行操作效果圖
? ? ? ? 2.1)多選文件
2.2)圖片預覽
2.3)提取選中文件
2.4)提取所有文件
3.上代碼
? ? ? ? 3.1)前端代碼index.html
<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Python OCR</title><link rel="stylesheet" href="/static/js/bootstrap/css/bootstrap.min.css"><style>.container {max-width: 1024px;margin-top: 20px;}.preview-image {max-width: 300px;max-height: 400px;margin-top: 10px;}.file-input {display: none;}</style>
</head>
<body><div class="container"><div class="row"><div class="col-md-12"><input type="file" id="fileInput" class="file-input" multiple></div></div><div class="row mt-3"><div class="col-md-6"><div class="list-group" id="picListBox"></div><button id="btnExtractSelected" class="btn btn-primary mt-2">提取選中文件</button><button id="btnDeleteSelected" class="btn btn-danger mt-2">刪除選中文件</button></div><div class="col-md-6"><img id="lblImage" class="preview-image" src="" alt="暫未選擇文件"></div></div><div class="row mt-3"><div class="col-md-12"><textarea id="txtPressInof" class="form-control" rows="5" readonly></textarea></div></div><div class="row mt-3"><div class="col-md-12 text-center"><button id="btnDo" class="btn btn-success">立即提取所有文件</button></div></div></div><script src="/static/js/jQuery-2.2.0.min.js"></script><script>$(document).ready(function () {// 處理文件選擇$('#fileInput').change(function () {const files = $(this)[0].files;$('#picListBox').empty();for (let i = 0; i < files.length; i++) {const filename = files[i].name;const item = $('<a href="#" class="list-group-item list-group-item-action">' + filename + '</a>');item.data('file', files[i]);$('#picListBox').append(item);}});// 圖片列表項點擊事件$('#picListBox').on('click', 'a', function () {// 移除其他列表項的 active 類$('#picListBox a').removeClass('active');// 為當前點擊的列表項添加 active 類$(this).addClass('active');const file = $(this).data('file');const reader = new FileReader();reader.onload = function (e) {$('#lblImage').attr('src', e.target.result);};reader.readAsDataURL(file);});// 刪除選中文件按鈕點擊事件$('#btnDeleteSelected').click(function () {const selectedFileItem = $('#picListBox a.active');if (!selectedFileItem.length) {alert('請選擇一個文件');return;}// 移除選中的文件項selectedFileItem.remove();// 清空 lblImage$('#lblImage').attr('src', '');$('#lblImage').attr('alt', '暫未選擇文件');});// 提取選中文件按鈕點擊事件$('#btnExtractSelected').click(function () {const selectedFile = $('#picListBox a.active').data('file');if (!selectedFile) {alert('請選擇一個文件');return;}const formData = new FormData();formData.append('file', selectedFile);$(this).prop('disabled', true).text('正在提取...');$.ajax({url: '/extract-selected', // 后端接口,處理選中的圖片并返回提取結果type: 'POST',data: formData,contentType: false,processData: false,success: function (data) {$('#txtPressInof').val(data);$('#btnExtractSelected').prop('disabled', false).text('提取選中文件');}});});// 立即提取所有文件按鈕點擊事件$('#btnDo').click(function () {const formData = new FormData();$('#picListBox a').each(function () {formData.append('files', $(this).data('file'));});$(this).prop('disabled', true).text('正在提取...');$.ajax({url: '/process-pictures', // 后端接口,處理所有圖片并返回提取結果type: 'POST',data: formData,contentType: false,processData: false,success: function (data) {$('#txtPressInof').val(data);$('#btnDo').prop('disabled', false).text('立即提取所有文件');}});});});</script>
</body>
</html>
????????3.2)后端代碼
from flask import Flask, request, jsonify, render_template
import os
import easyocr
import time
import re
app = Flask(__name__)@app.route('/')
def index():return render_template('index.html')def clean_filename(filename):"""清理文件名,移除非法字符"""return re.sub(r'[\\/*?:"<>|]', "", filename)def get_unique_filename(target_folder, filename):"""確保文件名唯一,避免覆蓋同名文件"""base, ext = os.path.splitext(filename)unique_filename = filenamecounter = 1while os.path.exists(os.path.join(target_folder, unique_filename)):unique_filename = f"{base}_{int(time.time())}{ext}" # 添加時間戳確保唯一性counter += 1return unique_filename@app.route('/process-pictures', methods=['POST'])
def process_pictures():# 指定保存圖片的目標文件夾target_folder = "D:/OCR_Images" # 請確保該文件夾存在或在代碼中創建它if not os.path.exists(target_folder):os.makedirs(target_folder)files = request.files.getlist('files')reader = easyocr.Reader(['ch_sim', 'en'], model_storage_directory='./model')result = ""for i, file in enumerate(files):# 獲取文件名(不包含路徑)filename = os.path.basename(file.filename)#print(filename)# 清理文件名safe_filename = clean_filename(filename)#print(safe_filename)# 確保文件名唯一unique_filename = get_unique_filename(target_folder, filename)#print(unique_filename)file_path = os.path.join(target_folder, unique_filename)file.save(file_path)result += f'【開始處理{i + 1}-{len(files)}張圖片,{unique_filename}】\n'#print(file_path,result)ocr_result = reader.readtext(file_path)restxt = ""for ln in ocr_result:restxt += ln[1]restxt += "\n"result += restxtresult += "全部完成"return jsonify(result)@app.route('/extract-selected', methods=['POST'])
def extract_selected():# 指定保存圖片的目標文件夾target_folder = "D:/OCR_Images" # 請確保該文件夾存在或在代碼中創建它if not os.path.exists(target_folder):os.makedirs(target_folder)file = request.files['file']# 獲取文件名(不包含路徑)filename = os.path.basename(file.filename)# 清理文件名safe_filename = clean_filename(filename)# 確保文件名唯一unique_filename = get_unique_filename(target_folder, safe_filename)file_path = os.path.join(target_folder, unique_filename)file.save(file_path)reader = easyocr.Reader(['ch_sim', 'en'], model_storage_directory='./model')ocr_result = reader.readtext(file_path)result = f'【開始處理文件,{unique_filename}】\n'restxt = ""for ln in ocr_result:restxt += ln[1]restxt += "\n"result += restxtreturn jsonify(result)if __name__ == '__main__':app.run(debug=True)
最后提示:文字模型需自行下載保存到本地,我保存在自己的項目下的model目錄下
模型的引用語句(有gpu的情況):