Spaces:
Sleeping
Sleeping
| import gradio as gr | |
| import requests | |
| import base64 | |
| import json | |
| from PIL import Image | |
| import io | |
| import os | |
| import re | |
| from datetime import datetime | |
| # Global counter for serial numbering | |
| equation_counter = 1 | |
| def clean_latex_for_cli(latex_text): | |
| """Clean and format LaTeX for CLI usage""" | |
| # Remove markdown code blocks if present | |
| latex_text = re.sub(r'```latex\n?', '', latex_text) | |
| latex_text = re.sub(r'```\n?', '', latex_text) | |
| # Escape special characters for CLI | |
| latex_text = latex_text.replace('"', '\\"') | |
| latex_text = latex_text.replace('\\', '\\\\') | |
| return latex_text | |
| def extract_equations_and_generate_commands(api_response): | |
| """Extract individual equations and generate maths2svg commands""" | |
| global equation_counter | |
| commands = [] | |
| # Split the response into lines and process each potential equation | |
| lines = api_response.split('\n') | |
| current_equation = "" | |
| for line in lines: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| # Check if line contains LaTeX math | |
| if '\\' in line and not line.startswith('#'): | |
| # Extract LaTeX content | |
| latex_content = line | |
| # Clean up common markdown artifacts | |
| latex_content = re.sub(r'^\*\*.*?\*\*:?\s*', '', latex_content) | |
| latex_content = re.sub(r'^#+\s*', '', latex_content) | |
| if latex_content.strip(): | |
| # Generate filename suggestion | |
| filename_prompt = f"equation_{equation_counter:03d}" | |
| # Clean LaTeX for CLI | |
| clean_latex = clean_latex_for_cli(latex_content) | |
| # Generate the maths2svg command | |
| command = f'maths2svg --latex "{clean_latex}" --output {filename_prompt}.svg' | |
| commands.append(command) | |
| equation_counter += 1 | |
| return commands | |
| def get_filename_suggestions(latex_equations): | |
| """Ask Gemini to suggest meaningful filenames for equations""" | |
| try: | |
| api_key = os.getenv('GEMINI_API_KEY') | |
| if not api_key: | |
| return [] | |
| url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={api_key}" | |
| equations_text = "\n".join(latex_equations) | |
| payload = { | |
| "contents": [ | |
| { | |
| "parts": [ | |
| { | |
| "text": f"""Given these LaTeX equations: | |
| {equations_text} | |
| Please suggest short, descriptive filenames (without extension) for each equation. The filenames should: | |
| - Be descriptive of what the equation represents | |
| - Use underscores instead of spaces | |
| - Be concise (max 20 characters) | |
| - Use mathematical terminology when appropriate | |
| Format your response as a simple list, one filename per line, in the same order as the equations. | |
| If you can't determine a meaningful name, suggest a generic mathematical term. | |
| Example format: | |
| quadratic_formula | |
| pythagorean_theorem | |
| area_circle""" | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| response = requests.post(url, headers={'Content-Type': 'application/json'}, json=payload) | |
| if response.status_code == 200: | |
| result = response.json() | |
| if 'candidates' in result and len(result['candidates']) > 0: | |
| suggestions = result['candidates'][0]['content']['parts'][0]['text'].strip().split('\n') | |
| return [s.strip() for s in suggestions if s.strip()] | |
| return [] | |
| except: | |
| return [] | |
| def image_to_latex(image): | |
| """ | |
| Convert an image containing mathematical equations to LaTeX format with maths2svg commands | |
| """ | |
| if image is None: | |
| return "Please upload an image first." | |
| try: | |
| # Convert PIL Image to base64 | |
| buffered = io.BytesIO() | |
| image.save(buffered, format="PNG") | |
| img_base64 = base64.b64encode(buffered.getvalue()).decode() | |
| # Get API key from environment variable | |
| api_key = os.getenv('GEMINI_API_KEY') | |
| if not api_key: | |
| return "Error: GEMINI_API_KEY environment variable not set. Please set your Google AI API key." | |
| # Prepare the request to Gemini API | |
| url = f"https://generativelanguage.googleapis.com/v1beta/models/gemini-2.0-flash:generateContent?key={api_key}" | |
| headers = { | |
| 'Content-Type': 'application/json' | |
| } | |
| # Construct the payload with image and text prompt | |
| payload = { | |
| "contents": [ | |
| { | |
| "parts": [ | |
| { | |
| "text": """Please analyze this image and convert any mathematical equations, formulas, or mathematical text you find into proper LaTeX format. | |
| Instructions: | |
| - Extract each mathematical equation or formula separately | |
| - Use proper LaTeX syntax for mathematical expressions | |
| - Output raw LaTeX without any $ delimiters | |
| - Put each equation on a separate line | |
| - Do not include any markdown formatting or explanatory text | |
| - Only output the raw LaTeX code for each equation | |
| - If there are multiple equations, list them one per line | |
| Example output format: | |
| \\frac{a}{b} = c | |
| x^2 + y^2 = z^2 | |
| \\int_0^1 x dx = \\frac{1}{2}""" | |
| }, | |
| { | |
| "inline_data": { | |
| "mime_type": "image/png", | |
| "data": img_base64 | |
| } | |
| } | |
| ] | |
| } | |
| ] | |
| } | |
| # Make the API request | |
| response = requests.post(url, headers=headers, json=payload) | |
| if response.status_code == 200: | |
| result = response.json() | |
| # Extract the generated text | |
| if 'candidates' in result and len(result['candidates']) > 0: | |
| latex_output = result['candidates'][0]['content']['parts'][0]['text'].strip() | |
| # Extract equations and generate commands | |
| equations = [] | |
| for line in latex_output.split('\n'): | |
| line = line.strip() | |
| if line and '\\' in line: | |
| equations.append(line) | |
| if not equations: | |
| return "No mathematical equations found in the image." | |
| # Get filename suggestions from Gemini | |
| filename_suggestions = get_filename_suggestions(equations) | |
| # Generate maths2svg commands | |
| commands = [] | |
| global equation_counter | |
| for i, equation in enumerate(equations): | |
| # Clean LaTeX for CLI | |
| clean_latex = clean_latex_for_cli(equation) | |
| # Use suggested filename or fall back to serial numbering | |
| if i < len(filename_suggestions) and filename_suggestions[i]: | |
| filename = filename_suggestions[i] | |
| else: | |
| filename = f"equation_{equation_counter:03d}" | |
| equation_counter += 1 | |
| # Generate the command | |
| command = f'maths2svg --latex "{clean_latex}" --output {filename}.svg' | |
| commands.append(command) | |
| # Format the final output - just the commands | |
| output = "\n".join(commands) | |
| return output | |
| else: | |
| return "No response generated from the API." | |
| else: | |
| return f"API Error: {response.status_code} - {response.text}" | |
| except Exception as e: | |
| return f"Error processing image: {str(e)}" | |
| def create_app(): | |
| """Create and configure the Gradio interface""" | |
| # Custom CSS for better styling | |
| css = """ | |
| .gradio-container { | |
| font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; | |
| } | |
| .output-latex { | |
| font-family: 'Courier New', monospace; | |
| background-color: #f8f9fa; | |
| border: 1px solid #dee2e6; | |
| border-radius: 4px; | |
| padding: 10px; | |
| } | |
| """ | |
| with gr.Blocks(css=css, title="Image to maths2svg Converter") as app: | |
| gr.Markdown( | |
| """ | |
| # πΈ Image to maths2svg Command Generator | |
| Upload an image containing mathematical equations and get ready-to-use `maths2svg` commands | |
| with intelligent filename suggestions. | |
| **Features:** | |
| - Extracts multiple equations from images | |
| - Generates proper LaTeX syntax | |
| - Creates maths2svg CLI commands | |
| - Suggests meaningful filenames using AI | |
| - Falls back to serial numbering if needed | |
| **Supported content:** | |
| - Mathematical equations and formulas | |
| - Mathematical symbols and expressions | |
| - Multiple equations in one image | |
| **Note:** Make sure to set your `GEMINI_API_KEY` environment variable before running this app. | |
| """ | |
| ) | |
| with gr.Row(): | |
| with gr.Column(scale=1): | |
| image_input = gr.Image( | |
| label="Upload Image with Math Equations", | |
| type="pil", | |
| height=400 | |
| ) | |
| convert_btn = gr.Button( | |
| "Generate maths2svg Commands", | |
| variant="primary", | |
| size="lg" | |
| ) | |
| with gr.Column(scale=1): | |
| latex_output = gr.Textbox( | |
| label="maths2svg Commands", | |
| lines=20, | |
| max_lines=30, | |
| placeholder="Generated maths2svg commands will appear here...", | |
| elem_classes=["output-latex"] | |
| ) | |
| # Tips section | |
| gr.Markdown("### π Tips for best results:") | |
| gr.Markdown( | |
| """ | |
| - Use clear, high-contrast images | |
| - Ensure mathematical expressions are clearly visible | |
| - Multiple equations in one image are supported | |
| - Handwritten or printed equations both work | |
| - The app will generate meaningful filenames automatically | |
| """ | |
| ) | |
| # Example section | |
| gr.Markdown("### π― Example Output Format:") | |
| gr.Code( | |
| """maths2svg --latex "h = h_0 + \\frac{2\\sigma \\cos\\theta}{\\rho g r}" --output capillary_rise.svg""" | |
| ) | |
| # Set up the conversion action | |
| convert_btn.click( | |
| fn=image_to_latex, | |
| inputs=[image_input], | |
| outputs=[latex_output] | |
| ) | |
| # Also allow conversion when image is uploaded | |
| image_input.change( | |
| fn=image_to_latex, | |
| inputs=[image_input], | |
| outputs=[latex_output] | |
| ) | |
| return app | |
| if __name__ == "__main__": | |
| # Create and launch the app | |
| app = create_app() | |
| # Launch with custom settings | |
| app.launch( | |
| share=False, # Set to True if you want to create a public link | |
| server_name="0.0.0.0", | |
| server_port=7860, | |
| show_error=True | |
| ) |