File size: 6,201 Bytes
3e6b063
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import os
import cv2
import numpy as np
import base64
import io
from flask import Blueprint, render_template, request, jsonify
from PIL import Image
from utils.math_solver import solve_equation  # optional, if you use math solving

# -------------------------------
# Blueprint setup
# -------------------------------
table_bp = Blueprint('table_bp', __name__)

UPLOAD_FOLDER = 'static/uploads'
os.makedirs(UPLOAD_FOLDER, exist_ok=True)


# -------------------------------
# Core Table Detection Logic
# -------------------------------
def detect_table(image_path):
    """Detect rows and columns from table image using Hough line detection."""
    img = cv2.imread(image_path)
    if img is None:
        raise ValueError(f"Cannot read image at {image_path}")

    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    gray = cv2.GaussianBlur(gray, (5, 5), 0)
    edges = cv2.Canny(gray, 50, 150, apertureSize=3)

    lines = cv2.HoughLinesP(
        edges,
        rho=1,
        theta=np.pi / 180,
        threshold=80,
        minLineLength=60,
        maxLineGap=15
    )

    if lines is None:
        return 0, 0

    horizontal, vertical = [], []

    for x1, y1, x2, y2 in lines[:, 0]:
        if abs(y2 - y1) < abs(x2 - x1):  # horizontal line
            horizontal.append((y1 + y2) / 2)
        elif abs(x2 - x1) < abs(y2 - y1):  # vertical line
            vertical.append((x1 + x2) / 2)

    def merge_lines(coords, gap=25):
        coords = sorted(coords)
        merged = []
        if not coords:
            return merged
        group = [coords[0]]
        for c in coords[1:]:
            if abs(c - group[-1]) < gap:
                group.append(c)
            else:
                merged.append(np.mean(group))
                group = [c]
        merged.append(np.mean(group))
        return merged

    horizontal = merge_lines(horizontal)
    vertical = merge_lines(vertical)

    num_rows = max(len(horizontal) - 1, 1)
    num_cols = max(len(vertical) - 1, 1)
    return num_rows, num_cols


# -------------------------------
# Generate LaTeX Table Code
# -------------------------------
def generate_latex_table(rows, cols):
    col_format = '|' + '|'.join(['c'] * cols) + '|'
    latex = f"\\begin{{tabular}}{{{col_format}}}\n\\hline\n"
    for _ in range(rows):
        latex += ' & '.join([''] * cols) + " \\\\ \\hline\n"
    latex += "\\end{tabular}"
    return latex


# -------------------------------
# Helpers
# -------------------------------
def save_base64_image(image_base64):
    """Convert base64 image string to file and return the saved path."""
    try:
        if ',' in image_base64:
            image_base64 = image_base64.split(',')[1]
        image_bytes = base64.b64decode(image_base64)
        image = Image.open(io.BytesIO(image_bytes)).convert("RGB")
        filename = f"table_{len(os.listdir(UPLOAD_FOLDER)) + 1}.png"
        filepath = os.path.join(UPLOAD_FOLDER, filename)
        image.save(filepath)
        return filepath
    except Exception as e:
        raise ValueError(f"Failed to decode base64 image: {e}")


# -------------------------------
# Routes
# -------------------------------

@table_bp.route("/table", methods=["GET"])
def table_page():
    """Render the main table-to-LaTeX page."""
    return render_template("table.html")


@table_bp.route("/table/process", methods=["POST"])
def process_table_image():
    """

    Handles both:

    - Uploaded image file (FormData)

    - Base64 image (drawn or camera)

    """
    try:
        # 🟒 Case 1: File upload (from drag & drop or browse)
        if 'image' in request.files:
            file = request.files['image']
            if not file.filename:
                return jsonify({'success': False, 'error': 'No file selected.'}), 400

            filename = file.filename
            filepath = os.path.join(UPLOAD_FOLDER, filename)
            file.save(filepath)

        # 🟒 Case 2: Base64 image (from canvas draw)
        elif request.is_json:
            data = request.get_json()
            if 'image' not in data:
                return jsonify({'success': False, 'error': 'No image data provided'}), 400
            filepath = save_base64_image(data['image'])

        else:
            return jsonify({'success': False, 'error': 'Invalid image input.'}), 400

        # Detect table structure
        rows, cols = detect_table(filepath)
        latex_code = generate_latex_table(rows, cols)

        return jsonify({
            'success': True,
            'latex': latex_code,
            'rows': rows,
            'cols': cols,
            'image_path': filepath
        })

    except Exception as e:
        print(f"❌ Error in /table/process: {e}")
        return jsonify({'success': False, 'error': str(e)}), 500


@table_bp.route("/table/generate", methods=["POST"])
def generate_table_from_input():
    """Generate LaTeX manually from user-input rows/cols."""
    try:
        data = request.get_json()
        rows = int(data.get('rows', 0))
        cols = int(data.get('cols', 0))

        if rows <= 0 or cols <= 0:
            return jsonify({'success': False, 'error': 'Invalid rows or columns'}), 400

        latex_code = generate_latex_table(rows, cols)

        return jsonify({'success': True, 'latex': latex_code})
    except Exception as e:
        print(f"❌ Error in /table/generate: {e}")
        return jsonify({'success': False, 'error': str(e)}), 500


@table_bp.route("/table/solve", methods=["POST"])
def solve_table_content():
    """Optional: solve math expressions inside the LaTeX table (if supported)."""
    try:
        data = request.get_json()
        if not data or 'latex' not in data:
            return jsonify({'success': False, 'error': 'No LaTeX data provided'}), 400

        solution = solve_equation(data['latex'])
        return jsonify({'success': True, 'solution': solution})

    except Exception as e:
        print(f"❌ Error in /table/solve: {e}")
        return jsonify({'success': False, 'error': str(e)}), 500