OCR im Browser mit Tesseract.js integrieren
Wählen Sie ein lokales Bild, klicken Sie auf „Recognize text“ und kopieren Sie den Text, ohne das Bild hochzuladen. Diese Anleitung bietet Webentwicklern ein vollständiges Tesseract.js-Beispiel mit Ladeanzeige, erneutem Versuch mit derselben Datei und jeweils nur einem Erkennungsauftrag. Beginnen Sie mit einem klaren Screenshot von gedrucktem englischem Text; OCR-Ergebnisse müssen weiterhin Korrektur gelesen werden.
Was im Browser ausgeführt wird
Tesseract.js führt die Tesseract-OCR-Engine mithilfe von WebAssembly in einem Worker aus. Der Browser übernimmt die Erkennung. Geschwindigkeit und Speicherbedarf hängen daher vom Gerät und vom Bild des Nutzers ab. Ein sofortiges Ergebnis ist nicht garantiert. Der Funktionsumfang des Projekts schließt auch die direkte PDF-Eingabe aus: Rendern Sie PDF-Seiten vor der Erkennung separat als Bilder.
Dieses Beispiel legt Tesseract.js und seinen Core auf 7.0.0 sowie die englischen Sprachdaten auf
1.0.0 fest. Es nutzt die standardmäßig aktivierte Textausgabe.
Die Worker-API
initialisiert die Sprache im asynchronen Aufruf createWorker('eng', 1, options); die älteren
Schritte loadLanguage() und initialize() sind nicht nötig.
Browserkompatibilität und Voraussetzungen
Verwenden Sie einen aktuellen Browser mit Web Workers, verschachtelten Workern und WebAssembly. Das vollständige Beispiel wurde in Chromium 145 und 152 unter Linux getestet. Sie benötigen außerdem Python 3, um die zwei Dateien lokal bereitzustellen, sowie eine Netzwerkverbindung, um die Skripte, WASM und Sprachdaten in den festgelegten Versionen von jsDelivr zu laden. Ein Node.js-Build oder eine Paketinstallation ist nicht erforderlich.
Das Bild bleibt in diesem Beispiel im Browser, doch die Downloads der Ressourcen kontaktieren weiterhin ein CDN. Allein das Zwischenspeichern der Sprachdaten macht die Seite nicht offline nutzbar. Eine Offline-Anwendung muss auch ihr HTML, ihre Skripte, Worker, WASM und Sprachressourcen bereitstellen oder zwischenspeichern. Diese Anleitung richtet keinen Offline-Cache ein. Siehe die Hosting-Optionen für Ressourcen des Projekts.
Erste Schritte mit Tesseract.js
Installation
Erstellen Sie ein neues, leeres Verzeichnis namens tesseract-browser. Falls dieser
Name bereits existiert, wählen Sie ein anderes Verzeichnis, statt seine Dateien zu ersetzen.
Speichern Sie die nächsten zwei Blöcke darin als index.html
und ocr-worker.js. Dies sind einfache Browserdateien ohne Framework oder Backend.
Einfaches Beispiel: Text aus einem Bild erkennen
Speichern Sie dies als index.html. Dateiauswahl und Schaltfläche bleiben
deaktiviert, solange ein Auftrag aussteht. Wenn Sie danach ein anderes Bild auswählen, wird das
vorherige Ergebnis gelöscht. Ein erneuter Klick auf die Schaltfläche startet einen neuen Versuch
mit der ausgewählten Datei, ohne dass ein neues Dateiauswahlereignis nötig ist.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Read text from a local image</title>
</head>
<body>
<h1>Read text from a local image</h1>
<form id="ocrForm">
<fieldset id="controls">
<legend>Recognize English text</legend>
<label for="imageInput">Image (JPEG, PNG, or WebP; up to 5 MiB)</label>
<input id="imageInput" type="file" accept="image/jpeg,image/png,image/webp" />
<button type="submit">Recognize text</button>
</fieldset>
</form>
<p id="status" role="status">Choose an image to begin.</p>
<label for="result">Recognized text</label>
<textarea id="result" rows="12" cols="60" readonly></textarea>
<script>
const form = document.getElementById('ocrForm')
const controls = document.getElementById('controls')
const imageInput = document.getElementById('imageInput')
const status = document.getElementById('status')
const result = document.getElementById('result')
let busy = false
async function recognizeImage(file) {
let task
let timer
let timedOut = false
try {
task = new Worker('./ocr-worker.js')
return await new Promise((resolve, reject) => {
const fail = () => reject(new Error('OCR task failed.'))
timer = setTimeout(() => {
timedOut = true
fail()
}, 90_000)
task.onerror = (event) => {
event.preventDefault()
fail()
}
task.onmessage = ({ data }) => {
if (data.type === 'result') resolve(data.text)
else if (data.type === 'error') fail()
else if (data.type === 'progress') {
status.textContent = data.status === 'recognizing text'
? 'Recognizing text… ' + Math.round(data.progress * 100) + '%'
: 'Loading OCR assets…'
}
}
task.postMessage(file)
})
} catch {
throw new Error(timedOut
? 'OCR timed out after 90 seconds. Try a smaller image or retry.'
: 'OCR failed. Check your connection and image, then try again.')
} finally {
clearTimeout(timer)
task?.terminate()
}
}
async function validateAndPerformOCR(file) {
if (!file || !['image/jpeg', 'image/png', 'image/webp'].includes(file.type)) {
throw new Error('Choose a JPEG, PNG, or WebP image.')
}
if (file.size === 0 || file.size > 5 * 1024 * 1024) {
throw new Error('Choose a nonempty image of 5 MiB or smaller.')
}
return recognizeImage(file)
}
imageInput.addEventListener('change', () => {
if (busy) return
result.value = ''
status.textContent = 'Selection changed. Click Recognize text.'
})
form.addEventListener('submit', async (event) => {
event.preventDefault()
if (busy || controls.disabled) return
const file = imageInput.files[0]
busy = true
controls.disabled = true
result.value = ''
status.textContent = 'Loading OCR assets…'
try {
const text = await validateAndPerformOCR(file)
result.value = text
status.textContent = text.trim()
? 'Finished: ' + file.name + '. You can copy the text below.'
: 'No text found. Try a clearer image of printed text.'
} catch (error) {
status.textContent = error instanceof Error
? error.message
: 'OCR failed. Try another image.'
} finally {
busy = false
controls.disabled = false
}
})
if (typeof Worker === 'undefined' || typeof WebAssembly === 'undefined') {
controls.disabled = true
status.textContent = 'Use a browser with Web Workers and WebAssembly.'
}
</script>
</body>
</html>
Speichern Sie dies als ocr-worker.js. Die Seite verwaltet diesen äußeren Worker
und kann ihn auch dann beenden, wenn Tesseract die Initialisierung nie abschließt. Das ist
wichtig, weil ein fehlgeschlagener Sprachdownload dazu führen kann, dass
createWorker() in Version 7.0.0 ausstehend bleibt.
Sein errorHandler meldet Fehler direkt an die Seite; das Zeitlimit von
90 Sekunden deckt auch einen hängenden Download ab. Das Zeitlimit ist eine Vorgabe dieser Demo,
keine erwartete Erkennungsdauer.
importScripts('https://cdn.jsdelivr.net/npm/tesseract.js@7.0.0/dist/tesseract.min.js')
async function performOCR(file) {
const worker = await Tesseract.createWorker('eng', 1, {
workerPath: 'https://cdn.jsdelivr.net/npm/tesseract.js@7.0.0/dist/worker.min.js',
corePath: 'https://cdn.jsdelivr.net/npm/tesseract.js-core@7.0.0',
langPath: 'https://cdn.jsdelivr.net/npm/@tesseract.js-data/eng@1.0.0/4.0.0_best_int',
logger: ({ status, progress }) => postMessage({ type: 'progress', status, progress }),
errorHandler: () => postMessage({ type: 'error' }),
})
try {
const { data: { text } } = await worker.recognize(file)
return text
} finally {
await worker.terminate()
}
}
self.onmessage = async ({ data: file }) => {
try {
const text = await performOCR(file)
postMessage({ type: 'result', text })
} catch {
postMessage({ type: 'error' })
}
}
Starten Sie im übergeordneten Verzeichnis von tesseract-browser einen lokalen Server
im Terminal:
(cd tesseract-browser && python3 -m http.server --bind 127.0.0.1 0)
Port 0 fordert beim Betriebssystem einen verfügbaren Port an. Öffnen
Sie die vom Server ausgegebene Adresse http://127.0.0.1:PORT/. Verwenden Sie HTTP, statt
die HTML-Datei per Doppelklick zu öffnen, da das Laden des Workers vom Ursprung der Seite abhängt.
Beenden Sie den Server anschließend mit Ctrl+C. Ein erneuter Start stellt dieselben Dateien
bereit, ohne sie zu überschreiben.
Wählen Sie einen kleinen Screenshot mit dem Text „BROWSER OCR TEST“ und klicken Sie auf „Recognize text“. Sie sollten eine Ladeanzeige, den Erkennungsfortschritt und die extrahierten Wörter unter „Recognized text“ sehen. Bei einem leeren Bild sollte stattdessen „No text found.“ erscheinen. Die Originaldatei wird nie verändert, und die Seite speichert weder Bilder noch erkannten Text ab.
Fehlerbehandlung und Validierung
Das Limit von fünf MiB ist die Eingabevorgabe dieser Demo, nicht das Maximum von Tesseract. Die
komprimierte Dateigröße begrenzt nicht den Speicherbedarf der decodierten Pixel. Beginnen Sie auf
Mobilgeräten daher mit kleinen Bildern. Die MIME-Zulassungsliste hilft, eine falsche Auswahl zu
erkennen; eine beschädigte Datei mit der Kennzeichnung image/png muss dennoch
während der Erkennung fehlschlagen. Weder ein Bild-MIME-Typ noch das
Attribut accept
der Dateiauswahl belegt einen gültigen Inhalt.
Wenn die OCR fehlschlägt, prüfen Sie das Bild und im Netzwerk-Bereich des Browsers, ob Anfragen für Skripte, WASM oder Sprachdaten fehlgeschlagen sind. Klicken Sie dann erneut auf „Recognize text“. Sie können es mit derselben Datei erneut versuchen. Ein leeres Ergebnis ist kein Worker-Fehler und beweist nicht, dass das Quellbild keinen Text enthält. Geringer Kontrast, winzige Buchstaben und die falsche Erkennungssprache können ebenfalls zu leeren oder ungenauen Ergebnissen führen.
Mehrere Sprachen verarbeiten
Fügen Sie für gemischten englischen und deutschen Text diese Funktion zu
ocr-worker.js hinzu und ersetzen Sie im Handler den Aufruf
performOCR(file) durch performMultilingualOCR(file). Das Sprachen-Array wählt
Modelle aus; es übersetzt deren Ausgabe nicht. Diese Variante nutzt die standardmäßigen
Sprach-URLs von Tesseract, damit jede Sprache ihre eigenen Daten laden kann, statt des
festgelegten Pfads im Hauptbeispiel, der nur Englisch abdeckt.
async function performMultilingualOCR(file, languages = ['eng', 'deu']) {
const worker = await Tesseract.createWorker(languages, 1, {
workerPath: 'https://cdn.jsdelivr.net/npm/tesseract.js@7.0.0/dist/worker.min.js',
corePath: 'https://cdn.jsdelivr.net/npm/tesseract.js-core@7.0.0',
logger: ({ status, progress }) => postMessage({ type: 'progress', status, progress }),
errorHandler: () => postMessage({ type: 'error' }),
})
try {
const { data: { text } } = await worker.recognize(file)
return text
} finally {
await worker.terminate()
}
}
Leistung optimieren
Bildvorverarbeitung
Vergleichen Sie zunächst die Ergebnisse anhand Ihrer tatsächlichen Bilder. Überflüssige Ränder zuzuschneiden oder die Drehung zu korrigieren kann helfen. Kleinere Buchstaben oder ein höherer Kontrast können jedoch nützliche Details entfernen. Die optionale Hilfsfunktion unten begrenzt die Breite auf 1.000 Pixel, um Speicher zu sparen, nicht als Empfehlung für höhere Genauigkeit.
Fügen Sie sie in das Skript index.html ein und ersetzen Sie
return recognizeImage(file) in validateAndPerformOCR durch
return optimizedOCR(file). Die Validierung muss weiterhin vor der Vorverarbeitung erfolgen.
async function preprocessImage(file) {
const url = URL.createObjectURL(file)
try {
const img = new Image()
await new Promise((resolve, reject) => {
img.onload = resolve
img.onerror = () => reject(new Error('Unable to decode image.'))
img.src = url
})
const canvas = document.createElement('canvas')
const maxWidth = 1000
const scale = img.width > maxWidth ? maxWidth / img.width : 1
canvas.width = Math.max(1, Math.round(img.width * scale))
canvas.height = Math.max(1, Math.round(img.height * scale))
const ctx = canvas.getContext('2d')
if (!ctx) throw new Error('Canvas processing is unavailable.')
ctx.filter = 'grayscale(100%) contrast(150%)'
ctx.drawImage(img, 0, 0, canvas.width, canvas.height)
return await new Promise((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob) resolve(blob)
else reject(new Error('Unable to encode processed image.'))
}, 'image/png')
})
} finally {
URL.revokeObjectURL(url)
}
}
async function optimizedOCR(file) {
const processedImage = await preprocessImage(file)
return recognizeImage(processedImage)
}
Speicherverwaltung
Die Einzelbild-Demo erstellt für jeden Versuch einen neuen Worker, um dessen Lebenszyklus
einfach zu halten. Verwenden Sie für die Stapelverarbeitung einen initialisierten Tesseract-Worker
wieder und erkennen Sie die Bilder nacheinander. Das erhält die Eingabereihenfolge und vermeidet,
für jedes Bild eine separate OCR-Engine zu laden. Die Funktion weist ihr Promise beim ersten
fehlgeschlagenen Bild zurück und beendet ihren initialisierten Worker in
finally.
Fügen Sie dies zu ocr-worker.js hinzu. Für einen minimalen Versuch mit zwei
Durchläufen auf der aktuellen Seite ersetzen Sie im Handler const text = await performOCR(file) durch
const text = (await batchProcessImages([file, file])).join('\n'). Die Ausgabe enthält zwei Kopien in der vorgegebenen Reihenfolge.
Übergeben Sie in einer Anwendung stattdessen Ihr geordnetes Array von Bilddateien. Das äußere
Zeitlimit gilt weiterhin für den gesamten Auftrag. Wählen Sie daher ein geeignetes Limit für die
Stapelverarbeitung, bevor Sie die Benutzeroberfläche erweitern.
async function batchProcessImages(files) {
const worker = await Tesseract.createWorker('eng', 1, {
workerPath: 'https://cdn.jsdelivr.net/npm/tesseract.js@7.0.0/dist/worker.min.js',
corePath: 'https://cdn.jsdelivr.net/npm/tesseract.js-core@7.0.0',
langPath: 'https://cdn.jsdelivr.net/npm/@tesseract.js-data/eng@1.0.0/4.0.0_best_int',
logger: ({ status, progress }) => postMessage({ type: 'progress', status, progress }),
errorHandler: () => postMessage({ type: 'error' }),
})
const results = []
try {
for (const file of files) {
const { data: { text } } = await worker.recognize(file)
results.push(text)
}
return results
} finally {
await worker.terminate()
}
}
Sicherheitsaspekte und bewährte Verfahren
Behandeln Sie erkannten Text weiterhin als Text. Dieses Beispiel weist ihn
value eines schreibgeschützten Textfelds zu, sodass erkanntes HTML nicht
ausgeführt werden kann. Wenn Sie das Ergebnis in eine andere Ansicht übernehmen, fügen Sie es
nicht mit innerHTML ein.
Lokale Erkennung beschreibt, wo dieser Code das Bild verarbeitet. Sie ist keine pauschale
Datenschutzgarantie für jede Seite, die ihn einbettet. Skripte auf einer Seite können auf
ausgewählte Dateien zugreifen. Prüfen Sie diese Abhängigkeiten und die anderen Skripte Ihrer
Website, bevor Sie sensible Dokumente verarbeiten. Wenn Sie die OCR-Ressourcen selbst hosten,
liefern Sie WASM mit application/wasm aus und behalten Sie das vollständige passende
Core-Paket bei, damit Tesseract einen vom Gerät unterstützten Build auswählen kann.
