Commit bffed0fe authored by dengjb's avatar dengjb
Browse files

update

parents
{
"name": "match-at",
"version": "0.1.0",
"description": "Relocatable regular expressions.",
"repository": {
"type": "git",
"url": "https://github.com/spicyj/match-at"
},
"main": "lib/matchAt.js",
"files": [
"lib/"
],
"devDependencies": {
"babel": "^4.7.16",
"jest-cli": "^0.4.0",
"react-tools": "^0.13.1"
},
"jest": {
"scriptPreprocessor": "<rootDir>/jestSupport/preprocessor.js",
"unmockedModulePathPatterns": [
""
]
},
"scripts": {
"prepublish": "babel -d lib/ src/",
"test": "jest"
},
"gitHead": "4197daff69720734c72ba3321ed68a41c0527fb2",
"bugs": {
"url": "https://github.com/spicyj/match-at/issues"
},
"homepage": "https://github.com/spicyj/match-at",
"_id": "match-at@0.1.0",
"_shasum": "f561e7709ff9a105b85cc62c6b8ee7c15bf24f31",
"_from": "match-at@",
"_npmVersion": "2.2.0",
"_nodeVersion": "0.10.35",
"_npmUser": {
"name": "spicyj",
"email": "ben@benalpert.com"
},
"maintainers": [
{
"name": "spicyj",
"email": "ben@benalpert.com"
}
],
"dist": {
"shasum": "f561e7709ff9a105b85cc62c6b8ee7c15bf24f31",
"tarball": "https://registry.npmjs.org/match-at/-/match-at-0.1.0.tgz"
},
"directories": {},
"_resolved": "https://registry.npmjs.org/match-at/-/match-at-0.1.0.tgz"
}
# taken and modified from https://github.com/harvardnlp/im2markup
# tokenize latex formulas
import sys
import os
import re
import argparse
import subprocess
import shutil
from threading import Timer
from datetime import datetime
def run_cmd(cmd, timeout_sec=30):
proc = subprocess.Popen(cmd, shell=True)
kill_proc = lambda p: p.kill()
timer = Timer(timeout_sec, kill_proc, [proc])
try:
timer.start()
stdout,stderr = proc.communicate()
finally:
timer.cancel()
def tokenize_latex(latex_code, latex_type="", middle_file=""):
if not latex_code:
return False, latex_code
if not latex_type:
latex_type = "tabular" if "tabular" in latex_code else "formula"
if not middle_file:
middle_file = "out-" + datetime.now().strftime('%Y_%m_%d_%H_%M_%S') + ".txt"
temp_file = middle_file + '.tmp'
if latex_type == "formula":
with open(temp_file, 'w') as f:
prepre = latex_code
# replace split, align with aligned
prepre = re.sub(r'\\begin{(split|align|alignedat|alignat|eqnarray)\*?}(.+?)\\end{\1\*?}', r'\\begin{aligned}\2\\end{aligned}', prepre, flags=re.S)
prepre = re.sub(r'\\begin{(smallmatrix)\*?}(.+?)\\end{\1\*?}', r'\\begin{matrix}\2\\end{matrix}', prepre, flags=re.S)
f.write(prepre)
cmd = r"cat %s | node %s %s > %s " % (temp_file, os.path.join(os.path.dirname(__file__), 'preprocess_formula.js'), 'normalize', middle_file)
ret = subprocess.call(cmd, shell=True)
os.remove(temp_file)
if ret != 0:
return False, latex_code
operators = '\s?'.join('|'.join(['arccos', 'arcsin', 'arctan', 'arg', 'cos', 'cosh', 'cot', 'coth', 'csc', 'deg', 'det', 'dim', 'exp', 'gcd', 'hom', 'inf',
'injlim', 'ker', 'lg', 'lim', 'liminf', 'limsup', 'ln', 'log', 'max', 'min', 'Pr', 'projlim', 'sec', 'sin', 'sinh', 'sup', 'tan', 'tanh']))
ops = re.compile(r'\\operatorname {(%s)}' % operators)
with open(middle_file, 'r') as fin:
for line in fin:
tokens = line.strip().split()
tokens_out = []
for token in tokens:
tokens_out.append(token)
post = ' '.join(tokens_out)
# use \sin instead of \operatorname{sin}
names = ['\\'+x.replace(' ', '') for x in re.findall(ops, post)]
post = re.sub(ops, lambda match: str(names.pop(0)), post).replace(r'\\ \end{array}', r'\end{array}')
os.remove(middle_file)
return True, post
elif latex_type == "tabular":
latex_code = latex_code.replace("\\\\%", "\\\\ %")
latex_code = latex_code.replace("\%", "<PERCENTAGE_TOKEN>")
latex_code = latex_code.split('%')[0]
latex_code = latex_code.replace("<PERCENTAGE_TOKEN>", "\%")
if not "\\end{tabular}" in latex_code:
latex_code += "\\end{tabular}"
with open(middle_file, 'w') as f:
f.write(latex_code.replace('\r', ' ').replace('\n', ' '))
cmd = "perl -pe 's|hskip(.*?)(cm\\|in\\|pt\\|mm\\|em)|hspace{\\1\\2}|g' %s > %s"%(middle_file, temp_file)
ret = subprocess.call(cmd, shell=True)
if ret != 0:
return False, latex_code
os.remove(middle_file)
cmd = r"cat %s | node %s %s > %s " % (temp_file, os.path.join(os.path.dirname(__file__), 'preprocess_tabular.js'), 'tokenize', middle_file)
ret = subprocess.call(cmd, shell=True)
os.remove(temp_file)
if ret != 0:
return False, latex_code
with open(middle_file, 'r') as fin:
for line in fin:
tokens = line.strip().split()
tokens_out = []
for token in tokens:
tokens_out.append(token)
post = ' '.join(tokens_out)
os.remove(middle_file)
return True, post
else:
print(f"latex type{latex_type} unrecognized.")
return False, latex_code
if __name__ == '__main__':
latex_code = open("2.txt", 'r').read().replace('\r', ' ')
print("=>", latex_code)
new_code = tokenize_latex(latex_code)
print("=>", new_code)
\ No newline at end of file
import time
import numpy as np
from PIL import Image
from scipy.spatial.distance import cdist
from scipy.optimize import linear_sum_assignment
class SimpleAffineTransform:
"""
simple affine transform, only translation and scale.
"""
def __init__(self, translation=(0, 0), scale=1.0):
self.translation = np.array(translation)
self.scale = scale
def estimate(self, src, dst):
src_center = np.mean(src, axis=0)
dst_center = np.mean(dst, axis=0)
self.translation = dst_center - src_center
src_dists = np.linalg.norm(src - src_center, axis=1)
dst_dists = np.linalg.norm(dst - dst_center, axis=1)
self.scale = np.mean(dst_dists) / (np.mean(src_dists) + 1e-10)
def inverse(self):
inverse_transform = AffineTransform(-self.translation, 1.0/self.scale)
return inverse_transform
def __call__(self, coords):
return self.scale * (coords - np.mean(coords, axis=0)) + np.mean(coords, axis=0) + self.translation
def residuals(self, src, dst):
return np.sqrt(np.sum((self(src) - dst) ** 2, axis=1))
def norm_coords(x, left, right):
if x < left:
return left
if x > right:
return right
return x
def norm_same_token(token):
special_map = {
"\\dot": ".",
"\\Dot": ".",
"\\cdot": ".",
"\\cdotp": ".",
"\\ldotp": ".",
"\\mid": "|",
"\\rightarrow": "\\to",
"\\top": "T",
"\\Tilde": "\\tilde",
"\\prime": "'",
"\\ast": "*",
"\\left<": "\\langle",
"\\right>": "\\rangle",
"\\lbrace": "\{",
"\\rbrace": "\}",
"\\lbrack": "[",
"\\rbrack": "]",
"\\blackslash": "/",
"\\slash": "/",
"\\leq": "\\le",
"\\geq": "\\ge",
"\\neq": "\\ne",
"\\Vert": "\\|",
"\\lVert": "\\|",
"\\rVert": "\\|",
"\\vert": "|",
"\\lvert": "|",
"\\rvert": "|",
"\\colon": ":",
"\\Ddot": "\\ddot",
"\\Bar": "\\bar",
"\\Vec": "\\vec",
"\\parallel": "\\|",
"\\dag": "\\dagger",
"\\ddag": "\\ddagger",
"\\textlangle": "<",
"\\textrangle": ">",
"\\textgreater": ">",
"\\textless": "<",
"\\textbackslash": "n",
"\\textunderscore": "_",
"\\=": "_",
"\\neg": "\\lnot",
"\\neq": "\\not=",
}
if token.startswith('\\left') or token.startswith('\\right'):
if "arrow" not in token and "<" not in token and ">" not in token and "harpoon" not in token:
token = token.replace("\\left", "").replace("\\right", "")
if token.startswith('\\big') or token.startswith('\\Big'):
if "\\" in token[4:]:
token = "\\"+token[4:].split("\\")[-1]
else:
token = token[-1]
if token in special_map.keys():
token = special_map[token]
if token.startswith('\\wide'):
return token.replace("wide", "")
if token.startswith('\\var'):
return token.replace("var", "")
if token.startswith('\\string'):
return token.replace("\\string", "")
return token
class HungarianMatcher:
def __init__(
self,
cost_token: float = 1,
cost_position: float = 0.05,
cost_order: float = 0.15,
):
self.cost_token = cost_token
self.cost_position = cost_position
self.cost_order = cost_order
self.cost = {}
def calculate_token_cost(self, box_gt, box_pred):
token2id = {}
for data in box_gt+box_pred:
if data['token'] not in token2id:
token2id[data['token']] = len(token2id)
num_classes = len(token2id)
token2id_norm = {}
for data in box_gt+box_pred:
if norm_same_token(data['token']) not in token2id_norm:
token2id_norm[norm_same_token(data['token'])] = len(token2id_norm)
num_classes_norm = len(token2id_norm)
gt_token_array = []
norm_gt_token_array = []
for data in box_gt:
gt_token_array.append(token2id[data['token']])
norm_gt_token_array.append(token2id_norm[norm_same_token(data['token'])])
pred_token_logits = []
norm_pred_token_logits = []
for data in box_pred:
logits = [0] * num_classes
logits[token2id[data['token']]] = 1
pred_token_logits.append(logits)
logits_norm = [0] * num_classes_norm
logits_norm[token2id_norm[norm_same_token(data['token'])]] = 1
norm_pred_token_logits.append(logits_norm)
gt_token_array = np.array(gt_token_array)
pred_token_logits = np.array(pred_token_logits)
norm_gt_token_array = np.array(norm_gt_token_array)
norm_pred_token_logits = np.array(norm_pred_token_logits)
token_cost = 1.0 - pred_token_logits[:, gt_token_array]
norm_token_cost = 1.0 - norm_pred_token_logits[:, norm_gt_token_array]
token_cost[np.logical_and(token_cost==1, norm_token_cost==0)] = 0.005
return token_cost.T
def box2array(self, box_list, size):
W, H = size
box_array = []
for box in box_list:
x_min, y_min, x_max, y_max = box['bbox']
box_array.append([x_min/W, y_min/H, x_max/W, y_max/H])
return np.array(box_array)
def order2array(self, box_list, max_token_lens=None):
if not max_token_lens:
max_token_lens = len(box_list)
order_array = []
for idx, box in enumerate(box_list):
order_array.append([idx / max_token_lens])
return np.array(order_array)
def calculate_l1_cost(self, gt_array, pred_array):
scale = gt_array.shape[-1]
l1_cost = cdist(gt_array, pred_array, 'minkowski', p=1)
return l1_cost / scale
def __call__(self, box_gt, box_pred, gt_size, pred_size):
aa = time.time()
gt_box_array = self.box2array(box_gt, gt_size)
pred_box_array = self.box2array(box_pred, pred_size)
max_token_lens = max(len(box_gt), len(box_pred))
gt_order_array = self.order2array(box_gt, max_token_lens)
pred_order_array = self.order2array(box_pred, max_token_lens)
token_cost = self.calculate_token_cost(box_gt, box_pred)
position_cost = self.calculate_l1_cost(gt_box_array, pred_box_array)
order_cost = self.calculate_l1_cost(gt_order_array, pred_order_array)
self.cost["token"] = token_cost
self.cost["position"] = position_cost
self.cost["order"] = order_cost
cost = self.cost_token * token_cost + self.cost_position * position_cost + self.cost_order * order_cost
cost[np.isnan(cost) | np.isinf(cost)] = 100
indexes = linear_sum_assignment(cost)
matched_idxes = []
for a, b in zip(*indexes):
matched_idxes.append((a, b))
return matched_idxes
\ No newline at end of file
tqdm
matplotlib
numpy<2.0.0
scikit-image<=0.20.0
opencv-python
gradio==4.43.0 # optional
\ No newline at end of file
model:
arch: unimernet
model_type: unimernet
model_config:
model_name: ./models/unimernet_base
max_seq_len: 1536
load_pretrained: True
pretrained: './models/unimernet_base/unimernet_base.pth'
tokenizer_config:
path: ./models/unimernet_base
datasets:
formula_rec_eval:
vis_processor:
eval:
name: "formula_image_eval"
image_size:
- 192
- 672
run:
runner: runner_iter
task: unimernet_train
batch_size_train: 64
batch_size_eval: 64
num_workers: 1
iters_per_inner_epoch: 2000
max_iters: 60000
seed: 42
output_dir: "../output/demo"
evaluate: True
test_splits: [ "eval" ]
device: "cuda"
world_size: 1
dist_url: "env://"
distributed: True
distributed_type: ddp # or fsdp when train llm
generate_cfg:
temperature: 0.0
\ No newline at end of file
model:
arch: unimernet
model_type: unimernet
model_config:
model_name: ./models/unimernet_base
max_seq_len: 1536
load_pretrained: False
load_finetuned: False
datasets:
formula_rec_train:
sample_ratio: 1
vis_processor:
train:
name: "formula_image_train"
image_size:
- 192
- 672
text_processor:
train:
name: "blip_caption"
max_words: 1536
build_info:
# unimath_train
images: ./data/UniMath1M/train/unimath_train
annotation: ./data/UniMath1M/train/unimath_train.txt
formula_rec_eval:
vis_processor:
eval:
name: "formula_image_eval"
image_size:
- 192
- 672
text_processor:
eval:
name: "blip_caption"
max_words: 1536
build_info:
images: ./data/UniMER-Test/cpe
annotation: ./data/UniMER-Test/cpe.txt
run:
runner: runner_iter
task: unimernet_train
# optimizer
lr_sched: "linear_warmup_cosine_lr"
init_lr: 1e-4
min_lr: 1e-8
warmup_lr: 1e-5
weight_decay: 0.05
batch_size_train: 8
batch_size_eval: 8
accum_grad_iters: 1
num_workers: 8
warmup_steps: 5000
iters_per_inner_epoch: 20000
max_iters: 300000
milestone: [1]
seed: 42
output_dir: "../outputs_unimernet/unimernet_base_encoder6666_decoder8_dim1024_30w_8xb8_f1_lr1e_4"
amp: True
resume_ckpt_path: null
evaluate: False
train_splits: [ "train" ]
valid_splits: [ "eval" ]
device: "cuda"
world_size: 1
dist_url: "env://"
distributed: True
distributed_type: ddp # or fsdp when train llm
generate_cfg:
temperature: 0.0
model:
arch: unimernet
model_type: unimernet
model_config:
model_name: ./models/unimernet_small
max_seq_len: 1536
load_pretrained: False
load_finetuned: False
datasets:
formula_rec_train:
sample_ratio: 1
vis_processor:
train:
name: "formula_image_train"
image_size:
- 192
- 672
text_processor:
train:
name: "blip_caption"
max_words: 1536
build_info:
# unimath_train
images: ./data/UniMath1M/train/unimath_train
annotation: ./data/UniMath1M/train/unimath_train.txt
formula_rec_eval:
vis_processor:
eval:
name: "formula_image_eval"
image_size:
- 192
- 672
text_processor:
eval:
name: "blip_caption"
max_words: 1536
build_info:
images: ./data/UniMER-Test/cpe
annotation: ./data/UniMER-Test/cpe.txt
run:
runner: runner_iter
task: unimernet_train
# optimizer
lr_sched: "linear_warmup_cosine_lr"
init_lr: 1e-4
min_lr: 1e-8
warmup_lr: 1e-5
weight_decay: 0.05
batch_size_train: 8
batch_size_eval: 8
accum_grad_iters: 1
num_workers: 8
warmup_steps: 5000
iters_per_inner_epoch: 20000
max_iters: 300000
milestone: [1]
seed: 42
output_dir: "../outputs_unimernet/unimernet_small_encoder6666_decoder8_dim1024_30w_8xb8_f1_lr1e_4"
amp: True
resume_ckpt_path: null
evaluate: False
train_splits: [ "train" ]
valid_splits: [ "eval" ]
device: "cuda"
world_size: 1
dist_url: "env://"
distributed: True
distributed_type: ddp # or fsdp when train llm
generate_cfg:
temperature: 0.0
model:
arch: unimernet
model_type: unimernet
model_config:
model_name: ./models/unimernet_tiny
max_seq_len: 1536
load_pretrained: False
load_finetuned: False
datasets:
formula_rec_train:
sample_ratio: 1
vis_processor:
train:
name: "formula_image_train"
image_size:
- 192
- 672
text_processor:
train:
name: "blip_caption"
max_words: 1536
build_info:
# unimath_train
images: ./data/UniMath1M/train/unimath_train
annotation: ./data/UniMath1M/train/unimath_train.txt
formula_rec_eval:
vis_processor:
eval:
name: "formula_image_eval"
image_size:
- 192
- 672
text_processor:
eval:
name: "blip_caption"
max_words: 1536
build_info:
images: ./data/UniMER-Test/cpe
annotation: ./data/UniMER-Test/cpe.txt
run:
runner: runner_iter
task: unimernet_train
# optimizer
lr_sched: "linear_warmup_cosine_lr"
init_lr: 1e-4
min_lr: 1e-8
warmup_lr: 1e-5
weight_decay: 0.05
batch_size_train: 8
batch_size_eval: 8
accum_grad_iters: 1
num_workers: 8
warmup_steps: 5000
iters_per_inner_epoch: 20000
max_iters: 300000
milestone: [1]
seed: 42
output_dir: "../outputs_unimernet/unimernet_base_encoder6666_decoder8_dim1024_30w_8xb8_f1_lr1e_4"
amp: True
resume_ckpt_path: null
evaluate: False
train_splits: [ "train" ]
valid_splits: [ "eval" ]
device: "cuda"
world_size: 1
dist_url: "env://"
distributed: True
distributed_type: ddp # or fsdp when train llm
generate_cfg:
temperature: 0.0
model:
arch: unimernet
model_type: unimernet
model_config:
model_name: ./models/unimernet_base
max_seq_len: 1536
load_pretrained: False
load_finetuned: True
finetuned: './models/unimernet_base.pth'
datasets:
formula_rec_train:
sample_ratio: 1
vis_processor:
train:
name: "formula_image_train"
image_size:
- 192
- 672
text_processor:
train:
name: "blip_caption"
max_words: 1536
build_info:
# unimath_train
images: ./data/UniMath1M/train/unimath_train
annotation: ./data/UniMath1M/train/unimath_train.txt
formula_rec_eval:
vis_processor:
eval:
name: "formula_image_eval"
image_size:
- 192
- 672
text_processor:
eval:
name: "blip_caption"
max_words: 1536
build_info:
images: ./data/UniMER-Test/cpe
annotation: ./data/UniMER-Test/cpe.txt
run:
runner: runner_iter
task: unimernet_train
# optimizer
lr_sched: "linear_warmup_cosine_lr"
init_lr: 1e-4
min_lr: 1e-8
warmup_lr: 1e-5
weight_decay: 0.05
batch_size_train: 8
batch_size_eval: 8
accum_grad_iters: 1
num_workers: 8
warmup_steps: 5000
iters_per_inner_epoch: 20000
max_iters: 300000
milestone: [1]
seed: 42
output_dir: "../outputs_unimernet/unimernet_base"
amp: True
resume_ckpt_path: null
evaluate: False
train_splits: [ "train" ]
valid_splits: [ "eval" ]
device: "cuda"
world_size: 1
dist_url: "env://"
distributed: True
distributed_type: ddp # or fsdp when train llm
generate_cfg:
temperature: 0.0
model:
arch: unimernet
model_type: unimernet
model_config:
model_name: ./models/unimernet_small
max_seq_len: 1536
load_pretrained: False
load_finetuned: True
finetuned: './models/unimernet_small.pth'
datasets:
formula_rec_train:
sample_ratio: 1
vis_processor:
train:
name: "formula_image_train"
image_size:
- 192
- 672
text_processor:
train:
name: "blip_caption"
max_words: 1536
build_info:
# unimath_train
images: ./data/UniMath1M/train/unimath_train
annotation: ./data/UniMath1M/train/unimath_train.txt
formula_rec_eval:
vis_processor:
eval:
name: "formula_image_eval"
image_size:
- 192
- 672
text_processor:
eval:
name: "blip_caption"
max_words: 1536
build_info:
images: ./data/UniMER-Test/cpe
annotation: ./data/UniMER-Test/cpe.txt
run:
runner: runner_iter
task: unimernet_train
# optimizer
lr_sched: "linear_warmup_cosine_lr"
init_lr: 1e-4
min_lr: 1e-8
warmup_lr: 1e-5
weight_decay: 0.05
batch_size_train: 8
batch_size_eval: 8
accum_grad_iters: 1
num_workers: 8
warmup_steps: 5000
iters_per_inner_epoch: 20000
max_iters: 300000
milestone: [1]
seed: 42
output_dir: "../outputs_unimernet/unimernet_small"
amp: True
resume_ckpt_path: null
evaluate: False
train_splits: [ "train" ]
valid_splits: [ "eval" ]
device: "cuda"
world_size: 1
dist_url: "env://"
distributed: True
distributed_type: ddp # or fsdp when train llm
generate_cfg:
temperature: 0.0
model:
arch: unimernet
model_type: unimernet
model_config:
model_name: ./models/unimernet_tiny
max_seq_len: 1536
load_pretrained: False
load_finetuned: True
finetuned: './models/unimernet_tiny.pth'
datasets:
formula_rec_train:
sample_ratio: 1
vis_processor:
train:
name: "formula_image_train"
image_size:
- 192
- 672
text_processor:
train:
name: "blip_caption"
max_words: 1536
build_info:
# unimath_train
images: ./data/UniMath1M/train/unimath_train
annotation: ./data/UniMath1M/train/unimath_train.txt
formula_rec_eval:
vis_processor:
eval:
name: "formula_image_eval"
image_size:
- 192
- 672
text_processor:
eval:
name: "blip_caption"
max_words: 1536
build_info:
images: ./data/UniMER-Test/cpe
annotation: ./data/UniMER-Test/cpe.txt
run:
runner: runner_iter
task: unimernet_train
# optimizer
lr_sched: "linear_warmup_cosine_lr"
init_lr: 1e-4
min_lr: 1e-8
warmup_lr: 1e-5
weight_decay: 0.05
batch_size_train: 8
batch_size_eval: 8
accum_grad_iters: 1
num_workers: 8
warmup_steps: 5000
iters_per_inner_epoch: 20000
max_iters: 300000
milestone: [1]
seed: 42
output_dir: "../outputs_unimernet/unimernet_tiny"
amp: True
resume_ckpt_path: null
evaluate: False
train_splits: [ "train" ]
valid_splits: [ "eval" ]
device: "cuda"
world_size: 1
dist_url: "env://"
distributed: True
distributed_type: ddp # or fsdp when train llm
generate_cfg:
temperature: 0.0
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### 1. Image processing class: accepts formula images, outputs LaTeX code and rendered images.\n"
]
},
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/bin/anaconda3/envs/unimernetv2_pip/lib/python3.10/site-packages/tqdm/auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n",
" from .autonotebook import tqdm as notebook_tqdm\n",
"/Users/bin/anaconda3/envs/unimernetv2_pip/lib/python3.10/site-packages/torchtext/data/__init__.py:4: UserWarning: \n",
"/!\\ IMPORTANT WARNING ABOUT TORCHTEXT STATUS /!\\ \n",
"Torchtext is deprecated and the last released version will be 0.18 (this one). You can silence this warning by calling the following at the beginnign of your scripts: `import torchtext; torchtext.disable_torchtext_deprecation_warning()`\n",
" warnings.warn(torchtext._TORCHTEXT_DEPRECATION_MSG)\n"
]
}
],
"source": [
"import argparse\n",
"import os\n",
"import random\n",
"import sys\n",
"\n",
"from IPython.display import display, Math\n",
"from PIL import Image\n",
"from rich import print as rprint\n",
"from rich.panel import Panel\n",
"from rich.rule import Rule\n",
"from rich.table import Table\n",
"from termcolor import colored\n",
"import torch\n",
"\n",
"sys.path.insert(0, os.path.join(os.getcwd(), \"..\"))\n",
"from unimernet.common.config import Config\n",
"from unimernet.datasets.builders import *\n",
"from unimernet.models import *\n",
"from unimernet.processors import *\n",
"import unimernet.tasks as tasks\n",
"from unimernet.processors import load_processor\n",
"\n",
"class ImageProcessor:\n",
" \n",
" def __init__(self, cfg_path, image_dir):\n",
" self.cfg_path = cfg_path\n",
" self.image_dir = image_dir\n",
" self.device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
" self.model, self.vis_processor = self.load_model_and_processor()\n",
"\n",
" def load_model_and_processor(self):\n",
" args = argparse.Namespace(cfg_path=self.cfg_path, options=None)\n",
" cfg = Config(args)\n",
" task = tasks.setup_task(cfg)\n",
" model = task.build_model(cfg).to(self.device)\n",
" vis_processor = load_processor('formula_image_eval', cfg.config.datasets.formula_rec_eval.vis_processor.eval)\n",
"\n",
" return model, vis_processor\n",
"\n",
" def process_single_image(self, image_path):\n",
" try:\n",
" raw_image = Image.open(image_path)\n",
" except IOError:\n",
" print(f\"Error: Unable to open image at {image_path}\")\n",
" return\n",
"\n",
" resized_image = self.resize_image(raw_image)\n",
" image = self.vis_processor(raw_image).unsqueeze(0).to(self.device)\n",
" output = self.model.generate({\"image\": image})\n",
" pred = output[\"pred_str\"][0]\n",
" self.print_result(0, image_path, resized_image, pred)\n",
" rprint(Rule(style=\"black\"))\n",
"\n",
" def process_images(self):\n",
" image_names = os.listdir(self.image_dir)\n",
" image_paths = [os.path.join(self.image_dir, name) for name in image_names]\n",
"\n",
" for id, image_path in enumerate(image_paths):\n",
" raw_image = Image.open(image_path)\n",
" resized_image = self.resize_image(raw_image)\n",
" image = self.vis_processor(raw_image).unsqueeze(0).to(self.device)\n",
" output = self.model.generate({\"image\": image})\n",
" pred = output[\"pred_str\"][0]\n",
" self.print_result(id, image_path, resized_image, pred)\n",
" rprint(Rule(style=\"black\"))\n",
"\n",
" @staticmethod\n",
" def resize_image(image, max_len=600):\n",
" width, height = image.size\n",
" if max(width, height) > max_len :\n",
" if width > height:\n",
" scale = float(max_len) / width\n",
" width = max_len\n",
" height = int(height * scale)\n",
" else:\n",
" scale = float(max_len) / height\n",
" height = max_len\n",
" width = int(width * scale)\n",
"\n",
" return image.resize((width, height))\n",
"\n",
" @staticmethod\n",
" def print_result(id, image_path, raw_image, pred):\n",
" colors = ['red', 'green', 'yellow', 'blue', 'magenta', 'cyan']\n",
" chosen_color = random.choice(colors)\n",
"\n",
" table = Table(show_header=True, header_style=chosen_color)\n",
" table.add_column(\"Sample ID\", style=\"dim\", width=12)\n",
" table.add_column(\"Image Path\", style=\"dim\", width=80)\n",
" table.add_row(str(id), image_path)\n",
" rprint(table)\n",
" print(colored(f\"{id}_1: Source image\", chosen_color), end=\" \")\n",
" display(raw_image)\n",
" print(colored(f'{id}_2: Rendered image from LaTeX', chosen_color), end=\" \")\n",
" render_katex(pred)\n",
" print(colored(f'{id}_3: Predicted LaTeX code', chosen_color), end=\" \")\n",
" pred_text_panel = Panel.fit(pred, title=\"Predicted LaTeX\", border_style=chosen_color)\n",
" rprint(pred_text_panel)\n",
"\n",
"def render_katex(latex_string, show=True):\n",
" display(Math(latex_string))\n"
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/bin/anaconda3/envs/unimernetv2_pip/lib/python3.10/site-packages/transformers/models/auto/image_processing_auto.py:510: FutureWarning: The image_processor_class argument is deprecated and will be removed in v4.42. Please use `slow_image_processor_class`, or `fast_image_processor_class` instead\n",
" warnings.warn(\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"CustomVisionEncoderDecoderModel init\n",
"VariableUnimerNetModel init\n",
"VariableUnimerNetPatchEmbeddings init\n",
"VariableUnimerNetModel init\n",
"VariableUnimerNetPatchEmbeddings init\n",
"CustomMBartForCausalLM init\n",
"CustomMBartDecoder init\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/Users/bin/anaconda3/envs/unimernetv2_pip/lib/python3.10/site-packages/transformers/generation/configuration_utils.py:540: UserWarning: `do_sample` is set to `False`. However, `temperature` is set to `0.2` -- this flag is only used in sample-based generation modes. You should set `do_sample=True` or unset `temperature`.\n",
" warnings.warn(\n",
"/Users/bin/anaconda3/envs/unimernetv2_pip/lib/python3.10/site-packages/transformers/generation/configuration_utils.py:545: UserWarning: `do_sample` is set to `False`. However, `top_p` is set to `0.95` -- this flag is only used in sample-based generation modes. You should set `do_sample=True` or unset `top_p`.\n",
" warnings.warn(\n"
]
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\">┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
"┃<span style=\"color: #000080; text-decoration-color: #000080\"> Sample ID </span>┃<span style=\"color: #000080; text-decoration-color: #000080\"> Image Path </span>┃\n",
"┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
"│<span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\"> 0 </span>│<span style=\"color: #7f7f7f; text-decoration-color: #7f7f7f\"> /Users/bin/code/GoGoGo/UniMERNet/asset/test_imgs/0000001.png </span>│\n",
"└──────────────┴──────────────────────────────────────────────────────────────────────────────────┘\n",
"</pre>\n"
],
"text/plain": [
"┏━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓\n",
"┃\u001b[34m \u001b[0m\u001b[34mSample ID \u001b[0m\u001b[34m \u001b[0m┃\u001b[34m \u001b[0m\u001b[34mImage Path \u001b[0m\u001b[34m \u001b[0m┃\n",
"┡━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩\n",
"│\u001b[2m \u001b[0m\u001b[2m0 \u001b[0m\u001b[2m \u001b[0m│\u001b[2m \u001b[0m\u001b[2m/Users/bin/code/GoGoGo/UniMERNet/asset/test_imgs/0000001.png \u001b[0m\u001b[2m \u001b[0m│\n",
"└──────────────┴──────────────────────────────────────────────────────────────────────────────────┘\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[34m0_1: Source image\u001b[0m "
]
},
{
"data": {
"image/jpeg": "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",
"image/png": "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",
"text/plain": [
"<PIL.Image.Image image mode=L size=600x136>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[34m0_2: Rendered image from LaTeX\u001b[0m "
]
},
{
"data": {
"text/latex": [
"$\\displaystyle \\begin{array} { r l } { \\mathrm { M i n i m i s e ~ } } & { { } J ( u . ; s , y ) = \\mathbb { E } \\left[ \\int _ { s } ^ { T } \\left( u _ { t } ^ { 2 } + 1 \\right) d t - \\ln \\left( \\cosh \\left( X _ { T } \\right) \\right) \\right] } \\\\ { \\mathrm { s u b j e c t ~ t o ~ } } & { { } \\left\\{ \\begin{array} { l l } { d X _ { t } = 2 u _ { t } d t + \\sqrt { 2 } d W _ { t } , t \\in [ s , T ] } \\\\ { X _ { s } = y } \\\\ { u _ { t } \\in [ - 1 , 1 ] , \\quad t \\in [ s , T ] } \\end{array} \\right. } \\end{array}$"
],
"text/plain": [
"<IPython.core.display.Math object>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[34m0_3: Predicted LaTeX code\u001b[0m "
]
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #000080; text-decoration-color: #000080\">╭──────────────────────────────────────────────── Predicted LaTeX ────────────────────────────────────────────────╮</span>\n",
"<span style=\"color: #000080; text-decoration-color: #000080\">│</span> \\begin{array} { r l } { \\mathrm { M i n i m i s e ~ } } &amp; { { } J ( u . ; s , y ) = \\mathbb { E } \\left[ \\int _ <span style=\"color: #000080; text-decoration-color: #000080\">│</span>\n",
"<span style=\"color: #000080; text-decoration-color: #000080\">│</span> { s } ^ { T } \\left( u _ { t } ^ { 2 } + 1 \\right) d t - \\ln \\left( \\cosh \\left( X _ { T } \\right) \\right) <span style=\"color: #000080; text-decoration-color: #000080\">│</span>\n",
"<span style=\"color: #000080; text-decoration-color: #000080\">│</span> \\right] } \\\\ { \\mathrm { s u b j e c t ~ t o ~ } } &amp; { { } \\left\\{ \\begin{array} { l l } { d X _ { t } = 2 u _ <span style=\"color: #000080; text-decoration-color: #000080\">│</span>\n",
"<span style=\"color: #000080; text-decoration-color: #000080\">│</span> { t } d t + \\sqrt { 2 } d W _ { t } , t \\in [ s , T ] } \\\\ { X _ { s } = y } \\\\ { u _ { t } \\in [ - 1 , 1 ] , <span style=\"color: #000080; text-decoration-color: #000080\">│</span>\n",
"<span style=\"color: #000080; text-decoration-color: #000080\">│</span> \\quad t \\in [ s , T ] } \\end{array} \\right. } \\end{array} <span style=\"color: #000080; text-decoration-color: #000080\">│</span>\n",
"<span style=\"color: #000080; text-decoration-color: #000080\">╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[34m╭─\u001b[0m\u001b[34m───────────────────────────────────────────────\u001b[0m\u001b[34m Predicted LaTeX \u001b[0m\u001b[34m───────────────────────────────────────────────\u001b[0m\u001b[34m─╮\u001b[0m\n",
"\u001b[34m│\u001b[0m \\begin{array} { r l } { \\mathrm { M i n i m i s e ~ } } & { { } J ( u . ; s , y ) = \\mathbb { E } \\left[ \\int _ \u001b[34m│\u001b[0m\n",
"\u001b[34m│\u001b[0m { s } ^ { T } \\left( u _ { t } ^ { 2 } + 1 \\right) d t - \\ln \\left( \\cosh \\left( X _ { T } \\right) \\right) \u001b[34m│\u001b[0m\n",
"\u001b[34m│\u001b[0m \\right] } \\\\ { \\mathrm { s u b j e c t ~ t o ~ } } & { { } \\left\\{ \\begin{array} { l l } { d X _ { t } = 2 u _ \u001b[34m│\u001b[0m\n",
"\u001b[34m│\u001b[0m { t } d t + \\sqrt { 2 } d W _ { t } , t \\in [ s , T ] } \\\\ { X _ { s } = y } \\\\ { u _ { t } \\in [ - 1 , 1 ] , \u001b[34m│\u001b[0m\n",
"\u001b[34m│\u001b[0m \\quad t \\in [ s , T ] } \\end{array} \\right. } \\end{array} \u001b[34m│\u001b[0m\n",
"\u001b[34m╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"<pre style=\"white-space:pre;overflow-x:auto;line-height:normal;font-family:Menlo,'DejaVu Sans Mono',consolas,'Courier New',monospace\"><span style=\"color: #000000; text-decoration-color: #000000\">───────────────────────────────────────────────────────────────────────────────────────────────────────────────────</span>\n",
"</pre>\n"
],
"text/plain": [
"\u001b[30m───────────────────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"root_path = os.path.abspath(os.getcwd())\n",
"config_path = os.path.join(root_path, \"configs/demo.yaml\")\n",
"image_directory = os.path.join(root_path, \"asset/test_imgs\")\n",
"\n",
"processor = ImageProcessor(config_path, image_directory)\n",
"\n",
"# Process a single image located at the specified path\n",
"processor.process_single_image(os.path.join(image_directory, '0000001.png'))\n",
"\n",
"# Uncomment the following line to process all images in the specified directory\n",
"# processor.process_images()\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.14"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
import argparse
import os
import sys
import numpy as np
import cv2
import torch
from PIL import Image
sys.path.insert(0, os.path.join(os.getcwd(), ".."))
from unimernet.common.config import Config
import unimernet.tasks as tasks
from unimernet.processors import load_processor
class ImageProcessor:
def __init__(self, cfg_path):
self.cfg_path = cfg_path
self.device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
self.model, self.vis_processor = self.load_model_and_processor()
def load_model_and_processor(self):
args = argparse.Namespace(cfg_path=self.cfg_path, options=None)
cfg = Config(args)
task = tasks.setup_task(cfg)
model = task.build_model(cfg).to(self.device)
vis_processor = load_processor('formula_image_eval', cfg.config.datasets.formula_rec_eval.vis_processor.eval)
return model, vis_processor
def process_single_image(self, image_path):
try:
raw_image = Image.open(image_path)
except IOError:
print(f"Error: Unable to open image at {image_path}")
return
# Convert PIL Image to OpenCV format
open_cv_image = np.array(raw_image)
# Convert RGB to BGR
if len(open_cv_image.shape) == 3:
# Convert RGB to BGR
open_cv_image = open_cv_image[:, :, ::-1].copy()
# Display the image using cv2
image = self.vis_processor(raw_image).unsqueeze(0).to(self.device)
output = self.model.generate({"image": image})
pred = output["pred_str"][0]
print(f'Prediction:\n{pred}')
cv2.imshow('Original Image', open_cv_image)
cv2.waitKey(0)
cv2.destroyAllWindows()
return pred
if __name__ == "__main__":
root_path = os.path.abspath(os.getcwd())
config_path = os.path.join(root_path, "configs/demo.yaml")
processor = ImageProcessor(config_path)
# Process a single image located at the specified path
image_path = os.path.join(root_path, 'asset/test_imgs', '0000001.png')
latex_code = processor.process_single_image(image_path)
CustomVisionEncoderDecoderModel init
CustomMBartForCausalLM init
CustomMBartDecoder init
arch_name:unimernet
model_type:unimernet
checkpoint:
====================================================================================================
Device:cuda
Load model: 23.643s
len_gts:6762, len_preds=6762
norm_gts[0]:S\sim\tilde{\psi}Q_{o}\tilde{\psi}+g_{s}^{1/2}\tilde{\psi}^{3}+\tilde{\phi}Q_{c}\tilde{\phi}+g_{s}\tilde{\phi}^{3}+\tilde{\phi}B(g_{s}^{1/2}\tilde{\psi})+\cdots.
norm_preds[0]:S\sim\tilde{\psi}Q_{o}\tilde{\psi}+g_{s}^{1/2}\tilde{\psi}^{3}+\tilde{\phi}Q_{c}\tilde{\phi}+g_{s}\tilde{\phi}^{3}+\tilde{\phi}B(g_{s}^{1/2}\tilde{\psi})+\cdots.
Evaluation Set:Simple Print Expression(SPE)
Inference Time: 690.366847038269s
bleu ⬆ edit ⬇
-------- --------
0.917711 0.058556
====================================================================================================
len_gts:5921, len_preds=5921
norm_gts[0]:\begin{array}{r l}{\mathcal{L}(\{\mathbf{u,v,w,z,x}\},\{\boldsymbol{\kappa,\lambda,\mu,\nu}\})=\frac{1}{2}\|\mathbf{y-Cu}\|_{2}^{2}}&{+\tau_{1}\|\mathbf{v}\|_{1}+\tau_{2}\|\mathbf{w}\|_{1}}\\ &{+\frac{\rho_{1}}{2}\|\mathbf{Ax-u}\|_{2}^{2}+\boldsymbol{\kappa}^{\top}(\mathbf{Ax-u})}\\ &{+\frac{\rho_{2}}{2}\|\mathbf{x-v}\|_{2}^{2}+\boldsymbol{\lambda}^{\top}(\mathbf{x-v})}\\ &{+\frac{\rho_{3}}{2}\|\mathbf{Dx-w}\|_{2}^{2}+\boldsymbol{\mu}^{\top}(\mathbf{Dx-w})}\\ &{+\frac{\rho_{4}}{2}\|\mathbf{x-z}\|_{2}^{2}+\boldsymbol{\nu}^{\top}(\mathbf{x-z})}\\ &{+\mathcal{I}_{+}(\mathbf{z})}\end{array}
norm_preds[0]:\begin{array}{r l}{\mathcal{L}(\{\mathbf{u},\mathbf{v},\mathbf{w},\mathbf{z},\mathbf{x}\},\{\kappa,\lambda,\mu,\nu\})=\frac{1}{2}\|\mathbf{y}-\mathbf{Cu}\|_{2}^{2}}&{+\tau_{1}\|\mathbf{v}\|_{1}+\tau_{2}\|\mathbf{w}\|_{1}}\\ &{+\frac{\rho_{1}}{2}\|\mathbf{Ax}-\mathbf{u}\|_{2}^{2}+\kappa^{\top}(\mathbf{Ax}-\mathbf{u})}\\ &{+\frac{\rho_{2}}{2}\|\mathbf{x}-\mathbf{v}\|_{2}^{2}+\boldsymbol{\lambda}^{\top}(\mathbf{x}-\mathbf{v})}\\ &{+\frac{\rho_{3}}{2}\|\mathbf{Dx}-\mathbf{w}\|_{2}^{2}+\mu^{\top}(\mathbf{Dx}-\mathbf{w})}\\ &{+\frac{\rho_{4}}{2}\|\mathbf{x}-\mathbf{z}\|_{2}^{2}+\nu^{\top}(\mathbf{x}-\mathbf{z})}\\ &{+\mathcal{I}_{+}(\mathbf{z})}\end{array}
Evaluation Set:Complex Print Expression(CPE)
Inference Time: 2109.273235321045s
bleu ⬆ edit ⬇
-------- --------
0.913843 0.057546
====================================================================================================
len_gts:4742, len_preds=4742
norm_gts[0]:F_{i}[z](x,y)=f_{i}(x,y,z)\ i=1,\ldots,n,
norm_preds[0]:F_{i}[z](x,y)=f_{i}(x,y,z)\ i=1,\ldots,n,
Evaluation Set:Screen Capture Expression(SCE)
Inference Time: 2425.0937654972076s
bleu ⬆ edit ⬇
-------- --------
0.617196 0.22953
====================================================================================================
len_gts:6332, len_preds=6332
norm_gts[0]:b_{n+1}-b_{n}=-1
norm_preds[0]:b_{n+1}-b_{n}=-1
Evaluation Set:Handwritten Expression(HWE)
Inference Time: 2543.7626719474792s
bleu ⬆ edit ⬇
-------- ---------
0.920905 0.0546271
====================================================================================================
CustomVisionEncoderDecoderModel init
VariableUnimerNetModel init
VariableUnimerNetPatchEmbeddings init
VariableUnimerNetModel init
VariableUnimerNetPatchEmbeddings init
CustomMBartForCausalLM init
CustomMBartDecoder init
arch_name:unimernet
model_type:unimernet
checkpoint:
====================================================================================================
Device:cuda
Load model: 25.267s
len_gts:6762, len_preds=6762
norm_gts[0]:S\sim\tilde{\psi}Q_{o}\tilde{\psi}+g_{s}^{1/2}\tilde{\psi}^{3}+\tilde{\phi}Q_{c}\tilde{\phi}+g_{s}\tilde{\phi}^{3}+\tilde{\phi}B(g_{s}^{1/2}\tilde{\psi})+\cdots.
norm_preds[0]:S\sim\tilde{\psi}Q_{o}\tilde{\psi}+g_{s}^{1/2}\tilde{\psi}^{3}+\tilde{\phi}Q_{c}\tilde{\phi}+g_{s}\tilde{\phi}^{3}+\tilde{\phi}B(g_{s}^{1/2}\tilde{\psi})+\cdots.
Evaluation Set:Simple Print Expression(SPE)
Inference Time: 561.8906960487366s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.915231 0.901589 0.0600379
====================================================================================================
len_gts:5921, len_preds=5921
norm_gts[0]:\begin{array}{r l}{\mathcal{L}(\{\mathbf{u,v,w,z,x}\},\{\boldsymbol{\kappa,\lambda,\mu,\nu}\})=\frac{1}{2}\|\mathbf{y-Cu}\|_{2}^{2}}&{+\tau_{1}\|\mathbf{v}\|_{1}+\tau_{2}\|\mathbf{w}\|_{1}}\\ &{+\frac{\rho_{1}}{2}\|\mathbf{Ax-u}\|_{2}^{2}+\boldsymbol{\kappa}^{\top}(\mathbf{Ax-u})}\\ &{+\frac{\rho_{2}}{2}\|\mathbf{x-v}\|_{2}^{2}+\boldsymbol{\lambda}^{\top}(\mathbf{x-v})}\\ &{+\frac{\rho_{3}}{2}\|\mathbf{Dx-w}\|_{2}^{2}+\boldsymbol{\mu}^{\top}(\mathbf{Dx-w})}\\ &{+\frac{\rho_{4}}{2}\|\mathbf{x-z}\|_{2}^{2}+\boldsymbol{\nu}^{\top}(\mathbf{x-z})}\\ &{+\mathcal{I}_{+}(\mathbf{z})}\end{array}
norm_preds[0]:\begin{array}{r l}{\mathcal{L}(\{\mathbf{u},\mathbf{v},\mathbf{w},\mathbf{z},\mathbf{x}\},\{\kappa,\lambda,\mu,\nu\})=\frac{1}{2}\|\mathbf{y}-\mathbf{Cu}\|_{2}^{2}}&{+\tau_{1}\|\mathbf{v}\|_{1}+\tau_{2}\|\mathbf{w}\|_{1}}\\ &{+\frac{\rho_{1}}{2}\|\mathbf{Ax}-\mathbf{u}\|_{2}^{2}+\kappa^{\top}(\mathbf{Ax}-\mathbf{u})}\\ &{+\frac{\rho_{2}}{2}\|\mathbf{x}-\mathbf{v}\|_{2}^{2}+\lambda^{\top}(\mathbf{x}-\mathbf{v})}\\ &{+\frac{\rho_{3}}{2}\|\mathbf{Dx}-\mathbf{w}\|_{2}^{2}+\mu^{\top}(\mathbf{Dx}-\mathbf{w})}\\ &{+\frac{\rho_{4}}{2}\|\mathbf{x}-\mathbf{z}\|_{2}^{2}+\nu^{\top}(\mathbf{x}-\mathbf{z})}\\ &{+\mathcal{I}_{+}(\mathbf{z})}\end{array}
Evaluation Set:Complex Print Expression(CPE)
Inference Time: 1556.0770707130432s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.924907 0.901307 0.0561145
====================================================================================================
len_gts:4742, len_preds=4742
norm_gts[0]:F_{i}[z](x,y)=f_{i}(x,y,z)\ i=1,\ldots,n,
norm_preds[0]:F_{i}[z](x,y)=f_{i}(x,y,z)\ i=1,\ldots,n,
Evaluation Set:Screen Capture Expression(SCE)
Inference Time: 1822.9997293949127s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- --------
0.626271 0.677379 0.223768
====================================================================================================
len_gts:6332, len_preds=6332
norm_gts[0]:b_{n+1}-b_{n}=-1
norm_preds[0]:b_{n+1}-b_{n}=-1.
Evaluation Set:Handwritten Expression(HWE)
Inference Time: 1900.8741807937622s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.894818 0.853878 0.0716135
====================================================================================================
\ No newline at end of file
CustomVisionEncoderDecoderModel init
VariableUnimerNetModel init
VariableUnimerNetPatchEmbeddings init
VariableUnimerNetModel init
VariableUnimerNetPatchEmbeddings init
CustomMBartForCausalLM init
CustomMBartDecoder init
arch_name:unimernet
model_type:unimernet
checkpoint:
====================================================================================================
Device:cuda
Load model: 14.039s
len_gts:6762, len_preds=6762
norm_gts[0]:S\sim\tilde{\psi}Q_{o}\tilde{\psi}+g_{s}^{1/2}\tilde{\psi}^{3}+\tilde{\phi}Q_{c}\tilde{\phi}+g_{s}\tilde{\phi}^{3}+\tilde{\phi}B(g_{s}^{1/2}\tilde{\psi})+\cdots.
norm_preds[0]:S\sim\tilde{\psi}Q_{o}\tilde{\psi}+g_{s}^{1/2}\tilde{\psi}^{3}+\tilde{\phi}Q_{c}\tilde{\phi}+g_{s}\tilde{\phi}^{3}+\tilde{\phi}B(g_{s}^{1/2}\tilde{\psi})+\cdots.
Evaluation Set:Simple Print Expression(SPE)
Inference Time: 489.57221508026123s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.913463 0.89984 0.0614923
====================================================================================================
len_gts:5921, len_preds=5921
norm_gts[0]:\begin{array}{r l}{\mathcal{L}(\{\mathbf{u,v,w,z,x}\},\{\boldsymbol{\kappa,\lambda,\mu,\nu}\})=\frac{1}{2}\|\mathbf{y-Cu}\|_{2}^{2}}&{+\tau_{1}\|\mathbf{v}\|_{1}+\tau_{2}\|\mathbf{w}\|_{1}}\\ &{+\frac{\rho_{1}}{2}\|\mathbf{Ax-u}\|_{2}^{2}+\boldsymbol{\kappa}^{\top}(\mathbf{Ax-u})}\\ &{+\frac{\rho_{2}}{2}\|\mathbf{x-v}\|_{2}^{2}+\boldsymbol{\lambda}^{\top}(\mathbf{x-v})}\\ &{+\frac{\rho_{3}}{2}\|\mathbf{Dx-w}\|_{2}^{2}+\boldsymbol{\mu}^{\top}(\mathbf{Dx-w})}\\ &{+\frac{\rho_{4}}{2}\|\mathbf{x-z}\|_{2}^{2}+\boldsymbol{\nu}^{\top}(\mathbf{x-z})}\\ &{+\mathcal{I}_{+}(\mathbf{z})}\end{array}
norm_preds[0]:\begin{array}{r l}{\mathcal{L}(\{\mathbf{u,v,w,z,x}\},\{\kappa,\lambda,\mu,\nu\})=\frac{1}{2}\|\mathbf{y-Cu}\|_{2}^{2}}&{+\tau_{1}\|\mathbf{v}\|_{1}+\tau_{2}\|\mathbf{w}\|_{1}}\\ &{+\frac{\rho_{1}}{2}\|\mathbf{Ax-u}\|_{2}^{2}+\kappa^{\top}(\mathbf{Ax-u})}\\ &{+\frac{\rho_{2}}{2}\|\mathbf{x-v}\|_{2}^{2}+\lambda^{\top}(\mathbf{x-v})}\\ &{+\frac{\rho_{3}}{2}\|\mathbf{Dx-w}\|_{2}^{2}+\mu^{\top}(\mathbf{Dx-w})}\\ &{+\frac{\rho_{4}}{2}\|\mathbf{x-z}\|_{2}^{2}+\nu^{\top}(\mathbf{x-z})}\\ &{+\mathcal{I}_{+}(\mathbf{z})}\end{array}
Evaluation Set:Complex Print Expression(CPE)
Inference Time: 1294.0490927696228s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.919805 0.895851 0.0597243
====================================================================================================
len_gts:4742, len_preds=4742
norm_gts[0]:F_{i}[z](x,y)=f_{i}(x,y,z)\ i=1,\ldots,n,
norm_preds[0]:F_{i}[z](x,y)=f_{i}(x,y,z)~i=1,\ldots,n,
Evaluation Set:Screen Capture Expression(SCE)
Inference Time: 1570.1024556159973s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- --------
0.617545 0.678047 0.22849
====================================================================================================
len_gts:6332, len_preds=6332
norm_gts[0]:b_{n+1}-b_{n}=-1
norm_preds[0]:b_{n+1}-b_{n}=-1
Evaluation Set:Handwritten Expression(HWE)
Inference Time: 1641.9185965061188s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.889342 0.850023 0.0748587
================================
\ No newline at end of file
CustomVisionEncoderDecoderModel init
VariableUnimerNetModel init
VariableUnimerNetPatchEmbeddings init
VariableUnimerNetModel init
VariableUnimerNetPatchEmbeddings init
CustomMBartForCausalLM init
CustomMBartDecoder init
arch_name:unimernet
model_type:unimernet
checkpoint:
====================================================================================================
Device:cuda
Load model: 10.674s
len_gts:6762, len_preds=6762
norm_gts[0]:S\sim\tilde{\psi}Q_{o}\tilde{\psi}+g_{s}^{1/2}\tilde{\psi}^{3}+\tilde{\phi}Q_{c}\tilde{\phi}+g_{s}\tilde{\phi}^{3}+\tilde{\phi}B(g_{s}^{1/2}\tilde{\psi})+\cdots.
norm_preds[0]:S\sim\tilde{\psi}Q_{o}\tilde{\psi}+g_{s}^{1/2}\tilde{\psi}^{3}+\tilde{\phi}Q_{c}\tilde{\phi}+g_{s}\tilde{\phi}^{3}+\tilde{\phi}B(g_{s}^{1/2}\tilde{\psi})+\cdots.
Evaluation Set:Simple Print Expression(SPE)
Inference Time: 437.5355474948883s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.909031 0.895183 0.0661603
====================================================================================================len_gts:5921, len_preds=5921
len_gts:5921, len_preds=5921
norm_gts[0]:\begin{array}{r l}{\mathcal{L}(\{\mathbf{u,v,w,z,x}\},\{\boldsymbol{\kappa,\lambda,\mu,\nu}\})=\frac{1}{2}\|\mathbf{y-Cu}\|_{2}^{2}}&{+\tau_{1}\|\mathbf{v}\|_{1}+\tau_{2}\|\mathbf{w}\|_{1}}\\ &{+\frac{\rho_{1}}{2}\|\mathbf{Ax-u}\|_{2}^{2}+\boldsymbol{\kappa}^{\top}(\mathbf{Ax-u})}\\ &{+\frac{\rho_{2}}{2}\|\mathbf{x-v}\|_{2}^{2}+\boldsymbol{\lambda}^{\top}(\mathbf{x-v})}\\ &{+\frac{\rho_{3}}{2}\|\mathbf{Dx-w}\|_{2}^{2}+\boldsymbol{\mu}^{\top}(\mathbf{Dx-w})}\\ &{+\frac{\rho_{4}}{2}\|\mathbf{x-z}\|_{2}^{2}+\boldsymbol{\nu}^{\top}(\mathbf{x-z})}\\ &{+\mathcal{I}_{+}(\mathbf{z})}\end{array}
norm_preds[0]:\begin{array}{r l}{\mathcal{L}(\{\mathbf{u},\mathbf{v},\mathbf{w},\mathbf{z},\mathbf{x}\},\{\mathbf{x},\lambda,\mu,\nu\})=\frac{1}{2}\|\mathbf{y}-\mathbf{Cu}\|_{2}^{2}}&{+\tau_{1}\|\mathbf{v}\|_{1}+\tau_{2}\|\mathbf{w}\|_{1}}\\ &{+\frac{\rho_{1}}{2}\|\mathbf{Ax}-\mathbf{u}\|_{2}^{2}+\kappa^{\top}(\mathbf{Ax}-\mathbf{u})}\\ &{+\frac{\rho_{2}}{2}\|\mathbf{x}-\mathbf{v}\|_{2}^{2}+\lambda^{\top}(\mathbf{x}-\mathbf{v})}\\ &{+\frac{\rho_{3}}{2}\|\mathbf{Dx}-\mathbf{w}\|_{2}^{2}+\mu^{\top}(\mathbf{Dx}-\mathbf{w})}\\ &{+\frac{\rho_{4}}{2}\|\mathbf{x}-\mathbf{z}\|_{2}^{2}+\nu^{\top}(\mathbf{x}-\mathbf{z})}\\ &{+\mathcal{I}_{+}(\mathbf{z})}\end{array}
Evaluation Set:Complex Print Expression(CPE)
Inference Time: 1043.2925176620483s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.902193 0.876609 0.0746548
====================================================================================================
len_gts:4742, len_preds=4742
norm_gts[0]:F_{i}[z](x,y)=f_{i}(x,y,z)\ i=1,\ldots,n,
norm_preds[0]:F_{i}[z](x,y)=f_{i}(x,y,z)~i=1,\ldots,n,
Evaluation Set:Screen Capture Expression(SCE)
Inference Time: 1415.7678081989288s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- --------
0.56585 0.672292 0.238716
====================================================================================================
len_gts:6332, len_preds=6332
norm_gts[0]:b_{n+1}-b_{n}=-1
norm_preds[0]:b_{n+1}-b_{n}=-1
Evaluation Set:Handwritten Expression(HWE)
Inference Time: 1480.4430103302002s
bleu ⬆ meteor ⬆ edit ⬇
-------- ---------- ---------
0.883151 0.845897 0.0783475
====================================================================================================
# 模型唯一标识
modelCode=1820
# 模型名称
modelName=unimernet_transformers
# 模型描述
modelDescription=UniMERNet是一款专为数学公式识别设计的深度学习模型,支持将手写或打印的数学公式图像转换为LaTeX代码,适用于多种应用场景
processType=推理
# 算法类别
appScenario=文本生成
# 框架类型
frameType=vllm
# 加速卡类型
accelerateType=K100AI
\ No newline at end of file
Put [model files](https://huggingface.co/wanderkid/unimernet/tree/main) here:
unimernet
├── README.md
├── config.json
├── preprocessor_config.json
├── pytorch_model.bin
├── tokenizer.json
└── tokenizer_config.json
\ No newline at end of file
[tool.poetry]
name = "unimernet"
version = "0.2.3"
description = 'UniMERNet: A Universal Network for Real-World Mathematical Expression Recognition'
authors = ["Bin Wang <ictwangbin@gmail.com>"]
readme = "README.md"
license = "Apache License 2.0"
repository = "https://github.com/opendatalab/UniMERNet"
keywords = ["MER", "latex", "markdown", "pdf"]
include = [
"train.py",
"test.py",
"demo.py",
"unimernet_app.py",
"run_unimernet_app.py",
]
[tool.poetry.dependencies]
python = ">=3.10"
torch = ">=2.2.2"
torchvision = ">=0.17.2"
omegaconf = "^2.3.0"
matplotlib = "^3.8.4"
iopath = "^0.1.9"
timm = "^0.9.16"
opencv-python = "^4.6.0"
transformers = "4.42.4"
fairscale = "^0.4.13"
# ftfy = "^6.2.0"
ftfy = {version = "^6.2.0", python = ">=3.10,<4.0"}
albumentations = "^1.4.4"
wand = "^0.6.13"
webdataset = "^0.2.86"
rapidfuzz = "^3.8.1"
termcolor = "^2.4.0"
pandas = "^2.2.2"
evaluate = "^0.4.1"
rich = "^13.7.1"
jupyterlab = "^4.1.6"
tabulate = "^0.9.0"
nltk = "^3.8.1"
streamlit = "^1.33.0"
pypdfium2 = "^4.29.0"
pdf2image = "^1.17.0"
streamlit_drawable_canvas = "^0.9.3"
[tool.poetry.extras]
full = [
"termcolor",
"pandas",
"rich",
"jupyterlab",
"tabulate",
"nltk",
"streamlit",
"pypdfium2",
"pdf2image",
"streamlit_drawable_canvas"
]
[tool.poetry.scripts]
unimernet = "demo:main"
unimernet_gui = "run_unimernet_app:run_app"
unimernet_eval = "test:main"
[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment