1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
| import fitz import cv2 import numpy as np import matplotlib.pyplot as plt import csv import os
question_intervals = [ (380, 403), (404, 439), (440, 466), (467, 495), (496, 525), ]
score_zones = [ {'x1': 380, 'y1': 380, 'x2': 405, 'y2': 403, 'color': (255, 0, 0), 'score': 1}, {'x1': 420, 'y1': 380, 'x2': 435, 'y2': 403, 'color': (0, 255, 0), 'score': 2}, {'x1': 450, 'y1': 380, 'x2': 465, 'y2': 403, 'color': (0, 0, 255), 'score': 3}, {'x1': 480, 'y1': 380, 'x2': 495, 'y2': 403, 'color': (255, 255, 0), 'score': 4}, {'x1': 510, 'y1': 380, 'x2': 534, 'y2': 403, 'color': (0, 255, 255), 'score': 5}, {'x1': 380, 'y1': 404, 'x2': 405, 'y2': 439, 'color': (255, 0, 0), 'score': 1}, {'x1': 420, 'y1': 404, 'x2': 435, 'y2': 439, 'color': (0, 255, 0), 'score': 2}, {'x1': 450, 'y1': 404, 'x2': 465, 'y2': 439, 'color': (0, 0, 255), 'score': 3}, {'x1': 480, 'y1': 404, 'x2': 495, 'y2': 439, 'color': (255, 255, 0), 'score': 4}, {'x1': 510, 'y1': 404, 'x2': 534, 'y2': 439, 'color': (0, 255, 255), 'score': 5}, {'x1': 380, 'y1': 440, 'x2': 405, 'y2': 466, 'color': (255, 0, 0), 'score': 1}, {'x1': 420, 'y1': 440, 'x2': 435, 'y2': 466, 'color': (0, 255, 0), 'score': 2}, {'x1': 450, 'y1': 440, 'x2': 465, 'y2': 466, 'color': (0, 0, 255), 'score': 3}, {'x1': 480, 'y1': 440, 'x2': 495, 'y2': 466, 'color': (255, 255, 0), 'score': 4}, {'x1': 510, 'y1': 440, 'x2': 534, 'y2': 466, 'color': (0, 255, 255), 'score': 5}, {'x1': 380, 'y1': 467, 'x2': 405, 'y2': 495, 'color': (255, 0, 0), 'score': 1}, {'x1': 420, 'y1': 467, 'x2': 435, 'y2': 495, 'color': (0, 255, 0), 'score': 2}, {'x1': 450, 'y1': 467, 'x2': 465, 'y2': 495, 'color': (0, 0, 255), 'score': 3}, {'x1': 480, 'y1': 467, 'x2': 495, 'y2': 495, 'color': (255, 255, 0), 'score': 4}, {'x1': 510, 'y1': 467, 'x2': 534, 'y2': 495, 'color': (0, 255, 255), 'score': 5}, {'x1': 380, 'y1': 496, 'x2': 405, 'y2': 525, 'color': (255, 0, 0), 'score': 1}, {'x1': 420, 'y1': 496, 'x2': 435, 'y2': 525, 'color': (0, 255, 0), 'score': 2}, {'x1': 450, 'y1': 496, 'x2': 465, 'y2': 525, 'color': (0, 0, 255), 'score': 3}, {'x1': 480, 'y1': 496, 'x2': 495, 'y2': 525, 'color': (255, 255, 0), 'score': 4}, {'x1': 510, 'y1': 496, 'x2': 534, 'y2': 525, 'color': (0, 255, 255), 'score': 5},
]
def process_page(page, csv_writer, question_intervals, score_zones):
pix = page.get_pixmap() output_image_path = f"page_{page.number}.png" pix.save(output_image_path)
image = cv2.imread(output_image_path)
gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
binary_image = cv2.adaptiveThreshold( gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY_INV, 19, 2)
kernel_length = max(binary_image.shape[1] // 100, binary_image.shape[0] // 100) vertical_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (1, kernel_length)) horizontal_kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_length, 1))
vertical_lines_img = cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, vertical_kernel, iterations=2) horizontal_lines_img = cv2.morphologyEx(binary_image, cv2.MORPH_OPEN, horizontal_kernel, iterations=2)
binary_image_no_lines = cv2.subtract(binary_image, horizontal_lines_img) binary_image_no_lines = cv2.subtract(binary_image_no_lines, vertical_lines_img)
contours, _ = cv2.findContours(binary_image_no_lines, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
filtered_contours = [] for contour in contours: x, y, w, h = cv2.boundingRect(contour) aspect_ratio = float(w) / h if 0.3 < aspect_ratio < 2.5 and 3 < cv2.contourArea(contour) < 500: filtered_contours.append(contour)
question_scores = ["" for _ in range(len(question_intervals))] total_score = 0
image_with_scores_and_boxes = cv2.cvtColor(binary_image_no_lines, cv2.COLOR_GRAY2BGR)
for contour in filtered_contours: x, y, w, h = cv2.boundingRect(contour) contour_center_x = x + w // 2 contour_center_y = y + h // 2 cv2.circle(image_with_scores_and_boxes, (contour_center_x, contour_center_y), 3, (0, 255, 0), -1)
cv2.rectangle(image_with_scores_and_boxes, (x, y), (x+w, y+h), (0, 255, 0), 2)
for idx, interval in enumerate(question_intervals, start=1): start_y, end_y = interval if start_y <= contour_center_y <= end_y: for zone in score_zones: zone_rect = (zone['x1'], zone['y1'], zone['x2'], zone['y2']) if (x < zone_rect[2] and x+w > zone_rect[0] and y < zone_rect[3] and y+h > zone_rect[1]): score = zone['score'] if question_scores[idx-1] == "": question_scores[idx-1] = score elif question_scores[idx-1] != score: question_scores[idx-1] = "???" cv2.putText(image_with_scores_and_boxes, str(score), (x, y-10), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0,0,255), 2) break
page_data = [page.number] + question_scores
csv_writer.writerow(page_data) cv2.imwrite(f"page_{page.number}_with_scores.png", image_with_scores_and_boxes)
pdf_path = input("請輸入完整的PDF名稱(需要放在同一個目錄下)")
with open('scores_data.csv', 'w', newline='') as csvfile: csv_writer = csv.writer(csvfile) headers = ['第幾頁'] + [f'Q{i}' for i in range(1, 15)] csv_writer.writerow(headers)
doc = fitz.open(pdf_path) for page_number in range(len(doc)): page = doc.load_page(page_number) process_page(page, csv_writer, question_intervals, score_zones)
|