Device: Edge device · ระดับ: beginner
เริ่มต้นประมวลผลภาพดิจิทัลด้วย OpenCV และ Python
สอนใช้ OpenCV และ Python ตั้งแต่ศูนย์ อ่าน-แสดงผล-บันทึกภาพ/วิดีโอ Crop Resize Rotate แปลง Color Space ลด Noise ด้วย Gaussian Blur และหาขอบภาพด้วย Canny Edge Detector พร้อมโค้ดที่ทดสอบรันจริง
เริ่มต้นประมวลผลภาพดิจิทัลด้วย OpenCV และ Python
สวัสดีครับผม พิวดี้เองครับ! 🦉 จากบทความ 1.1 ปูพื้นฐาน Computer Vision: จากจุดพิกเซลสู่การรับรู้ทางทัศนศิลป์ของ AI เราคุยกันไปแล้วว่าภาพดิจิทัลก็คือตารางตัวเลขพิกเซลเท่านั้นเอง วันนี้เราจะเอาความรู้นั้นมาลงมือทำจริงกันครับ — เปลี่ยนทฤษฎีให้กลายเป็นโค้ด Python ที่รันได้จริง ด้วยไลบรารีที่เป็นเสาหลักของวงการคอมพิวเตอร์วิชันมาหลายสิบปีอย่าง OpenCV
บทความนี้เหมาะกับทุกคนที่เขียน Python เป็นพื้นฐานอยู่แล้ว แต่ยังไม่เคยแตะงานด้านภาพมาก่อน ผมจะพาทำทีละขั้นตอน ไม่มีการข้าม รับรองว่าตามทันแน่นอนครับผม
อุปกรณ์ที่ต้องใช้
- Python 3.9 ขึ้นไป
- ไลบรารี
opencv-pythonและnumpy - ภาพตัวอย่าง 1 ไฟล์ (หรือจะใช้โค้ดสร้างภาพทดสอบที่ผมเตรียมไว้ให้ก็ได้)
- (ทางเลือก) Google Colab — ถ้าไม่อยากติดตั้งอะไรบนเครื่องเลย เปิดลิงก์ Colab ท้ายบทความแล้วกด Run ได้ทันที
pip install opencv-python numpy matplotlib
Step 1: Import ไลบรารีและทำความเข้าใจว่าภาพคืออะไรในสายตา Python
เมื่อ OpenCV อ่านภาพเข้ามา มันจะแปลงภาพนั้นให้กลายเป็น NumPy array (ndarray) — โครงสร้างข้อมูลหลักของไลบรารี NumPy ที่ใช้เก็บข้อมูลเชิงตัวเลขแบบหลายมิติ (NumPy Developers, n.d.) พูดง่ายๆ คือภาพสี 1 ภาพจะถูกเก็บเป็น array 3 มิติ ขนาด (ความสูง, ความกว้าง, จำนวนช่องสี) ตรงกับที่เราเรียนไปในบทความ 1.1 เป๊ะเลยครับผม
import cv2
import numpy as np
Step 2: อ่าน แสดงผล และบันทึกภาพ
# อ่านภาพเข้ามาเป็น NumPy array (สำคัญ: OpenCV อ่านเป็นลำดับสี BGR ไม่ใช่ RGB!)
image = cv2.imread('sample.jpg')
print("ชนิดข้อมูล:", type(image))
print("รูปร่าง (Height, Width, Channels):", image.shape)
print("dtype:", image.dtype)
# แสดงผลภาพในหน้าต่างใหม่ (ใช้ได้เฉพาะเครื่องที่มี GUI)
cv2.imshow("Sample", image)
cv2.waitKey(0)
cv2.destroyAllWindows()
# บันทึกภาพ
cv2.imwrite('output_copy.jpg', image)
จุดที่ต้องระวังเป็นพิเศษ: OpenCV อ่านและเก็บภาพสีด้วยลำดับช่องสี BGR (Blue-Green-Red) ไม่ใช่ RGB แบบที่ไลบรารีอื่น เช่น matplotlib หรือ PIL ใช้กัน ถ้าคุณเผลอเอาไปแสดงผลด้วย matplotlib ตรงๆ สีในภาพจะเพี้ยนทันที (ประเด็นนี้ผมจะพูดถึงอีกครั้งในหัวข้อ Troubleshooting)
Step 3: อ่าน แสดงผล และบันทึกวิดีโอ
วิดีโอก็คือชุดของภาพ (เฟรม) ที่เล่นต่อกันเป็นลำดับ OpenCV จัดการเรื่องนี้ผ่านคลาส VideoCapture (อ่าน) และ VideoWriter (เขียน)
# เขียนวิดีโอ
fourcc = cv2.VideoWriter_fourcc(*'mp4v')
writer = cv2.VideoWriter('sample_video.mp4', fourcc, 5.0, (400, 300))
for i in range(10):
frame = np.full((300, 400, 3), (235, 225, 215), dtype=np.uint8)
cv2.circle(frame, (40 + i * 30, 150), 30, (180, 60, 20), -1)
writer.write(frame)
writer.release()
# อ่านวิดีโอกลับมาทีละเฟรม
cap = cv2.VideoCapture('sample_video.mp4')
frame_count = 0
while True:
ret, frame = cap.read()
if not ret:
break
frame_count += 1
cap.release()
print(f"อ่านวิดีโอได้ทั้งหมด {frame_count} เฟรม")
รูปแบบ while True + เช็ค ret (return value) เป็น pattern มาตรฐานที่คุณจะเจอทุกครั้งที่ต้องอ่านวิดีโอหรือกล้องแบบ real-time ด้วย OpenCV — จำแพทเทิร์นนี้ไว้เลยครับผม
Step 4: ทำความสะอาดภาพเบื้องต้น — Crop, Resize, Rotate
เพราะภาพคือ NumPy array เราจึงสามารถ Crop ภาพได้ด้วยการ slicing แบบเดียวกับที่ตัด list ใน Python เลยครับ
image = cv2.imread('sample.jpg')
# Crop ด้วย NumPy slicing: image[y1:y2, x1:x2]
cropped = image[40:210, 40:210]
cv2.imwrite('cropped.jpg', cropped)
# Resize ด้วย cv2.resize
resized = cv2.resize(image, (200, 150), interpolation=cv2.INTER_AREA)
cv2.imwrite('resized.jpg', resized)
# Rotate 90 องศาตามเข็มนาฬิกา
rotated_90 = cv2.rotate(image, cv2.ROTATE_90_CLOCKWISE)
cv2.imwrite('rotated_90.jpg', rotated_90)
# Rotate แบบกำหนดองศาอิสระด้วย warpAffine
(h, w) = image.shape[:2]
center = (w // 2, h // 2)
matrix = cv2.getRotationMatrix2D(center, angle=15, scale=1.0)
rotated_15 = cv2.warpAffine(image, matrix, (w, h))
cv2.imwrite('rotated_15deg.jpg', rotated_15)
สังเกตว่า Crop ใช้ลำดับ [y1:y2, x1:x2] ไม่ใช่ [x1:x2, y1:y2] เพราะ NumPy array นับมิติแรกเป็นแถว (แนวตั้ง = Height) ก่อนเสมอ — สลับกันบ่อยมากสำหรับมือใหม่ครับผม
Step 5: แปลง Color Space — BGR ↔ Grayscale / HSV
การแปลง Color Space คือการเปลี่ยนวิธีที่คอมพิวเตอร์ใช้แทนค่าสีของแต่ละพิกเซล (Gonzalez & Woods, 2018) งานประมวลผลภาพหลายอย่างไม่จำเป็นต้องใช้สีครบ 3 ช่อง เช่น การหาขอบภาพในหัวข้อถัดไปที่เราจะทำงานกับภาพ Grayscale (ระดับสีเทา ช่องเดียว) เพื่อลดความซับซ้อนในการคำนวณ
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
cv2.imwrite('gray.jpg', gray)
print("Gray shape:", gray.shape) # เหลือแค่ (H, W) ไม่มี channel สี
hsv = cv2.cvtColor(image, cv2.COLOR_BGR2HSV)
cv2.imwrite('hsv.jpg', hsv)
HSV (Hue-Saturation-Value) เป็น Color Space ที่แยก "เฉดสี" (Hue) ออกจาก "ความสว่าง" (Value) อย่างชัดเจน จึงเหมาะกับงานที่ต้องการตรวจจับวัตถุตามสี เพราะทนต่อการเปลี่ยนแปลงของแสงได้ดีกว่า RGB/BGR — เป็นเทคนิคที่ใช้บ่อยในงานคัดแยกวัตถุตามสีก่อนเข้าสู่ขั้นตอน Object Detection ขั้นสูง
Step 6: ลด Noise ด้วย Gaussian Blur
ภาพที่ถ่ายจากเซนเซอร์จริงมักมี Noise ปะปนอยู่เสมอ ก่อนนำภาพไปหาขอบวัตถุ (Edge Detection) จึงควรลด Noise ก่อนเสมอ เพราะ Noise ที่หลงเหลืออยู่จะถูกตัวตรวจจับขอบภาพเข้าใจผิดว่าเป็นขอบวัตถุจริง
blurred = cv2.GaussianBlur(gray, (5, 5), sigmaX=0)
cv2.imwrite('blurred.jpg', blurred)
(5, 5) คือขนาดของ Kernel (หน้าต่างคำนวณ) ยิ่งตัวเลขมากยิ่งเบลอมาก แต่ก็เสี่ยงที่จะเบลอขอบภาพจริงที่เราอยากเก็บไว้ด้วย — เป็นการ trade-off ที่ต้องปรับตามลักษณะภาพแต่ละงานครับผม
Step 7: หาขอบภาพด้วย Canny Edge Detector
มาถึงไฮไลต์ของบทความนี้ — Canny Edge Detector อัลกอริทึมหาขอบภาพที่ถูกคิดค้นโดย Canny (1986) และยังคงเป็นหนึ่งใน Edge Detector ที่ใช้กันแพร่หลายที่สุดจนถึงทุกวันนี้ เพราะให้ผลลัพธ์ที่แม่นยำและมีขอบภาพบางเป็นเส้นเดียว (ไม่ซ้ำซ้อน) ตามเกณฑ์ที่ผู้คิดค้นตั้งไว้ตั้งแต่ต้น
edges = cv2.Canny(blurred, threshold1=50, threshold2=150)
cv2.imwrite('edges.jpg', edges)
พารามิเตอร์ threshold1 และ threshold2 คือค่า Threshold สองระดับที่ Canny Edge Detector ใช้ตัดสินใจว่าพิกเซลไหน "เป็นขอบแน่นอน" พิกเซลไหน "อาจจะเป็นขอบ" และพิกเซลไหน "ไม่ใช่ขอบ" — ถ้าตั้งค่าต่ำเกินไปจะได้ขอบเยอะเกินจำเป็น (รวม Noise เข้าไปด้วย) ถ้าตั้งสูงเกินไปขอบที่จางจะหายไป ต้องลองปรับตามภาพจริงของแต่ละงานครับผม
ผลลัพธ์ (Expected Output)
ภาพด้านล่างคือผลลัพธ์แต่ละขั้นตอนที่ผมรันจริงจากโค้ดข้างต้น (ทดสอบผ่านแล้ว ไม่มี error):

จากภาพจะเห็นว่าหลังผ่าน Canny Edge Detector เราจะได้เฉพาะ "โครงร่าง" ของวัตถุ (เส้นขอบสี่เหลี่ยมและวงกลม) โดยตัดข้อมูลสีและพื้นผิวส่วนเกินออกไปทั้งหมด — นี่คือหัวใจของ Pre-processing ที่จะถูกนำไปใช้ต่อในงาน Object Detection หรือ Contour Analysis ในบทความถัดๆ ไปของซีรีส์นี้ครับผม
Troubleshooting — ปัญหาที่พบบ่อย
1) cv2.imshow() ไม่ขึ้นหน้าต่าง หรือรันบน Google Colab แล้ว Error
Colab และ Jupyter Notebook หลายตัวไม่มีหน้าต่าง GUI ให้ OpenCV เปิดแสดงผล ให้ใช้ from google.colab.patches import cv2_imshow แทนบน Colab หรือใช้ matplotlib.pyplot.imshow() แทนบนสภาพแวดล้อมอื่นๆ (อย่าลืมแปลง BGR → RGB ก่อนแสดงผลด้วย matplotlib ตามข้อถัดไป)
2) สีในภาพเพี้ยนเมื่อแสดงผลด้วย matplotlib
เพราะ OpenCV เก็บภาพเป็น BGR แต่ matplotlib คาดหวัง RGB ให้แปลงก่อนแสดงผลเสมอด้วย cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
สรุป
วันนี้เราพาโค้ดของเราเดินทางจากการอ่านภาพธรรมดา ไปจนถึงการหาขอบภาพด้วย Canny Edge Detector ครบทุกขั้นตอนของ Pre-processing พื้นฐานที่จะเป็นรากฐานสำคัญก่อนต่อยอดไปสู่งาน Object Detection หรือ Image Segmentation ในบทความถัดๆ ไปครับผม
อยากให้ทุกคนลองโหลดโค้ดไปรันดูกับภาพของตัวเองบ้าง แล้วมาแชร์ผลลัพธ์กันในคอมเมนต์ หรือแท็กมาที่ Facebook PWD Vision Works ได้เลยนะครับผม ใครติดปัญหาไหนทักมาถามพิวดี้ได้ตลอดครับ 🦉
เนื้อหาที่เกี่ยวข้อง:
- อ่านพื้นฐานทฤษฎีเรื่องพิกเซลและการรับรู้ภาพของ AI ต่อได้ที่บทความ 1.1 ปูพื้นฐาน Computer Vision
- ดูรีวิวเปรียบเทียบ Libraries สำหรับงาน Computer Vision เพิ่มเติมที่บทความ 4.1
ลองรันโค้ดเองได้ที่: [ต้องการ URL GitHub Repository จริงจากเจ้าของกิจการ — ไฟล์ทั้งหมดพร้อมอัปโหลดแล้ว โปรดดูหมายเหตุท้ายการส่งมอบ] หรือเปิด Google Colab Notebook ที่แนบมาในไฟล์ส่งมอบ รันได้ทันทีไม่ต้องติดตั้งอะไรเลยครับผม
คำถามที่พบบ่อย (FAQ)
Q1: OpenCV กับ Python ต้องใช้เวอร์ชันไหน?
A1: แนะนำ Python 3.9 ขึ้นไป และไลบรารี opencv-python เวอร์ชันล่าสุดที่ติดตั้งผ่าน pip ได้โดยตรง ไม่ต้อง compile เอง ยกเว้นมีความต้องการพิเศษ เช่น GPU acceleration
Q2: ทำไม OpenCV ถึงอ่านภาพเป็นสี BGR แทนที่จะเป็น RGB เหมือนไลบรารีอื่น? A2: เป็นการตัดสินใจทางประวัติศาสตร์ตั้งแต่ยุคแรกของการพัฒนา OpenCV ที่ใช้ลำดับ BGR ตามธรรมเนียมของกล้องและซอฟต์แวร์บางตัวในยุคนั้น และยังคงรักษาไว้เพื่อความเข้ากันได้กับโค้ดเก่า ผู้ใช้งานจึงต้องแปลงเป็น RGB เองเมื่อจะใช้ร่วมกับไลบรารีอื่น เช่น matplotlib หรือ PIL
Q3: Gaussian Blur กับการลด Noise แบบอื่นต่างกันอย่างไร? A3: Gaussian Blur ใช้การถ่วงน้ำหนักพิกเซลรอบข้างตามการกระจายแบบ Gaussian ทำให้เบลอแบบนุ่มนวลและรักษาโครงสร้างขอบภาพได้ดีกว่าการเบลอแบบ Average ทั่วไป จึงเป็นตัวเลือกยอดนิยมสำหรับขั้นตอนเตรียมภาพก่อนหาขอบวัตถุ
Q4: ค่า threshold ของ Canny Edge Detector ควรตั้งเท่าไหร่? A4: ไม่มีค่าตายตัวที่ใช้ได้กับทุกภาพ ค่าที่นิยมเริ่มต้นทดลองคืออัตราส่วน threshold สูงต่อ threshold ต่ำประมาณ 2:1 หรือ 3:1 (เช่น 50 และ 150) แล้วปรับตามลักษณะ Noise และความคมชัดของภาพจริงที่ใช้งาน
Q5: ต้องมีกล้องหรือภาพจริงก่อนถึงจะลองโค้ดนี้ได้ไหม? A5: ไม่จำเป็นครับ โค้ดในบทความนี้มีส่วนสร้างภาพตัวอย่างด้วย NumPy ให้ในตัว สามารถรันทดสอบทุกขั้นตอนได้ทันทีโดยไม่ต้องมีไฟล์ภาพหรือกล้องเลย
Q6: เนื้อหานี้ต่อยอดไปเรียนอะไรได้ต่อ? A6: หลังเข้าใจ Pre-processing พื้นฐานแล้ว ขั้นตอนถัดไปตามลำดับการเรียนรู้คือ Feature Extraction แบบดั้งเดิม และการเข้าสู่ Deep Learning สำหรับงาน Computer Vision เช่น Convolutional Neural Network (CNN) ซึ่งจะกล่าวถึงในบทความคลัสเตอร์ถัดไปของซีรีส์นี้
คำถามที่พบบ่อย
OpenCV กับ Python ต้องใช้เวอร์ชันไหน?
แนะนำ Python 3.9 ขึ้นไป และไลบรารี opencv-python เวอร์ชันล่าสุดที่ติดตั้งผ่าน pip ได้โดยตรง ไม่ต้อง compile เอง ยกเว้นมีความต้องการพิเศษ เช่น GPU acceleration
ทำไม OpenCV ถึงอ่านภาพเป็นสี BGR แทนที่จะเป็น RGB เหมือนไลบรารีอื่น?
เป็นการตัดสินใจทางประวัติศาสตร์ตั้งแต่ยุคแรกของการพัฒนา OpenCV ที่ใช้ลำดับ BGR ตามธรรมเนียมของกล้องและซอฟต์แวร์บางตัวในยุคนั้น และยังคงรักษาไว้เพื่อความเข้ากันได้กับโค้ดเก่า ผู้ใช้งานจึงต้องแปลงเป็น RGB เองเมื่อจะใช้ร่วมกับไลบรารีอื่น เช่น matplotlib หรือ PIL
Gaussian Blur กับการลด Noise แบบอื่นต่างกันอย่างไร?
Gaussian Blur ใช้การถ่วงน้ำหนักพิกเซลรอบข้างตามการกระจายแบบ Gaussian ทำให้เบลอแบบนุ่มนวลและรักษาโครงสร้างขอบภาพได้ดีกว่าการเบลอแบบ Average ทั่วไป จึงเป็นตัวเลือกยอดนิยมสำหรับขั้นตอนเตรียมภาพก่อนหาขอบวัตถุ
ค่า threshold ของ Canny Edge Detector ควรตั้งเท่าไหร่?
ไม่มีค่าตายตัวที่ใช้ได้กับทุกภาพ ค่าที่นิยมเริ่มต้นทดลองคืออัตราส่วน threshold สูงต่อ threshold ต่ำประมาณ 2:1 หรือ 3:1 (เช่น 50 และ 150) แล้วปรับตามลักษณะ Noise และความคมชัดของภาพจริงที่ใช้งาน
ต้องมีกล้องหรือภาพจริงก่อนถึงจะลองโค้ดนี้ได้ไหม?
ไม่จำเป็นครับ โค้ดในบทความนี้มีส่วนสร้างภาพตัวอย่างด้วย NumPy ให้ในตัว สามารถรันทดสอบทุกขั้นตอนได้ทันทีโดยไม่ต้องมีไฟล์ภาพหรือกล้องเลย
เนื้อหานี้ต่อยอดไปเรียนอะไรได้ต่อ?
หลังเข้าใจ Pre-processing พื้นฐานแล้ว ขั้นตอนถัดไปตามลำดับการเรียนรู้คือ Feature Extraction แบบดั้งเดิม และการเข้าสู่ Deep Learning สำหรับงาน Computer Vision เช่น Convolutional Neural Network (CNN) ซึ่งจะกล่าวถึงในบทความคลัสเตอร์ถัดไปของซีรีส์นี้