diff --git a/test.py b/test.py index 3fb88cb..c38173f 100644 --- a/test.py +++ b/test.py @@ -1,4 +1,4 @@ - +import numpy as np import cv2 # 設定連接到 Android 手機的相機 @@ -6,48 +6,138 @@ cap = cv2.VideoCapture(0) # 0 表示第一個相機(通常是後置相機) cap.set(cv2.CAP_PROP_FRAME_WIDTH, 1920) # 設定寬度 cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 1080) # 設定高度 +object_points = np.array([[0, 0, 0], [26, 0, 0], [0, 18.5, 0], [26, 18.5, 0]], dtype=np.float32) -while True: - ret, frame = cap.read() # 讀取影片幀 +def get_new_approx(approx, previous_approx): + def distance(point, pre_point): + point = point[0] + pre_point = pre_point[0] + d = ((point[0]-pre_point[0]) ** 2) + ((point[1]-pre_point[1]) ** 2) - if not ret: - break + return np.sqrt(d) - # 預處理影像(例如:轉為灰度) - gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + new_approx = [] + for pre_point in previous_approx: + min_d, min_index = 1e9, -100 + for index, point in enumerate(approx): + d = distance(point, pre_point) + if d < min_d and d<=500: + min_d = d + min_index = index + + if min_index != -100: + np.delete(approx, min_index) + new_approx.append(approx[min_index]) - # 偵測邊緣 - edges = cv2.Canny(gray, 100, 200) + if len(new_approx) == 4: + return new_approx + else: + return previous_approx - # 偵測輪廓 - contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) +def initial_approx(approx): + distance = [] + for point in approx: + x, y = point[0] + distance.append(x**2+y**2) + min_d, max_d = 1e9, -1 + min_index, max_index = -100, -100 + for index, d in enumerate(distance): + if d < min_d: + min_d = d + min_index = index + if d > max_d: + max_d = d + max_index = index + new_approx = [] - # 找到最大的輪廓 - max_contour = max(contours, key=cv2.contourArea) + remain_index = list(range(4)) + remain_index.remove(max_index) + remain_index.remove(min_index) - # 找到輪廓的近似多邊形 - epsilon = 0.05 * cv2.arcLength(max_contour, True) - approx = cv2.approxPolyDP(max_contour, epsilon, True) + if approx[remain_index[0]][0][0] > approx[remain_index[1]][0][0]: # remain 的第一個點的 x 大於第二個點的,代表他在右上角 + second_index = remain_index[0] + third_index = remain_index[1] + else: + second_index = remain_index[1] + third_index = remain_index[0] - # 繪製多邊形 - if len(approx) == 4: # 確保是四個角點 - for point in approx: - print(point) - x, y = point[0] - cv2.circle(frame, (x, y), 5, (0, 0, 255), -1) # 在角點位置畫紅色圓圈 - - cv2.drawContours(frame, [approx], -1, (0, 255, 0), 2) # 繪製輪廓 - print() - - # 顯示結果 - cv2.imshow('Paper Detection', frame) - # 按下 'q' 鍵退出迴圈 - if cv2.waitKey(33) & 0xFF == ord('q'): # 等待 33ms (1秒 = 1000ms, 1秒顯示30幀) - break + new_approx.append(approx[min_index]) + new_approx.append(approx[second_index]) + new_approx.append(approx[third_index]) + new_approx.append(approx[max_index]) + + return new_approx + +if __name__ == '__main__': -cap.release() -cv2.destroyAllWindows() + previous_approx = [] + + while True: + ret, frame = cap.read() # 讀取影片幀 + + if not ret: + break + + # 預處理影像(例如:轉為灰度) + gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) + gray = cv2.GaussianBlur(gray, (5, 5), 0) + + # 偵測邊緣 + edges = cv2.Canny(gray, 30, 90) + dilate_kernel = np.ones((5, 5), np.uint8) + dilate_edges = cv2.dilate(edges, dilate_kernel, iterations=1) + + # 偵測輪廓 + contours, _ = cv2.findContours(dilate_edges, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) + + if contours != []: + # 找到最大的輪廓 + max_contour = max(contours, key=cv2.contourArea) + + # 找到輪廓的近似多邊形 + epsilon = 0.05 * cv2.arcLength(max_contour, True) + approx = cv2.approxPolyDP(max_contour, epsilon, True) + + # 繪製多邊形 + if len(approx) == 4: # 確保是四個角點 + if previous_approx == []: + print("INITIAL") + previous_approx = initial_approx(approx) + new_approx = get_new_approx(approx, previous_approx) + previous_approx = new_approx + + x, y = new_approx[0][0] + cv2.circle(frame, (x, y), 15, (0, 0, 255), -1) # 在角點位置畫紅色圓圈 + + x, y = new_approx[1][0] + cv2.circle(frame, (x, y), 30, (0, 0, 255), -1) # 在角點位置畫紅色圓圈 + + x, y = new_approx[2][0] + cv2.circle(frame, (x, y), 15, (255, 0, 0), -1) # 在角點位置畫紅色圓圈 + + x, y = new_approx[3][0] + cv2.circle(frame, (x, y), 30, (255, 0, 0), -1) # 在角點位置畫紅色圓圈 + + cv2.drawContours(frame, [approx], -1, (0, 255, 0), 2) # 繪製輪廓 + + # 顯示結果 + cv2.namedWindow('Paper Detection(edge)', 0) + cv2.imshow('Paper Detection(edge)', edges) + cv2.namedWindow('Paper Detection(dilate edge)', 0) + cv2.imshow('Paper Detection(dilate edge)', dilate_edges) + cv2.namedWindow('Paper Detection', 0) + cv2.imshow('Paper Detection', frame) + + # 按下 'q' 鍵退出迴圈 + if cv2.waitKey(33) & 0xFF == ord('q'): # 等待 33ms (1秒 = 1000ms, 1秒顯示幀) + break + if cv2.waitKey(33) & 0xFF == ord(' '): + print("Clear previous approxes") + previous_approx = [] + + cap.release() + cv2.destroyAllWindows()