ลองสอน Haar Cascade เอง

สวัสดีอีกครั้งครับ ขอบคุณทุกๆท่านที่ให้ความสนใจกับบทความก่อนๆด้วยนะครับ ถ้าใครยังไม่ได้ก็ขอโฆษณาหน่อยนึง https://goo.gl/aSVu9e เป็นบทความสอนใช้ Tensorflow ทำ Linear Regression ครับ

สำหรับบทความนี้ผมตั้งใจจะเขียนเรื่องการ train Haar Cascade ซึ่งถ้าใครเคยเล่น OpenCV มาสักพักก็น่าจะเคยรู้จักกับ Haar Cascade อยู่บ้าง มันเป็น feature นึงในไลบรารี่ OpenCV ที่ใช้ Detect แล้วก็ Recognize วัตถุต่างๆจาก Feature ที่มีอยู่ในรูปนั้นๆครับ (อนึ่งในบทความนี้ผมทำบน Windows 10 โดยใช้ Bash for windows ครับ)

ตัวอย่างการทำงาน รูปจาก http://docs.opencv.org/trunk/d7/d8b/tutorial_py_face_detection.html

ถ้าใครอยากลองใช้ผมแนะนำ Tutorial นี้ครับ https://goo.gl/bDXf8Z

สำหรับวันนี้ผมจะมาสอนวิธีการ train cascade ด้วยตัวเองโดยสิ่งที่ต้องการมี

  1. รูป positive
  2. รูป negative

รูป positive คือรูปๆนึงที่มีวัตถุอยู่ในนั้น ส่วนรูป negative คือรูปที่ไม่มีวัตถุนั้นอยู่ในนั้นครับ ปกติเราจะต้องการรูปเยอะพอควรเพื่อที่จะให้ Cascade ที่เรา train แล้วทำงานได้ดีที่สุด แต่ว่าใน tutorial นี้ผมต้องการแค่ที่จะทดลองเฉยๆ ผมเลยใช้รูป positive แค่รูปเดียวคือดินสอตราม้ารูปนี้ครับ

รูปจาก https://goo.gl/qRfmt9

ส่วนรูป negative จะเป็นรูปอะไรก็ได้ที่ไม่มีวัตถุอยู่ข้างใน ผมจึงหารูปอะไรก็ได้ในโทรศัพท์ แล้วก็รูปในเน็ตมา 500 กว่ารูปครับ

พอเราหาเสร็จแล้วให้ crop รูปวัตถุของเราให้เหลือแต่วัตถุหลังจากนั้นเราจะนำรูปที่ crop มาสร้างรูป positive เพิ่มเพราะลำพังรูปเดียวของผมคงใช้ทำอะไรไม่ได้ครับ (ฮ่าๆ) แต่ก่อนอื่นเราต้องทำการ list ชื่อรูป negative ทั้งหมดก่อนโดยใช้คำสั่ง

find ./negative-photos -iname "*.jpg" > bg.txt

หลังจากนั้นเราก็สามารถรันคำสั่งในการสร้างรูป positive ด้วย (ก่อนอื่นต้องลง libopencv-dev จาก sudo apt-get install libopencv-dev ก่อนนะครับ)

opencv_createsamples -img <ชื่อรูป> -bg bg.txt -info info/info.lst -pngoutput info -maxxangle 0.5 -maxyangle 0.5 -maxzangle 0.5 -num <จำนวนรูปที่ต้องการ>

เมื่อรันโค้ดด้านบนเสร็จจะได้รูปในโฟลเดอร์ info ครับสามารถเข้าไปดูได้ว่ารูปที่เราสร้างมาเป็นยังไง หลังจากได้รูปมาเราต้องแปลงไปเป็นไฟล์ .vec ก่อนโดยรันคำสั่งนี้ครับ

opencv_createsamples -info info/info.lst -num <จำนวนรูป> -w <ขนาด width> -h <ขนาด height> -vec positives.vec

ถ้าหากเราใช้รูป Positive มากกว่า 1 รูปก็ไปรันคำสั่งในการ createsamples รอบแรกใหม่ครับ พอรันเสร็จเราจะได้ไฟล์ .vec หลายไฟล์โดยเราสามารถรวมกันเข้าเป็นไฟล์เดียวโดยใช้ mergevec.py จากที่นี่ครับ https://github.com/wulfebw/mergevec

โดยเราจะรวมโดยใช้คำสั่ง (ก่อนอื่นต้องนำ .vec ที่มีไปวางไว้ในโฟลเดอร์เดียวกันก่อน)

python ที่อยู่/mergevec.py ที่อยู่ของไฟล์เวคเตอร์

หลังจากนั้นเราจะเริ่มทำการ train โดยใช้คำสั่ง (อย่าลืมสร้างโฟลเดอร์ชื่อ data ด้วยนะครับ)

opencv_traincascade -data data -vec positives.vec -bg bg.txt -numPos 1800 -numNeg 900 -numStages 10 -w <ขนาด> -h <ขนาด>

โดยยิ่งตั้งค่า -numStages ไว้มากขนาดไหนยิ่งใช้เวลานานมากเท่านั้น แต่ผลที่ได้ก็จะแม่นยำมากขึ้นครับ โดยผมใช้เวลาประมาณ 1 ชั่วโมงในการ train ไฟล์ของผมแต่ถ้าไฟล์ Positive ยิ่งเยอะก็อาจจะใช้เวลามากขึ้นเรื่อยๆครับ

เมื่อมัน train เสร็จจะได้ file ชื่อ cascade.xml ในโฟลเดอร์ data

เราจะนำ file นั้นมาลองใช้โดยใช้โค้ด

import cv2
img = cv2.imread("รูปที่มีดินสอ")
hc = cv2.CascadeClassifier("cascade.xml")
pencil = hc.detectMultiScale(img)

for (x,y,w,h) in pencils:
font = cv2.FONT_HERSHEY_SIMPLEX
cv2.putText(img,'pencil',(x-w,y-h), font, 0.5, (11,255,255), 2, cv2.LINE_AA)
cv2.imshow('img',img)
cv2.waitKey()

และนี่คือรูปที่ทดลองใช้ครับ

จะเห็นได้ว่าทำงานดีพอสมควรแต่บางครั้งก็ detect ไม่ติดแต่ถ้า train หลายๆ stage มากขึ้นก็อาจจะทำงานได้ดีขึ้น

สำหรับบทความนี้ก็จบเพียงเท่านี้ครับขอบคุณทุกท่านมากๆครับ

ป.ล. ถ้าใครชอบบทความนี้ถ้ารบกวนกด Recommended หรือ Follow ให้จะเป็นพระคุณมากครับ :D