Fix bug in visualizer
This commit is contained in:
parent
46b22770dc
commit
8cd93df1a9
2 changed files with 191 additions and 56 deletions
|
|
@ -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")
|
||||
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue