Loading... ## 引言 随着人工智能和机器学习技术的快速发展,文字识别(OCR)技术已经成为了许多应用场景中的核心功能。无论是从图像中提取文本,还是识别手写文字,OCR技术都在不断进步。本文将介绍如何使用Tesseract.js开发一个基于Web的智能手写文字识别工具,并分享一些开发过程中的技术细节。 ## 项目概述 本项目是一个基于Tesseract.js的在线手写文字识别系统,用户可以在网页上通过鼠标或触摸屏手写文字,系统会自动识别并显示识别结果。该工具支持笔画粗细调整、自动识别、图像预处理等功能,适用于多种场景下的手写文字识别需求。 ## 技术栈 * **Tesseract.js** :一个基于Tesseract OCR引擎的JavaScript库,支持在浏览器中进行文字识别。 * **HTML5 Canvas** :用于绘制手写文字的画布。 * **JavaScript** :处理用户交互、图像预处理和文字识别逻辑。 ## 实现细节 ### 1. 初始化Tesseract OCR引擎 Tesseract.js是一个强大的OCR库,支持多种语言的文字识别。在项目中,我们首先需要初始化Tesseract引擎,并加载中文和英文的语言模型。 ```javascript async function initTesseract() { try { scheduler = Tesseract.createScheduler(); worker = await Tesseract.createWorker({ logger: m => console.log(m) }); // 加载中文和英文语言包 await worker.loadLanguage('chi_sim+eng'); await worker.initialize('chi_sim+eng'); // 设置识别参数 await worker.setParameters({ tessedit_char_whitelist: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ你我他的是在有和人这中大为上个国我的时要出会可也得里说之用年行家方后作成开面而于经', tessedit_pageseg_mode: '6', // 假设文本是统一块 }); scheduler.addWorker(worker); console.log('Tesseract 初始化完成'); } catch (error) { console.error('Tesseract 初始化失败:', error); } } ``` ### 2. 画布绘制与交互 用户可以通过鼠标或触摸屏在HTML5 Canvas上绘制手写文字。我们通过监听鼠标和触摸事件来捕获用户的绘制动作,并将绘制的笔画存储在一个数组中,以便后续的重绘和识别。 ```javascript function startDrawing(e) { isDrawing = true; const rect = canvas.getBoundingClientRect(); [lastX, lastY] = [ e.clientX - rect.left, e.clientY - rect.top ]; currentStroke = [{x: lastX, y: lastY}]; // 清除之前的识别计时器 if (recognizeTimer) { clearTimeout(recognizeTimer); recognizeTimer = null; } } function draw(e) { if (!isDrawing) return; const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; ctx.beginPath(); ctx.moveTo(lastX, lastY); ctx.lineTo(x, y); ctx.stroke(); currentStroke.push({x: x, y: y}); [lastX, lastY] = [x, y]; } function stopDrawing() { if (isDrawing) { isDrawing = false; strokes.push(currentStroke); currentStroke = []; scheduleRecognition(); } } ``` ### 3. 自动识别与图像预处理 为了提高识别准确率,我们提供了自动识别功能。当用户停止书写1.5秒后,系统会自动触发文字识别。此外,用户还可以选择是否对图像进行预处理,如二值化处理,以增强识别效果。 ```javascript function scheduleRecognition() { if (!document.getElementById('autoRecognize').checked) return; if (recognizeTimer) { clearTimeout(recognizeTimer); } recognizeTimer = setTimeout(() => { recognizeText(); }, 1500); // 1.5秒后自动识别 } function preprocessImage(imageData) { const canvas2 = document.createElement('canvas'); canvas2.width = imageData.width; canvas2.height = imageData.height; const ctx2 = canvas2.getContext('2d'); ctx2.putImageData(imageData, 0, 0); const data = ctx2.getImageData(0, 0, canvas2.width, canvas2.height); const pixels = data.data; // 二值化处理 for (let i = 0; i < pixels.length; i += 4) { const gray = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3; const value = gray > 128 ? 255 : 0; pixels[i] = pixels[i + 1] = pixels[i + 2] = value; } ctx2.putImageData(data, 0, 0); return canvas2.toDataURL('image/png'); } ``` ### 4. 文字识别与结果显示 当用户完成书写后,系统会将Canvas中的图像数据传递给Tesseract.js进行识别,并将识别结果显示在页面上。 ```javascript async function recognizeText() { if (!scheduler || !worker) { console.log('Tesseract未初始化'); return; } const loading = document.getElementById('loading'); const result = document.getElementById('result'); loading.style.display = 'block'; result.textContent = '正在识别中...'; try { let imageData; if (document.getElementById('preprocess').checked) { imageData = preprocessImage(ctx.getImageData(0, 0, canvas.width, canvas.height)); } else { imageData = canvas.toDataURL('image/png'); } const { data: { text } } = await scheduler.addJob('recognize', imageData); result.textContent = `识别结果: ${text.trim()}`; } catch (error) { console.error(error); result.textContent = '识别失败: ' + error.message; } finally { loading.style.display = 'none'; } } ``` ## 总结 通过本项目,我们实现了一个基于Tesseract.js的智能手写文字识别工具。该工具不仅支持手写文字的实时识别,还提供了图像预处理、自动识别等功能,能够有效提高识别准确率。未来,我们可以进一步优化识别算法,增加更多语言支持,并将其应用于更多实际场景中。 --- 完整代码: ```html <!-- * 智能手写文字识别 * * @author: Arvin-Hugh * @created: 2025-02-07 06:49:42 UTC * @description: 一个基于Tesseract.js的在线手写文字识别系统 * @version: 1.0.0 --> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>智能手写文字识别</title> <script src='https://cdn.jsdelivr.net/npm/tesseract.js@4/dist/tesseract.min.js'></script> <style> /* 全局样式 */ * { margin: 0; padding: 0; box-sizing: border-box; } body { display: flex; flex-direction: column; align-items: center; min-height: 100vh; font-family: 'Segoe UI', Arial, sans-serif; background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); padding: 20px; } h1 { color: #2c3e50; margin-bottom: 30px; text-shadow: 2px 2px 4px rgba(0,0,0,0.1); font-size: 2.5em; } /* 画布样式 */ #canvas { border: 2px solid #3498db; margin-bottom: 20px; background-color: white; border-radius: 15px; box-shadow: 0 10px 20px rgba(0,0,0,0.1); transition: all 0.3s ease; } #canvas:hover { box-shadow: 0 15px 30px rgba(0,0,0,0.15); transform: translateY(-2px); } /* 按钮容器样式 */ .buttons { margin: 15px 0; display: flex; gap: 15px; } /* 按钮样式 */ button { padding: 12px 24px; font-size: 16px; border: none; border-radius: 8px; background: #3498db; color: white; cursor: pointer; transition: all 0.3s ease; box-shadow: 0 4px 6px rgba(0,0,0,0.1); } button:hover { background: #2980b9; transform: translateY(-2px); box-shadow: 0 6px 8px rgba(0,0,0,0.15); } /* 结果显示区域样式 */ #result { font-size: 18px; margin-top: 20px; padding: 15px 25px; border-radius: 10px; background-color: white; box-shadow: 0 4px 6px rgba(0,0,0,0.1); min-width: 300px; text-align: center; transition: all 0.3s ease; } /* 加载提示样式 */ .loading { color: #666; font-style: italic; margin-top: 10px; } /* 设置面板样式 */ .settings { background: white; padding: 20px; border-radius: 12px; box-shadow: 0 8px 16px rgba(0,0,0,0.1); margin-bottom: 20px; width: 100%; max-width: 600px; } /* 控制项样式 */ .controls { margin: 15px 0; display: flex; align-items: center; gap: 10px; } /* 范围滑块样式 */ input[type="range"] { -webkit-appearance: none; width: 150px; height: 6px; background: #e0e0e0; border-radius: 3px; outline: none; } input[type="range"]::-webkit-slider-thumb { -webkit-appearance: none; width: 18px; height: 18px; background: #3498db; border-radius: 50%; cursor: pointer; transition: all 0.3s ease; } input[type="range"]::-webkit-slider-thumb:hover { background: #2980b9; transform: scale(1.1); } /* 复选框样式 */ input[type="checkbox"] { width: 18px; height: 18px; cursor: pointer; } /* 自动识别选项样式 */ .auto-recognize { margin-top: 15px; padding-top: 15px; border-top: 1px solid #eee; } label { color: #2c3e50; font-weight: 500; } /* 版权信息样式 */ .copyright { margin-top: 20px; color: #666; font-size: 14px; text-align: center; } </style> </head> <body> <h1>智能手写文字识别</h1> <div class="settings"> <div class="controls"> <label>笔画粗细:</label> <input type="range" id="lineWidth" min="1" max="20" value="8"> <span id="lineWidthValue">8</span> </div> <div class="controls"> <label>预处理:</label> <input type="checkbox" id="preprocess" checked> <label for="preprocess">增强对比度</label> </div> <div class="auto-recognize"> <input type="checkbox" id="autoRecognize" checked> <label for="autoRecognize">自动识别(停笔1.5秒后)</label> </div> </div> <canvas id="canvas" width="600" height="300"></canvas> <div class="buttons"> <button onclick="clearCanvas()">清除</button> <button onclick="undoLastStroke()">撤销</button> </div> <div id="loading" class="loading">正在识别中...</div> <div id="result">准备就绪,请开始书写...</div> <div class="copyright"> © 2025 by Arvin-Hugh. All rights reserved. </div> <script> // 全局变量声明 const canvas = document.getElementById('canvas'); const ctx = canvas.getContext('2d'); let isDrawing = false; // 是否正在绘制 let lastX = 0; // 上一次绘制的X坐标 let lastY = 0; // 上一次绘制的Y坐标 let strokes = []; // 存储所有笔画 let currentStroke = []; // 当前正在绘制的笔画 let recognizeTimer = null; // 自动识别计时器 let worker = null; // Tesseract worker实例 let scheduler = null; // Tesseract scheduler实例 /** * 初始化Tesseract OCR引擎 * 加载语言模型并设置识别参数 */ async function initTesseract() { try { scheduler = Tesseract.createScheduler(); worker = await Tesseract.createWorker({ logger: m => console.log(m) }); // 加载中文和英文语言包 await worker.loadLanguage('chi_sim+eng'); await worker.initialize('chi_sim+eng'); // 设置识别参数 await worker.setParameters({ // 设置字符白名单,提高识别准确率 tessedit_char_whitelist: '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ你我他的是在有和人这中大为上个国我的时要出会可也得里说之用年行家方后作成开面而于经', tessedit_pageseg_mode: '6', // 假设文本是统一块 }); scheduler.addWorker(worker); console.log('Tesseract 初始化完成'); } catch (error) { console.error('Tesseract 初始化失败:', error); } } /** * 初始化画布设置 */ function initCanvas() { ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); ctx.strokeStyle = 'black'; ctx.lineWidth = document.getElementById('lineWidth').value; ctx.lineCap = 'round'; ctx.lineJoin = 'round'; } // 监听笔画粗细变化 document.getElementById('lineWidth').addEventListener('input', function(e) { ctx.lineWidth = e.target.value; document.getElementById('lineWidthValue').textContent = e.target.value; }); /** * 撤销上一笔画 */ function undoLastStroke() { if (strokes.length > 0) { strokes.pop(); redrawCanvas(); scheduleRecognition(); } } /** * 重绘整个画布 */ function redrawCanvas() { ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); strokes.forEach(stroke => { ctx.beginPath(); ctx.moveTo(stroke[0].x, stroke[0].y); stroke.forEach(point => { ctx.lineTo(point.x, point.y); }); ctx.stroke(); }); } /** * 开始绘制 * @param {Event} e - 鼠标/触摸事件 */ function startDrawing(e) { isDrawing = true; const rect = canvas.getBoundingClientRect(); [lastX, lastY] = [ e.clientX - rect.left, e.clientY - rect.top ]; currentStroke = [{x: lastX, y: lastY}]; // 清除之前的识别计时器 if (recognizeTimer) { clearTimeout(recognizeTimer); recognizeTimer = null; } } /** * 绘制过程 * @param {Event} e - 鼠标/触摸事件 */ function draw(e) { if (!isDrawing) return; const rect = canvas.getBoundingClientRect(); const x = e.clientX - rect.left; const y = e.clientY - rect.top; ctx.beginPath(); ctx.moveTo(lastX, lastY); ctx.lineTo(x, y); ctx.stroke(); currentStroke.push({x: x, y: y}); [lastX, lastY] = [x, y]; } /** * 结束绘制 */ function stopDrawing() { if (isDrawing) { isDrawing = false; strokes.push(currentStroke); currentStroke = []; scheduleRecognition(); } } /** * 安排自动识别任务 */ function scheduleRecognition() { if (!document.getElementById('autoRecognize').checked) return; if (recognizeTimer) { clearTimeout(recognizeTimer); } recognizeTimer = setTimeout(() => { recognizeText(); }, 1500); // 1.5秒后自动识别 } /** * 图像预处理 * @param {ImageData} imageData - 画布图像数据 * @returns {string} - 处理后的图像数据URL */ function preprocessImage(imageData) { const canvas2 = document.createElement('canvas'); canvas2.width = imageData.width; canvas2.height = imageData.height; const ctx2 = canvas2.getContext('2d'); ctx2.putImageData(imageData, 0, 0); const data = ctx2.getImageData(0, 0, canvas2.width, canvas2.height); const pixels = data.data; // 二值化处理 for (let i = 0; i < pixels.length; i += 4) { const gray = (pixels[i] + pixels[i + 1] + pixels[i + 2]) / 3; const value = gray > 128 ? 255 : 0; pixels[i] = pixels[i + 1] = pixels[i + 2] = value; } ctx2.putImageData(data, 0, 0); return canvas2.toDataURL('image/png'); } /** * 执行文字识别 */ async function recognizeText() { if (!scheduler || !worker) { console.log('Tesseract未初始化'); return; } const loading = document.getElementById('loading'); const result = document.getElementById('result'); loading.style.display = 'block'; result.textContent = '正在识别中...'; try { let imageData; if (document.getElementById('preprocess').checked) { imageData = preprocessImage(ctx.getImageData(0, 0, canvas.width, canvas.height)); } else { imageData = canvas.toDataURL('image/png'); } const { data: { text } } = await scheduler.addJob('recognize', imageData); result.textContent = `识别结果: ${text.trim()}`; } catch (error) { console.error(error); result.textContent = '识别失败: ' + error.message; } finally { loading.style.display = 'none'; } } /** * 清空画布 */ function clearCanvas() { ctx.fillStyle = 'white'; ctx.fillRect(0, 0, canvas.width, canvas.height); strokes = []; document.getElementById('result').textContent = '准备就绪,请开始书写...'; if (recognizeTimer) { clearTimeout(recognizeTimer); recognizeTimer = null; } } /** * 处理触摸开始事件 * @param {TouchEvent} e - 触摸事件 */ function handleTouchStart(e) { e.preventDefault(); const touch = e.touches[0]; const rect = canvas.getBoundingClientRect(); startDrawing({ clientX: touch.clientX, clientY: touch.clientY }); } /** * 处理触摸移动事件 * @param {TouchEvent} e - 触摸事件 */ function handleTouchMove(e) { e.preventDefault(); const touch = e.touches[0]; draw({ clientX: touch.clientX, clientY: touch.clientY }); } // 触摸事件监听器 canvas.addEventListener('touchstart', handleTouchStart); canvas.addEventListener('touchmove', handleTouchMove); canvas.addEventListener('touchend', stopDrawing); // 鼠标事件监听器 canvas.addEventListener('mousedown', startDrawing); canvas.addEventListener('mousemove', draw); canvas.addEventListener('mouseup', stopDrawing); canvas.addEventListener('mouseout', stopDrawing); // 当页面加载完成后初始化 window.onload = function() { initCanvas(); // 初始化画布 initTesseract(); // 初始化Tesseract // 添加版权信息到控制台 console.log(` 智能手写文字识别系统 版本: 1.0.0 作者: Arvin-Hugh 创建时间: 2025-02-07 06:51:27 UTC 版权所有 © 2025 `); }; </script> </body> </html> ``` 最后修改:2025 年 02 月 07 日 © 允许规范转载 赞 都滑到这里了,不点赞再走!?