File size: 8,331 Bytes
7e55a7c de2c13c 8cac5bb 7e55a7c 8cac5bb 0bc3219 f68692c 8cac5bb bec810e 0f9d543 bec810e 325bf57 737ea8f bec810e 480f7a3 bec810e 480f7a3 30f343b ad0779c 737ea8f c63cd75 0f9d543 3aa5fcb 0f9d543 62eb7bb 0f9d543 b3dcf15 f68692c 1bd6ac5 b4b9859 f68692c e2e0855 f68692c 9f32fef f68692c 83cf3fc a760562 f68692c 9f32fef f68692c 83cf3fc f68692c a760562 f68692c 83cf3fc 68e99a6 7e55a7c de2c13c 8cac5bb f68692c a09b9ed 8cac5bb 143901b 3e4bb31 8cf9b2c a18e934 be29b4b 95cb2cd fbda286 95cb2cd fbda286 8cac5bb 95cb2cd 6884711 68e99a6 f68692c 0ae7b15 f68692c fd61a77 d042fe9 f68692c d042fe9 b4b9859 83cf3fc d042fe9 83cf3fc d042fe9 e943cdd f68692c e943cdd f68692c e943cdd d042fe9 9e07120 d042fe9 83cf3fc fd61a77 0f9d543 7e55a7c 299839b |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
import gradio as gr
from PIL import Image
from pix2tex.cli import LatexOCR
import sympy as sp
from sympy.parsing.latex import parse_latex
import re
model = LatexOCR()
def preprocess_handwritten_image(pil_img):
return pil_img.convert('RGB')
# Tab 1 cleaning (KEEP UNCHANGED)
def clean_latex(latex):
latex = latex.replace('\\ ', '')
latex = latex.replace('\\\\', '\\')
latex = re.sub(r'\\[ \t\n\r\f\v]*', '', latex)
latex = re.sub(r'\\([+\-=])', r'\1', latex)
replacements = {
r'\\chi': 'x', r'chi': 'x',
r'\\xi': 'x', r'xi': 'x',
r'\\alpha': 'x', r'alpha': 'x',
r'\\beta': 'b', r'beta': 'b',
r'\\gamma': 'y', r'gamma': 'y',
r'\\vartheta': '3', r'vartheta': '3',
r'\\mathcalW': 'x', r'mathcalW': 'x',
r'\\pi': 'pi', r'pi': 'pi',
r'\\mathrm': '', r'mathrm': '',
}
for wrong, correct in replacements.items():
latex = re.sub(wrong, correct, latex)
latex = re.sub(r'\\(cal|mathcal)\s*\{?\s*[Xx]\s*\}?', 'x', latex)
latex = re.sub(r'\\(cal|mathcal)\s*\{?\s*[Yy]\s*\}?', 'y', latex)
latex = re.sub(r'\\(cal|mathcal)\s*\{?\s*[Zz]\s*\}?', 'z', latex)
latex = latex.replace('cal x', 'x').replace('cal X', 'x')
latex = latex.replace('mathcal x', 'x').replace('mathcal X', 'x')
latex = latex.replace('{', '').replace('}', '')
latex = latex.strip().rstrip(',.')
latex = re.sub(r'(?<![a-zA-Z0-9])e(?![a-zA-Z0-9])', 'E', latex)
latex = re.sub(r'(\d)([a-zA-Z])', r'\1*\2', latex)
latex = re.sub(r'(\d+)\s*i', r'\1*I', latex)
latex = re.sub(r'(?<![a-zA-Z0-9])i(?![a-zA-Z0-9])', 'I', latex)
latex = re.sub(r'\(([^()]+?)\)\s*([a-zA-Z](\^\d+)?)', r'(\1)*\2', latex)
latex = latex.replace(r'\cdot', '*')
latex = latex.replace('β', '-')
latex = re.sub(r'[^\w\s^=+*\-().]', '', latex)
if '=' not in latex:
latex += '=0'
latex = latex.replace('pi', '3.1416')
latex = latex.replace('e', '2.7183')
latex = latex.replace('E', '2.7183')
return latex
# β
Tab 2 specific cleaner
def clean_latex2(latex):
latex = latex.replace('\\ ', '')
latex = latex.replace('\\\\', '\\')
latex = re.sub(r'\\[ \t\n\r\f\v]*', '', latex)
latex = latex.replace(r'\times', 'x')
latex = latex.replace(r'\cdot', '*')
latex = latex.replace('β', '-').replace('β', '-')
# Remove LaTeX wrappers like \mathcal{}, \mathbf{}, etc.
latex = re.sub(r'\\(text|mathbf|mathrm|mathit|textbf|mathcal|cal)\s*\{([^{}]+)\}', r'\2', latex)
# Remove remaining LaTeX commands (like \!\!\! and \,)
latex = re.sub(r'\\[a-zA-Z]+', '', latex)
# Remove nested or empty braces
latex = re.sub(r'\{+\}+', '', latex)
latex = re.sub(r'\{([^\{\}]*)\}', r'\1', latex)
# Remove extra commas, semicolons, weird formatting characters
latex = re.sub(r'[;,]', '\n', latex)
latex = re.sub(r'[^\w\s=+\-*/().]', '', latex)
# Replace multiple spaces with single
latex = re.sub(r'\s+', ' ', latex)
# Normalize equals
latex = re.sub(r'(?<![=<>])=(?![=<>])', ' = ', latex)
# Replace ambiguous variable artifacts
replacements = {
'chi': 'x', 'xi': 'x', 'alpha': 'x', 'beta': 'b',
'gamma': 'y', 'vartheta': '3', 'mathcal': '',
'cal': '', 'mathrm': ''
}
for wrong, right in replacements.items():
latex = re.sub(wrong, right, latex)
return latex.strip()
def solve_polynomial(image):
try:
img = preprocess_handwritten_image(image)
latex_result = model(img)
if not latex_result or len(latex_result.strip()) < 2:
return "β Could not extract valid LaTeX from image.", "", ""
cleaned_latex = clean_latex(latex_result)
try:
expr = parse_latex(cleaned_latex)
expr = expr.subs(sp.pi, sp.Float(3.1416)).subs(sp.E, sp.Float(2.7183))
if not isinstance(expr, sp.Equality):
raise ValueError("Expression is not an equation.")
lhs = expr.lhs - expr.rhs
if not lhs.is_polynomial():
raise ValueError("Not a polynomial")
except:
return f"β Could not parse expression:\n\n```latex\n{cleaned_latex}\n```", cleaned_latex, ""
output = f"## π Extracted LaTeX\n```latex\n{latex_result}\n```\n"
output += f"---\n## π§Ή Cleaned LaTeX\n```latex\n{cleaned_latex}\n```\n"
output += f"---\n## π§ Parsed Expression\n$$ {sp.latex(expr)} $$\n"
lhs = expr.lhs - expr.rhs
factor = sp.factor(lhs)
output += f"---\n## βοΈ Standard Form\n$$ {sp.latex(lhs)} = 0 $$\n"
output += f"---\n## π§© Factorized\n$$ {sp.latex(factor)} = 0 $$\n"
x = next(iter(lhs.free_symbols))
roots = sp.solve(lhs, x)
output += "## β
Roots\n"
output += "$$\n\\begin{aligned}\n"
for i, r in enumerate(roots, 1):
root_val = sp.N(r, 6)
output += f"\\text{{Root {i}}}:\\quad x &\\approx {sp.latex(root_val)} \\\\\n"
output += "\\end{aligned}\n$$\n"
return output, cleaned_latex, ""
except Exception as e:
return f"β Error: {str(e)}", "", ""
def wrapped_solver(img):
result, cleaned, prompt = solve_polynomial(img)
return result, cleaned, prompt
# β
FIXED: moved outside of gr.Blocks()
def solve_system_of_equations(image):
try:
img = preprocess_handwritten_image(image)
raw_latex = model(img)
if not raw_latex or len(raw_latex.strip()) < 3:
return "β Could not extract valid LaTeX."
# Handle array environment
if r"\begin{array" in raw_latex:
lines = re.findall(r'\\begin{array}{[^}]+}(.+?)\\end{array}', raw_latex, re.DOTALL)
if lines:
raw_latex = lines[0]
raw_latex = raw_latex.replace(r'\\', '\n')
raw_latex = re.sub(r'&', '', raw_latex)
raw_latex = raw_latex.replace("\n", " ")
equations = re.split(r'[;,]', raw_latex)
cleaned_equations = []
for eq in equations:
cleaned = clean_latex2(eq)
cleaned = re.sub(r'(?<![=<>])=(?![=<>])', ' = ', cleaned)
if '=' in cleaned:
try:
parsed = parse_latex(cleaned)
if isinstance(parsed, sp.Equality):
cleaned_equations.append(cleaned)
except Exception:
continue
sympy_eqs = []
symbols = set()
for eq in cleaned_equations:
parsed = parse_latex(eq)
if isinstance(parsed, sp.Equality):
sympy_eqs.append(parsed)
symbols.update(parsed.free_symbols)
if len(sympy_eqs) < 2:
return f"β Not enough valid equations detected.\n\n```latex\n{raw_latex}\n```"
sol = sp.solve(sympy_eqs, list(symbols), dict=True)
output = f"## π§Ύ Detected Equations:\n```latex\n{raw_latex}\n```\n"
output += "---\n## βοΈ Parsed & Cleaned:\n"
for eq in sympy_eqs:
output += f"$$ {sp.latex(eq)} $$\n"
output += "---\n## β
Solution:\n"
if sol:
output += "$$" + sp.latex(sol[0]) + "$$"
else:
output += "β No solution found or system may be inconsistent."
return output
except Exception as e:
return f"β Error: {str(e)}"
# === UI ===
with gr.Blocks() as demo:
with gr.Tab("πΌοΈ Parse from Image"):
image_input = gr.Image(type="pil", label="π· Upload Image of Polynomial")
hidden_latex = gr.Textbox(visible=False)
explanation_prompt = gr.Textbox(visible=False)
output_box = gr.Markdown(label="π Step-by-step Solution")
submit_btn = gr.Button("π Solve")
submit_btn.click(fn=wrapped_solver, inputs=[image_input], outputs=[output_box, hidden_latex, explanation_prompt])
with gr.Tab("π Solve Simultaneous Equations"):
sim_image_input = gr.Image(type="pil", label="π· Upload Image with Simultaneous Equations")
sim_output = gr.Markdown(label="π Solved System Output")
sim_button = gr.Button("π Solve System")
sim_button.click(fn=solve_system_of_equations, inputs=[sim_image_input], outputs=[sim_output])
if __name__ == "__main__":
demo.launch()
|