import numpy as np import cv2 # --- 1. Convolution 2D (พื้นฐานของ Blur และ Sobel) --- def convolution2d(image, kernel): """ ทำ Convolution ระหว่างภาพกับ Kernel """ # ดึงขนาดภาพและ Kernel image_h, image_w = image.shape kernel_h, kernel_w = kernel.shape # คำนวณ Padding (เพื่อให้ภาพผลลัพธ์ขนาดเท่าเดิม) pad_h = kernel_h // 2 pad_w = kernel_w // 2 # สร้างภาพที่มี Padding (ใช้ขอบเป็น 0) padded_image = np.pad(image, ((pad_h, pad_h), (pad_w, pad_w)), mode='constant') output = np.zeros_like(image) # วนลูป (Vectorized way เพื่อความเร็วที่ดีกว่า Loop ปกติ) # หมายเหตุ: การเขียน Loop ซ้อนกัน 2 ชั้นใน Python จะช้ามาก # ตรงนี้จึงขอใช้ Numpy ช่วยในการคูณ Matrix เพื่อประสิทธิภาพ for y in range(image_h): for x in range(image_w): # ตัดส่วนของภาพที่ตรงกับ Kernel roi = padded_image[y:y+kernel_h, x:x+kernel_w] # คูณกันแล้วหาผลรวม (Element-wise multiplication & Sum) k = (roi * kernel).sum() output[y, x] = k return output def gaussian_kernel(size=5, sigma=1.0): """ สร้าง Gaussian Kernel สำหรับทำ Smoothing """ ax = np.linspace(-(size - 1) / 2., (size - 1) / 2., size) xx, yy = np.meshgrid(ax, ax) kernel = np.exp(-0.5 * (np.square(xx) + np.square(yy)) / np.square(sigma)) return kernel / np.sum(kernel) # --- 2. Sobel Edge Detection (เขียนเอง) --- def sobel_edge_detection(image): # Kernel ของ Sobel แกน X และ Y Kx = np.array([[-1, 0, 1], [-2, 0, 2], [-1, 0, 1]], dtype=np.float32) Ky = np.array([[-1, -2, -1], [0, 0, 0], [1, 2, 1]], dtype=np.float32) # ทำ Convolution Ix = convolution2d(image, Kx) Iy = convolution2d(image, Ky) # คำนวณความแรงของขอบ (Gradient Magnitude) G = np.sqrt(Ix**2 + Iy**2) # Normalize ให้ค่าอยู่ช่วง 0-255 G = (G / G.max()) * 255 return G.astype(np.uint8) # --- 3. Threshold (เขียนเอง) --- def manual_threshold(image, thresh_value=100): """ เปลี่ยนภาพเป็น Binary (ขาว-ดำ) ตามค่า Threshold """ binary_image = np.zeros_like(image) # ถ้า pixel ไหนค่ามากกว่า thresh_value ให้เป็น 255 (ขาว) นอกนั้น 0 (ดำ) binary_image[image > thresh_value] = 255 return binary_image # --- 4. Morphology - Dilation (เขียนเอง) --- def manual_dilation(image, kernel_size=3): """ ขยายเส้นขอบให้หนาขึ้น (เชื่อมเส้นที่ขาด) """ h, w = image.shape pad = kernel_size // 2 padded_image = np.pad(image, ((pad, pad), (pad, pad)), mode='constant') output = np.zeros_like(image) # หลักการ Dilation: เลือกค่าที่ "มากที่สุด" ในหน้าต่าง Kernel for y in range(h): for x in range(w): roi = padded_image[y:y+kernel_size, x:x+kernel_size] output[y, x] = np.max(roi) return output # --- 5. จัดเรียงจุดมุม (Perspective Transform Helper) --- def order_points(pts): # (โค้ดเดิมที่เขียนเองถูกต้องแล้ว) rect = np.zeros((4, 2), dtype="float32") # บนซ้าย (ผลบวกน้อยสุด) / ล่างขวา (ผลบวกมากสุด) s = pts.sum(axis=1) rect[0] = pts[np.argmin(s)] rect[2] = pts[np.argmax(s)] # บนขวา (ผลต่างน้อยสุด) / ล่างซ้าย (ผลต่างมากสุด) diff = np.diff(pts, axis=1) rect[1] = pts[np.argmin(diff)] rect[3] = pts[np.argmax(diff)] return rect def four_point_transform(image, pts): rect = order_points(pts) (tl, tr, br, bl) = rect # คำนวณความกว้าง widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2)) widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2)) maxWidth = max(int(widthA), int(widthB)) # คำนวณความสูง heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2)) heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2)) maxHeight = max(int(heightA), int(heightB)) dst = np.array([ [0, 0], [maxWidth - 1, 0], [maxWidth - 1, maxHeight - 1], [0, maxHeight - 1]], dtype="float32") M = cv2.getPerspectiveTransform(rect, dst) warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight)) return warped