Anpassungen und Reduzierung auf OCR

This commit is contained in:
Andreas Knuth 2025-01-17 23:11:10 +00:00
parent f3cd175ae6
commit a569b9a1ab
7 changed files with 289 additions and 511 deletions

6
app_factory.py Normal file
View File

@ -0,0 +1,6 @@
from flask import Flask
# Erstelle die Flask-App
app = Flask(__name__)
# Konfigurationen und Erweiterungen hier hinzufügen

View File

@ -1,10 +1,13 @@
# deck_endpoints.py
from app_factory import app
from flask import Blueprint, request, jsonify
import sqlite3
import os
import shutil
import logging
from sqlalchemy import create_engine
from sqlalchemy.orm import scoped_session, sessionmaker
from sqlalchemy.sql import text
deck_bp = Blueprint('deck_bp', __name__)
@ -195,6 +198,43 @@ def get_decks():
deck_list = list(decks.values())
return jsonify(deck_list)
@deck_bp.route('/api/decks/<deckname>', methods=['GET'])
def get_deck(deckname):
conn = get_db_connection()
cursor = conn.cursor()
# Einträge für das spezifische Deck abrufen
entries = cursor.execute('SELECT * FROM Deck WHERE deckname = ?', (deckname,)).fetchall()
conn.close()
if not entries:
return jsonify({'error': 'Deck not found'}), 404
deck = {
'name': deckname,
'images': []
}
for entry in entries:
if entry['bildname'] and entry['bildid']:
image = {
'name': entry['bildname'],
'id': entry['bildid'],
'iconindex': entry['iconindex'],
'boxid': entry['id'],
'x1': entry['x1'],
'x2': entry['x2'],
'y1': entry['y1'],
'y2': entry['y2'],
'due': entry['due'],
'ivl': entry['ivl'],
'factor': entry['factor'],
'reps': entry['reps'],
'lapses': entry['lapses'],
'isGraduated': bool(entry['isGraduated'])
}
deck['images'].append(image)
return jsonify(deck)
@deck_bp.route('/api/decks/<deckname>', methods=['DELETE'])
def delete_deck(deckname):
conn = get_db_connection()

View File

@ -1,17 +1,98 @@
from flask import Flask, request, jsonify
from flask import Flask, request, jsonify, send_file
from paddleocr import PaddleOCR
import base64
from PIL import Image
from io import BytesIO
import traceback
import numpy as np # Importieren von numpy
import numpy as np
import cv2
import logging
import os
import uuid
import datetime
from app_factory import app
# from deck_endpoints import deck_bp # Importieren des Blueprints
app = Flask(__name__)
ocr = PaddleOCR(use_angle_cls=True, lang='en') # Passen Sie die Sprache nach Bedarf an
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
@app.route('/ocr', methods=['POST'])
# app = Flask(__name__)
# app.register_blueprint(deck_bp) # Registrieren des Blueprints
def get_dir_name():
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
unique_id = str(uuid.uuid4())[:8]
return f"{timestamp}_{unique_id}"
def create_debug_directory(dir_name):
"""Erstellt ein eindeutiges Verzeichnis für Debug-Bilder"""
base_dir = 'debug_images'
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
unique_id = str(uuid.uuid4())[:8]
full_path = os.path.join(base_dir, dir_name)
# Erstelle Hauptverzeichnis falls nicht vorhanden
if not os.path.exists(base_dir):
os.makedirs(base_dir)
# Erstelle spezifisches Verzeichnis für diesen Durchlauf
os.makedirs(full_path)
return full_path
def preprocess_image(image, debug_dir):
"""
Verarbeitet das Bild und speichert Zwischenergebnisse im angegebenen Verzeichnis,
einschließlich einer komprimierten JPG-Version und eines Thumbnails.
"""
try:
# Umwandlung in Graustufen
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# Anwendung von CLAHE zur Kontrastverbesserung
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
enhanced = clahe.apply(gray)
# Rauschunterdrückung
denoised = cv2.fastNlMeansDenoising(enhanced)
# Optional: Binärschwellenwert (auskommentiert)
# _, binary = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Speichern der Zwischenergebnisse im spezifischen Verzeichnis
cv2.imwrite(os.path.join(debug_dir, 'gray.png'), gray)
cv2.imwrite(os.path.join(debug_dir, 'enhanced.png'), enhanced)
cv2.imwrite(os.path.join(debug_dir, 'denoised.png'), denoised)
# cv2.imwrite(os.path.join(debug_dir, 'binary.png'), binary)
# Speichern der komprimierten JPG-Version des Originalbildes
compressed_jpg_path = os.path.join(debug_dir, 'original_compressed.jpg')
original_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
cv2.imwrite(compressed_jpg_path, original_bgr, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) # Qualität auf 80 setzen
logger.info(f"Komprimiertes Original JPG gespeichert: {compressed_jpg_path}")
# Erstellen und Speichern des Thumbnails
thumbnail_path = os.path.join(debug_dir, 'thumbnail.jpg')
image_pil = Image.fromarray(denoised)
image_pil.thumbnail((128, 128)) # Thumbnail-Größe auf 128x128 Pixel setzen
image_pil.save(thumbnail_path, 'JPEG')
logger.info(f"Thumbnail gespeichert: {thumbnail_path}")
logger.info(f"Debug images saved in: {debug_dir}")
return denoised
except Exception as e:
logger.error(f"Preprocessing error: {str(e)}")
raise
@app.route('/api/ocr', methods=['POST'])
def ocr_endpoint():
try:
# Erstelle eindeutiges Debug-Verzeichnis für diesen Request
dir_name = get_dir_name()
debug_dir = create_debug_directory(dir_name)
logger.info(f"Created debug directory: {debug_dir}")
if not request.is_json:
return jsonify({'error': 'Content-Type must be application/json'}), 400
@ -20,41 +101,167 @@ def ocr_endpoint():
return jsonify({'error': 'No image provided'}), 400
image_b64 = data['image']
if not image_b64:
return jsonify({'error': 'Empty image data'}), 400
# Base64 Dekodierung
try:
image_data = base64.b64decode(image_b64)
except Exception as decode_err:
return jsonify({'error': 'Base64 decode error', 'details': str(decode_err)}), 400
logger.error(f"Base64 decode error: {str(decode_err)}")
return jsonify({'error': 'Base64 decode error'}), 400
# Bildverarbeitung
try:
image = Image.open(BytesIO(image_data)).convert('RGB')
image = np.array(image) # Konvertieren zu numpy.ndarray
image = np.array(image)
logger.info(f"Image loaded successfully. Shape: {image.shape}")
# Originalbild speichern
cv2.imwrite(os.path.join(debug_dir, 'original.png'),
cv2.cvtColor(image, cv2.COLOR_RGB2BGR))
except Exception as img_err:
return jsonify({'error': 'Invalid image data', 'details': str(img_err)}), 400
# Optional: Bildgröße anpassen, falls erforderlich
# PaddleOCR kann große Bilder verarbeiten, aber zur Effizienz können Sie eine maximale Größe setzen
max_width = 1920
max_height = 1080
height, width, _ = image.shape
if width > max_width or height > max_height:
aspect_ratio = width / height
if aspect_ratio > 1:
new_width = max_width
new_height = int(max_width / aspect_ratio)
else:
new_height = max_height
new_width = int(max_height * aspect_ratio)
image = np.array(Image.fromarray(image).resize((new_width, new_height)))
result = ocr.ocr(image, rec=True, cls=True)
return jsonify(result)
logger.error(f"Image processing error: {str(img_err)}")
return jsonify({'error': 'Invalid image data'}), 400
# Bildvorverarbeitung
processed_image = preprocess_image(image, debug_dir)
logger.info("Preprocessing completed")
# PaddleOCR Konfiguration
ocr = PaddleOCR(
use_angle_cls=True,
lang='en',
det_db_thresh=0.3,
det_db_box_thresh=0.3,
det_db_unclip_ratio=2.0,
rec_char_type='en',
det_limit_side_len=960,
det_limit_type='max',
use_dilation=True,
det_db_score_mode='fast',
show_log=True
)
# OCR durchführen
try:
result = ocr.ocr(processed_image, rec=True, cls=True)
# Debug-Informationen in Datei speichern
with open(os.path.join(debug_dir, 'ocr_results.txt'), 'w') as f:
f.write(f"Raw OCR result:\n{result}\n\n")
if not result:
logger.warning("No results returned from OCR")
return jsonify({
'warning': 'No text detected',
'debug_dir': debug_dir
}), 200
if not result[0]:
logger.warning("Empty results list from OCR")
return jsonify({
'warning': 'Empty results list',
'debug_dir': debug_dir
}), 200
# Ergebnisse verarbeiten
extracted_results = []
for idx, item in enumerate(result[0]):
try:
box = item[0]
text = item[1][0] if item[1] else ''
confidence = float(item[1][1]) if item[1] and len(item[1]) > 1 else 0.0
extracted_results.append({
'box': box,
'text': text,
'confidence': confidence,
'name': dir_name
})
except Exception as proc_err:
logger.error(f"Error processing result {idx}: {str(proc_err)}")
# Statistiken in Debug-Datei speichern
with open(os.path.join(debug_dir, 'statistics.txt'), 'w') as f:
f.write(f"Total results: {len(extracted_results)}\n")
if extracted_results:
avg_confidence = np.mean([r['confidence'] for r in extracted_results])
f.write(f"Average confidence: {avg_confidence}\n")
f.write("\nDetailed results:\n")
for idx, result in enumerate(extracted_results):
f.write(f"Result {idx+1}:\n")
f.write(f"Text: {result['text']}\n")
f.write(f"Confidence: {result['confidence']}\n")
f.write(f"Name: {dir_name}\n")
f.write(f"Box coordinates: {result['box']}\n\n")
return jsonify({
'status': 'success',
'results': extracted_results,
})
except Exception as ocr_err:
logger.error(f"OCR processing error: {str(ocr_err)}")
logger.error(traceback.format_exc())
return jsonify({
'error': 'OCR processing failed',
'details': str(ocr_err),
'debug_dir': debug_dir
}), 500
except Exception as e:
traceback.print_exc()
return jsonify({'error': str(e)}), 500
logger.error(f"Unexpected error: {str(e)}")
logger.error(traceback.format_exc())
return jsonify({
'error': 'Internal server error',
'debug_dir': debug_dir if 'debug_dir' in locals() else None
}), 500
@app.route('/api/debug_image/<name>/<filename>', methods=['GET'])
def get_debug_image(name, filename):
"""
Gibt das angeforderte Bild unter 'debug_images/[name]/[filename]' direkt zurück.
"""
try:
# Sicherheitsmaßnahme: Nur erlaubte Zeichen im Verzeichnisnamen
if not all(c.isalnum() or c in ('_', '-') for c in name):
logger.warning(f"Ungültiger Verzeichnisname angefordert: {name}")
return jsonify({'error': 'Invalid directory name'}), 400
# Sicherheitsmaßnahme: Nur erlaubte Zeichen im Dateinamen
if not all(c.isalnum() or c in ('_', '-', '.',) for c in filename):
logger.warning(f"Ungültiger Dateiname angefordert: {filename}")
return jsonify({'error': 'Invalid file name'}), 400
# Vollständigen Pfad zum Bild erstellen
image_path = os.path.join('debug_images', name, filename)
# Überprüfen, ob die Datei existiert
if not os.path.isfile(image_path):
logger.warning(f"Bild nicht gefunden: {image_path}")
return jsonify({'error': 'Image not found'}), 404
# Bestimmen des MIME-Typs basierend auf der Dateiendung
mime_type = 'image/png' # Standard-MIME-Typ
if filename.lower().endswith('.jpg') or filename.lower().endswith('.jpeg'):
mime_type = 'image/jpeg'
elif filename.lower().endswith('.gif'):
mime_type = 'image/gif'
elif filename.lower().endswith('.bmp'):
mime_type = 'image/bmp'
elif filename.lower().endswith('.tiff') or filename.lower().endswith('.tif'):
mime_type = 'image/tiff'
return send_file(
image_path,
mimetype=mime_type,
as_attachment=False
)
except Exception as e:
logger.error(f"Fehler beim Abrufen des Bildes '{name}/{filename}': {str(e)}")
logger.error(traceback.format_exc())
return jsonify({'error': 'Failed to retrieve image'}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)
app.run(host='0.0.0.0', port=5000, debug=True)

View File

@ -1,90 +0,0 @@
from flask import Flask, request, jsonify
from paddleocr import PaddleOCR
import base64
from PIL import Image
from io import BytesIO
import traceback
import numpy as np
import cv2 # Import von OpenCV
app = Flask(__name__)
def preprocess_image(image):
# Konvertierung zu Graustufen
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# Kontrastverstärkung
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
enhanced = clahe.apply(gray)
# Rauschreduzierung
denoised = cv2.fastNlMeansDenoising(enhanced)
# Binarisierung
_, binary = cv2.threshold(denoised, 127, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
return binary
@app.route('/ocr', methods=['POST'])
def ocr_endpoint():
try:
if not request.is_json:
return jsonify({'error': 'Content-Type must be application/json'}), 400
data = request.get_json()
if not data or 'image' not in data:
return jsonify({'error': 'No image provided'}), 400
image_b64 = data['image']
if not image_b64:
return jsonify({'error': 'Empty image data'}), 400
try:
image_data = base64.b64decode(image_b64)
except Exception as decode_err:
return jsonify({'error': 'Base64 decode error', 'details': str(decode_err)}), 400
try:
image = Image.open(BytesIO(image_data)).convert('RGB')
image = preprocess_image(image)
image = np.array(image) # Konvertieren zu numpy.ndarray
except Exception as img_err:
return jsonify({'error': 'Invalid image data', 'details': str(img_err)}), 400
# Optional: Bildgröße anpassen, falls erforderlich
max_width = 1920
max_height = 1080
height, width, _ = image.shape
if width > max_width or height > max_height:
aspect_ratio = width / height
if aspect_ratio > 1:
new_width = max_width
new_height = int(max_width / aspect_ratio)
else:
new_height = max_height
new_width = int(max_height * aspect_ratio)
image = np.array(Image.fromarray(image).resize((new_width, new_height)))
# Initialisieren Sie PaddleOCR innerhalb des Handlers
ocr = PaddleOCR(use_angle_cls=True, lang='en') # Initialisierung innerhalb des Handlers
result = ocr.ocr(image, rec=True, cls=True)
# Extrahieren der Texte und Konfidenzwerte
extracted_results = []
for item in result[0]:
box = item[0] # Die Koordinaten der Textbox
text = item[1][0] # Der erkannte Text
confidence = item[1][1] # Der Konfidenzwert
extracted_results.append({
'box': box,
'text': text,
'confidence': confidence
})
return jsonify(extracted_results)
except Exception as e:
traceback.print_exc()
return jsonify({'error': str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)

View File

@ -1,114 +0,0 @@
from flask import Flask, request, jsonify
from paddleocr import PaddleOCR
import base64
from PIL import Image
from io import BytesIO
import traceback
import numpy as np
import cv2 # Import von OpenCV
import os # Import für das Speichern von Dateien
import time # Import für Zeitstempel
app = Flask(__name__)
# Initialisiere PaddleOCR einmal außerhalb der Anfrage, um die Leistung zu verbessern
ocr = PaddleOCR(use_angle_cls=True, lang='en') # Initialisierung außerhalb des Handlers
@app.route('/ocr', methods=['POST'])
def ocr_endpoint():
try:
if not request.is_json:
return jsonify({'error': 'Content-Type must be application/json'}), 400
data = request.get_json()
if not data or 'image' not in data:
return jsonify({'error': 'No image provided'}), 400
image_b64 = data['image']
if not image_b64:
return jsonify({'error': 'Empty image data'}), 400
try:
image_data = base64.b64decode(image_b64)
except Exception as decode_err:
return jsonify({'error': 'Base64 decode error', 'details': str(decode_err)}), 400
try:
image = Image.open(BytesIO(image_data)).convert('RGB')
image_np = np.array(image) # Konvertieren zu numpy.ndarray
except Exception as img_err:
return jsonify({'error': 'Invalid image data'}), 400
# Vorverarbeitung: Behalte nur dunkle (schwarze) Bereiche des Bildes
# Konvertiere das Bild zu Graustufen
gray = cv2.cvtColor(image_np, cv2.COLOR_RGB2GRAY)
# Wende einen Schwellenwert an, um nur die dunklen Bereiche zu behalten
threshold_value = 150 # Passe diesen Wert nach Bedarf an
_, mask = cv2.threshold(gray, threshold_value, 255, cv2.THRESH_BINARY_INV)
# Optional: Morphologische Operationen zur Verbesserung der Maske
kernel = np.ones((3,3), np.uint8)
mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel, iterations=1)
mask = cv2.morphologyEx(mask, cv2.MORPH_DILATE, kernel, iterations=1)
# Wende die Maske auf das Originalbild an
filtered_image_np = cv2.bitwise_and(image_np, image_np, mask=mask)
# Konvertiere das gefilterte Bild zurück zu PIL Image
filtered_image = Image.fromarray(filtered_image_np)
# Optional: Bildgröße anpassen, falls erforderlich
max_width = 1920
max_height = 1080
height, width, _ = filtered_image_np.shape
if width > max_width or height > max_height:
aspect_ratio = width / height
if aspect_ratio > 1:
new_width = max_width
new_height = int(max_width / aspect_ratio)
else:
new_height = max_height
new_width = int(max_height * aspect_ratio)
filtered_image = filtered_image.resize((new_width, new_height))
filtered_image_np = np.array(filtered_image)
# **Speichern des vorverarbeiteten Bildes zur Überprüfung**
output_dir = 'processed_images'
if not os.path.exists(output_dir):
os.makedirs(output_dir)
# Generiere einen einzigartigen Dateinamen basierend auf dem aktuellen Zeitstempel
timestamp = int(time.time() * 1000)
processed_image_path = os.path.join(output_dir, f'processed_{timestamp}.png')
filtered_image.save(processed_image_path)
print(f'Processed image saved at: {processed_image_path}')
# **Speichern der Maske zur Überprüfung**
mask_image = Image.fromarray(mask)
mask_image_path = os.path.join(output_dir, f'mask_{timestamp}.png')
mask_image.save(mask_image_path)
print(f'Mask image saved at: {mask_image_path}')
# Führe OCR auf dem gefilterten Bild durch
result = ocr.ocr(filtered_image_np, rec=True, cls=True)
# Extrahieren der Texte und Konfidenzwerte
extracted_results = []
for item in result:
box = item[0] # Die Koordinaten der Textbox
text = item[1][0] # Der erkannte Text
confidence = item[1][1] # Der Konfidenzwert
extracted_results.append({
'box': box,
'text': text,
'confidence': confidence
})
return jsonify(extracted_results)
except Exception as e:
traceback.print_exc()
return jsonify({'error': str(e)}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True, threaded=False) # Single-Threaded

View File

@ -1,272 +0,0 @@
from flask import Flask, request, jsonify, send_file
from paddleocr import PaddleOCR
import base64
from PIL import Image
from io import BytesIO
import traceback
import numpy as np
import cv2
import logging
import os
import uuid
import datetime
from deck_endpoints import deck_bp # Importieren des Blueprints
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s'
)
logger = logging.getLogger(__name__)
app = Flask(__name__)
app.register_blueprint(deck_bp) # Registrieren des Blueprints
def get_dir_name():
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
unique_id = str(uuid.uuid4())[:8]
return f"{timestamp}_{unique_id}"
def create_debug_directory(dir_name):
"""Erstellt ein eindeutiges Verzeichnis für Debug-Bilder"""
base_dir = 'debug_images'
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
unique_id = str(uuid.uuid4())[:8]
full_path = os.path.join(base_dir, dir_name)
# Erstelle Hauptverzeichnis falls nicht vorhanden
if not os.path.exists(base_dir):
os.makedirs(base_dir)
# Erstelle spezifisches Verzeichnis für diesen Durchlauf
os.makedirs(full_path)
return full_path
def preprocess_image(image, debug_dir):
"""
Verarbeitet das Bild und speichert Zwischenergebnisse im angegebenen Verzeichnis,
einschließlich einer komprimierten JPG-Version und eines Thumbnails.
"""
try:
# Umwandlung in Graustufen
gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)
# Anwendung von CLAHE zur Kontrastverbesserung
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
enhanced = clahe.apply(gray)
# Rauschunterdrückung
denoised = cv2.fastNlMeansDenoising(enhanced)
# Optional: Binärschwellenwert (auskommentiert)
# _, binary = cv2.threshold(denoised, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
# Speichern der Zwischenergebnisse im spezifischen Verzeichnis
cv2.imwrite(os.path.join(debug_dir, 'gray.png'), gray)
cv2.imwrite(os.path.join(debug_dir, 'enhanced.png'), enhanced)
cv2.imwrite(os.path.join(debug_dir, 'denoised.png'), denoised)
# cv2.imwrite(os.path.join(debug_dir, 'binary.png'), binary)
# Speichern der komprimierten JPG-Version des Originalbildes
compressed_jpg_path = os.path.join(debug_dir, 'original_compressed.jpg')
original_bgr = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
cv2.imwrite(compressed_jpg_path, original_bgr, [int(cv2.IMWRITE_JPEG_QUALITY), 80]) # Qualität auf 80 setzen
logger.info(f"Komprimiertes Original JPG gespeichert: {compressed_jpg_path}")
# Erstellen und Speichern des Thumbnails
thumbnail_path = os.path.join(debug_dir, 'thumbnail.jpg')
image_pil = Image.fromarray(denoised)
image_pil.thumbnail((128, 128)) # Thumbnail-Größe auf 128x128 Pixel setzen
image_pil.save(thumbnail_path, 'JPEG')
logger.info(f"Thumbnail gespeichert: {thumbnail_path}")
logger.info(f"Debug images saved in: {debug_dir}")
return denoised
except Exception as e:
logger.error(f"Preprocessing error: {str(e)}")
raise
@app.route('/api/ocr', methods=['POST'])
def ocr_endpoint():
try:
# Erstelle eindeutiges Debug-Verzeichnis für diesen Request
dir_name = get_dir_name()
debug_dir = create_debug_directory(dir_name)
logger.info(f"Created debug directory: {debug_dir}")
if not request.is_json:
return jsonify({'error': 'Content-Type must be application/json'}), 400
data = request.get_json()
if not data or 'image' not in data:
return jsonify({'error': 'No image provided'}), 400
image_b64 = data['image']
# Base64 Dekodierung
try:
image_data = base64.b64decode(image_b64)
except Exception as decode_err:
logger.error(f"Base64 decode error: {str(decode_err)}")
return jsonify({'error': 'Base64 decode error'}), 400
# Bildverarbeitung
try:
image = Image.open(BytesIO(image_data)).convert('RGB')
image = np.array(image)
logger.info(f"Image loaded successfully. Shape: {image.shape}")
# Originalbild speichern
cv2.imwrite(os.path.join(debug_dir, 'original.png'),
cv2.cvtColor(image, cv2.COLOR_RGB2BGR))
except Exception as img_err:
logger.error(f"Image processing error: {str(img_err)}")
return jsonify({'error': 'Invalid image data'}), 400
# Bildvorverarbeitung
processed_image = preprocess_image(image, debug_dir)
logger.info("Preprocessing completed")
# PaddleOCR Konfiguration
ocr = PaddleOCR(
use_angle_cls=True,
lang='en',
det_db_thresh=0.3,
det_db_box_thresh=0.3,
det_db_unclip_ratio=2.0,
rec_char_type='en',
det_limit_side_len=960,
det_limit_type='max',
use_dilation=True,
det_db_score_mode='fast',
show_log=True
)
# OCR durchführen
try:
result = ocr.ocr(processed_image, rec=True, cls=True)
# Debug-Informationen in Datei speichern
with open(os.path.join(debug_dir, 'ocr_results.txt'), 'w') as f:
f.write(f"Raw OCR result:\n{result}\n\n")
if not result:
logger.warning("No results returned from OCR")
return jsonify({
'warning': 'No text detected',
'debug_dir': debug_dir
}), 200
if not result[0]:
logger.warning("Empty results list from OCR")
return jsonify({
'warning': 'Empty results list',
'debug_dir': debug_dir
}), 200
# Ergebnisse verarbeiten
extracted_results = []
for idx, item in enumerate(result[0]):
try:
box = item[0]
text = item[1][0] if item[1] else ''
confidence = float(item[1][1]) if item[1] and len(item[1]) > 1 else 0.0
extracted_results.append({
'box': box,
'text': text,
'confidence': confidence,
'name': dir_name
})
except Exception as proc_err:
logger.error(f"Error processing result {idx}: {str(proc_err)}")
# Statistiken in Debug-Datei speichern
with open(os.path.join(debug_dir, 'statistics.txt'), 'w') as f:
f.write(f"Total results: {len(extracted_results)}\n")
if extracted_results:
avg_confidence = np.mean([r['confidence'] for r in extracted_results])
f.write(f"Average confidence: {avg_confidence}\n")
f.write("\nDetailed results:\n")
for idx, result in enumerate(extracted_results):
f.write(f"Result {idx+1}:\n")
f.write(f"Text: {result['text']}\n")
f.write(f"Confidence: {result['confidence']}\n")
f.write(f"Name: {dir_name}\n")
f.write(f"Box coordinates: {result['box']}\n\n")
return jsonify({
'status': 'success',
'results': extracted_results,
# 'debug_info': {
# 'total_boxes_detected': len(result[0]) if result and result[0] else 0,
# 'processed_results': len(extracted_results),
# 'debug_dir': debug_dir
# }
})
except Exception as ocr_err:
logger.error(f"OCR processing error: {str(ocr_err)}")
logger.error(traceback.format_exc())
return jsonify({
'error': 'OCR processing failed',
'details': str(ocr_err),
'debug_dir': debug_dir
}), 500
except Exception as e:
logger.error(f"Unexpected error: {str(e)}")
logger.error(traceback.format_exc())
return jsonify({
'error': 'Internal server error',
'debug_dir': debug_dir if 'debug_dir' in locals() else None
}), 500
@app.route('/api/debug_image/<name>/<filename>', methods=['GET'])
def get_debug_image(name, filename):
"""
Gibt das angeforderte Bild unter 'debug_images/[name]/[filename]' direkt zurück.
"""
try:
# Sicherheitsmaßnahme: Nur erlaubte Zeichen im Verzeichnisnamen
if not all(c.isalnum() or c in ('_', '-') for c in name):
logger.warning(f"Ungültiger Verzeichnisname angefordert: {name}")
return jsonify({'error': 'Invalid directory name'}), 400
# Sicherheitsmaßnahme: Nur erlaubte Zeichen im Dateinamen
if not all(c.isalnum() or c in ('_', '-', '.',) for c in filename):
logger.warning(f"Ungültiger Dateiname angefordert: {filename}")
return jsonify({'error': 'Invalid file name'}), 400
# Vollständigen Pfad zum Bild erstellen
image_path = os.path.join('debug_images', name, filename)
# Überprüfen, ob die Datei existiert
if not os.path.isfile(image_path):
logger.warning(f"Bild nicht gefunden: {image_path}")
return jsonify({'error': 'Image not found'}), 404
# Bestimmen des MIME-Typs basierend auf der Dateiendung
mime_type = 'image/png' # Standard-MIME-Typ
if filename.lower().endswith('.jpg') or filename.lower().endswith('.jpeg'):
mime_type = 'image/jpeg'
elif filename.lower().endswith('.gif'):
mime_type = 'image/gif'
elif filename.lower().endswith('.bmp'):
mime_type = 'image/bmp'
elif filename.lower().endswith('.tiff') or filename.lower().endswith('.tif'):
mime_type = 'image/tiff'
return send_file(
image_path,
mimetype=mime_type,
as_attachment=False
)
except Exception as e:
logger.error(f"Fehler beim Abrufen des Bildes '{name}/{filename}': {str(e)}")
logger.error(traceback.format_exc())
return jsonify({'error': 'Failed to retrieve image'}), 500
if __name__ == '__main__':
app.run(host='0.0.0.0', port=5000, debug=True)

View File

@ -4,4 +4,5 @@ pillow>=10.0.0
numpy>=1.24.4,<2.0.0
opencv-python==4.6.0.66
paddlepaddle==2.6.2
werkzeug<2.3
werkzeug<2.3
SQLAlchemy==2.0.20