merterbak commited on
Commit
988d7f0
Β·
verified Β·
1 Parent(s): e16ff08

Converting to DeepSeek-OCR-2

Browse files
Files changed (1) hide show
  1. app.py +55 -33
app.py CHANGED
@@ -14,23 +14,25 @@ import numpy as np
14
  import base64
15
  from io import StringIO, BytesIO
16
 
17
- MODEL_NAME = 'deepseek-ai/DeepSeek-OCR'
18
 
19
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
20
  model = AutoModel.from_pretrained(MODEL_NAME, _attn_implementation='flash_attention_2', torch_dtype=torch.bfloat16, trust_remote_code=True, use_safetensors=True)
21
  model = model.eval().cuda()
22
 
23
  MODEL_CONFIGS = {
24
- "Gundam": {"base_size": 1024, "image_size": 640, "crop_mode": True},
25
- "Tiny": {"base_size": 512, "image_size": 512, "crop_mode": False},
26
- "Small": {"base_size": 640, "image_size": 640, "crop_mode": False},
27
- "Base": {"base_size": 1024, "image_size": 1024, "crop_mode": False},
28
- "Large": {"base_size": 1280, "image_size": 1280, "crop_mode": False}
29
  }
30
 
31
  TASK_PROMPTS = {
32
  "πŸ“‹ Markdown": {"prompt": "<image>\n<|grounding|>Convert the document to markdown.", "has_grounding": True},
33
  "πŸ“ Free OCR": {"prompt": "<image>\nFree OCR.", "has_grounding": False},
 
 
34
  "πŸ“ Locate": {"prompt": "<image>\nLocate <|ref|>text<|/ref|> in the image.", "has_grounding": True},
35
  "πŸ” Describe": {"prompt": "<image>\nDescribe this image in detail.", "has_grounding": False},
36
  "✏️ Custom": {"prompt": "", "has_grounding": False}
@@ -97,6 +99,8 @@ def clean_output(text, include_images=False):
97
  else:
98
  text = re.sub(rf'(?m)^[^\n]*{re.escape(match[0])}[^\n]*\n?', '', text)
99
 
 
 
100
  return text.strip()
101
 
102
  def embed_images(markdown, crops):
@@ -109,12 +113,12 @@ def embed_images(markdown, crops):
109
  markdown = markdown.replace(f'**[Figure {i + 1}]**', f'\n\n![Figure {i + 1}](data:image/png;base64,{b64})\n\n', 1)
110
  return markdown
111
 
112
- @spaces.GPU(duration=60)
113
  def process_image(image, mode, task, custom_prompt):
114
  if image is None:
115
- return " Error Upload image", "", "", None, []
116
  if task in ["✏️ Custom", "πŸ“ Locate"] and not custom_prompt.strip():
117
- return "Enter prompt", "", "", None, []
118
 
119
  if image.mode in ('RGBA', 'LA', 'P'):
120
  image = image.convert('RGB')
@@ -140,8 +144,16 @@ def process_image(image, mode, task, custom_prompt):
140
  stdout = sys.stdout
141
  sys.stdout = StringIO()
142
 
143
- model.infer(tokenizer=tokenizer, prompt=prompt, image_file=tmp.name, output_path=out_dir,
144
- base_size=config["base_size"], image_size=config["image_size"], crop_mode=config["crop_mode"])
 
 
 
 
 
 
 
 
145
 
146
  result = '\n'.join([l for l in sys.stdout.getvalue().split('\n')
147
  if not any(s in l for s in ['image:', 'other:', 'PATCHES', '====', 'BASE:', '%|', 'torch.Size'])]).strip()
@@ -151,7 +163,7 @@ def process_image(image, mode, task, custom_prompt):
151
  shutil.rmtree(out_dir, ignore_errors=True)
152
 
153
  if not result:
154
- return "No text", "", "", None, []
155
 
156
  cleaned = clean_output(result, False)
157
  markdown = clean_output(result, True)
@@ -168,7 +180,7 @@ def process_image(image, mode, task, custom_prompt):
168
 
169
  return cleaned, markdown, result, img_out, crops
170
 
171
- @spaces.GPU(duration=60)
172
  def process_pdf(path, mode, task, custom_prompt, page_num):
173
  doc = fitz.open(path)
174
  total_pages = len(doc)
@@ -184,7 +196,7 @@ def process_pdf(path, mode, task, custom_prompt, page_num):
184
 
185
  def process_file(path, mode, task, custom_prompt, page_num):
186
  if not path:
187
- return "Error Upload file", "", "", None, []
188
  if path.lower().endswith('.pdf'):
189
  return process_pdf(path, mode, task, custom_prompt, page_num)
190
  else:
@@ -192,9 +204,9 @@ def process_file(path, mode, task, custom_prompt, page_num):
192
 
193
  def toggle_prompt(task):
194
  if task == "✏️ Custom":
195
- return gr.update(visible=True, label="Custom Prompt", placeholder="Add <|grounding|> for boxes")
196
  elif task == "πŸ“ Locate":
197
- return gr.update(visible=True, label="Text to Locate", placeholder="Enter text")
198
  return gr.update(visible=False)
199
 
200
  def select_boxes(task):
@@ -233,12 +245,12 @@ def update_page_selector(file_path):
233
  label=f"Select Page (1-{page_count})")
234
  return gr.update(visible=False)
235
 
236
- with gr.Blocks(title="DeepSeek-OCR") as demo:
237
  gr.Markdown("""
238
- # πŸš€ DeepSeek-OCR Demo
239
- **Convert documents to markdown, extract raw text, and locate specific content with bounding boxes. It takes 20~ sec for markdown and 3~ sec for locate task examples. Check the info at the bottom of the page for more information.**
240
 
241
- **Hope this tool was helpful! If so, a quick like ❀️ would mean a lot :)**
242
  """)
243
 
244
  with gr.Row():
@@ -246,7 +258,7 @@ with gr.Blocks(title="DeepSeek-OCR") as demo:
246
  file_in = gr.File(label="Upload Image or PDF", file_types=["image", ".pdf"], type="filepath")
247
  input_img = gr.Image(label="Input Image", type="pil", height=300)
248
  page_selector = gr.Number(label="Select Page", value=1, minimum=1, step=1, visible=False)
249
- mode = gr.Dropdown(list(MODEL_CONFIGS.keys()), value="Gundam", label="Mode")
250
  task = gr.Dropdown(list(TASK_PROMPTS.keys()), value="πŸ“‹ Markdown", label="Task")
251
  prompt = gr.Textbox(label="Prompt", lines=2, visible=False)
252
  btn = gr.Button("Extract", variant="primary", size="lg")
@@ -266,8 +278,8 @@ with gr.Blocks(title="DeepSeek-OCR") as demo:
266
 
267
  gr.Examples(
268
  examples=[
269
- ["examples/ocr.jpg", "Gundam", "πŸ“‹ Markdown", ""],
270
- ["examples/reachy-mini.jpg", "Gundam", "πŸ“ Locate", "Robot"]
271
  ],
272
  inputs=[input_img, mode, task, prompt],
273
  cache_examples=False
@@ -276,18 +288,28 @@ with gr.Blocks(title="DeepSeek-OCR") as demo:
276
  with gr.Accordion("ℹ️ Info", open=False):
277
  gr.Markdown("""
278
  ### Modes
279
- - **Gundam**: 1024 base + 640 tiles with cropping - Best balance
280
- - **Tiny**: 512Γ—512, no crop - Fastest
281
- - **Small**: 640Γ—640, no crop - Quick
282
- - **Base**: 1024Γ—1024, no crop - Standard
283
- - **Large**: 1280Γ—1280, no crop - Highest quality
284
 
285
  ### Tasks
286
- - **Markdown**: Convert document to structured markdown (grounding βœ…)
287
- - **Free OCR**: Simple text extraction
288
- - **Locate**: Find specific things in image (grounding βœ…)
 
 
289
  - **Describe**: General image description
290
- - **Custom**: Your own prompt (add `<|grounding|>` for boxes)
 
 
 
 
 
 
 
 
291
  """)
292
 
293
  file_in.change(load_image, [file_in, page_selector], [input_img])
@@ -301,7 +323,7 @@ with gr.Blocks(title="DeepSeek-OCR") as demo:
301
  return process_file(file_path, mode, task, custom_prompt, int(page_num))
302
  if image is not None:
303
  return process_image(image, mode, task, custom_prompt)
304
- return "Error uploading file or image", "", "", None, []
305
 
306
  submit_event = btn.click(run, [input_img, file_in, mode, task, prompt, page_selector],
307
  [text_out, md_out, raw_out, img_out, gallery])
 
14
  import base64
15
  from io import StringIO, BytesIO
16
 
17
+ MODEL_NAME = 'deepseek-ai/DeepSeek-OCR-2'
18
 
19
  tokenizer = AutoTokenizer.from_pretrained(MODEL_NAME, trust_remote_code=True)
20
  model = AutoModel.from_pretrained(MODEL_NAME, _attn_implementation='flash_attention_2', torch_dtype=torch.bfloat16, trust_remote_code=True, use_safetensors=True)
21
  model = model.eval().cuda()
22
 
23
  MODEL_CONFIGS = {
24
+ "Default": {"base_size": 1024, "image_size": 768, "crop_mode": True},
25
+ "Quality": {"base_size": 1280, "image_size": 960, "crop_mode": True},
26
+ "Fast": {"base_size": 1024, "image_size": 640, "crop_mode": True},
27
+ "No Crop": {"base_size": 1024, "image_size": 768, "crop_mode": False},
28
+ "Small": {"base_size": 768, "image_size": 512, "crop_mode": False},
29
  }
30
 
31
  TASK_PROMPTS = {
32
  "πŸ“‹ Markdown": {"prompt": "<image>\n<|grounding|>Convert the document to markdown.", "has_grounding": True},
33
  "πŸ“ Free OCR": {"prompt": "<image>\nFree OCR.", "has_grounding": False},
34
+ "πŸ–ΌοΈ OCR Image": {"prompt": "<image>\n<|grounding|>OCR this image.", "has_grounding": True},
35
+ "πŸ“Š Parse Figure": {"prompt": "<image>\nParse the figure.", "has_grounding": False},
36
  "πŸ“ Locate": {"prompt": "<image>\nLocate <|ref|>text<|/ref|> in the image.", "has_grounding": True},
37
  "πŸ” Describe": {"prompt": "<image>\nDescribe this image in detail.", "has_grounding": False},
38
  "✏️ Custom": {"prompt": "", "has_grounding": False}
 
99
  else:
100
  text = re.sub(rf'(?m)^[^\n]*{re.escape(match[0])}[^\n]*\n?', '', text)
101
 
102
+ text = text.replace('\\coloneqq', ':=').replace('\\eqqcolon', '=:')
103
+
104
  return text.strip()
105
 
106
  def embed_images(markdown, crops):
 
113
  markdown = markdown.replace(f'**[Figure {i + 1}]**', f'\n\n![Figure {i + 1}](data:image/png;base64,{b64})\n\n', 1)
114
  return markdown
115
 
116
+ @spaces.GPU(duration=90)
117
  def process_image(image, mode, task, custom_prompt):
118
  if image is None:
119
+ return "Error: Upload an image", "", "", None, []
120
  if task in ["✏️ Custom", "πŸ“ Locate"] and not custom_prompt.strip():
121
+ return "Please enter a prompt", "", "", None, []
122
 
123
  if image.mode in ('RGBA', 'LA', 'P'):
124
  image = image.convert('RGB')
 
144
  stdout = sys.stdout
145
  sys.stdout = StringIO()
146
 
147
+ model.infer(
148
+ tokenizer=tokenizer,
149
+ prompt=prompt,
150
+ image_file=tmp.name,
151
+ output_path=out_dir,
152
+ base_size=config["base_size"],
153
+ image_size=config["image_size"],
154
+ crop_mode=config["crop_mode"],
155
+ save_results=False
156
+ )
157
 
158
  result = '\n'.join([l for l in sys.stdout.getvalue().split('\n')
159
  if not any(s in l for s in ['image:', 'other:', 'PATCHES', '====', 'BASE:', '%|', 'torch.Size'])]).strip()
 
163
  shutil.rmtree(out_dir, ignore_errors=True)
164
 
165
  if not result:
166
+ return "No text detected", "", "", None, []
167
 
168
  cleaned = clean_output(result, False)
169
  markdown = clean_output(result, True)
 
180
 
181
  return cleaned, markdown, result, img_out, crops
182
 
183
+ @spaces.GPU(duration=90)
184
  def process_pdf(path, mode, task, custom_prompt, page_num):
185
  doc = fitz.open(path)
186
  total_pages = len(doc)
 
196
 
197
  def process_file(path, mode, task, custom_prompt, page_num):
198
  if not path:
199
+ return "Error: Upload a file", "", "", None, []
200
  if path.lower().endswith('.pdf'):
201
  return process_pdf(path, mode, task, custom_prompt, page_num)
202
  else:
 
204
 
205
  def toggle_prompt(task):
206
  if task == "✏️ Custom":
207
+ return gr.update(visible=True, label="Custom Prompt", placeholder="Add <|grounding|> for bounding boxes")
208
  elif task == "πŸ“ Locate":
209
+ return gr.update(visible=True, label="Text to Locate", placeholder="Enter text to locate")
210
  return gr.update(visible=False)
211
 
212
  def select_boxes(task):
 
245
  label=f"Select Page (1-{page_count})")
246
  return gr.update(visible=False)
247
 
248
+ with gr.Blocks(title="DeepSeek-OCR-2") as demo:
249
  gr.Markdown("""
250
+ # πŸš€ DeepSeek-OCR-2 Demo
251
+ **Convert documents to markdown, extract text, parse figures, and locate specific content with bounding boxes.**
252
 
253
+ **If this tool was helpful, please consider giving it a like ❀️!**
254
  """)
255
 
256
  with gr.Row():
 
258
  file_in = gr.File(label="Upload Image or PDF", file_types=["image", ".pdf"], type="filepath")
259
  input_img = gr.Image(label="Input Image", type="pil", height=300)
260
  page_selector = gr.Number(label="Select Page", value=1, minimum=1, step=1, visible=False)
261
+ mode = gr.Dropdown(list(MODEL_CONFIGS.keys()), value="Default", label="Mode")
262
  task = gr.Dropdown(list(TASK_PROMPTS.keys()), value="πŸ“‹ Markdown", label="Task")
263
  prompt = gr.Textbox(label="Prompt", lines=2, visible=False)
264
  btn = gr.Button("Extract", variant="primary", size="lg")
 
278
 
279
  gr.Examples(
280
  examples=[
281
+ ["examples/ocr.jpg", "Default", "πŸ“‹ Markdown", ""],
282
+ ["examples/reachy-mini.jpg", "Default", "πŸ“ Locate", "Robot"]
283
  ],
284
  inputs=[input_img, mode, task, prompt],
285
  cache_examples=False
 
288
  with gr.Accordion("ℹ️ Info", open=False):
289
  gr.Markdown("""
290
  ### Modes
291
+ - **Default**: 1024 base + 768 tiles with cropping - Recommended for most use cases
292
+ - **Quality**: 1280 base + 960 tiles with cropping - Higher quality, slower
293
+ - **Fast**: 1024 base + 640 tiles with cropping - Faster processing
294
+ - **No Crop**: 1024 base + 768 tiles without cropping - Single image processing
295
+ - **Small**: 768 base + 512 tiles without cropping - Fastest, lower quality
296
 
297
  ### Tasks
298
+ - **Markdown**: Convert document to structured markdown with layout detection (grounding βœ…)
299
+ - **Free OCR**: Simple text extraction without layout
300
+ - **OCR Image**: OCR for general images with grounding (grounding βœ…)
301
+ - **Parse Figure**: Parse figures and charts in documents
302
+ - **Locate**: Find and highlight specific text/elements in image (grounding βœ…)
303
  - **Describe**: General image description
304
+ - **Custom**: Your own prompt (add `<|grounding|>` for bounding boxes)
305
+
306
+ Document: <image>\\n<|grounding|>Convert the document to markdown.
307
+ Free OCR: <image>\\nFree OCR.
308
+ Other Image: <image>\\n<|grounding|>OCR this image.
309
+ Parse Figure: <image>\\nParse the figure.
310
+ Describe: <image>\\nDescribe this image in detail.
311
+ Locate: <image>\\nLocate <|ref|>text<|/ref|> in the image.
312
+ ```
313
  """)
314
 
315
  file_in.change(load_image, [file_in, page_selector], [input_img])
 
323
  return process_file(file_path, mode, task, custom_prompt, int(page_num))
324
  if image is not None:
325
  return process_image(image, mode, task, custom_prompt)
326
+ return "Error: Upload a file or image", "", "", None, []
327
 
328
  submit_event = btn.click(run, [input_img, file_in, mode, task, prompt, page_selector],
329
  [text_out, md_out, raw_out, img_out, gallery])