| """
|
| Handwriting Transcriber Module
|
| Wrapper for handwritten-math-transcription repository
|
| """
|
|
|
| import sys
|
| import os
|
| import torch
|
| from typing import Optional, Tuple
|
|
|
|
|
| sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'handwritten-math-transcription'))
|
|
|
| try:
|
| from model import Encoder, Decoder, Seq2Seq
|
| from dataset.hme_ink import read_inkml_file
|
| from utils import tokenize_latex
|
| from corrector import correct_latex
|
| from config import *
|
| except ImportError:
|
| Encoder = None
|
| Decoder = None
|
| Seq2Seq = None
|
| read_inkml_file = None
|
| tokenize_latex = None
|
| correct_latex = None
|
|
|
|
|
| class HandwritingTranscriber:
|
| """
|
| Handwriting transcriber for mathematical expressions.
|
| Converts handwritten math (InkML format) to LaTeX.
|
| """
|
|
|
| def __init__(self,
|
| model_path: str = None,
|
| device: str = None,
|
| use_corrector: bool = True):
|
| """
|
| Initialize handwriting transcriber.
|
|
|
| Args:
|
| model_path: Path to trained model checkpoint
|
| device: Device to run model on ('cpu', 'cuda', 'mps')
|
| use_corrector: Whether to use LLM corrector for post-processing
|
| """
|
| self.device = device or ('cuda' if torch.cuda.is_available() else 'cpu')
|
| self.use_corrector = use_corrector
|
| self.model = None
|
| self.model_path = model_path
|
|
|
| if model_path and os.path.exists(model_path):
|
| self.load_model(model_path)
|
|
|
| def load_model(self, model_path: str):
|
| """
|
| Load trained model from checkpoint.
|
|
|
| Args:
|
| model_path: Path to model checkpoint
|
| """
|
| if Seq2Seq is None:
|
| raise ImportError("Handwriting transcription model not available")
|
|
|
| try:
|
|
|
| input_dim = 11
|
| enc_hidden_dim = 256
|
| dec_hidden_dim = 256
|
| embed_dim = 128
|
| output_dim = LATEX_VOCAB_SIZE if 'LATEX_VOCAB_SIZE' in globals() else 300
|
| encoder_num_layers = 2
|
| decoder_num_layers = 2
|
|
|
|
|
| encoder = Encoder(input_dim, enc_hidden_dim,
|
| num_layers=encoder_num_layers, bidirectional=True)
|
| decoder = Decoder(output_dim, embed_dim,
|
| enc_hidden_dim, dec_hidden_dim,
|
| num_layers=decoder_num_layers)
|
| self.model = Seq2Seq(encoder, decoder, self.device).to(self.device)
|
|
|
|
|
| checkpoint = torch.load(model_path, map_location=self.device)
|
| self.model.load_state_dict(checkpoint)
|
| self.model.eval()
|
|
|
| print(f"Model loaded from {model_path}")
|
| except Exception as e:
|
| print(f"Error loading model: {e}")
|
| self.model = None
|
|
|
| def transcribe_inkml(self, inkml_path: str) -> Tuple[str, str]:
|
| """
|
| Transcribe an InkML file to LaTeX.
|
|
|
| Args:
|
| inkml_path: Path to InkML file
|
|
|
| Returns:
|
| Tuple of (predicted_latex, ground_truth_latex if available)
|
| """
|
| if self.model is None:
|
| raise ValueError("Model not loaded. Please load a model first.")
|
|
|
| if read_inkml_file is None:
|
| raise ImportError("InkML reading functionality not available")
|
|
|
| try:
|
|
|
| strokes, ground_truth = read_inkml_file(inkml_path)
|
|
|
|
|
|
|
|
|
|
|
| predicted_latex = "\\placeholder"
|
|
|
|
|
| if self.use_corrector and correct_latex:
|
| try:
|
| predicted_latex = correct_latex(predicted_latex)
|
| except Exception as e:
|
| print(f"Corrector error: {e}")
|
|
|
| return predicted_latex, ground_truth
|
| except Exception as e:
|
| print(f"Error transcribing InkML: {e}")
|
| return "", ""
|
|
|
| def transcribe_image(self, image_path: str) -> str:
|
| """
|
| Transcribe a handwritten math image to LaTeX.
|
| Note: This is a placeholder - actual implementation would require
|
| image preprocessing and conversion to InkML or direct image processing.
|
|
|
| Args:
|
| image_path: Path to image file
|
|
|
| Returns:
|
| Predicted LaTeX string
|
| """
|
|
|
|
|
| return "\\placeholder"
|
|
|
| def is_model_loaded(self) -> bool:
|
| """
|
| Check if model is loaded.
|
|
|
| Returns:
|
| True if model is loaded, False otherwise
|
| """
|
| return self.model is not None
|
|
|
|
|