Gabriel commited on
Commit
349175e
·
verified ·
1 Parent(s): bb2fbb1

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +157 -171
app.py CHANGED
@@ -4,14 +4,11 @@ import tempfile
4
  import os
5
  import zipfile
6
  import shutil
7
- from typing import List, Optional, Literal, Tuple, Union, Dict
8
  from PIL import Image
9
  import requests
10
- from io import BytesIO
11
- from concurrent.futures import ThreadPoolExecutor, as_completed
12
-
13
- import spaces
14
  from pathlib import Path
 
15
  from visualizer import htrflow_visualizer
16
  from htrflow.volume.volume import Collection
17
  from htrflow.pipeline.pipeline import Pipeline
@@ -277,7 +274,7 @@ def _process_htr_pipeline_batch(
277
  results[image_name] = processed_collection
278
 
279
  if progress:
280
- progress((idx + 0.9) / total_images,
281
  desc=f"Completed image {idx+1}/{total_images}: {image_name}")
282
 
283
  except Exception as e:
@@ -292,12 +289,22 @@ def _process_htr_pipeline_batch(
292
  pass
293
 
294
  if progress:
295
- progress(1.0, desc=f"Completed processing {total_images} images!")
296
 
297
  return results
298
 
299
 
300
- def htr_text_batch(
 
 
 
 
 
 
 
 
 
 
301
  image_input: Union[str, List[str]],
302
  document_type: FormatChoices = "letter_swedish",
303
  custom_settings: Optional[str] = None,
@@ -305,22 +312,22 @@ def htr_text_batch(
305
  progress: gr.Progress = gr.Progress()
306
  ) -> str:
307
  """
308
- Extract text from multiple handwritten documents using HTR.
309
-
310
- This tool processes multiple historical handwritten documents and extracts text content from each.
311
- You can provide multiple image paths/URLs separated by newlines, or upload multiple files.
312
 
313
  Args:
314
  image_input: Single image path/URL, multiple paths/URLs (newline-separated), or list of uploaded files
315
  document_type: Type of document layout - choose based on your documents' structure and language
316
  custom_settings: Optional JSON configuration for advanced pipeline customization
317
  return_format: "separate" to show each document's text separately, "combined" to merge all text
 
318
 
319
  Returns:
320
  Extracted text from all handwritten documents
321
  """
322
  try:
323
- progress(0, desc="Starting batch HTR text extraction...")
 
324
 
325
  # Parse input to get list of images
326
  image_paths = parse_image_input(image_input)
@@ -328,7 +335,12 @@ def htr_text_batch(
328
  if not image_paths:
329
  return "No images provided. Please upload images or provide URLs."
330
 
331
- progress(0.1, desc=f"Processing {len(image_paths)} images...")
 
 
 
 
 
332
 
333
  # Process all images
334
  results = _process_htr_pipeline_batch(
@@ -347,6 +359,7 @@ def htr_text_batch(
347
  else:
348
  all_texts.append(text)
349
 
 
350
  if return_format == "separate":
351
  return "\n".join(all_texts)
352
  else:
@@ -355,35 +368,33 @@ def htr_text_batch(
355
  except ValueError as e:
356
  return f"Input error: {str(e)}"
357
  except Exception as e:
358
- return f"Batch HTR text extraction failed: {str(e)}"
359
 
360
 
361
- def htrflow_file_batch(
362
  image_input: Union[str, List[str]],
363
  document_type: FormatChoices = "letter_swedish",
364
  output_format: FileChoices = DEFAULT_OUTPUT,
365
  custom_settings: Optional[str] = None,
366
- server_name: str = "https://gabriel-htrflow-mcp.hf.space",
367
  progress: gr.Progress = gr.Progress()
368
  ) -> str:
369
  """
370
- Process multiple handwritten documents and generate formatted output files.
371
-
372
- This tool performs HTR on multiple documents and exports the results in various formats.
373
- Returns a ZIP file containing all processed documents.
374
 
375
  Args:
376
  image_input: Single image path/URL, multiple paths/URLs (newline-separated), or list of uploaded files
377
  document_type: Type of document layout - affects segmentation and reading order
378
  output_format: Desired output format (txt for plain text, alto/page for XML with coordinates, json for structured data)
379
  custom_settings: Optional JSON configuration for advanced pipeline customization
380
- server_name: Base URL of the server (used for generating download links)
381
 
382
  Returns:
383
- Path to ZIP file containing all generated files
384
  """
385
  try:
386
- progress(0, desc="Starting batch HTR file processing...")
 
387
 
388
  # Parse input to get list of images
389
  image_paths = parse_image_input(image_input)
@@ -394,14 +405,18 @@ def htrflow_file_batch(
394
  error_file.close()
395
  return error_file.name
396
 
397
- progress(0.1, desc=f"Processing {len(image_paths)} images...")
 
 
 
398
 
399
  # Process all images
400
  results = _process_htr_pipeline_batch(
401
  image_paths, document_type, custom_settings, progress
402
  )
403
 
404
- progress(0.9, desc="Creating ZIP archive...")
 
405
 
406
  # Create temporary directory for output files
407
  temp_dir = Path(tempfile.mkdtemp())
@@ -434,15 +449,23 @@ def htrflow_file_batch(
434
  output_files.append(new_path)
435
  break
436
 
437
- # Create ZIP file
438
- zip_path = temp_dir / f"htr_batch_{output_format}.zip"
439
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
440
- for file_path in output_files:
441
- zipf.write(file_path, file_path.name)
442
-
443
- progress(1.0, desc=f"Batch processing complete! Processed {len(image_paths)} images.")
444
-
445
- return str(zip_path)
 
 
 
 
 
 
 
 
446
 
447
  except ValueError as e:
448
  error_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
@@ -451,33 +474,31 @@ def htrflow_file_batch(
451
  return error_file.name
452
  except Exception as e:
453
  error_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
454
- error_file.write(f"Batch HTR file generation failed: {str(e)}")
455
  error_file.close()
456
  return error_file.name
457
 
458
 
459
- def htrflow_visualizer_batch(
460
  image_input: Union[str, List[str]],
461
  htr_documents: Union[str, List[str]],
462
- server_name: str = "https://gabriel-htrflow-mcp.hf.space",
463
  progress: gr.Progress = gr.Progress()
464
  ) -> str:
465
  """
466
- Create visualizations for multiple HTR results overlaid on original documents.
467
-
468
- This tool generates annotated images showing detected text regions and recognized text
469
- for multiple documents. Returns a ZIP file containing all visualization images.
470
 
471
  Args:
472
  image_input: Original document image paths/URLs (newline-separated if string)
473
  htr_documents: HTR output files (ALTO/PAGE XML) - must match order of images
474
- server_name: Base URL of the server (used for generating download links)
475
 
476
  Returns:
477
- Path to ZIP file containing all visualization images
478
  """
479
  try:
480
- progress(0, desc="Starting batch visualization generation...")
 
481
 
482
  # Parse inputs
483
  image_paths = parse_image_input(image_input)
@@ -492,7 +513,10 @@ def htrflow_visualizer_batch(
492
  if len(image_paths) != len(htr_paths):
493
  raise ValueError(f"Number of images ({len(image_paths)}) doesn't match number of HTR documents ({len(htr_paths)})")
494
 
495
- progress(0.1, desc=f"Creating visualizations for {len(image_paths)} documents...")
 
 
 
496
 
497
  temp_dir = Path(tempfile.mkdtemp())
498
  output_files = []
@@ -502,17 +526,18 @@ def htrflow_visualizer_batch(
502
  try:
503
  image_name = Path(image_path).stem if not image_path.startswith("http") else f"image_{idx+1}"
504
 
505
- progress((idx + 0.3) / len(image_paths),
506
- desc=f"Visualizing document {idx+1}/{len(image_paths)}: {image_name}")
 
507
 
508
  # Handle image input
509
  processed_image = handle_image_input(image_path, progress,
510
- desc_prefix=f"[{idx+1}/{len(image_paths)}] ")
511
  if processed_image.startswith(tempfile.gettempdir()):
512
  temp_files.append(processed_image)
513
 
514
  # Generate visualization
515
- viz_result = htrflow_visualizer(processed_image, htr_path, server_name)
516
 
517
  if viz_result and os.path.exists(viz_result):
518
  # Move to temp dir with proper name
@@ -534,44 +559,43 @@ def htrflow_visualizer_batch(
534
  except:
535
  pass
536
 
537
- progress(0.9, desc="Creating ZIP archive...")
538
-
539
- # Create ZIP file
540
- zip_path = temp_dir / "htr_visualizations.zip"
541
- with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
542
- for file_path in output_files:
543
- zipf.write(file_path, file_path.name)
544
-
545
- progress(1.0, desc=f"Visualization complete! Created {len(output_files)} visualizations.")
546
-
547
- return str(zip_path)
 
 
 
 
 
 
 
 
 
548
 
549
  except Exception as e:
550
  error_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
551
- error_file.write(f"Batch visualization failed: {str(e)}")
552
  error_file.close()
553
  return error_file.name
554
 
555
 
556
- def extract_text_from_collection(collection: Collection) -> str:
557
- """Extract and combine text from all nodes in the collection."""
558
- text_lines = []
559
- for page in collection.pages:
560
- for node in page.traverse():
561
- if hasattr(node, "text") and node.text:
562
- text_lines.append(node.text)
563
- return "\n".join(text_lines)
564
-
565
-
566
  def create_htrflow_mcp_server():
567
- # Batch HTR Text extraction interface
568
- htr_text_batch_interface = gr.Interface(
569
- fn=htr_text_batch,
570
  inputs=[
571
  gr.Textbox(
572
- label="Image Paths/URLs (one per line) or Upload Files",
573
- placeholder="https://example.com/image1.jpg\nhttps://example.com/image2.jpg\n\nOR drag and drop multiple files",
574
- lines=5
575
  ),
576
  gr.Dropdown(
577
  choices=FORMAT_CHOICES,
@@ -593,20 +617,19 @@ def create_htrflow_mcp_server():
593
  ),
594
  ],
595
  outputs=[gr.Textbox(label="Extracted Text", lines=20)],
596
- title="Batch Extract Text from Handwritten Documents",
597
- description="Process multiple handwritten document images at once. Upload files or provide URLs (one per line)",
598
  api_name="htr_text_batch",
599
- api_description="Extract text from multiple handwritten historical documents using advanced HTR models. Supports batch processing of letters and book spreads in English and Swedish. If a user passes a file as an input, use the upload_file_to_gradio tool, if present, to upload the file to the gradio app and create a Gradio File Input. Then use the returned path as the input to the tool",
600
  )
601
 
602
- # Batch HTR File generation interface
603
- htrflow_file_batch_interface = gr.Interface(
604
- fn=htrflow_file_batch,
605
  inputs=[
606
  gr.Textbox(
607
- label="Image Paths/URLs (one per line) or Upload Files",
608
- placeholder="https://example.com/image1.jpg\nhttps://example.com/image2.jpg\n\nOR drag and drop multiple files",
609
- lines=5
610
  ),
611
  gr.Dropdown(
612
  choices=FORMAT_CHOICES,
@@ -626,126 +649,89 @@ def create_htrflow_mcp_server():
626
  value="",
627
  lines=3
628
  ),
629
- gr.Textbox(
630
- label="Server Name",
631
- value="https://gabriel-htrflow-mcp.hf.space",
632
- placeholder="Server URL for download links",
633
- visible=False # Hide this from UI but keep for API
634
- ),
635
  ],
636
- outputs=[gr.File(label="Download ZIP with HTR Output Files")],
637
- title="Batch Generate HTR Output Files",
638
- description="Process multiple handwritten documents and export in various formats. Returns a ZIP file with all results.",
639
  api_name="htrflow_file_batch",
640
- api_description="Process multiple handwritten documents and generate formatted output files. Returns a ZIP containing outputs in ALTO XML (with text coordinates), PAGE XML, JSON (structured data), or plain text format. If a user passes a file as an input, use the upload_file_to_gradio tool, if present, to upload the file to the gradio app and create a Gradio File Input. Then use the returned path as the input to the tool",
641
  )
642
 
643
- # Batch HTR Visualization interface
644
- htrflow_viz_batch_interface = gr.Interface(
645
- fn=htrflow_visualizer_batch,
646
  inputs=[
647
  gr.Textbox(
648
- label="Original Image Paths/URLs (one per line)",
649
- placeholder="https://example.com/image1.jpg\nhttps://example.com/image2.jpg",
650
- lines=5
651
  ),
652
  gr.File(
653
  label="Upload HTR XML Files (ALTO/PAGE)",
654
  file_types=[".xml"],
655
  file_count="multiple"
656
  ),
657
- gr.Textbox(
658
- label="Server Name",
659
- value="https://gabriel-htrflow-mcp.hf.space",
660
- placeholder="Server URL for download links",
661
- visible=False # Hide this from UI but keep for API
662
- ),
663
  ],
664
- outputs=gr.File(label="Download ZIP with Visualization Images"),
665
- title="Batch Visualize HTR Results",
666
- description="Create annotated images for multiple documents. Images and XML files must be in matching order.",
667
  api_name="htrflow_visualizer_batch",
668
- api_description="Generate visualization images showing HTR results overlaid on multiple original documents. Shows detected text regions, reading order, and recognized text for quality control. Returns a ZIP file with all visualizations. If a user passes a file as an input, use the upload_file_to_gradio tool, if present, to upload the file to the gradio app and create a Gradio File Input. Then use the returned path as the input to the tool",
669
  )
670
 
671
- # Legacy single-file interfaces (kept for backward compatibility)
672
- htr_text_interface = gr.Interface(
673
- fn=lambda img, doc_type, settings: htr_text_batch(img, doc_type, settings, "separate"),
674
  inputs=[
675
- gr.Image(type="filepath", label="Upload Image or Enter URL"),
676
- gr.Dropdown(
677
- choices=FORMAT_CHOICES,
678
- value="letter_swedish",
679
- label="Document Type"
680
- ),
681
- gr.Textbox(
682
- label="Custom Settings (JSON)",
683
- placeholder='{"steps": [...]}',
684
- value="",
685
- lines=3
686
- ),
687
  ],
688
  outputs=[gr.Textbox(label="Extracted Text", lines=15)],
689
- title="Extract Text (Single Document)",
690
- description="Upload a single handwritten document image to extract text",
691
- api_name="htr_text",
692
- api_description="Extract text from handwritten historical documents using advanced HTR models. Supports letters and book spreads in English and Swedish. If a user passes a file as an input, use the upload_file_to_gradio tool, if present, to upload the file to the gradio app and create a Gradio File Input. Then use the returned path as the input to the tool",
693
  )
694
 
695
- htrflow_file_interface = gr.Interface(
696
- fn=lambda img, doc_type, fmt, settings, srv: htrflow_file_batch([img], doc_type, fmt, settings, srv),
697
  inputs=[
698
- gr.Image(type="filepath", label="Upload Image or Enter URL"),
699
- gr.Dropdown(
700
- choices=FORMAT_CHOICES,
701
- value="letter_swedish",
702
- label="Document Type"
703
- ),
704
- gr.Dropdown(
705
- choices=FILE_CHOICES,
706
- value=DEFAULT_OUTPUT,
707
- label="Output Format"
708
- ),
709
- gr.Textbox(
710
- label="Custom Settings (JSON)",
711
- value="",
712
- lines=3
713
- ),
714
- gr.Textbox(
715
- label="Server Name",
716
- value="https://gabriel-htrflow-mcp.hf.space",
717
- visible=False
718
- ),
719
  ],
720
- outputs=[gr.File(label="Download HTR Output File")],
721
- title="Generate File (Single Document)",
722
- description="Process a single handwritten document and export in various formats",
723
- api_name="htrflow_file",
724
- api_description="Process handwritten documents and generate formatted output files. Outputs can be in ALTO XML (with text coordinates), PAGE XML, JSON (structured data), or plain text format. If a user passes a file as an input, use the upload_file_to_gradio tool, if present, to upload the file to the gradio app and create a Gradio File Input. Then use the returned path as the input to the tool",
725
  )
726
 
727
- # Create tabbed interface with better organization
728
  demo = gr.TabbedInterface(
729
  [
730
- htr_text_batch_interface,
731
- htrflow_file_batch_interface,
732
- htrflow_viz_batch_interface,
733
  htr_text_interface,
734
- htrflow_file_interface,
 
 
 
735
  ],
736
  [
737
- "📚 Batch Text Extract",
738
- "📁 Batch File Generate",
739
- "🖼️ Batch Visualize",
740
- "📄 Single Text",
741
- "💾 Single File",
742
  ],
743
- title="🖋️ HTRflow - Handwritten Text Recognition (Batch & Single Processing)",
744
  analytics_enabled=False,
745
  )
746
 
 
 
 
 
 
 
 
747
  return demo
748
 
 
749
  if __name__ == "__main__":
750
  demo = create_htrflow_mcp_server()
751
  demo.launch(
 
4
  import os
5
  import zipfile
6
  import shutil
7
+ from typing import List, Optional, Literal, Union, Dict
8
  from PIL import Image
9
  import requests
 
 
 
 
10
  from pathlib import Path
11
+ import spaces
12
  from visualizer import htrflow_visualizer
13
  from htrflow.volume.volume import Collection
14
  from htrflow.pipeline.pipeline import Pipeline
 
274
  results[image_name] = processed_collection
275
 
276
  if progress:
277
+ progress((idx + 1.0) / total_images,
278
  desc=f"Completed image {idx+1}/{total_images}: {image_name}")
279
 
280
  except Exception as e:
 
289
  pass
290
 
291
  if progress:
292
+ progress(1.0, desc=f"Completed processing all {total_images} images!")
293
 
294
  return results
295
 
296
 
297
+ def extract_text_from_collection(collection: Collection) -> str:
298
+ """Extract and combine text from all nodes in the collection."""
299
+ text_lines = []
300
+ for page in collection.pages:
301
+ for node in page.traverse():
302
+ if hasattr(node, "text") and node.text:
303
+ text_lines.append(node.text)
304
+ return "\n".join(text_lines)
305
+
306
+
307
+ def htr_text(
308
  image_input: Union[str, List[str]],
309
  document_type: FormatChoices = "letter_swedish",
310
  custom_settings: Optional[str] = None,
 
312
  progress: gr.Progress = gr.Progress()
313
  ) -> str:
314
  """
315
+ Extract text from handwritten documents using HTR.
316
+ Handles both single images and multiple images.
 
 
317
 
318
  Args:
319
  image_input: Single image path/URL, multiple paths/URLs (newline-separated), or list of uploaded files
320
  document_type: Type of document layout - choose based on your documents' structure and language
321
  custom_settings: Optional JSON configuration for advanced pipeline customization
322
  return_format: "separate" to show each document's text separately, "combined" to merge all text
323
+ progress: Progress tracker for UI updates
324
 
325
  Returns:
326
  Extracted text from all handwritten documents
327
  """
328
  try:
329
+ if progress:
330
+ progress(0, desc="Starting HTR text extraction...")
331
 
332
  # Parse input to get list of images
333
  image_paths = parse_image_input(image_input)
 
335
  if not image_paths:
336
  return "No images provided. Please upload images or provide URLs."
337
 
338
+ # Adjust description based on single vs multiple
339
+ num_images = len(image_paths)
340
+ desc = f"Processing {num_images} image{'s' if num_images > 1 else ''}..."
341
+
342
+ if progress:
343
+ progress(0.1, desc=desc)
344
 
345
  # Process all images
346
  results = _process_htr_pipeline_batch(
 
359
  else:
360
  all_texts.append(text)
361
 
362
+ # Return formatted result
363
  if return_format == "separate":
364
  return "\n".join(all_texts)
365
  else:
 
368
  except ValueError as e:
369
  return f"Input error: {str(e)}"
370
  except Exception as e:
371
+ return f"HTR text extraction failed: {str(e)}"
372
 
373
 
374
+ def htr_generate_files(
375
  image_input: Union[str, List[str]],
376
  document_type: FormatChoices = "letter_swedish",
377
  output_format: FileChoices = DEFAULT_OUTPUT,
378
  custom_settings: Optional[str] = None,
 
379
  progress: gr.Progress = gr.Progress()
380
  ) -> str:
381
  """
382
+ Process handwritten documents and generate formatted output files.
383
+ Returns a ZIP file for multiple documents, or single file for single document.
 
 
384
 
385
  Args:
386
  image_input: Single image path/URL, multiple paths/URLs (newline-separated), or list of uploaded files
387
  document_type: Type of document layout - affects segmentation and reading order
388
  output_format: Desired output format (txt for plain text, alto/page for XML with coordinates, json for structured data)
389
  custom_settings: Optional JSON configuration for advanced pipeline customization
390
+ progress: Progress tracker for UI updates
391
 
392
  Returns:
393
+ Path to generated file(s)
394
  """
395
  try:
396
+ if progress:
397
+ progress(0, desc="Starting HTR file processing...")
398
 
399
  # Parse input to get list of images
400
  image_paths = parse_image_input(image_input)
 
405
  error_file.close()
406
  return error_file.name
407
 
408
+ num_images = len(image_paths)
409
+
410
+ if progress:
411
+ progress(0.1, desc=f"Processing {num_images} image{'s' if num_images > 1 else ''}...")
412
 
413
  # Process all images
414
  results = _process_htr_pipeline_batch(
415
  image_paths, document_type, custom_settings, progress
416
  )
417
 
418
+ if progress:
419
+ progress(0.9, desc="Creating output files...")
420
 
421
  # Create temporary directory for output files
422
  temp_dir = Path(tempfile.mkdtemp())
 
449
  output_files.append(new_path)
450
  break
451
 
452
+ # Return single file or ZIP based on input count
453
+ if len(output_files) == 1 and len(image_paths) == 1:
454
+ # Single file - return directly
455
+ if progress:
456
+ progress(1.0, desc="Processing complete!")
457
+ return str(output_files[0])
458
+ else:
459
+ # Multiple files - create ZIP
460
+ zip_path = temp_dir / f"htr_output_{output_format}.zip"
461
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
462
+ for file_path in output_files:
463
+ zipf.write(file_path, file_path.name)
464
+
465
+ if progress:
466
+ progress(1.0, desc=f"Processing complete! Generated {len(output_files)} files.")
467
+
468
+ return str(zip_path)
469
 
470
  except ValueError as e:
471
  error_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
 
474
  return error_file.name
475
  except Exception as e:
476
  error_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
477
+ error_file.write(f"HTR file generation failed: {str(e)}")
478
  error_file.close()
479
  return error_file.name
480
 
481
 
482
+ def htr_visualize(
483
  image_input: Union[str, List[str]],
484
  htr_documents: Union[str, List[str]],
 
485
  progress: gr.Progress = gr.Progress()
486
  ) -> str:
487
  """
488
+ Create visualizations for HTR results overlaid on original documents.
489
+ Returns a ZIP file for multiple documents, or single image for single document.
 
 
490
 
491
  Args:
492
  image_input: Original document image paths/URLs (newline-separated if string)
493
  htr_documents: HTR output files (ALTO/PAGE XML) - must match order of images
494
+ progress: Progress tracker for UI updates
495
 
496
  Returns:
497
+ Path to visualization file(s)
498
  """
499
  try:
500
+ if progress:
501
+ progress(0, desc="Starting visualization generation...")
502
 
503
  # Parse inputs
504
  image_paths = parse_image_input(image_input)
 
513
  if len(image_paths) != len(htr_paths):
514
  raise ValueError(f"Number of images ({len(image_paths)}) doesn't match number of HTR documents ({len(htr_paths)})")
515
 
516
+ num_docs = len(image_paths)
517
+
518
+ if progress:
519
+ progress(0.1, desc=f"Creating visualization{'s' if num_docs > 1 else ''} for {num_docs} document{'s' if num_docs > 1 else ''}...")
520
 
521
  temp_dir = Path(tempfile.mkdtemp())
522
  output_files = []
 
526
  try:
527
  image_name = Path(image_path).stem if not image_path.startswith("http") else f"image_{idx+1}"
528
 
529
+ if progress:
530
+ progress((idx + 0.3) / num_docs,
531
+ desc=f"Visualizing document {idx+1}/{num_docs}: {image_name}")
532
 
533
  # Handle image input
534
  processed_image = handle_image_input(image_path, progress,
535
+ desc_prefix=f"[{idx+1}/{num_docs}] ")
536
  if processed_image.startswith(tempfile.gettempdir()):
537
  temp_files.append(processed_image)
538
 
539
  # Generate visualization
540
+ viz_result = htrflow_visualizer(processed_image, htr_path, "")
541
 
542
  if viz_result and os.path.exists(viz_result):
543
  # Move to temp dir with proper name
 
559
  except:
560
  pass
561
 
562
+ # Return single file or ZIP based on input count
563
+ if len(output_files) == 1 and num_docs == 1:
564
+ # Single visualization - return directly
565
+ if progress:
566
+ progress(1.0, desc="Visualization complete!")
567
+ return str(output_files[0])
568
+ else:
569
+ # Multiple visualizations - create ZIP
570
+ if progress:
571
+ progress(0.9, desc="Creating ZIP archive...")
572
+
573
+ zip_path = temp_dir / "htr_visualizations.zip"
574
+ with zipfile.ZipFile(zip_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
575
+ for file_path in output_files:
576
+ zipf.write(file_path, file_path.name)
577
+
578
+ if progress:
579
+ progress(1.0, desc=f"Visualization complete! Created {len(output_files)} visualization{'s' if len(output_files) > 1 else ''}.")
580
+
581
+ return str(zip_path)
582
 
583
  except Exception as e:
584
  error_file = tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt')
585
+ error_file.write(f"Visualization failed: {str(e)}")
586
  error_file.close()
587
  return error_file.name
588
 
589
 
 
 
 
 
 
 
 
 
 
 
590
  def create_htrflow_mcp_server():
591
+ # HTR Text extraction interface
592
+ htr_text_interface = gr.Interface(
593
+ fn=htr_text,
594
  inputs=[
595
  gr.Textbox(
596
+ label="Image Input",
597
+ placeholder="Single image path/URL or multiple (one per line)\nYou can also drag and drop files here",
598
+ lines=3
599
  ),
600
  gr.Dropdown(
601
  choices=FORMAT_CHOICES,
 
617
  ),
618
  ],
619
  outputs=[gr.Textbox(label="Extracted Text", lines=20)],
620
+ title="Extract Text from Handwritten Documents",
621
+ description="Process one or more handwritten document images. Works with letters and book spreads in English and Swedish.",
622
  api_name="htr_text_batch",
 
623
  )
624
 
625
+ # HTR File generation interface
626
+ htr_files_interface = gr.Interface(
627
+ fn=htr_generate_files,
628
  inputs=[
629
  gr.Textbox(
630
+ label="Image Input",
631
+ placeholder="Single image path/URL or multiple (one per line)\nYou can also drag and drop files here",
632
+ lines=3
633
  ),
634
  gr.Dropdown(
635
  choices=FORMAT_CHOICES,
 
649
  value="",
650
  lines=3
651
  ),
 
 
 
 
 
 
652
  ],
653
+ outputs=[gr.File(label="Download HTR Output")],
654
+ title="Generate HTR Output Files",
655
+ description="Process handwritten documents and export in various formats. Returns ZIP for multiple files.",
656
  api_name="htrflow_file_batch",
 
657
  )
658
 
659
+ # HTR Visualization interface
660
+ htr_viz_interface = gr.Interface(
661
+ fn=htr_visualize,
662
  inputs=[
663
  gr.Textbox(
664
+ label="Original Image Paths/URLs",
665
+ placeholder="One path/URL per line",
666
+ lines=3
667
  ),
668
  gr.File(
669
  label="Upload HTR XML Files (ALTO/PAGE)",
670
  file_types=[".xml"],
671
  file_count="multiple"
672
  ),
 
 
 
 
 
 
673
  ],
674
+ outputs=gr.File(label="Download Visualization"),
675
+ title="Visualize HTR Results",
676
+ description="Create annotated images showing detected regions and text. Files must be in matching order.",
677
  api_name="htrflow_visualizer_batch",
 
678
  )
679
 
680
+ # Simplified interface for lambda compatibility (keeping for backward compatibility)
681
+ simple_text_interface = gr.Interface(
682
+ fn=lambda img, doc_type, settings: htr_text(img, doc_type, settings, "separate"),
683
  inputs=[
684
+ gr.Image(type="filepath", label="Upload Image"),
685
+ gr.Dropdown(choices=FORMAT_CHOICES, value="letter_swedish", label="Document Type"),
686
+ gr.Textbox(label="Custom Settings (JSON)", value="", lines=3),
 
 
 
 
 
 
 
 
 
687
  ],
688
  outputs=[gr.Textbox(label="Extracted Text", lines=15)],
689
+ api_name="_lambda_",
 
 
 
690
  )
691
 
692
+ simple_file_interface = gr.Interface(
693
+ fn=lambda img, doc_type, fmt, settings, srv: htr_generate_files(img, doc_type, fmt, settings),
694
  inputs=[
695
+ gr.Image(type="filepath"),
696
+ gr.Dropdown(choices=FORMAT_CHOICES, value="letter_swedish"),
697
+ gr.Dropdown(choices=FILE_CHOICES, value=DEFAULT_OUTPUT),
698
+ gr.Textbox(value="", lines=3),
699
+ gr.Textbox(value="https://gabriel-htrflow-mcp.hf.space", visible=False),
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
700
  ],
701
+ outputs=[gr.File()],
702
+ api_name="_lambda__1",
 
 
 
703
  )
704
 
705
+ # Create tabbed interface
706
  demo = gr.TabbedInterface(
707
  [
 
 
 
708
  htr_text_interface,
709
+ htr_files_interface,
710
+ htr_viz_interface,
711
+ simple_text_interface,
712
+ simple_file_interface,
713
  ],
714
  [
715
+ "📚 Extract Text",
716
+ "📁 Generate Files",
717
+ "🖼️ Visualize Results",
718
+ "", # Hidden tabs for backward compatibility
719
+ "",
720
  ],
721
+ title="🖋️ HTRflow - Handwritten Text Recognition",
722
  analytics_enabled=False,
723
  )
724
 
725
+ # Hide the last two tabs (for backward compatibility only)
726
+ demo.css = """
727
+ .tabitem:nth-child(4), .tabitem:nth-child(5) {
728
+ display: none !important;
729
+ }
730
+ """
731
+
732
  return demo
733
 
734
+
735
  if __name__ == "__main__":
736
  demo = create_htrflow_mcp_server()
737
  demo.launch(