layout-analyzer

layout-analyzer

熱門

>

320星標
70分支
更新於 2026/1/31
SKILL.md
唯讀
名稱
layout-analyzer
描述

>

版本
1.0

版面分析技能

概述

本技能使用 surya(一個先進的文件理解系統)進行文件版面分析。可偵測文字區塊、表格、圖形、標題,並判斷複雜文件中的閱讀順序。

使用方式

  1. 提供文件圖片或 PDF
  2. 指定要偵測的版面元素
  3. 我將分析結構並回傳偵測到的區域

範例提示:

  • "分析這份文件頁面的版面"
  • "偵測這張圖片中的所有表格和文字區塊"
  • "判斷這個 PDF 頁面的閱讀順序"
  • "找出這份文件中的標題和段落"

領域知識

surya 基礎

from surya.detection import DetectionPredictor
from surya.layout import LayoutPredictor
from surya.reading_order import ReadingOrderPredictor
from PIL import Image

# 載入圖片
image = Image.open("document.png")

# 偵測版面元素
layout_predictor = LayoutPredictor()
layout_result = layout_predictor([image])

版面元素類型

元素 說明
Text 一般段落文字
Title 文件/章節標題
Section-header 章節標題
List-item 項目符號/編號項目
Table 表格資料
Figure 圖片/圖表
Caption 圖表說明
Footnote 註腳
Formula 數學公式
Page-header 頁首
Page-footer 頁尾

文字偵測

from surya.detection import DetectionPredictor
from PIL import Image

# 初始化偵測器
detector = DetectionPredictor()

# 載入圖片
image = Image.open("document.png")

# 偵測文字區域
results = detector([image])

# 存取結果
for page_result in results:
    for bbox in page_result.bboxes:
        print(f"文字區域:{bbox.bbox}")
        print(f"信心度:{bbox.confidence}")

版面分析

from surya.layout import LayoutPredictor
from PIL import Image

# 初始化版面預測器
layout_predictor = LayoutPredictor()

# 分析版面
image = Image.open("document.png")
layout_results = layout_predictor([image])

# 處理結果
for page_result in layout_results:
    for element in page_result.bboxes:
        print(f"類型:{element.label}")
        print(f"邊界框:{element.bbox}")
        print(f"信心度:{element.confidence}")

閱讀順序偵測

from surya.reading_order import ReadingOrderPredictor
from surya.layout import LayoutPredictor
from PIL import Image

# 先取得版面
layout_predictor = LayoutPredictor()
image = Image.open("document.png")
layout_results = layout_predictor([image])

# 判斷閱讀順序
reading_order_predictor = ReadingOrderPredictor()
order_results = reading_order_predictor([image], layout_results)

# 存取排序後的元素
for page_result in order_results:
    for i, element in enumerate(page_result.ordered_bboxes):
        print(f"{i+1}. {element.label}:{element.bbox}")

結合版面的 OCR

from surya.ocr import OCRPredictor
from surya.layout import LayoutPredictor
from PIL import Image

# 初始化預測器
ocr_predictor = OCRPredictor()
layout_predictor = LayoutPredictor()

# 載入圖片
image = Image.open("document.png")

# 取得版面
layout_results = layout_predictor([image])

# 執行 OCR
ocr_results = ocr_predictor([image])

# 合併結果
for layout, ocr in zip(layout_results, ocr_results):
    for layout_elem in layout.bboxes:
        print(f"元素:{layout_elem.label}")
        
        # 找出此版面元素內的 OCR 文字
        for text_line in ocr.text_lines:
            if boxes_overlap(layout_elem.bbox, text_line.bbox):
                print(f"  文字:{text_line.text}")

處理 PDF

from surya.layout import LayoutPredictor
from pdf2image import convert_from_path

def analyze_pdf_layout(pdf_path):
    """分析 PDF 中所有頁面的版面。"""
    
    # 將 PDF 轉換為圖片
    images = convert_from_path(pdf_path)
    
    # 初始化預測器
    layout_predictor = LayoutPredictor()
    
    # 分析所有頁面
    results = layout_predictor(images)
    
    document_structure = []
    
    for page_num, page_result in enumerate(results):
        page_elements = []
        
        for element in page_result.bboxes:
            page_elements.append({
                'type': element.label,
                'bbox': element.bbox,
                'confidence': element.confidence
            })
        
        document_structure.append({
            'page': page_num + 1,
            'elements': page_elements
        })
    
    return document_structure

structure = analyze_pdf_layout("document.pdf")

視覺化

from surya.layout import LayoutPredictor
from PIL import Image, ImageDraw, ImageFont

def visualize_layout(image_path, output_path):
    """視覺化偵測到的版面元素。"""
    
    image = Image.open(image_path)
    layout_predictor = LayoutPredictor()
    results = layout_predictor([image])
    
    # 建立繪圖上下文
    draw = ImageDraw.Draw(image)
    
    # 元素類型的顏色對應
    colors = {
        'Text': 'blue',
        'Title': 'red',
        'Table': 'green',
        'Figure': 'purple',
        'Section-header': 'orange',
        'List-item': 'cyan',
    }
    
    for element in results[0].bboxes:
        bbox = element.bbox
        color = colors.get(element.label, 'gray')
        
        # 繪製矩形
        draw.rectangle(bbox, outline=color, width=2)
        
        # 加入標籤
        draw.text((bbox[0], bbox[1] - 15), 
                  f"{element.label} ({element.confidence:.2f})",
                  fill=color)
    
    image.save(output_path)
    return output_path

最佳實務

  1. 使用高品質圖片:建議 150 DPI 以上以獲得最佳結果
  2. 必要時進行前處理:矯正旋轉的文件
  3. 驗證結果:檢查信心度分數
  4. 處理多頁文件:逐頁處理
  5. 結合 OCR:取得偵測區域內的文字

常見模式

文件結構萃取

def extract_document_structure(image_path):
    """萃取階層式文件結構。"""
    
    from surya.layout import LayoutPredictor
    from surya.reading_order import ReadingOrderPredictor
    
    image = Image.open(image_path)
    
    # 取得版面
    layout_predictor = LayoutPredictor()
    layout_results = layout_predictor([image])
    
    # 取得閱讀順序
    order_predictor = ReadingOrderPredictor()
    order_results = order_predictor([image], layout_results)
    
    structure = {
        'title': None,
        'sections': [],
        'tables': [],
        'figures': []
    }
    
    current_section = None
    
    for element in order_results[0].ordered_bboxes:
        if element.label == 'Title':
            structure['title'] = element
        elif element.label == 'Section-header':
            current_section = {'header': element, 'content': []}
            structure['sections'].append(current_section)
        elif element.label == 'Table':
            structure['tables'].append(element)
        elif element.label == 'Figure':
            structure['figures'].append(element)
        elif current_section and element.label in ['Text', 'List-item']:
            current_section['content'].append(element)
    
    return structure

表格區域萃取

def extract_table_regions(image_path):
    """從文件中萃取表格區域。"""
    
    from surya.layout import LayoutPredictor
    
    image = Image.open(image_path)
    layout_predictor = LayoutPredictor()
    results = layout_predictor([image])
    
    tables = []
    
    for element in results[0].bboxes:
        if element.label == 'Table':
            bbox = element.bbox
            
            # 裁切表格區域
            table_image = image.crop(bbox)
            
            tables.append({
                'bbox': bbox,
                'image': table_image,
                'confidence': element.confidence
            })
    
    return tables

範例

範例 1:學術論文分析

from surya.layout import LayoutPredictor
from surya.reading_order import ReadingOrderPredictor
from pdf2image import convert_from_path

def analyze_academic_paper(pdf_path):
    """分析學術論文的結構。"""
    
    images = convert_from_path(pdf_path)
    
    layout_predictor = LayoutPredictor()
    order_predictor = ReadingOrderPredictor()
    
    paper_structure = {
        'pages': [],
        'element_counts': {
            'Title': 0,
            'Section-header': 0,
            'Text': 0,
            'Table': 0,
            'Figure': 0,
            'Formula': 0,
            'Footnote': 0
        }
    }
    
    layout_results = layout_predictor(images)
    order_results = order_predictor(images, layout_results)
    
    for page_num, (layout, order) in enumerate(zip(layout_results, order_results)):
        page_structure = {
            'page': page_num + 1,
            'elements': []
        }
        
        for element in order.ordered_bboxes:
            page_structure['elements'].append({
                'type': element.label,
                'bbox': element.bbox,
                'order': element.position
            })
            
            # 計算元素類型數量
            if element.label in paper_structure['element_counts']:
                paper_structure['element_counts'][element.label] += 1
        
        paper_structure['pages'].append(page_structure)
    
    return paper_structure

paper = analyze_academic_paper('research_paper.pdf')
print(f"表格總數:{paper['element_counts']['Table']}")
print(f"圖形總數:{paper['element_counts']['Figure']}")

範例 2:表單欄位偵測

from surya.layout import LayoutPredictor
from PIL import Image

def detect_form_fields(image_path):
    """偵測表單欄位與標籤。"""
    
    image = Image.open(image_path)
    
    layout_predictor = LayoutPredictor()
    results = layout_predictor([image])
    
    form_fields = []
    
    for element in results[0].bboxes:
        # 尋找可能是標籤的文字元素
        if element.label == 'Text':
            # 檢查附近是否有方框/線條(可能的輸入欄位)
            form_fields.append({
                'type': 'potential_label',
                'bbox': element.bbox,
                'confidence': element.confidence
            })
    
    return form_fields

fields = detect_form_fields('form.png')
print(f"找到 {len(fields)} 個潛在表單元素")

範例 3:多欄文章

from surya.layout import LayoutPredictor
from surya.reading_order import ReadingOrderPredictor
from PIL import Image

def process_multicolumn_article(image_path):
    """處理多欄文章版面。"""
    
    image = Image.open(image_path)
    
    layout_predictor = LayoutPredictor()
    order_predictor = ReadingOrderPredictor()
    
    layout_results = layout_predictor([image])
    order_results = order_predictor([image], layout_results)
    
    # 依欄位分組元素
    image_width = image.width
    column_threshold = image_width / 2
    
    columns = {
        'left': [],
        'right': [],
        'full_width': []
    }
    
    for element in order_results[0].ordered_bboxes:
        bbox = element.bbox
        element_center = (bbox[0] + bbox[2]) / 2
        element_width = bbox[2] - bbox[0]
        
        # 判斷欄位
        if element_width > column_threshold * 1.5:
            columns['full_width'].append(element)
        elif element_center < column_threshold:
            columns['left'].append(element)
        else:
            columns['right'].append(element)
    
    return {
        'layout': 'multi-column',
        'columns': columns,
        'reading_order': order_results[0].ordered_bboxes
    }

article = process_multicolumn_article('newspaper_page.png')
print(f"左欄:{len(article['columns']['left'])} 個元素")
print(f"右欄:{len(article['columns']['right'])} 個元素")

限制

  • 手寫版面的準確度可能較低
  • 非常小的文字區域可能被遺漏
  • 複雜的巢狀版面較具挑戰性
  • 批次處理建議使用 GPU
  • 多語言支援程度不一

安裝

pip install surya-ocr

# 用於 PDF 處理
pip install pdf2image

資源