pix2tex-el: 在 Emacs 里用 OCR 识别并插入 LaTeX 数学公式

作业输入公式虽然有 cdlatex 和 yasnippet 的帮助, 但是还是好累, 尤其是冬天手冻得慌. 于是到网上去找了一圈公式识别的开源库, 然后抄 epc 的 example 糊了一个简单的 OCR 公式识别并插入的东西.

demo

但是现在的问题就是不会写 async 的调用, 卡 Emacs 还是有点不太舒服. (打算去抄 lsp-bridge, 但是有点力不从心 :sweat_drops:. 或者能有其他的替代也很好. 不过最近赶期末 ddl 可能不会勤修).

以及没有 mac 以外的系统, 所以屏幕截取函数写得很粗暴.

项目地址:

4 个赞

我也有类似的需求🤣试试看 lz 的插件

我用 GitHub - jethrokuan/mathpix.el: Mathpix Emacs Plugin

这个是不是是要api?

对,需要 mathpix 的 api

现在是异步的了, 虽然仍然不是很流畅的感觉…

在线的那几个感觉应该都挺厉害的 (没仔细用过), 主要是用 API 的开销有点吃不消 :sweat_drops: (也没有支付方式就是了

非常感谢分享。

Linux上也可以的运行。

截图工具替换成: xfce4-screenshooter 或是 gnome-screenshot

不过,在我的linux i3 桌面环境下有些小问题。

  1. clipman 必须要运行。
  2. 上面两个工具,把截图送入剪贴版时,非常慢,似乎要2-3秒。

暂时解决方案,把截图放到 python 代码中:

# img = ImageGrab.grabclipboard()    
fh, filepath = tempfile.mkstemp(".png")
os.close(fh)
# subprocess.call(["gnome-screenshot", "-a", "-f", filepath])
subprocess.call(["xfce4-screenshooter", "-r", "-s", filepath])
img = Image.open(filepath)
img.load()
os.unlink(filepath)

会不会是截图工具的问题?

把截图工具放入“异步” 在mac上会不会快一点…

我看了一下, pix2tex 是有 cli 版本的,为什么要用 py 呢?

照着 mathpix.el 自己搞了个纯 elisp 版本的:

https://github.com/Elilif/.elemacs/blob/devel/lib/pix2tex.el

效果:

Peek 2023-12-19 12-47

3 个赞

有道理, 学习了.

用 py 的原因是为了再做一些图像预处理的工作 (主要是用于黑板板书的拍照).

1 个赞

报错了

Debugger entered--Lisp error: (wrong-type-argument integer-or-marker-p nil)
  buffer-substring-no-properties(nil 3771)
  (let* ((info (alist-get process pix2tex--process-alist)) (file (car info)) (pos (cdr info)) (beg (search-forward (concat file ": ") (pos-eol) t)) (end (re-search-forward "^$" nil t)) (result (buffer-substring-no-properties beg (1- end)))) (save-current-buffer (set-buffer (marker-buffer pos)) (save-excursion (goto-char pos) (insert result))) (delete-file file) (let* ((p (assq process pix2tex--process-alist))) (progn (if p (setq pix2tex--process-alist (delq p pix2tex--process-alist))) nil)) (kill-buffer proc-buf))
  (save-current-buffer (set-buffer proc-buf) (goto-char (point-min)) (let* ((info (alist-get process pix2tex--process-alist)) (file (car info)) (pos (cdr info)) (beg (search-forward (concat file ": ") (pos-eol) t)) (end (re-search-forward "^$" nil t)) (result (buffer-substring-no-properties beg (1- end)))) (save-current-buffer (set-buffer (marker-buffer pos)) (save-excursion (goto-char pos) (insert result))) (delete-file file) (let* ((p (assq process pix2tex--process-alist))) (progn (if p (setq pix2tex--process-alist (delq p pix2tex--process-alist))) nil)) (kill-buffer proc-buf)))
  (progn (save-current-buffer (set-buffer proc-buf) (goto-char (point-min)) (let* ((info (alist-get process pix2tex--process-alist)) (file (car info)) (pos (cdr info)) (beg (search-forward (concat file ": ") (pos-eol) t)) (end (re-search-forward "^$" nil t)) (result (buffer-substring-no-properties beg (1- end)))) (save-current-buffer (set-buffer (marker-buffer pos)) (save-excursion (goto-char pos) (insert result))) (delete-file file) (let* ((p (assq process pix2tex--process-alist))) (progn (if p (setq pix2tex--process-alist (delq p pix2tex--process-alist))) nil)) (kill-buffer proc-buf))))
  (if (eq (process-status process) 'exit) (progn (save-current-buffer (set-buffer proc-buf) (goto-char (point-min)) (let* ((info (alist-get process pix2tex--process-alist)) (file (car info)) (pos (cdr info)) (beg (search-forward (concat file ": ") (pos-eol) t)) (end (re-search-forward "^$" nil t)) (result (buffer-substring-no-properties beg (1- end)))) (save-current-buffer (set-buffer (marker-buffer pos)) (save-excursion (goto-char pos) (insert result))) (delete-file file) (let* ((p (assq process pix2tex--process-alist))) (progn (if p (setq pix2tex--process-alist ...)) nil)) (kill-buffer proc-buf)))))
  (let ((proc-buf (process-buffer process))) (if (eq (process-status process) 'exit) (progn (save-current-buffer (set-buffer proc-buf) (goto-char (point-min)) (let* ((info (alist-get process pix2tex--process-alist)) (file (car info)) (pos (cdr info)) (beg (search-forward ... ... t)) (end (re-search-forward "^$" nil t)) (result (buffer-substring-no-properties beg ...))) (save-current-buffer (set-buffer (marker-buffer pos)) (save-excursion (goto-char pos) (insert result))) (delete-file file) (let* ((p ...)) (progn (if p ...) nil)) (kill-buffer proc-buf))))))
  pix2tex--sentinel(#<process pix2tex> "exited abnormally with code 1\n")

这是 pix2tex 异常退出导致的,我在 Linux 上是没问题的,不清楚 mac 上情况。

另外我这是自己写着玩玩的,只考虑自己用的情况,代码仅供参考。

真要用还得看 lz 的包。

我也试用了下,用的是 LaTeX-OCR 提供的 Docker API 的方式调用的,安装起来比较方便,下面是代码,有兴趣的人可以参考:

(defun weiss-org--parse-latex-image-and-insert (img-path on-success &optional server port)
  "DOCSTRING"
  (let ((server (or server "http://localhost"))
        (port (or port "8502")))
    (request (format "%s:%s/predict/" server port) 
      :type "POST"
      :files `(("file" . ,img-path))
      :parser 'buffer-string
      :success (cl-function (lambda (&key data &allow-other-keys)
                              (when data (funcall on-success data)
                                    )))
      :error
      (cl-function (lambda (&rest args &key error-thrown &allow-other-keys)
                     (message "Got error: %S" error-thrown))))
    ))

另外,因为用了 request 库,async 是天然支持的。

不过和 mathpix 比了下,效果不太行啊……尤其是多行带 aligned 的,下面是对比

LaTeX-OCR

\begin{array}{c}{{\nabla_{\mathrm{w}}{\cal L}(\bar{\bf w}_{\mathrm{MAP}}\mid{\bf y})=\sigma_{y}^{-2}({\bf X}{\bf X}^{T}\bar{\bf w}_{\mathrm{MAP}}-{\bf X}{\bf y})+\sigma_{w}^{-2}\bar{\bf w}_{\mathrm{MAP}}\stackrel{\prime}{=}{\bf0}}}\\ {{\qquad(\sigma_{y}^{-2}{\bf X}{\bf X}^{T}+\sigma_{w}^{-2}I)\bar{\bf w}_{\mathrm{MAP}}=\sigma_{y}^{-2}{\bf X}{\bf X}^{T}+\sigma_{w}^{-2}I)\bar{\bf w}_{\mathrm{MAP}}=({\bf X}{\bf X}^{T}+\sigma_{y}^{2}\sigma_{w}^{-2}I)^{-1}{\bf X}_{\bf y}}}\\ {{\bf w}}}\end{array}

Mathpix:

\begin{aligned}
\nabla_{\mathbf{w}} L\left(\hat{\mathbf{w}}_{\mathrm{MAP}} \mid \mathbf{y}\right)=\sigma_y^{-2}\left(\mathbf{X} \mathbf{X}^T \hat{\mathbf{w}}_{\mathrm{MAP}}-\mathbf{X} \mathbf{y}\right)+\sigma_w^{-2} \hat{\mathbf{w}}_{\mathrm{MAP}} & \stackrel{!}{=} \mathbf{0} \\
\left(\sigma_y^{-2} \mathbf{X} \mathbf{X}^T+\sigma_w^{-2} I\right) \hat{\mathbf{w}}_{\mathrm{MAP}} & =\sigma_y^{-2} \mathbf{X} \mathbf{y} \\
\hat{\mathbf{w}}_{\mathrm{MAP}} & =\sigma_y^{-2}\left(\sigma_y^{-2} \mathbf{X X}^T+\sigma_w^{-2} I\right)^{-1} \mathbf{X} \mathbf{y} \\
\hat{\mathbf{w}}_{\mathrm{MAP}} & =\left(\mathbf{X X}^T+\sigma_y^2 \sigma_w^{-2} I\right)^{-1} \mathbf{X} \mathbf{y}
\end{aligned}

感觉确实得买个 Mathpix 的 API 了,以我这使用量,估计初始的 20 刀就能用一辈子了 :joy: