simpletex实现api识别
现在的simpletex太难使用了,每次使用都要等好久,不是很方便,上网去搜,结果没有现有的程序支持这个api,那我来当第一个吃螃蟹的吧
下面是代码实现api识别公式
首先是导入库:
import tkinter as tk
from tkinter import messagebox, scrolledtext
import cv2
import numpy as np
from PIL import ImageGrab
import requests
import json
import threading
import os
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import matplotlib.pyplot as plt
信息token
# 上传文件的配置信息
SIMPLETEX_UAT = "*******"
api_url = "https://server.simpletex.cn/api/latex_ocr_turbo" # 接口地址
data = {} # 请求参数数据(非文件型参数),视情况填入,可以参考各个接口的参数说明
headers = {"token": SIMPLETEX_UAT} # 鉴权信息,此处使用UAT方式
截图的函数部分
def scrren_cut():
image = ImageGrab.grab()
image.save("screen.jpg")
def on_mouse(event, x, y, flags, param):
global img, point1, point2, cropping
img2 = img.copy()
if event == cv2.EVENT_LBUTTONDOWN:
point1 = (x, y)
cropping = True
elif event == cv2.EVENT_MOUSEMOVE and cropping:
cv2.rectangle(img2, point1, (x, y), (255, 0, 0), 2)
cv2.imshow('image', img2)
elif event == cv2.EVENT_LBUTTONUP:
point2 = (x, y)
cropping = False
cv2.rectangle(img2, point1, point2, (0, 0, 255), 2)
cv2.imshow('image', img2)
min_x = min(point1[0], point2[0])
min_y = min(point1[1], point2[1])
width = abs(point1[0] - point2[0])
height = abs(point1[1] - point2[1])
cut_img = img[min_y:min_y+height, min_x:min_x+width]
cv2.imwrite('cut.jpg', cut_img)
cv2.destroyAllWindows()
upload_screenshot('cut.jpg')
def cut():
global img, cropping
cropping = False
scrren_cut()
img = cv2.imread('screen.jpg')
cv2.namedWindow('image')
cv2.setMouseCallback('image', on_mouse)
cv2.imshow('image', img)
cv2.waitKey(0)
os.remove('screen.jpg')
上传截图
def upload_screenshot(filename):
try:
with open(filename, "rb") as file:
files = {"file": (filename, file)}
response = requests.post(api_url, files=files, headers=headers)
response_data = json.loads(response.text)
latex_code = response_data["res"]["latex"]
display_latex(latex_code)
messagebox.showinfo("成功", "截图并上传成功!")
os.remove(filename)
except Exception as e:
messagebox.showerror("错误", f"发生错误:{str(e)}")
实现gui界面
def display_latex(latex_code):
# 清除之前的内容
output_text.delete(1.0, tk.END)
output_text.insert(tk.END, latex_code)
def take_screenshot_and_upload():
threading.Thread(target=cut).start()
# 创建主窗口
root = tk.Tk()
root.title("截图上传工具")
# 创建按钮
screenshot_button = tk.Button(root, text="截图并上传", command=take_screenshot_and_upload)
screenshot_button.pack(pady=20)
# 创建可复制的输出显示框
output_text = scrolledtext.ScrolledText(root, wrap=tk.WORD, width=60, height=10)
output_text.pack(padx=20, pady=20, fill="both", expand=True)
# 运行主循环
root.mainloop()
效果展示:

哎,还是直接粘贴不了,用MATLAB绘制又复制不了,脱离了初衷,

放弃了,还是老老实实等吧,相对于给simpletex捐了20
---2025.11.18---
我进行了升级,大家直接用:
点击查看代码
# coding=gbk
import tkinter as tk
from tkinter import scrolledtext, ttk
from PIL import ImageGrab, Image, ImageTk
import cv2
import requests
import json
import threading
import os
import io
import re
import latex2mathml.converter
import keyboard # 全局快捷键
# ==================== 配置 ====================
SIMPLETEX_UAT = "************"
HEADERS = {"token": SIMPLETEX_UAT}
ACCENT_RULES = [
("_hat", r'\\hat'),
("_bar", r'\\bar'),
("_tilde", r'\\tilde'),
("_dot", r'\\dot'),
("_ddot", r'\\ddot'),
]
VAR_PAT = r'(\\?[A-Za-z]+(?:_[A-Za-z0-9]+)?)({suffix})'
FUNC_SKIP = {'sin','cos','tan','log','ln','exp','min','max','argmin','argmax','det','dim','rank','Pr'}
FUNC_NAME_RE = re.compile(r'(?<![\\_\}\{\w])([A-Za-z][A-Za-z0-9_]{2,})\s*(?=\()')
def wrap_operatorname(s: str) -> str:
def _repl(m):
name = m.group(1)
if name in FUNC_SKIP:
return name
return r'\operatorname{' + name + '}'
return FUNC_NAME_RE.sub(_repl, s)
def normalize_latex(latex: str) -> str:
s = latex
for suffix, cmd in ACCENT_RULES:
pat = VAR_PAT.format(suffix=re.escape(suffix))
def _repl(m):
token = m.group(1)
return f'{cmd}{{{token}}}'
s = re.sub(pat, _repl, s)
s = re.sub(r'(?<![A-Za-z0-9\\])([A-Za-z])\s*\^\s*(?!\{)', r'\\hat{\1}', s)
s = wrap_operatorname(s)
s = re.sub(r'\\operatorname\{([A-Za-z][A-Za-z0-9_]{2,})\}\s+\(', r'\\operatorname{\1}(', s)
return s
ACCENT_CHARS = r'(?:\^|^|¯|~|~|˙|·|¨)'
def postprocess_mathml(xml_text: str) -> str:
math_blocks = re.findall(r'(<math[\s\S]*?</math>)', xml_text)
if math_blocks:
xml_text = math_blocks[0]
xml_text = re.sub(
rf'<mover([^>]*)>\s*<mrow>\s*(.*?)\s*</mrow>\s*<mo[^>]*>\s*{ACCENT_CHARS}\s*</mo>\s*</mover>',
r'<mover\1 accent="true">\2<mo>^</mo></mover>',
xml_text,
flags=re.S
)
xml_text = re.sub(
r'<mtext>\s*([A-Za-z][A-Za-z0-9_]{2,})\s*</mtext>',
r'<mi mathvariant="normal">\1</mi>',
xml_text
)
xml_text = re.sub(r'<mspace[^>]*/>', '', xml_text)
xml_text = re.sub(r'>\s+<', '> <', xml_text)
xml_text = re.sub(r'\s+', ' ', xml_text)
xml_text = re.sub(r'\s+xmlns="http://www\.w3\.org/1998/Math/MathML"', '', xml_text)
if not xml_text.lstrip().startswith('<math'):
xml_text = f'<math xmlns="http://www.w3.org/1998/Math/MathML">{xml_text}</math>'
xml_text = re.sub(r'^<math(?![^>]*xmlns=)', r'<math xmlns="http://www.w3.org/1998/Math/MathML"', xml_text)
return xml_text
# ==================== GUI ====================
class MathMLConverterApp:
def __init__(self, root):
self.root = root
self.root.title("公式转换工具")
self.root.geometry("1000x700")
self.root.configure(bg="#f0f2f5")
self.img = None
self.point1 = ()
self.point2 = ()
self.setup_ui()
threading.Thread(target=self.bind_global_hotkey, daemon=True).start()
def setup_ui(self):
# 顶部按钮
top_frame = tk.Frame(self.root, bg="#f0f2f5", pady=10)
top_frame.pack(fill="x")
self.screenshot_btn = tk.Button(top_frame, text="截图识别 (F4)", bg="#4a90e2", fg="white",
font=("Arial",12,"bold"), command=self.start_capture_thread, relief="flat", padx=10, pady=5)
self.screenshot_btn.pack()
# 中间四分布局
middle_frame = tk.Frame(self.root, bg="#f0f2f5")
middle_frame.pack(fill="both", expand=True, padx=10, pady=5)
left_frame = tk.Frame(middle_frame, bg="#f0f2f5")
left_frame.pack(side="left", fill="both", expand=True)
right_frame = tk.Frame(middle_frame, bg="#f0f2f5")
right_frame.pack(side="right", fill="both", expand=True)
# 左上:截图显示 - 增大高度比例
screenshot_frame = tk.LabelFrame(left_frame, text="截图内容", bg="white", font=("Arial",10,"bold"), padx=5, pady=5)
screenshot_frame.pack(fill="both", expand=True, padx=5, pady=5, ipady=10) # 增加内部填充
# 新增一个Canvas容器实现滚动效果
screenshot_canvas = tk.Canvas(screenshot_frame, bg="white", highlightthickness=0)
screenshot_canvas.pack(side="left", fill="both", expand=True)
screenshot_scroll = tk.Scrollbar(screenshot_frame, orient="vertical", command=screenshot_canvas.yview)
screenshot_scroll.pack(side="right", fill="y")
screenshot_canvas.configure(yscrollcommand=screenshot_scroll.set)
self.screenshot_label = tk.Label(screenshot_canvas, bg="white")
screenshot_canvas.create_window((0,0), window=self.screenshot_label, anchor="nw")
# 设置滚动区域
self.screenshot_label.bind("<Configure>", lambda e: screenshot_canvas.configure(
scrollregion=screenshot_canvas.bbox("all")))
# 左下:公式图片 - 增大高度比例
img_frame = tk.LabelFrame(left_frame, text="公式图片", bg="white", font=("Arial",10,"bold"), padx=5, pady=5)
img_frame.pack(fill="both", expand=True, padx=5, pady=5, ipady=10) # 增加内部填充
# 新增一个Canvas容器实现滚动效果
img_canvas = tk.Canvas(img_frame, bg="white", highlightthickness=0)
img_canvas.pack(side="left", fill="both", expand=True)
img_scroll = tk.Scrollbar(img_frame, orient="vertical", command=img_canvas.yview)
img_scroll.pack(side="right", fill="y")
img_canvas.configure(yscrollcommand=img_scroll.set)
self.image_label = tk.Label(img_canvas, bg="white")
img_canvas.create_window((0,0), window=self.image_label, anchor="nw")
# 设置滚动区域
self.image_label.bind("<Configure>", lambda e: img_canvas.configure(
scrollregion=img_canvas.bbox("all")))
# 右上:原始 LaTeX
latex_frame = tk.LabelFrame(right_frame, text="原始 LaTeX", bg="white", font=("Arial",10,"bold"), padx=5, pady=5)
latex_frame.pack(fill="both", expand=True, padx=5, pady=5)
self.latex_text = scrolledtext.ScrolledText(latex_frame, wrap=tk.WORD, font=("Arial",15), height=8)
self.latex_text.pack(fill="both", expand=True)
# 右下:MathML
mathml_frame = tk.LabelFrame(right_frame, text="MathML 输出", bg="white", font=("Arial",10,"bold"), padx=5, pady=5)
mathml_frame.pack(fill="both", expand=True, padx=5, pady=5)
self.output_text = scrolledtext.ScrolledText(mathml_frame, wrap=tk.WORD, font=("Courier New",8))
self.output_text.pack(fill="both", expand=True)
# 底部状态栏
self.status_label = tk.Label(self.root, text="F4 截图识别公式", anchor="w", bg="#d9edf7", fg="#31708f", font=("Arial",10))
self.status_label.pack(fill="x", side="bottom")
def bind_global_hotkey(self):
keyboard.add_hotkey('F4', lambda: self.start_capture_thread())
def start_capture_thread(self):
threading.Thread(target=self.capture_area, daemon=True).start()
def capture_area(self):
img = ImageGrab.grab()
img.save("screen.jpg")
self.img = cv2.imread('screen.jpg')
screen_height, screen_width = self.root.winfo_screenheight(), self.root.winfo_screenwidth()
img_height, img_width = self.img.shape[:2]
scale = min(screen_width/img_width, screen_height/img_height)
self.img = cv2.resize(self.img, (int(img_width*scale), int(img_height*scale)))
self.display_screenshot(self.img)
cv2.namedWindow('image')
cv2.setMouseCallback('image', self.on_mouse)
cv2.imshow('image', self.img)
cv2.waitKey(0)
os.remove('screen.jpg')
def display_screenshot(self, cv_img):
img = cv2.cvtColor(cv_img, cv2.COLOR_BGR2RGB)
img = Image.fromarray(img)
img = ImageTk.PhotoImage(img)
self.screenshot_label.config(image=img)
self.screenshot_label.image = img
def on_mouse(self, event, x, y, flags, param):
if event == cv2.EVENT_LBUTTONDOWN:
self.point1 = (x, y)
elif event == cv2.EVENT_MOUSEMOVE and (flags & cv2.EVENT_FLAG_LBUTTON):
img_copy = self.img.copy()
cv2.rectangle(img_copy, self.point1, (x, y), (0, 255, 0), 1)
cv2.imshow('image', img_copy)
elif event == cv2.EVENT_LBUTTONUP:
self.point2 = (x, y)
cv2.destroyAllWindows()
self.process_selection()
def process_selection(self):
min_x = min(self.point1[0], self.point2[0])
min_y = min(self.point1[1], self.point2[1])
width = abs(self.point1[0] - self.point2[0])
height = abs(self.point1[1] - self.point2[1])
cut_img = self.img[min_y:min_y + height, min_x:min_x + width]
cv2.imwrite('cut.jpg', cut_img)
self.display_screenshot(cut_img)
self.upload_image('cut.jpg')
def upload_image(self, filename):
try:
with open(filename, "rb") as file:
response = requests.post(API_URL, files={"file": (filename, file)}, headers=HEADERS)
data = json.loads(response.text)
if "res" in data and "latex" in data["res"]:
latex = data["res"]["latex"]
self.latex_text.delete(1.0, tk.END)
self.latex_text.insert(tk.END, latex)
self.convert_to_mathml(latex)
self.display_latex_image(latex)
os.remove(filename)
except Exception as e:
self.show_error(f"识别失败: {str(e)}")
def display_latex_image(self, latex):
try:
response = requests.get(f"https://latex.codecogs.com/png.latex?{latex}")
img_data = response.content
img = Image.open(io.BytesIO(img_data))
img = ImageTk.PhotoImage(img)
self.image_label.config(image=img)
self.image_label.image = img
except Exception as e:
self.show_error(f"图片显示失败: {str(e)}")
def convert_to_mathml(self, latex):
try:
latex = normalize_latex(latex)
raw_mathml = latex2mathml.converter.convert(latex)
mathml = postprocess_mathml(raw_mathml)
self.output_text.delete(1.0, tk.END)
self.output_text.insert(tk.END, mathml)
self.root.clipboard_clear()
self.root.clipboard_append(mathml)
self.status_label.config(text="MathML 已复制到剪贴板")
except Exception as e:
self.show_error(f"转换失败: {str(e)}")
def show_error(self, message):
self.status_label.config(text=message)
# ==================== 入口 ====================
if __name__ == "__main__":
root = tk.Tk()
app = MathMLConverterApp(root)
root.mainloop()
posted on 2024-11-25 16:32 Hello_Wood 阅读(345) 评论(3) 收藏 举报
浙公网安备 33010602011771号