rU-ShawJI-07 commited on
Commit
f04e267
·
verified ·
1 Parent(s): 5c43ec5

Create image.py

Browse files
Files changed (1) hide show
  1. image.py +527 -0
image.py ADDED
@@ -0,0 +1,527 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # image.py
2
+ import gradio as gr
3
+ import sympy as sp
4
+ from pix2text import Pix2Text
5
+ from PIL import Image
6
+ import numpy as np
7
+ import matplotlib.pyplot as plt
8
+ import re
9
+ import io
10
+ import logging
11
+
12
+ # Configure logging for debugging
13
+ logging.basicConfig(level=logging.INFO)
14
+ logger = logging.getLogger(__name__)
15
+
16
+ # Define symbolic variables for polynomial and linear system solving
17
+ x, y = sp.symbols('x y')
18
+
19
+ # Initialize Pix2Text model globally to avoid reloading
20
+ try:
21
+ p2t_model = Pix2Text.from_config()
22
+ logger.info("Pix2Text model loaded successfully")
23
+ except Exception as e:
24
+ logger.error(f"Failed to load Pix2Text model: {e}")
25
+ p2t_model = None
26
+
27
+ def clean_latex_expression(latex_str):
28
+ """Clean and normalize LaTeX expression for better parsing"""
29
+ if not latex_str:
30
+ return ""
31
+
32
+ # Remove common LaTeX artifacts
33
+ latex_str = latex_str.strip()
34
+ latex_str = re.sub(r'\\[a-zA-Z]+\{([^}]*)\}', r'\1', latex_str) # Remove LaTeX commands
35
+ latex_str = re.sub(r'\\\\', r'\\', latex_str) # Fix double backslashes
36
+ latex_str = re.sub(r'\s+', ' ', latex_str) # Normalize whitespace
37
+
38
+ return latex_str
39
+
40
+ def parse_equation_type(latex_str):
41
+ """Determine if the equation is polynomial or linear system"""
42
+ try:
43
+ # Clean the latex string
44
+ cleaned = clean_latex_expression(latex_str)
45
+
46
+ # Check if it contains system indicators
47
+ if '\\\\' in cleaned or '\n' in cleaned or 'system' in cleaned.lower():
48
+ return 'linear_system'
49
+
50
+ # Check for polynomial indicators
51
+ if 'x^' in cleaned or '^' in cleaned:
52
+ return 'polynomial'
53
+
54
+ # Check for linear equation indicators
55
+ if ('x' in cleaned and 'y' in cleaned) or ('=' in cleaned and any(op in cleaned for op in ['+', '-'])):
56
+ # Could be either, try to parse as polynomial first
57
+ try:
58
+ expr = sp.sympify(cleaned.split('=')[0] if '=' in cleaned else cleaned)
59
+ if x in expr.free_symbols:
60
+ degree = sp.degree(expr, x)
61
+ if degree > 1:
62
+ return 'polynomial'
63
+ elif degree == 1 and y in expr.free_symbols:
64
+ return 'linear_system'
65
+ else:
66
+ return 'polynomial'
67
+ except:
68
+ pass
69
+
70
+ return 'polynomial' # Default fallback
71
+
72
+ except Exception as e:
73
+ logger.error(f"Error determining equation type: {e}")
74
+ return 'polynomial'
75
+
76
+ def extract_polynomial_coefficients(latex_str):
77
+ """Extract polynomial coefficients from LaTeX string"""
78
+ try:
79
+ # Clean and parse the expression
80
+ cleaned = clean_latex_expression(latex_str)
81
+ if '=' in cleaned:
82
+ cleaned = cleaned.split('=')[0].strip()
83
+
84
+ # Try to parse with SymPy
85
+ expr = sp.sympify(cleaned)
86
+
87
+ if x not in expr.free_symbols:
88
+ raise ValueError("No variable x found in expression")
89
+
90
+ # Get polynomial degree
91
+ degree = sp.degree(expr, x)
92
+
93
+ # Extract coefficients
94
+ poly = sp.Poly(expr, x)
95
+ coeffs = [float(poly.coeff_monomial(x**i)) for i in range(degree, -1, -1)]
96
+
97
+ return {
98
+ "type": "polynomial",
99
+ "degree": degree,
100
+ "coeffs": " ".join(map(str, coeffs)),
101
+ "latex": latex_str,
102
+ "success": True
103
+ }
104
+
105
+ except Exception as e:
106
+ logger.error(f"Error extracting polynomial coefficients: {e}")
107
+ return {
108
+ "type": "polynomial",
109
+ "degree": 2,
110
+ "coeffs": "1 0 0", # Default quadratic
111
+ "latex": latex_str,
112
+ "success": False,
113
+ "error": str(e)
114
+ }
115
+
116
+ def extract_linear_system_coefficients(latex_str):
117
+ """Extract linear system coefficients from LaTeX string"""
118
+ try:
119
+ # Clean and split equations
120
+ cleaned = clean_latex_expression(latex_str)
121
+
122
+ # Split by common separators
123
+ equations = re.split(r'\\\\|\n|;', cleaned)
124
+ if len(equations) < 2:
125
+ # Try to detect two equations in one line
126
+ equations = re.split(r'(?<=[0-9])\s*(?=[+-]?\s*[0-9]*[xy])', cleaned)
127
+
128
+ if len(equations) < 2:
129
+ raise ValueError("Could not find two equations in system")
130
+
131
+ eq1_str = equations[0].strip()
132
+ eq2_str = equations[1].strip()
133
+
134
+ # Parse each equation
135
+ def parse_linear_eq(eq_str):
136
+ # Convert to standard form ax + by = c
137
+ if '=' not in eq_str:
138
+ raise ValueError("No equals sign found")
139
+
140
+ left, right = eq_str.split('=')
141
+ expr = sp.sympify(left) - sp.sympify(right)
142
+
143
+ # Extract coefficients
144
+ a = float(expr.coeff(x, 1)) if expr.coeff(x, 1) else 0
145
+ b = float(expr.coeff(y, 1)) if expr.coeff(y, 1) else 0
146
+ c = float(-expr.as_coefficients_dict()[1]) if 1 in expr.as_coefficients_dict() else 0
147
+
148
+ return f"{a} {b} {c}"
149
+
150
+ eq1_coeffs = parse_linear_eq(eq1_str)
151
+ eq2_coeffs = parse_linear_eq(eq2_str)
152
+
153
+ return {
154
+ "type": "linear",
155
+ "eq1_coeffs": eq1_coeffs,
156
+ "eq2_coeffs": eq2_coeffs,
157
+ "latex": latex_str,
158
+ "success": True
159
+ }
160
+
161
+ except Exception as e:
162
+ logger.error(f"Error extracting linear system coefficients: {e}")
163
+ return {
164
+ "type": "linear",
165
+ "eq1_coeffs": "1 1 3", # Default system
166
+ "eq2_coeffs": "1 -1 1",
167
+ "latex": latex_str,
168
+ "success": False,
169
+ "error": str(e)
170
+ }
171
+
172
+ def extract_equation_from_image(image_file):
173
+ """Extract equation from image using Pix2Text with improved error handling"""
174
+ try:
175
+ if p2t_model is None:
176
+ return {
177
+ "type": "error",
178
+ "latex": "❌ Pix2Text model not loaded. Please check installation.",
179
+ "success": False
180
+ }
181
+
182
+ if image_file is None:
183
+ return {
184
+ "type": "error",
185
+ "latex": "❌ No image file provided.",
186
+ "success": False
187
+ }
188
+
189
+ # Open and process the image
190
+ if isinstance(image_file, str):
191
+ image = Image.open(image_file)
192
+ else:
193
+ image = Image.open(image_file.name)
194
+
195
+ # Convert to RGB if needed
196
+ if image.mode != 'RGB':
197
+ image = image.convert('RGB')
198
+
199
+ logger.info(f"Processing image of size: {image.size}")
200
+
201
+ # Extract text and formulas using Pix2Text
202
+ result = p2t_model.recognize_text_formula(image)
203
+
204
+ if not result or result.strip() == "":
205
+ return {
206
+ "type": "error",
207
+ "latex": "❌ No text or formulas detected in the image.",
208
+ "success": False
209
+ }
210
+
211
+ logger.info(f"Extracted text: {result}")
212
+
213
+ # Determine equation type
214
+ eq_type = parse_equation_type(result)
215
+
216
+ if eq_type == 'polynomial':
217
+ return extract_polynomial_coefficients(result)
218
+ else:
219
+ return extract_linear_system_coefficients(result)
220
+
221
+ except Exception as e:
222
+ logger.error(f"Error processing image: {e}")
223
+ return {
224
+ "type": "error",
225
+ "latex": f"❌ Error processing image: {str(e)}",
226
+ "success": False
227
+ }
228
+
229
+ # Import solve functions from other modules
230
+ def solve_polynomial(degree, coeff_string, real_only):
231
+ """Solve polynomial equation (imported from polynomial.py logic)"""
232
+ try:
233
+ coeffs = list(map(float, coeff_string.strip().split()))
234
+ if len(coeffs) != degree + 1:
235
+ return f"⚠️ Please enter exactly {degree + 1} coefficients.", None, None
236
+
237
+ poly = sum([coeffs[i] * x**(degree - i) for i in range(degree + 1)])
238
+ simplified = sp.simplify(poly)
239
+ factored = sp.factor(simplified)
240
+ roots = sp.solve(sp.Eq(simplified, 0), x)
241
+
242
+ if real_only:
243
+ roots = [r for r in roots if sp.im(r) == 0]
244
+
245
+ roots_output = "$$\n" + "\\ ".join(
246
+ [f"r_{{{i}}} = {sp.latex(sp.nsimplify(r, rational=True))}" for i, r in enumerate(roots, 1)]
247
+ ) + "\n$$"
248
+
249
+ steps_output = f"""
250
+ ### 🧐 Polynomial Expression
251
+ $$ {sp.latex(poly)} = 0 $$
252
+ ### ✏️ Simplified
253
+ $$ {sp.latex(simplified)} = 0 $$
254
+ ### 🤩 Factored
255
+ $$ {sp.latex(factored)} = 0 $$
256
+ ### 🥮 Roots {'(Only Real)' if real_only else '(All Roots)'}
257
+ {roots_output}
258
+ """
259
+
260
+ x_vals = np.linspace(-10, 10, 400)
261
+ y_vals = np.polyval(coeffs, x_vals)
262
+
263
+ fig, ax = plt.subplots(figsize=(6, 4))
264
+ ax.plot(x_vals, y_vals, label="Polynomial", color="blue")
265
+ ax.axhline(0, color='black', linewidth=0.5)
266
+ ax.axvline(0, color='black', linewidth=0.5)
267
+ ax.grid(True)
268
+ ax.set_title("📈 Graph of the Polynomial")
269
+ ax.set_xlabel("x")
270
+ ax.set_ylabel("f(x)")
271
+ ax.legend()
272
+
273
+ return steps_output, fig, ""
274
+ except Exception as e:
275
+ return f"❌ Error: {e}", None, ""
276
+
277
+ def solve_linear_system_from_coeffs(eq1_str, eq2_str):
278
+ """Solve linear system (imported from linear.py logic)"""
279
+ try:
280
+ coeffs1 = list(map(float, eq1_str.strip().split()))
281
+ coeffs2 = list(map(float, eq2_str.strip().split()))
282
+
283
+ if len(coeffs1) != 3 or len(coeffs2) != 3:
284
+ return "⚠️ Please enter exactly 3 coefficients for each equation.", None, None, None
285
+
286
+ a1, b1, c1 = coeffs1
287
+ a2, b2, c2 = coeffs2
288
+
289
+ eq1 = sp.Eq(a1 * x + b1 * y, c1)
290
+ eq2 = sp.Eq(a2 * x + b2 * y, c2)
291
+
292
+ sol = sp.solve([eq1, eq2], (x, y), dict=True)
293
+ if not sol:
294
+ return "❌ No unique solution.", None, None, None
295
+
296
+ solution = sol[0]
297
+ eq_latex = f"$$ {sp.latex(eq1)} \\ {sp.latex(eq2)} $$"
298
+
299
+ steps = rf"""
300
+ ### 📌 Step-by-step Solution
301
+ 1. **Original Equations:**
302
+ $$ {sp.latex(eq1)} $$
303
+ $$ {sp.latex(eq2)} $$
304
+ 2. **Standard Form:** Already provided.
305
+ 3. **Solve using SymPy `solve`:** Internally applies substitution/elimination.
306
+ 4. **Solve for `x` and `y`:**
307
+ $$ x = {sp.latex(solution[x])}, \quad y = {sp.latex(solution[y])} $$
308
+ 5. **Verification:** Substitute back into both equations."""
309
+
310
+ x_vals = np.linspace(-10, 10, 400)
311
+ f1 = sp.solve(eq1, y)
312
+ f2 = sp.solve(eq2, y)
313
+
314
+ fig, ax = plt.subplots()
315
+ if f1:
316
+ f1_func = sp.lambdify(x, f1[0], modules='numpy')
317
+ ax.plot(x_vals, f1_func(x_vals), label=sp.latex(eq1))
318
+ if f2:
319
+ f2_func = sp.lambdify(x, f2[0], modules='numpy')
320
+ ax.plot(x_vals, f2_func(x_vals), label=sp.latex(eq2))
321
+
322
+ ax.plot(solution[x], solution[y], 'ro', label=f"Solution ({solution[x]}, {solution[y]})")
323
+ ax.axhline(0, color='black', linewidth=0.5)
324
+ ax.axvline(0, color='black', linewidth=0.5)
325
+ ax.legend()
326
+ ax.set_title("📊 Graph of the Equations")
327
+ ax.grid(True)
328
+
329
+ return eq_latex, steps, fig, ""
330
+ except Exception as e:
331
+ return f"❌ Error: {e}", None, None, None
332
+
333
+ def solve_extracted_equation(eq_data, real_only):
334
+ """Route to appropriate solver based on equation type"""
335
+ if eq_data["type"] == "polynomial":
336
+ return solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
337
+ elif eq_data["type"] == "linear":
338
+ return solve_linear_system_from_coeffs(eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
339
+ else:
340
+ return "❌ Unknown equation type", None, ""
341
+
342
+ def image_tab():
343
+ """Create the Image Upload Solver tab with improved functionality"""
344
+ with gr.Tab("📷 Image Upload Solver"):
345
+ gr.Markdown("## 📷 Solve Equations from Image")
346
+
347
+ with gr.Row():
348
+ # File input for uploading images
349
+ image_input = gr.File(
350
+ label="Upload Question Image",
351
+ file_types=[".pdf", ".png", ".jpg", ".jpeg"],
352
+ file_count="single"
353
+ )
354
+ # Button to trigger image processing
355
+ image_upload_btn = gr.Button("📤 Process Image")
356
+
357
+ gr.Markdown("**Supported Formats:** .pdf, .png, .jpg, .jpeg")
358
+
359
+ with gr.Row():
360
+ # Checkbox to toggle real roots only for polynomials
361
+ real_image_checkbox = gr.Checkbox(label="Show Only Real Roots (for Polynomials)", value=False)
362
+ # Button to preview the extracted equation
363
+ preview_image_btn = gr.Button("🔍 Preview Equation")
364
+
365
+ # Markdown component to display the extracted equation
366
+ image_equation_display = gr.Markdown()
367
+
368
+ with gr.Row():
369
+ # Button to confirm and display the solution (initially hidden)
370
+ confirm_image_btn = gr.Button("✅ Display Solution", visible=False)
371
+ # Button to edit the equation manually (initially hidden)
372
+ edit_image_btn = gr.Button("✏️ Make Changes Manually", visible=False)
373
+
374
+ # Textbox for manual LaTeX editing (initially hidden)
375
+ edit_latex_input = gr.Textbox(label="Edit LaTeX Equation", visible=False, lines=3)
376
+ # Button to save manual changes (initially hidden)
377
+ save_edit_btn = gr.Button("💾 Save Changes", visible=False)
378
+
379
+ # Markdown component to display solution steps
380
+ image_steps_md = gr.Markdown()
381
+ # Plot component to display the graph
382
+ image_plot_output = gr.Plot()
383
+ # Textbox to display errors
384
+ image_error_box = gr.Textbox(label="Status", visible=True, interactive=False)
385
+
386
+ # State to store the extracted equation data
387
+ extracted_eq_state = gr.State()
388
+
389
+ def handle_image_upload(image_file):
390
+ """Handle image upload and initial processing"""
391
+ if image_file is None:
392
+ return "⚠️ Please upload an image.", None, "", None, None
393
+
394
+ try:
395
+ eq_data = extract_equation_from_image(image_file)
396
+ if eq_data["success"]:
397
+ status = "✅ Image processed successfully. Click 'Preview Equation' to see the extracted equation."
398
+ else:
399
+ status = f"⚠️ Processing completed with issues: {eq_data.get('error', 'Unknown error')}"
400
+
401
+ return status, eq_data, "", None, None
402
+ except Exception as e:
403
+ return f"❌ Error processing image: {str(e)}", None, "", None, None
404
+
405
+ # Event handler for image upload button
406
+ image_upload_btn.click(
407
+ fn=handle_image_upload,
408
+ inputs=[image_input],
409
+ outputs=[image_error_box, extracted_eq_state, image_equation_display,
410
+ image_steps_md, image_plot_output]
411
+ )
412
+
413
+ def preview_image_equation(eq_data, real_only):
414
+ """Preview the extracted equation"""
415
+ if eq_data is None:
416
+ return ("⚠️ No equation data available. Please upload and process an image first.",
417
+ gr.update(visible=False), gr.update(visible=False), "", None)
418
+
419
+ if eq_data["type"] == "error":
420
+ return (eq_data["latex"], gr.update(visible=False), gr.update(visible=False), "", None)
421
+
422
+ # Create preview display
423
+ if eq_data["type"] == "polynomial":
424
+ eq_type_display = "Polynomial Equation"
425
+ details = f"Degree: {eq_data['degree']}, Coefficients: {eq_data['coeffs']}"
426
+ else:
427
+ eq_type_display = "Linear System"
428
+ details = f"Equation 1: {eq_data['eq1_coeffs']}, Equation 2: {eq_data['eq2_coeffs']}"
429
+
430
+ preview_text = f"""
431
+ ### ✅ Confirm {eq_type_display}
432
+
433
+ **Extracted LaTeX:** {eq_data['latex']}
434
+
435
+ **Parsed Details:** {details}
436
+
437
+ **Status:** {'✅ Successfully parsed' if eq_data.get('success', True) else '⚠️ Parsing had issues but proceeding with defaults'}
438
+ """
439
+
440
+ return (preview_text, gr.update(visible=True), gr.update(visible=True), "", None)
441
+
442
+ # Event handler for preview button
443
+ preview_image_btn.click(
444
+ fn=preview_image_equation,
445
+ inputs=[extracted_eq_state, real_image_checkbox],
446
+ outputs=[image_equation_display, confirm_image_btn, edit_image_btn,
447
+ image_steps_md, image_plot_output]
448
+ )
449
+
450
+ def confirm_image_solution(eq_data, real_only):
451
+ """Confirm and solve the extracted equation"""
452
+ if eq_data is None or eq_data["type"] == "error":
453
+ return "⚠️ No valid equation to solve.", None, "No equation available"
454
+
455
+ try:
456
+ if eq_data["type"] == "polynomial":
457
+ steps, plot, error = solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
458
+ return steps, plot, error if error else "✅ Solution completed"
459
+ elif eq_data["type"] == "linear":
460
+ eq_latex, steps, plot, error = solve_linear_system_from_coeffs(
461
+ eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
462
+ return steps, plot, error if error else "✅ Solution completed"
463
+ else:
464
+ return "❌ Unknown equation type", None, "Unknown equation type"
465
+ except Exception as e:
466
+ return f"❌ Error solving equation: {str(e)}", None, str(e)
467
+
468
+ # Event handler for confirm button
469
+ confirm_image_btn.click(
470
+ fn=confirm_image_solution,
471
+ inputs=[extracted_eq_state, real_image_checkbox],
472
+ outputs=[image_steps_md, image_plot_output, image_error_box]
473
+ )
474
+
475
+ def enable_manual_edit(eq_data):
476
+ """Enable manual editing of the equation"""
477
+ if eq_data is None:
478
+ latex_value = "No equation to edit. Please upload an image first."
479
+ elif eq_data["type"] == "error":
480
+ latex_value = "Error in extraction. Please enter your equation manually."
481
+ else:
482
+ latex_value = eq_data.get("latex", "")
483
+
484
+ return (gr.update(visible=True, value=latex_value),
485
+ gr.update(visible=True),
486
+ gr.update(visible=False),
487
+ gr.update(visible=False))
488
+
489
+ # Event handler for edit button
490
+ edit_image_btn.click(
491
+ fn=enable_manual_edit,
492
+ inputs=[extracted_eq_state],
493
+ outputs=[edit_latex_input, save_edit_btn, confirm_image_btn, edit_image_btn]
494
+ )
495
+
496
+ def save_manual_changes(latex_input, real_only):
497
+ """Save manual changes and solve"""
498
+ try:
499
+ if not latex_input or latex_input.strip() == "":
500
+ return "⚠️ Please enter a valid equation.", None, "Empty input"
501
+
502
+ # Determine equation type from manual input
503
+ eq_type = parse_equation_type(latex_input)
504
+
505
+ if eq_type == 'polynomial':
506
+ eq_data = extract_polynomial_coefficients(latex_input)
507
+ steps, plot, error = solve_polynomial(eq_data["degree"], eq_data["coeffs"], real_only)
508
+ else:
509
+ eq_data = extract_linear_system_coefficients(latex_input)
510
+ eq_latex, steps, plot, error = solve_linear_system_from_coeffs(
511
+ eq_data["eq1_coeffs"], eq_data["eq2_coeffs"])
512
+
513
+ return steps, plot, error if error else "✅ Manual equation solved"
514
+
515
+ except Exception as e:
516
+ return f"❌ Error parsing manual input: {str(e)}", None, str(e)
517
+
518
+ # Event handler for save button
519
+ save_edit_btn.click(
520
+ fn=save_manual_changes,
521
+ inputs=[edit_latex_input, real_image_checkbox],
522
+ outputs=[image_steps_md, image_plot_output, image_error_box]
523
+ )
524
+
525
+ return (image_input, image_upload_btn, real_image_checkbox, preview_image_btn,
526
+ image_equation_display, confirm_image_btn, edit_image_btn, edit_latex_input,
527
+ save_edit_btn, image_steps_md, image_plot_output, image_error_box, extracted_eq_state)