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):

ผลลัพธ์เปรียบเทียบ 4 ขั้นตอน: ภาพต้นฉบับ, Grayscale, Gaussian Blur, Canny Edge Detection

จากภาพจะเห็นว่าหลังผ่าน 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) ซึ่งจะกล่าวถึงในบทความคลัสเตอร์ถัดไปของซีรีส์นี้