มาสร้างไฟล์ Cascade ไว้ใช้ตรวจจับวัตถุกัน

ในตอนที่แล้วนั้น ได้แสดงตัวอย่างการเขียนโปรแกรม Face Detection โดยใช้ Library SimpleCV บนภาษา Python ที่มีข้อดีคือ ใช้งานง่าย ไม่ยุ่งยาก โดยในตอนนั้นเราจะเห็นว่า มีไฟล์ xml ชื่อ face.xml ที่ถูกใช้ในฟังก์ชัน findHaarFeature  และในตอนนี้เราจะมาทำความรู้จักกับ Haar feature-based ซึ่งถูกพัฒนาขึ้นมาเพื่อใช้ในการตรวจจับวัตถุ และเราจะมาสร้างไฟล์ xml ของเราเองกันครับ

แต่เดิมกระบวนการตรวจจับวัตถุ หรือแยกแยะวัตถุนั้น เป็นกระบวนการที่ใช้พลังงาน และทรัพยากรสูงมาก แต่เราจะมาทำความรู้จักกับหนึ่งในรูปแบบของกระบวนการ Haar feature-based ที่ถูกพัฒนาเพื่อให้สามารถทำงานได้อย่างรวดเร็วมากขึ้น โดยวิธีนี้ถูกนำเสนอเมื่อปี 2001 โดย Paul Viola และ Michael Jones ได้ตีพิมพ์ผลงานของพวกเขาในหัวข้อ “Rapid Object Detection using a Boosted Cascade of Simple Features” ซึ่งใช้กระบวนการ Machine Learning ตามที่ฟังก์ชันคาสเคดได้ถูกสอนผ่านการวิเคราะห์รูป ซึ่งถูกแบ่งเป็นสองกลุ่มคือ กลุ่มรูปถูกต้อง ซึ่งคือรูปของสิ่งที่เราอยากจะตรวจจับ ในรูปแบบต่างๆ แต่ต้องมีการตัดเอาส่วนอื่นออก ให้เหลือเพียงส่วนของสิ่งที่เราต้องการเท่านั้น และกลุ่มของรูปทั่วไป ที่ไม่มีสิ่งที่เราอยากจะตรวจจับอยู่ในรูปเลย เพื่อที่จะสามารถนำข้อมูลที่ได้ไปตรวจจับ ในรูปอื่นๆต่อไป

วิธีที่ Paul Viola และ Michael Jones คิดค้นขึ้นมาใช้นั้น ช่วยให้ลดการใช้ทรัพยากร และเวลาในการประมวลลงได้อย่างมาก โดยพวกเขาเรียกว่า “Fast computation of Haar-like features” วิธีคือ การแบ่งพื้นที่ของภาพด้วยรูปแบบสำเร็จรูป 4 แบบที่พวกเขาได้กำหนดจาก Haar-like features ไว้ดังรูป ( A – B – C – D ) จากนั้นจึงคำนวณตารางสีของภาพโดยการ อินทิเกรตตารางสีที่อยู่ในพื้นที่อยู่ในช่องสีขาว ลบด้วยตารางสีที่อยู่ในพื้นที่อยู่ในช่องสีดำ ในกระบวนการสร้าง รูปอินทิกรัล แล้วนำผลที่ได้ไปใช้ใน Adaboost ที่เป็นอัลกอริทึมสำหรับการเรียนรู้แบบหนึ่ง ( Adaptive Boost Learning Algorithm ) เนื่องจากจำนวนข้อมูลมีจำนวนมหาศาล ( ขนาด 24 x 24 ช่อง จะมีรูปแบบที่เป็นได้ทั้งหมด 162,336 รูปแบบ ) การเลือกใช้ Adaboost จึงเป็นทางออกที่ดี และเหมาะสมที่สุด ( ปล. เรื่อง Adaboost ยังไม่ได้อ่านอย่างละเอียดครับ และไม่มีความเชี่ยวชาญเลย ดังนั้นข้อมูลทั้งหมดตรงนี้ ผมแปลเอาครับ ) และใช้ Cascading Classifiers ในการทำให้ระบบเรียนรู้ผ่าน ตัวอย่างข้อมูลที่ถูกต้องจำนวนหนึ่ง และข้อมูลที่ไม่เกี่ยวข้องอีกจำนวนหนึ่ง เพื่อให้ระบบสามารถเรียนรู้ความแตกต่างได้ครับ

รูปแบบ Haar Like Feature 4 แบบที่ถูกเลือกใช้

รูปแบบเพิ่มเติมที่ถูกพัฒนาโดย Rainer Lienhart and Jochen Maydt

ทีนี้เราจะมาลองสร้างไฟล์ cascade ที่เป็น xml สำหรับใช้ในการตรวจจับวัตถุ จากกระบวนการ Haar Cascade Training กันครับ

ขั้นตอนแรกคือ เตรียมไลบรารี่ และข้อมูล ต่างๆ

เราจะต้องติดตั้ง OpenCV และ Library ต่างๆที่จำเป็น

sudo apt-get install cmake git libgtk2.0-dev pkg-config libavcodec-dev libavformat-dev libswscale-dev python-dev python-numpy libtbb2 libtbb-dev libjpeg-dev libpng-dev libtiff-dev libjasper-dev libdc1394-22-dev libopencv-dev

และเราต้องการใช้รูปอีกจำนวนมาก โดยแบ่งรูปออกเป็น สองกลุ่ม

กลุ่มแรก คือ รูปเฉพาะของสิ่งที่เราต้องการจะตรวจจับ โดยเราจะเรียกรูปเหล่านี้ว่า “รูปของสิ่งที่เราสนใจ” ในที่นี้เราจะเก็บมันไว้ในโฟลเดอร์ pos

กลุ่มที่สองคือ รูปที่ไม่มีสิ่งที่เราต้องการจะตรวจจับ และควรจะเป็นรูปทั่วๆไป ไม่ใช่รูปเฉพาะของสิ่งของใดครับ
และเราจะเรียกรูปกลุ่มนี้ว่า “รูปของสิ่งที่ไม่เกี่ยวข้อง”

การหารูปของสิ่งที่เราต้องการจะตรวจจับ จำนวนมากๆ ไม่ใช่เรื่องง่าย
ผมเลือกที่จะดาวน์โหลดจากเว็บนี้ http://image-net.org/ ซึ่งไปเจอในตัวอย่างการทำ Cascade File มาครับ โดยเว็บนี้จะรวบรวมรูปภาพต่างๆไว้เป็นจำนวนมากในรูปแบบ url และมีการแบ่งหมวดหมู่ไว้ค่อนข้างดีทีเดียว ทำให้สะดวกมากถ้าต้องการรูปอะไรสักอย่างจำนวนมากๆ

การเตรียมภาพ ที่จะใช้เป็นข้อมูล

รูปที่ผมสนใจคือ รูปเครื่องบินรบ ครับ นั่นคือสิ่งที่จะใช้ทดลองในตัวอย่างนี้ โดยการค้นหาด้วยคำว่า “Aircraft”

ซึ่งหน้าต่างทางซ้ายนั้นเราสามารถเลือกประเภทที่เฉพาะเจาะจงเข้าไปอีกได้ หรือจะเลือกจากกลุ่มภาพตัวอย่างที่มีให้ดูก็ได้

เมื่อผมเลือกชุดของรูปเครื่องบินที่ผมต้องการได้แล้ว ในเว็บนี้เราสามารถดาวน์โหลดรูปได้จาก url โดยการเลือกที่ แถบ Download ขางบนก็จะปรากฏดังภาพ

ให้เราคัดลอก URLs ข้างบนไว้ เพราะเราจะโหลดรูปโดยใช้ URLs นี้ ไปเก็บไว้ใน โฟลเดอร์ pos ครับ

ต่อมาให้เราหาภาพวิวที่คิดว่าจะไม่มีสิ่งที่เราสนใจจะตรวจจับไปปรากฏในนั้น ผมเลือกที่จะใช้ภาพสนามบิน และท่าอากาศยาน ไปเก็บไว้ใน โฟลเดอร์ neg ( ซึ่งการเลือกท่าอากาศยานนั้นไม่ใช่ความคิดที่ดีแน่ๆครับ แต่อยากลองว่าถ้าภาพใกล้ๆกันมันยังทำงานได้รึเปล่าครับ แนะนำว่าควรใช้ภาพแนวอื่น เพื่อสิ่งที่ดีกว่านะครับ ) และผมก็ได้ URLs นี้มา http://image-net.org/api/text/imagenet.synset.geturls?wnid=n02692232

เริ่มต้นโหลดภาพ

ภาพที่เราจะใช้ในการสร้างไฟล์ Cascade ครั้งนี้ เราจะต้องทำให้มันเป็นภาพ Grayscale และย่อขนาดของมันให้เล็กลงเสียก่อน ซึ่งขนาดของภาพที่ถูกต้อง และภาพที่ไม่เกี่ยวข้องนั้น จะต้องมีขนาดเท่ากัน โดยในที่นี้เราจะย่อให้เหลือเพียง 200 x 200 pixels เท่านั้น โดยใช้ python code ด้านล่างนี้

ในโค้ดนี้ จะมีหน้าที่ดาวน์โหลดรูป และแปลงรูปที่ดาวน์โหลดมาให้เป็นรูป Grayscale และย่อยขนาดให้เหลือ 200 x 200 pixels และนำไปเก็บไว้ยัง โฟลเดอร์ที่เราระบุไว้

นี่คือส่วนที่ระบุ urls และเปิดหน้าเพจที่ถูกระบุไว้

แสดงลิงค์ที่ได้จากการอ่านหน้าเพจนั้น

ตั้งค่าเริ่มต้นของภาพ ในที่นี้ให้เป็น 0

เช็คโฟลเดอร์ที่ต้องการเก็บว่ามีหรือไม่ ถ้าไม่มีให้สร้างโฟลเดอร์ไว้รอ

ดาวน์โหลดภาพจากลิงค์ที่ได้จากหน้าเพจนั้น และเก็บในโฟลเดอร์ neg ในนามสกุล jpg

เปิดไฟล์รูปที่โหลดไว้ขึ้นมาเพื่อแปลงรูปให้เป็น Grayscale และย่อขนาดรูปให้เหลือ 200 x 200 pixels

เช็คว่ารูปที่โหลดมาทำการแปลงรูปสำเร็จหรือไม่ และเก็บไว้ในโฟลเดอร์ neg อีกครั้ง
และแสดงจำนวนลิงค์ที่อยู่ใน urls ทั้งหมด พร้อมกับจำนวนภาพล่าสุดที่นับไว้

รันฟังก์ชัน store_raw_images() เพื่อเรียกใช้ฟังก์ชันที่เขียนไว้

ควรจะบันทึกชื่อรูปล่าสุดที่ได้ไว้นะครับ เพราะลำดับนั้น เราจะเอาไปตั้งให้กับไฟล์ที่ถูกต้องของเราอีกที

ทีนี้เมื่อเราได้รูปมาแล้ว ต่อมาเราต้องมาตรวจสอบความสมบูรณ์ของรูป ว่าใช้งานได้หรือไม่ มีรูปใดมีปัญหาในการโหลด หรือแสดงผลผิดไปหรือไม่ เนื่องจากลิงค์ที่ได้นั้นบางครั้ง รูปก็ไม่ได้พร้อมให้ใช้งานครับ เริ่มจากสำรวจรูปที่ผิดปกติในโฟลเดอร์ที่เราโหลดมาเก็บไว้ก่อนหน้านี้ครับ ภาพที่ผิดปกติจะมีลักษณะประมาณนี้ครับ

ตัวอย่างลักษณะของภาพที่เราไม่ต้องการ

ซึ่งหลายภาพคือภาพที่ไม่สามารถเปิดได้ บางภาพมีการระบุว่าหาไม่เจอ และอะไรอีกมากมาย ซึ่งเราไม่ต้องการครับโดยให้เราสร้างโฟลเดอร์ที่ชื่อ BadNeg ไว้ข้างนอกโฟลเดอร์ neg แล้วย้ายภาพตัวอย่างที่พบปัญหาไปไว้ในโฟลเดอร์นั้นแล้วเราจะใช้โค้ดชุดนี้ในการตรวจสอบครับ

เก็บชื่อรูปแต่ละรูปที่มีปัญหาซึ่งเราได้เลือกไว้ในโฟลเดอร์ BadNeg ไว้ในตัวแปร ugly

อ่านภาพจากชื่อที่เก็บไว้

เช็คว่าภาพเปิดได้รึเปล่า ถ้าเปิดได้ให้แสดงภาพขึ้นมา

แต่ถ้ารูปตัวอย่างเปิดไม่ได้ก็ลบรูปตัวอย่างเช่นกัน

เก็บชื่อรูปแต่ละรูปในโฟลเดอร์ neg ไว้ในตัวแปล img

อ่านภาพจากที่อยู่ที่เก็บไว้

ถ้าภาพที่อ่านไว้ สามารถเปิดได้ ให้แสดงภาพ

เปรียบเทียบภาพระหว่างภาพตัวอย่าง กับภาพในโฟลเดอร์ที่เราต้องการคัดกรอง
ถ้าเหมือนกัน ให้ลบรูปในโฟลเดอร์ที่เราต้องการคัดกรองออก

แต่ถ้าเปิดไม่ได้ ให้ลบรูปนั้นทันที

รันฟังก์ชัน find_uglies() เพื่อเรียกใช้ฟังก์ชันที่เขียนไว้

หลังจากทำส่วนนี้แล้ว จำนวนรูปจะลดลง แต่อาจจะยังไม่หมด เราจึงควรกลับเข้าไปเช็ค และทำซ้ำอีกครั้ง จนกว่าจะหมด

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

เปลี่ยนลิงค์ไปยังรูปที่เราสนใจ

เช็ค และสร้างโฟลเดอร์ pos สำหรับเก็บรูปที่เราสนใจ

เก็บไฟล์รูปที่เราสนใจไว้ในโฟลเดอร์ pos ดังนั้นเราจึงต้องเปลี่ยนจาก neg เป็น pos ให้หมด

จากนั้นให้จัดการไฟล์ที่ไม่สามารถเปิดได้ และไฟล์ที่ผิดปกติ สำหรับโฟลเดอร์ pos นี้ด้วย โดยแก้ชื่อโฟลเดอร์ให้เป็น pos เช่นกัน แต่ยังคงใช้โฟลเดอร์ BadNeg สำหรับเก็บไฟล์ตัวอย่างที่ผิดปกติเช่นเดิม

แก้ for img in glob.glob(“neg/*.jpg”): เป็น for img in glob.glob(“pos/*.jpg”):
แก้ print “neg – ” +str(e.value) เป็น print “pos – ” +str(e.value)
และลองรันแบบเดียวกันไฟล์ที่เราทำในโฟลเดอร์ neg เช่นเดียวกัน

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

เปิด terminal console เตรียมไฟล์ที่ใช้รวมภาพที่เราสนใจโดยใช้คำสั่ง

และเตรียมไฟล์ที่ใช้รวมภาพที่ไม่เกี่ยวข้องกับสิ่งที่เราสนใจ โดยใช้คำสั่ง

สรุป

ถึงตอนนี้ เราจะมีไฟล์อยู่ 2 ไฟล์คือ
pos.txt ที่ใช้เก็บที่อยู่รูปที่เราสนใจ
neg.txt ที่ใช้เก็บที่อยู่รูปที่ไม่เกี่ยวข้องกับรูปที่เราสนใจ

และโฟลเดอร์เก็บรูป 2 โฟลเดอร์คือ
pos ที่ใช้เก็บรูปที่เราสนใจ
neg ที่ใช้เก็บรูปที่ไม่เกี่ยวข้องกับรูปที่เราสนใจ

สร้างเวกเตอร์ไฟล์

ต่อมาเราต้องสร้างไฟล์เวกเตอร์ ( vec ) จากรูปทั้งหมดที่เรามี โดยใช้ opencv_createsamples ที่มีมาให้ใน opencv หลังจากที่เราได้ติดตั้งไปแล้ว โดยที่ผมใช้จะติดตั้งอยู่ในโฟลเดอร์ /usr/bin/opencv_createsamples แต่สำหรับบางท่านอาจจะแตกต่างจากนี้

สำหรับโปรแกรม opencv_createsamples นี้ จะสร้างไฟล์เวกเตอร์ สำหรับรูปที่เราสนใจ 1 รูป เทียบกับรูปที่ไม่เกี่ยวข้องกับสิ่งที่เราสนใจทุกรูป และเราจะได้ไฟล์นามสกุล vec ขึ้นมา 1 ไฟล์ แต่ในตอนนี้เรามีรูปที่เราสนใจอยู่หลายรูป ถ้าจะมาเรียกจากรูปที่เราสนใจทีละรูปคงจะใช้เวลานานมากแน่ๆ จึงมีคนเขียนสคริปส์ให้สามารถทำงานได้อัตโนมัติ โดยเรียกตามที่อยู่ในไฟล์ นั่นคือเหตุผลที่ทำให้เราสร้างไฟล์ txt ไว้ก่อนหน้านี้ครับ

นี่คือ โค้ด ที่ใช้ในการเรียก opencv_createsamp

หรือจะ clone ไฟล์นี้มาก็ได้ครับ https://github.com/mrnugget/opencv-haar-classifier-training หากโหลดไฟล์นี้มา createsamples.pl จะอยู่ในโฟลเดอร์ bin และท่านอาจจะแก้ไขตำแหน่งการเรียกใช้ opencv_createsamp ในบรรทัดที่ 17 ของโค้ดให้ถูกต้องครับ ท่านจึงจะสามารถเรียกใช้ได้ แนะนำว่านำไฟล์นี้ออกมาอยู่ในตำแหน่งเดียวกันกับไฟล์ pos.txt และ neg.txt จะทำให้ใช้งานได้ง่ายกว่านะครับ

โดย createsamples.pl จะเรียกใช้ opencv_createsamp ทุกๆไฟล์ใน pos.txt เทียบกับ neg.txt และสร้างไฟล์ vec ไว้ให้ในโฟลเดอร์ที่เราได้กำหนดไว้ดังนี้

$perl usr/bin/createsamples.pl pos.txt neg.txt samples 1200 "opencv_createsamples -bgcolor 0 -bgthresh 0 -maxxangle 1.1 -maxyangle 1.1 maxzangle 0.5 -maxidev 10 -w 20 -h 20"

จากคำสั่งข้างต้นนั้นระบุว่า เราใช้ไฟล์ pos.txt สำหรับรูปที่เราสนใจ neg.txt สำหรับรูปที่ไม่เกี่ยวข้องกับสิ่งที่เราสนใจ เก็บไฟล์ vec ไว้ในโฟลเดอร์ samples ซึ่งหลังจากรันแล้ว เราสามารถเข้าไปดูได้ จำนวนรูปที่จะใช้งานมีทั้งหมด 1200 รูป ( pos มี 240 รูป และ neg มี 987 รูป รวมแล้ว 1227 รูป แต่ใช้เพียง 1200 รูป )

-bgcolor ตั้งค่าสีของรูปที่ไม่เกี่ยวข้องกับสิ่งที่เราสนใจ ค่าปกติเป็น 0 คือเป็นภาพ grayscale

-bgthresh จะทำงานร่วมกับ -bgcolor โดย pixel ที่มีสีอยู่ระหว่าง -bgcolor +/- -bgthresh จะถูกทำให้โปร่งใส (transparent) ปกติเป็น 0

-maxangle ต่างๆคือ มุมการหมุนในหน่วยเรเดียน

-maxidev คือค่าความเบี่ยงเบนของความหนาแน่นสูงสุดของรูปที่เราสนใจ

-w, -h คือกำหนดขนาด output ในหน่วยของ pixels

เมื่อเรารันไฟล์ createsamples.pl ได้ถูกต้องแล้ว จะได้รายงานผลดังรูป

ภาพตัวอย่าง รายงานการทำงานจากการทำงานของ createtrainsamples.pl

และในโฟลเดอร์ samples จะมีไฟล์นามสกุล vec อยู่
เมื่อเราได้ไฟล์ vec แล้ว เราจะต้องทำการรวมเวกเตอร์ไฟล์ทั้งหมดให้เป็นไฟล์เดียวกันเสียก่อน เพราะเราต้องการไฟล์ cascade เพียง 1 ไฟล์เท่านั้น เราสามารถรวมไฟล์ได้โดยใช้สคริปส์ไฟล์นี้

เราสามารถรันไฟล์ mergevec.py เพื่อรวมไฟล์ vec ในโฟลเดอร์ที่เราสร้างไว้ ให้กลายเป็นไฟล์เดียวด้วยคำสั่ง

เราก็จะได้ไฟล์ samples.vec ที่เกิดจากการรวมไฟล์เวกเตอร์ในโฟลเดอร์ samples แล้ว
สรุปว่าในตอนนี้เรามีไฟล์เวกเตอร์ ของรูปที่เราสนใจ และไฟล์ที่อยู่ของรูปที่ไม่เกี่ยวข้องกับสิ่งที่เราไม่สนใจแล้ว

สุดท้ายก็คือการเอาไฟล์เวกเตอร์ของรูปที่เราสนใจ กับไฟล์ที่อยู่ของรูปที่ไม่เกี่ยวข้อง ไปสอนเพื่อให้ได้ xml ไฟล์ของรูปที่เราสนใจเพื่อนำไปใช้กับรูปอื่นๆต่อไป
แต่โปรแกรมที่ใช้เทรนนี้มีอยู่ 2 ตัวคือ opencv_haartraining และ opencv_traincascade ซึ่งให้ไฟล์ xml เช่นกัน แต่เป็นคนละมาตรฐาน โดย opencv_haartraining ยังจะคงใช้ในมาตรฐานเก่า และโปรแกรมอื่นๆก็ยังรองรับมาตรฐานนี้
แต่ opencv_traincascade จะเป็นมาตรฐานที่ใหม่กว่า opencv สามารถใช้มาตรฐานนี้ได้ และใช้เวลาในการสร้าง xml ไฟล์น้อยกว่า

ดังนั้นเราจึงเลือกใช้ opencv_traincascade แต่เนื่องจากการทำงานนี้จะกินเวลาที่ยาวนานมาก… อาจจะถึงข้ามวันกันได้เลยทีเดียว ( เคยลองรันไปสามวันเลย ) ดังนั้นเราจึงควรให้มันรันในระดับ Background และ monitor การทำงานผ่าน htop ( โปรแกรมดู task และการทำงาน บน linux ) จะดีกว่า โดยใช้คำสั่ง nohup และปิดท้ายด้วย &
ดังนี้

$nohup /usr/bin/opencv_traincascade -data data -vec samples.vec -bg neg.txt -numPos 230 -numNeg 950 -numStage 15 -w 20 -h 20 -precalcValBufSize 64 -precalcIdxBufSize 64 &

-data data คือระบุให้เก็บไฟล์ xml ไว้ในโฟลเดอร์นี้
-vec samples.vec ใช้ไฟล์เวกเตอร์ไฟล์นี้
-bg neg.txt ใช้รูปที่ไม่เกี่ยวข้องตามลิสต์นี้
-numPos 230 จำนวนรูปของสิ่งที่เราสนใจ ที่ใช้ในการคำนวณ
-numNeg 950 จำนวนของรูปที่ไม่เกี่ยวข้องกับสิ่งที่เราสนใจ ที่ใช้ในการคำนวน
-numStage 15 ระดับสเตจในการคำนวณ ยิ่งมากยิ่งละเอียด แต่ก็จะยิ่งนานมากเป็นทวีคูณ เพราะต้องคำนวณมากขึ้นเป็นจำนวนเท่า (Big O)
-w 20 -h 20 ขนาดภาพที่ใช้ในการคำนวณ แบบเดียวกับที่ใช้ในการทำเวกเตอร์ไฟล์
-precalcValBufSize 64 -precalcIdxBufSize 64 กำหนดขนาดของบัฟเฟอร์ซึ่งเมื่อรวมกันแล้วไม่ควรมากกว่าจำนวนแรมที่มีเหลือให้ใช้ ในกรณีนี้รันบน Orange Pi One ที่มีแรมเพียง 512MB และเหลือใช้จริงราวๆ 200MB นิดๆ เลยกำหนดเท่านี้และเปิดไว้นานๆๆๆๆๆๆๆๆๆ ครับ

จากนั้นก็รอ…ครับ เราจะสังเกตว่าจะมี Process ID ที่ 4536 ที่โปรแกรมแจ้งเลข PID ให้กับเรา ทำงานอยู่ จากหน้าต่างของ htop

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

ตัวอย่าง ข้อมูลใน nohup.out

ข้อมูลใน nohup.out จะบอกค่าพารามิเตอร์ต่างๆ ซึ่งมีผลต่อประสิทธิภาพการทำงาน และความแม่นยำของไฟล์ cascade ที่เราจะได้ในแต่ละสเตจครับ

เมื่อระบบทำงานเสร็จ ผมเลือกที่จะก๊อปปี้ไฟล์ cascade.xml  จากโฟลเดอร์ data ออกมาแล้วเปลี่ยนชื่อเป็น Aircascade15.xml เพื่อระบุว่าเป็นไฟล์สำหรับตรวจ Aircraft ที่ผลิตจากสเตจจำนวน 15 สเตจ จากนั้นเราก็สามารถนำไฟล์ที่ได้มาทดลองได้ครับ – ผมได้แชร์ไฟล์ที่ได้กระบวนการนี้ไว้ครับ สามารถดาวน์โหลดมาลองกันได้ก่อนครับ
https://drive.google.com/open?id=0B_9dScmDNMaCUE9DQTRkUkdrRWc

ทดลองใช้งาน

เราจะสร้างไฟล์ python ขึ้นมา โดยให้ทดลองกับการจับภาพเครื่องบินในไฟล์วิดีโอ โดยใช้ xml ที่เราสร้างขึ้นครับ
โดยผมเลือกดาวน์โหลดไฟล์วิดีโอ Airshow ที่มั่นใจว่ามีเครื่องบินแน่ๆ มาลองนะครับ

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

ตีกรอบรอบวัตถุที่ตรวจับได้ โดยค่าที่ใช้จะเป็น ตำแหน่ง x1, y1 ของภาพ และขนาดกว้าง , สูง x2, y2 ของวัตถุในภาพ

ตั้งขนาดภาพ และเลือกไฟล์วิดีโอที่จะตรวจสอบ ถ้าหากเปิดจากกล้อง ให้เปลี่ยนเป็น
cap = cv2.VideoCapture(0) ครับ

อ่านภาพทีละเฟรม และเอาแต่ละเฟรมมาตรวจหา และวาดกรอบ แล้วแสดงทั้งหมดขึ้นบนจอ

ช่วงแรกจากทางด้านหน้า ตรวจไม่เจออะไร

ช่วงถัดมาจากด้านข้าง ยังคงตรวจไม่เจอ

จากข้างหลัง กลับตรวจเจอ

อาจจะเป็นไปได้ว่ารูปตัวอย่างมีรูปจากมุมนี้ค่อนข้างเยอะ

ภาพจากด้านบน และอีกมุม ตรวจจับได้ค่อนข้างดี และในหลายๆมุมนั้นตรวจจับได้เป็นส่วนใหญ่เลยครับ

สรุปผล

จากกระบวนการอันยาวเหยียดข้างบนนี้ เราสามารถสร้างไฟล์ Cascade ที่เอาไว้ใช้กับฟังค์ชั่น Haar Cascade เพื่อตรวจจับสิ่งที่เราสนใจจากภาพถ่ายหรือวิดีโอได้

สามารถนำไปประยุกต์ใช้ในงานได้หลากหลายครับ
ไม่ว่าจะเป็นงานที่ต้องใช้การคัดเลือกด้วยสายตา ( Vision Inspect )
งานที่ต้องการ การติดตามวัตถุบางอย่าง
งานด้านการเกษตร ที่ใช้นับจำนวนผลผลิต หรือติดตามการเติบโต และวัดขนาดของผลผลิต
งานสำหรับนับคนในพื้นที่หนึ่ง
งานตรวจหาคน จากกลุ่มคน ก็มาสามารถนำไปประยุคต์ใช้ได้เช่นกัน

และยังสามารถขยายไปยังการพัฒนาให้ระบบสามารถเรียนรู้วัตถุ ได้จากภาพในเฟรมของวิดีโอ
เพื่อการะบุที่แม่นยำ และเจาะจงมากขึ้นได้ครับ

หวังว่าวิธีการนี้คงจะมีประโยชน์สำหรับทุกๆท่านนะครับ

อ้างอิง

https://pythonprogramming.net/haar-cascade-object-detection-python-opencv-tutorial/

http://coding-robin.de/2013/07/22/train-your-own-opencv-haar-classifier.html

http://docs.opencv.org/trunk/d7/d8b/tutorial_py_face_detection.html

http://docs.opencv.org/2.4/modules/objdetect/doc/cascade_classification.html

https://en.wikipedia.org/wiki/Viola%E2%80%93Jones_object_detection_framework

http://docs.opencv.org/2.4/doc/user_guide/ug_traincascade.html

An Extended Set of Haar-like Features for Rapid Object Detection
https://pdfs.semanticscholar.org/72e0/8cf12730135c5ccd7234036e04536218b6c1.pdf

Rapid Object Detection using a Boosted Cascade of Simple Features
https://www.cs.cmu.edu/~efros/courses/LBMV07/Papers/viola-cvpr-01.pdf

https://en.wikipedia.org/wiki/Cascading_classifiers

https://github.com/mrnugget/opencv-haar-classifier-training

https://github.com/mrnugget/opencv-haar-classifier-training/blob/master/bin/createsamples.pl