---
# System prepended metadata

title: 報紙工作欄位自動擷取專案報告

---

# 報紙工作欄位自動擷取專案報告

## 摘要 (Abstract)

本專案開發了一個自動化工具，用於從掃描報紙圖像或 PDF 文件中提取獨立的工作招募欄位，並將其保存為單獨的圖像檔案，以支援數位化存檔或後續處理。該工具採用傳統電腦視覺技術，通過圖像預處理、輪廓檢測、智能過濾、嵌套分析和缺失區域檢測等步驟實現目標。經過多次優化，成功解決了過大/過小區塊、重複提取及部分遺漏等問題，在測試數據集（8 張報紙，約 249 個欄位）中達到 89.16% 的正確提取率和 93.17% 的多區塊提取率。該工具適用於文檔數位化、OCR 前處理及版面分析等場景，未來可通過深度學習、參數自適應及文字方向分析進一步提升性能。

---

## 1. 當時情境描述 (Scenario Description)

本次專案的目標是自動化處理掃描的報紙圖片（或 PDF 文件），希望能精確地將報紙上刊登的每一個獨立的工作招募欄位（Job Advertisement Block）擷取下來，儲存成獨立的圖片檔案，以便後續處理或數位化存檔。輸入可以是單張圖片或多頁 PDF 文件，輸出則為每個檢測到的欄位獨立保存的圖片以及一個組合圖像。

## 2. 第一次預想方式 (Initial Approach Idea)

初步構想是利用傳統電腦視覺技術來達成目標，主要思路如下：

- **圖片預處理**：對原始報紙圖片進行預處理，以強化欄位邊界並降低背景干擾。具體步驟包括：
  - 灰階轉換（`cv2.cvtColor` 轉為灰階）。
  - 高斯模糊（`cv2.GaussianBlur` 去除雜訊）。
  - 自適應二值化（`cv2.adaptiveThreshold` 處理光照不均並突出邊界）。
  - Canny 邊緣檢測（`cv2.Canny` 找出明確的邊緣線）。
- **輪廓檢測**：在預處理後的邊緣圖像上，使用輪廓檢測演算法（`cv2.findContours`）找出代表各個欄位的封閉區域。
- **區域裁剪與儲存**：將檢測到的每個輪廓對應的原始圖片區域裁剪下來，儲存為單獨的圖片檔案。

## 3. 第一次成果 (Initial Result)

根據初步構想實作後，成功擷取出部分工作欄位，但結果不理想，存在以下問題：

- **尺寸過大的區塊**：檢測到許多大型區域，例如報紙標題或橫跨多欄的文章區塊。
- **尺寸過小的雜訊區塊**：圖片噪點或細小線條被誤認為獨立輪廓。
- **重複或嵌套區塊**：同一區域因不同層次的邊界檢測而被重複提取。
- **遺漏明顯欄位**：部分工作欄位因邊界模糊或品質問題未被檢測到。

## 4. 優化過程與策略 (Optimization Process & Strategies)

針對初步成果中發現的問題，進行了以下幾項優化：

### a. 處理過大圖塊 (Handling Oversized Blocks)

- **問題描述**：初始輪廓檢測將報紙上的大型區域（如標題區或多欄文章）視為單一輪廓，這些並非目標工作欄位。
- **圖片範例**：![job_section_95](https://hackmd.io/_uploads/Sy_l-yEJeg.jpg)
  - **說明**：此圖展示一個錯誤檢測的大型區塊，包含報紙標題與多個廣告。
- **解法 (Code Implementation)**：
  - 在 `process_image` 函數中，設定面積閾值 `max_area_threshold = 0.2 * image.shape[0] * image.shape[1]`（圖片總面積的 20%）。
  - 對每個輪廓計算邊界框面積（`w * h`），若超過閾值則忽略。
  ```python
  max_area_threshold = 0.2 * image.shape[0] * image.shape[1]
  filtered_contours_size = []
  for contour in contours:
      x, y, w, h = cv2.boundingRect(contour)
      area = w * h
      if area > max_area_threshold:
          continue
      filtered_contours_size.append(contour)
  ```
- **思維 (Rationale)**：工作欄位通常不會佔據整張報紙的大比例面積，設定上限可排除無關的大型區塊。
- **成效 (Effectiveness)**：顯著減少了輸出中大型無關區塊的數量，保留更合理的候選區域。

### b. 處理過小圖塊/噪點 (Handling Small Blocks/Noise)

- **問題描述**：圖片噪點、文字間空隙或版面細線被誤檢測為小輪廓，這些不是有效工作欄位。
- **圖片範例**：![job_section_41](https://hackmd.io/_uploads/B1HqfJEJgg.jpg) ![job_section_21](https://hackmd.io/_uploads/Hy4CzyEyel.jpg)
  - **說明**：這些圖展示錯誤檢測的小型區塊，如標題或廣告的片段。
- **解法 (Code Implementation)**：
  - 設定最小尺寸 `min_dim = 120`（寬度和高度均需大於 120 像素）。
  - 設定長寬比限制 `max_aspect_ratio = 5.0`（寬高比和 Highness/Width 比需小於 5）。
  ```python
  min_dim = 120
  max_aspect_ratio = 5.0
  for contour in filtered_contours_size:
      x, y, w, h = cv2.boundingRect(contour)
      if w > min_dim and h > min_dim:
          if w / h < max_aspect_ratio and h / w < max_aspect_ratio:
              valid_contours_initial.append(contour)
  ```
- **思維 (Rationale)**：有效欄位應有一定最小尺寸，且形狀不應過於狹長，這些條件可濾除噪點和線條。
- **成效 (Effectiveness)**：大幅減少了微小、無意義的圖片數量，提升輸出品質。

### c. 處理重複/嵌套圖塊 (Handling Duplicate/Nested Blocks)

- **問題描述**：輪廓檢測可能在不同層次檢測到邊界，導致嵌套或重複提取同一區域。
- **圖片範例**：<img src="https://hackmd.io/_uploads/SkUOKyNyee.jpg" width="150" /> <img src="https://hackmd.io/_uploads/B1af5yVyxg.jpg" width="150" />
  - **說明**：此兩張圖為同一區域，因大小不同被切成兩張。
- **解法 (Code Implementation)**：
  - 使用 `is_contained_bbox(bbox1, bbox2, tolerance=10)` 函數檢查一個邊界框是否被另一個包含（容差 10 像素）。
  - 在初次過濾和最終檢查中，移除被包含的較小輪廓，保留較大區域。
  ```python
  def is_contained_bbox(bbox1, bbox2, tolerance=10):
      x1, y1, w1, h1 = bbox1
      x2, y2, w2, h2 = bbox2
      return (x1 >= (x2 - tolerance) and y1 >= (y2 - tolerance) and
              (x1 + w1) <= (x2 + w2 + tolerance) and (y1 + h1) <= (y2 + h2 + tolerance))
  ```
  - 初次過濾中比較面積，保留較大者：
  ```python
  for i, contour in enumerate(valid_contours_initial):
      bbox_i = temp_initial_bboxes[i]
      for j, other_bbox in enumerate(temp_initial_bboxes):
          if i != j and other_bbox is not None and is_contained_bbox(bbox_i, other_bbox):
              area_i = bbox_i[2] * bbox_i[3]
              area_j = other_bbox[2] * other_bbox[3]
              if area_i >= area_j:
                  temp_initial_bboxes[j] = None
              else:
                  is_contained_by_others = True
                  break
  ```
- **思維 (Rationale)**：被包含的輪廓通常是外部輪廓的一部分，移除冗餘可保留完整區塊。
- **成效 (Effectiveness)**：減少了重複提取的情況，輸出更乾淨。

### d. 處理遺漏的區塊 (Handling Missed Blocks)

- **問題描述**：因圖片品質或欄位邊界模糊，`cv2.findContours` 未檢測到部分工作欄位。
- **圖片範例**：<img src="https://hackmd.io/_uploads/r1gR_e4Jex.jpg" width="200" /> <img src="https://hackmd.io/_uploads/Bke0OxN1gl.jpg" width="200" />
  - **說明**：左圖顯示初次處理後遺漏區塊，右圖為填補後結果。
- **解法 (Code Implementation)**：
  - **後處理填補策略**：
    1. 使用初次檢測的區塊建立遮罩（`mask`），標記已覆蓋區域。
    2. 應用形態學操作（膨脹和侵蝕，`cv2.dilate` 和 `cv2.erode`，核大小 30）平滑遮罩。
    3. 在反轉遮罩（`inv_mask`）上檢測未覆蓋區域（`cv2.findContours`）。
    4. 過濾條件：最小面積 5000、最小尺寸 120、長寬比小於 5、與初次區塊重疊比例低於 50%。
    ```python
    mask = np.zeros((mask_height, mask_width), dtype=np.uint8)
    for (x, y, w, h) in initial_block_bboxes:
        relative_y = y - min_y_overall
        relative_x = x - min_x_overall
        mask[relative_y:relative_y + h, relative_x:relative_x + w] = 255
    kernel = np.ones((30, 30), np.uint8)
    mask = cv2.dilate(mask, kernel, iterations=3)
    mask = cv2.erode(mask, kernel, iterations=3)
    inv_mask = cv2.bitwise_not(mask)
    missing_contours, _ = cv2.findContours(inv_mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
    ```
  - 將通過過濾的未覆蓋區域加入最終輸出。
- **思維 (Rationale)**：假設初次檢測勾勒出主要內容，空白區域可能是遺漏欄位，填補可提高完整性。
- **成效 (Effectiveness)**：生成更完整的組合圖像（`_final_combined.jpg`），填補部分遺漏，但可能引入非目標內容。

## 5. 潛在問題與限制 (Potential Issues & Limitations)

- **輪廓合併問題**：多個欄位緊密相連或被共同邊框包圍時，`cv2.findContours` 可能將其視為單一輪廓，導致提取的圖片包含多個欄位。
- **參數敏感性**：預處理參數（如高斯模糊核大小 5x5、自適應閾值塊大小 11）、過濾條件（如 `min_dim=120`、`max_area_threshold=0.2`）對不同圖片敏感，需針對具體輸入調優。
- **填補限制**：後處理填補可能誤將空白或分隔線納入，且無法保證語義獨立性。
- **旋轉未處理**：程式碼未處理欄位旋轉問題，需額外分析文字方向。

## 6. 最終成功率 (Final Success Rate)

經過多次優化後，系統在測試數據集（8 張報紙掃描圖片，包含約 249 個工作招募欄位）上的表現如下：

- **正確擷取**：222 個欄位（獨立、完整、無重複或錯誤的欄位）。
- **錯誤擷取**：
    - 10 個欄位（多區塊區域）。
    - 22 個欄位（無效區域）。
- **遺漏欄位**：27 個欄位（未檢測到的真實工作欄位）。
- **成功率計算**：
  - 總真實欄位數 = 正確擷取數 + 遺漏數 = 222 + 27 = 249。
  - 多區塊欄位數 = 正確擷取數 + 多區塊欄位數 = 222 + 10 = 232。
  - 真實成功率 = 正確擷取數 / 總真實欄位數 = 222 / 249 = **89.16%**。
  - 多區塊成功率 = 多區塊擷取數 / 總真實欄位數 = 232 / 249 = **93.17%**。

## 7. 可應用的場域 (Applicable Fields)

此方法適用於以下場景：
- **文檔數位化**：從掃描件中快速分離內容區塊。
- **OCR 前處理**：為文字識別提供候選區域。
- **資料標註輔助**：生成初步區域，減少人工標註工作。
- **版面分析**：研究出版物佈局特徵。

## 8. 優化建議 (Optimization Suggestions)

- **進階分割**：引入分水嶺演算法或深度學習（如 YOLO、Mask R-CNN）處理輪廓合併問題。
- **參數自適應**：根據圖片分辨率動態調整閾值和尺寸參數。
- **旋轉檢測**：整合文字方向分析（如 Tesseract OCR）處理旋轉欄位。
- **整合 OCR**：提取欄位後自動識別文字，提升自動化程度。

---

 [GitHub 專案連結](https://github.com/tom1030507/newspaper_job_extractor)
 
 ---