Fix bug in visualizer

This commit is contained in:
pjmalandrino 2025-06-03 14:50:23 +02:00
parent 46b22770dc
commit 8cd93df1a9
2 changed files with 191 additions and 56 deletions

View file

@ -7,7 +7,7 @@ import argparse
import os
import re
from pathlib import Path
from PIL import Image, ImageDraw
from PIL import Image, ImageDraw, ImageFont
# Add parent directory to path for imports
import sys
@ -59,92 +59,215 @@ def parse_doctags(doctags_path):
doctag_content = doctag_match.group(1)
zones = []
# Find all tags with location information
tag_starts = re.finditer(r'<(\w+)>', doctag_content)
# Define all possible tag types to look for
tag_types = [
'section_header_level_1', 'section_header_level_2', 'section_header_level_3',
'text', 'picture', 'table', 'page_header', 'page_footer',
'title', 'author', 'abstract', 'keywords', 'paragraph',
'list_item', 'code_block', 'footnote', 'caption'
]
for tag_match in tag_starts:
tag_name = tag_match.group(1)
# Find all zones with location information using a more robust pattern
for tag_type in tag_types:
# Pattern to match the complete tag with location data
pattern = rf'<{tag_type}>.*?{LOC_PATTERN}.*?</{tag_type}>'
matches = re.finditer(pattern, doctag_content, re.DOTALL)
for match in matches:
full_match = match.group(0)
loc_match = re.search(LOC_PATTERN, full_match)
if loc_match:
x1, y1, x2, y2 = map(int, loc_match.groups())
# Extract text content (remove location tags)
content_start = full_match.find('>') + 1
content_end = full_match.rfind('</')
content = full_match[content_start:content_end]
content = re.sub(r'<loc_\d+>', '', content).strip()
zones.append({
'type': tag_type,
'x1': x1, 'y1': y1,
'x2': x2, 'y2': y2,
'content': content
})
# Also try a more general pattern for any tags we might have missed
general_pattern = r'<(\w+)>.*?' + LOC_PATTERN + r'.*?</\1>'
general_matches = re.finditer(general_pattern, doctag_content, re.DOTALL)
found_tags = set()
for zone in zones:
found_tags.add(f"{zone['type']}_{zone['x1']}_{zone['y1']}")
for match in general_matches:
tag_name = match.group(1)
if tag_name.startswith('loc_'):
continue
tag_start_pos = tag_match.start()
tag_end_pattern = f'</({tag_name})>'
tag_end_match = re.search(tag_end_pattern, doctag_content[tag_start_pos:])
x1, y1, x2, y2 = map(int, match.groups()[1:5])
tag_key = f"{tag_name}_{x1}_{y1}"
if not tag_end_match:
continue
tag_content = doctag_content[tag_start_pos:tag_start_pos + tag_end_match.end()]
loc_match = re.search(LOC_PATTERN, tag_content)
if loc_match:
x1, y1, x2, y2 = map(int, loc_match.groups())
# Extract text content
content_pattern = f'{LOC_PATTERN}(.*?)</{tag_name}>'
content_match = re.search(content_pattern, tag_content, re.DOTALL)
text_content = content_match.group(5).strip() if content_match else ""
# Avoid duplicates
if tag_key not in found_tags:
full_match = match.group(0)
content_start = full_match.find('>') + 1
content_end = full_match.rfind('</')
content = full_match[content_start:content_end]
content = re.sub(r'<loc_\d+>', '', content).strip()
zones.append({
'type': tag_name,
'x1': x1, 'y1': y1,
'x2': x2, 'y2': y2,
'content': text_content
'content': content
})
found_tags.add(tag_key)
# If no zones found, it might be a page with no detectable content
# If no zones found, log the content for debugging
if not zones:
print(f"Warning: No zones with location data found in {doctags_path}")
print(f"DocTags content preview: {doctag_content[:500]}...")
# Try to find any loc_ tags to debug
loc_tags = re.findall(r'<loc_\d+>', doctag_content)
if loc_tags:
print(f"Found {len(loc_tags)} location tags in the file")
else:
print("No location tags found in the file at all")
# Sort zones by position (top to bottom, left to right)
zones.sort(key=lambda z: (z['y1'], z['x1']))
print(f"Parsed {len(zones)} zones from DocTags")
for zone in zones[:5]: # Show first 5 zones for debugging
print(f" - {zone['type']}: ({zone['x1']},{zone['y1']})-({zone['x2']},{zone['y2']})")
return zones
def create_visualization(image, zones, page_num, output_path):
"""Create a visualization image with rectangles around zones."""
debug_img = image.copy()
draw = ImageDraw.Draw(debug_img)
draw = ImageDraw.Draw(debug_img, mode='RGBA') # Use RGBA mode for transparency
print(f"Creating visualization with {len(zones)} zones")
# Try to use a default font, fallback to PIL default if not available
try:
font = ImageFont.truetype("/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf", 14)
except:
try:
# Try macOS font locations
font = ImageFont.truetype("/System/Library/Fonts/Helvetica.ttc", 14)
except:
try:
# Try Windows font locations
font = ImageFont.truetype("C:\\Windows\\Fonts\\Arial.ttf", 14)
except:
font = ImageFont.load_default()
# Draw rectangles for each zone
zone_count = 0
for zone in zones:
zone_type = zone['type']
color = ZONE_COLORS.get(zone_type, ZONE_COLORS['default'])
# Draw rectangle
# Ensure coordinates are integers
x1, y1 = int(zone['x1']), int(zone['y1'])
x2, y2 = int(zone['x2']), int(zone['y2'])
# Skip invalid zones
if x1 >= x2 or y1 >= y2:
print(f"Skipping invalid zone {zone_type}: ({x1},{y1})-({x2},{y2})")
continue
# Ensure coordinates are within image bounds
x1 = max(0, min(x1, image.width - 1))
y1 = max(0, min(y1, image.height - 1))
x2 = max(0, min(x2, image.width))
y2 = max(0, min(y2, image.height))
print(f"Drawing {zone_type} at ({x1},{y1})-({x2},{y2}) with color {color}")
# Draw rectangle with thicker line
draw.rectangle(
[(zone['x1'], zone['y1']), (zone['x2'], zone['y2'])],
[(x1, y1), (x2, y2)],
outline=color,
width=3 # Increased from 2 to make more visible
)
# Draw corners for better visibility
corner_length = 10
corner_width = 4
# Top-left corner
draw.line([(x1, y1), (x1 + corner_length, y1)], fill=color, width=corner_width)
draw.line([(x1, y1), (x1, y1 + corner_length)], fill=color, width=corner_width)
# Top-right corner
draw.line([(x2 - corner_length, y1), (x2, y1)], fill=color, width=corner_width)
draw.line([(x2, y1), (x2, y1 + corner_length)], fill=color, width=corner_width)
# Bottom-left corner
draw.line([(x1, y2 - corner_length), (x1, y2)], fill=color, width=corner_width)
draw.line([(x1, y2), (x1 + corner_length, y2)], fill=color, width=corner_width)
# Bottom-right corner
draw.line([(x2 - corner_length, y2), (x2, y2)], fill=color, width=corner_width)
draw.line([(x2, y2 - corner_length), (x2, y2)], fill=color, width=corner_width)
# Add zone type label with better visibility
label_text = zone_type.replace('_', ' ').title()
# Get text size
text_bbox = draw.textbbox((0, 0), label_text, font=font)
text_width = text_bbox[2] - text_bbox[0]
text_height = text_bbox[3] - text_bbox[1]
# Position label
label_x = min(x1 + 2, image.width - text_width - 4)
label_y = max(y1 - text_height - 4, 2)
# Draw label background
draw.rectangle(
[(label_x - 2, label_y - 2),
(label_x + text_width + 2, label_y + text_height + 2)],
fill=(255, 255, 255, 200),
outline=color,
width=2
)
# Add zone type label
label_width = len(zone_type) * 7 + 6
label_x = min(zone['x1'], image.width - label_width)
draw.rectangle(
[(label_x, zone['y1']), (label_x + label_width, zone['y1'] + 20)],
fill=(255, 255, 255, 180),
outline=color
)
# Draw label text
draw.text(
(label_x + 3, zone['y1'] + 3),
zone_type,
fill=color
(label_x, label_y),
label_text,
fill=color,
font=font
)
# Draw page number
zone_count += 1
print(f"Drew {zone_count} zones on the image")
# Draw page number with better visibility
page_text = f"Page {page_num}"
page_bbox = draw.textbbox((0, 0), page_text, font=font)
page_width = page_bbox[2] - page_bbox[0]
page_height = page_bbox[3] - page_bbox[1]
draw.rectangle(
[(10, 10), (100, 40)],
fill=(0, 0, 0, 180),
[(10, 10), (20 + page_width, 20 + page_height)],
fill=(0, 0, 0, 200),
outline=(255, 255, 255)
)
draw.text(
(15, 15),
f"Page {page_num}",
fill=(255, 255, 255)
page_text,
fill=(255, 255, 255),
font=font
)
# Save the image
debug_img.save(output_path)
debug_img.save(output_path, format="PNG")
print(f"Visualization saved to: {output_path}")
print(f"Output image size: {debug_img.size}")
return debug_img
@ -167,6 +290,12 @@ def process_page(pdf_path, page_num, doctags_path, output_path, dpi, adjust):
print(f"Found {len(zones)} zones in DocTags")
if zones:
# Debug: print coordinate ranges
x_coords = [zone['x1'] for zone in zones] + [zone['x2'] for zone in zones]
y_coords = [zone['y1'] for zone in zones] + [zone['y2'] for zone in zones]
print(f"Coordinate ranges: X({min(x_coords)}-{max(x_coords)}), Y({min(y_coords)}-{max(y_coords)})")
print(f"Image dimensions: {image.width}x{image.height}")
# Check if we need to adjust coordinates
max_x = max([zone['x2'] for zone in zones])
max_y = max([zone['y2'] for zone in zones])
@ -175,8 +304,26 @@ def process_page(pdf_path, page_num, doctags_path, output_path, dpi, adjust):
if max_x <= DEFAULT_GRID_SIZE and max_y <= DEFAULT_GRID_SIZE:
print(f"Detected normalized coordinates (0-{DEFAULT_GRID_SIZE} grid)")
zones = normalize_coordinates(zones, image.width, image.height)
print(f"After normalization - X range: {min([z['x1'] for z in zones])}-{max([z['x2'] for z in zones])}")
elif adjust:
print(f"Applying auto-adjustment (max coords: {max_x}, {max_y})")
zones = auto_adjust_coordinates(zones, image.width, image.height)
print(f"After adjustment - X range: {min([z['x1'] for z in zones])}-{max([z['x2'] for z in zones])}")
else:
print("No coordinate adjustment applied")
# Verify coordinates are within image bounds
out_of_bounds = 0
for zone in zones:
if (zone['x2'] > image.width or zone['y2'] > image.height or
zone['x1'] < 0 or zone['y1'] < 0):
out_of_bounds += 1
print(f"Warning: Zone {zone['type']} has out-of-bounds coordinates: "
f"({zone['x1']},{zone['y1']})-({zone['x2']},{zone['y2']})")
if out_of_bounds > 0:
print(f"Warning: {out_of_bounds} zones have coordinates outside image bounds!")
else:
print(f"Warning: No zones found for page {page_num}, creating blank visualization")

View file

@ -169,18 +169,6 @@
<!-- Output Section -->
<div id="output" class="output hidden"></div>
<!-- Environment Check -->
<div id="environment-check" class="hidden">
<h3>System Environment</h3>
<div id="env-details"></div>
</div>
<!-- Debug Section -->
<div class="debug-section">
<button onclick="checkEnvironment()">System Check</button>
<button onclick="manuallyRunScript()">Manual Command</button>
</div>
</div>
<script src="static/app.js"></script>