← All work
Side project  Write-up

MathNotes AI

A notebook for math class. Snap a photo of your notes or drop in a PDF, check the text it pulls out, and ask a Gemini tutor questions that get answered from your own notes.

5
image cleanup steps before OCR
3
ways in: image, clipboard, PDF
1
review step before the AI sees any OCR text
15
Flask routes
Role · Solo Stack · Python · Flask · OpenCV · Tesseract · PyMuPDF · Firebase · Gemini
Public repo
Jun 14, 2026
Commits
1cleaned-up export
Code
3.9k lines794 Python, 3.1k HTML/CSS
Files
12
01 · Timeline

One export commit

Like my other side projects, MathNotes went public as one cleaned-up commit, so GitHub only shows the publish date.

  1. Jun 14, 2026
    Published

    Accounts and notes in Firebase, the note editor, uploads for images, pasted screenshots, and PDFs, the OCR pipeline with its confirmation step, and the Gemini assistant with per-note chat history.

    GitHub12 files · +3,993 linesd64a57a ↗
02 · The idea

Answers grounded in your own notes

A general chatbot doesn't know what your teacher wrote on the board or which problem set you're stuck on. MathNotes treats everything you upload as part of a note's study context. The text from each file is stored next to the note, then combined into one block that goes to the assistant along with the note itself and your recent questions. That way the answers come from your own materials.

03 · Getting clean text out of photos

Five cleanup passes before Tesseract

Phone photos of handwritten math are noisy, unevenly lit, and usually a little crooked. Tesseract does far better on clean, high-contrast input, so each image goes through OpenCV first: denoise, boost contrast locally with CLAHE, convert to black and white with an adaptive threshold, close small gaps, and straighten the page if it's tilted more than half a degree.

app.py
denoised = cv2.fastNlMeansDenoising(gray)

clahe = cv2.createCLAHE(clipLimit=3.0, tileGridSize=(8, 8))
enhanced = clahe.apply(denoised)

binary = cv2.adaptiveThreshold(enhanced, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
                               cv2.THRESH_BINARY, 11, 2)
cleaned = cv2.morphologyEx(binary, cv2.MORPH_CLOSE, np.ones((1, 1), np.uint8))

coords = np.column_stack(np.where(cleaned > 0))
angle = cv2.minAreaRect(coords)[-1]
angle = -(90 + angle) if angle < -45 else -angle
if abs(angle) > 0.5:                       # only deskew a real tilt
    cleaned = cv2.warpAffine(cleaned, cv2.getRotationMatrix2D(center, angle, 1.0), (w, h),
                             flags=cv2.INTER_CUBIC, borderMode=cv2.BORDER_REPLICATE)

Condensed from EnhancedOCR.preprocess_image in app.py.

PDFs go through PyMuPDF and PyPDF2 instead. Even with all of that, OCR still gets symbols wrong sometimes, so nothing goes to the AI until you've looked at the extracted text and confirmed or fixed it. The review screen exists because a tutor that confidently explains a misread equation does more harm than good.

04 · What I'd do next

Before anyone else should use it

Read the code.

Needs Tesseract installed locally, plus Firebase and Gemini keys.

More work