
ช่วงนี้เล่นกับกล้อง Webcam บ่อย เลยเอาคำสั่งที่ใช้ๆอยู่มาทดลองทำอะไรเล่นๆบ้าง ก็เลยเข้า Lab ทดลองอะไรซักอย่าง
(พูดซะเวอร์ จริงๆก็นั่งหน้าคอม)
จริงๆโดยส่วนตัว ทึ่งกับแนวคิดการประมวลผลจากภาพมาก คำสั่งพื้นฐานที่เราคิดว่าไม่น่าจะเกี่ยว ก็สามารถทำผลลัพธ์อะไรออกมาได้
วันนี้จึงหยิบคลาส BitmapData และใช้ Methode ที่ชื่อว่า Threshold มาทดลองทำการตรวจจับวัตถุ ที่มีสีที่ต้องการ
จริงๆแล้วสารภาพว่า แนวคิดนี้ไม่ได้คิดขึ้นมาเอง แต่ก็ได้ศึกษาแนวคิดง่ายๆพวกนี้มาจากเว็ปหลายเว็ปเหมือนกัน
สำหรับมือใหม่ที่เข้ามาอ่าน ขออธิบายคร่าวๆว่าโค้ดที่จะเขียนนี้ใช้คลาสต่อไปนี้
BitmapData คลาสจัดการเกี่ยวกับภาพ Bitmap ซึ่งก็คือภาพที่เราจะแคบเจอร์จากกล้อง มาประมวลผลนั่นแหละครับ
Camera คลาสนี้แน่นอน ใช้ดึงกล้องมาแสงบนแฟลช
Video เป็นเหมือนผืนผ้าใบที่ใช้กาง แล้วฉายภาพจากกล้อง มาแสดงบนหน้าจอ
เอาหละ งั้นติดตามดูกันดีกว่าว่า การเข้า Lap ครั้งนี้ผลเป็นเช่นไรบ้าง….
เริ่มจาก ผมก็ค้นหาข้าวของภายในบ้าน ที่มีสีโดดเด่น ไปเจอใช้บัตร เดี่ยว 8 ของพี่โน๊สอุดม ด้านหลังบัตรนั้นสีเขียวได้ใจมาก
แล้วผมก็เขียนโค้ดลงในแฟลช เพื่อแสดงภาพจาก Webcam

import flash.media.Video; import flash.media.Camera; import flash.display.BitmapData; import flash.display.Bitmap; import flash.events.Event; import flash.geom.Point; import flash.filters.BlurFilter; import flash.geom.Rectangle; //สร้าง Video ไว้แสดงผลบนจอ var vid:Video = new Video(640,480); //เซ็ทกล้อง ให้ได้ขนาด var cam:Camera = Camera.getCamera(); cam.setMode(640,480,15); //เอากล้องไปฉายบน Video vid.attachCamera(cam); addChild(vid);
ซึ่งโค้ดในช่วงนี้ไม่มีอะไรมาก แค่ import สิ่งที่จำเป็น และแสดงผล Video ลงบนหน้าจอแฟลช
ผมจึงถ่ายภาพ และนำเข้า Photoshop เพื่อวิเคราะห์ว่า สิ่งที่ผมจะหานี้ มีค่าสีเป็นเช่นไรบ้างคร่าวๆ
ซึ่งค่าสีแต่ละพิกเซลก็จะต่างกัน แต่โดยรวมจะไม่ห่างกันมากนัก

จากนั้นจึงเขียนโค้ด เพื่อสร้าง BitmapData และนำมาประมวลผลด้วยคำสั่ง threshold
วิธีการใช้งาน thresHold เคยเขียนไปคร่าวๆแล้ว ในเรื่องการแยกสีด้วย hresHold ตามไปอ่านก่อนได้
//สร้าง BitmapData var b:BitmapData = new BitmapData(640,480,true,0); //แสดงผล BitmapData นี้ var m:Bitmap = new Bitmap(b); addChild(m); //สั่ง enterframe เพื่อวน loop คำนวนตลอดเวลา this.addEventListener(Event.ENTER_FRAME , loop ); function loop(e:Event):void{ b.draw(vid); //เราจะใส่โค้ด thresHold เพื่อค้นหาค่าสีที่นี่ }
โดยที่โค้ดด้านบนก็ตะทำการ Capture ภาพจากกล้อง Webcam เพื่อใช้ในการค้นหาสีที่เราอ่านได้จาก Photoshop
เอาหละพร้อมแล้ว เราจะมาเริ่มคัดเลือกสี ที่ไม่จำเป็นออกไปก่อนด้วย thresHold นี้
b.threshold( b , b.rect , new Point , ">" , 0xFF328c46 , 0xFF000000 , 0xFFFFFFFF );
โค้ดด้านบนหมายความว่า เราจะแปลงสีที่มีค่าสีมากกว่า #328C46 ให้กลายเป็นสีดำซะ (ค่าสี #328C46 เราอ่านได้จาก Photoshop ไง)
ผลก็คือ เราจะสามารถตัดสีที่สว่างขาวสว่างออกไปได้เยอะพอสมควร

สีที่มีค่าสีสูงกว่า หรือสีในแนวสว่าง สีขาว สีเนื้อ ถูกตัดเป็นสีดำหมด แต่สีเขียวยังอยู่ ถือว่าใช้ได้ครับ
เรายังเห็นคิ้ว ตาดำ เส้นผมชัดเจน นั่นหมายความว่า ค่าสีในโทนที่ มืด กว่าสีที่เราอยากได้ ยังไม่ถูกตัดไป ดังนั้นผมก็จะตัดสีมืดๆออกไป
ด้วย thresHold ดังต่อไปนี้
b.threshold( b , b.rect , new Point , "<" , 0xFF005500 , 0xFF000000 , 0xFF00FF00 );
หมายความว่า เราจัดตัดสี ที่มีค่าใน Chanel สีเขียว น้อยกว่า 55 ออกไป เราจะสามารถตัดาีในโทนมืดออกได้
ซึ่งจริงๆแล้วผมก็ไม่สามารถหาสูตรสำเร็จได้ ผมทดลองตัดค่าสีมาแล้วหลายรอบ ด้วยการตัดวิธีนี้ เวิร์คสุด ในกรณีผม
ซึ่งคุณก็ต้องนำไปดัดแปลงเอาเองนะครับ
ตัดโทนดำ แล้วกลายเป็นเช่นนี้

เหลือเพียงสีเขียวที่เราต้องการแล้ว เย้……
หูตาจมูกปาก หายไปหมดแล้ว เหลือเพียงสีเขียว
ขั้นตอนต่อไป ผมจะเพิ่มสีเขียวที่เหลือ ให้เขียวยิ่งขึ้นไปอีก เพื่อความสะดวกในการนำมาใช้งาน
ด้วย thresHold อีกแล้ว
b.threshold( b , b.rect , new Point , "!=" , 0xFF000000 , 0xFF00FF00 , 0xFFFFFFFF );
thresHold ตัวนี้ หมายความว่า เราจะเอาสี ที่ไม่ใช่สีดำทั้งหมดในภาพ ให้กลายเป็นสีเขียวให้หมด
ก็จะได้ความเขียวดังนี้

เ็ป็นไงหละครับ เขียวปื๊ดเลยทีเดียว
แต่เรายังเห็นว่า มีจุดเล็กๆน้อย ที่เป็นทั้งสีดำ และจุดสีเขียวเต็มไปหมด เป็น noise ที่ไม่ต้องการ
ผมจะทำการลดจุดพวกนี้ด้วยการเบลอมันซะ
ด้วยการเซ็ทฟิลเตอร์เบลอให้กับมัน
var bf:BlurFilter = new BlurFilter(10,10,3); b.applyFilter(b,b.rect,new Point,bf);
ผลที่ได้คือ noise หายไปเพียบ

สำเร็จในส่วนของการตรวจจับสีแล้ว
จากนั้นผมจะตัดสีดำออกให้หมด ให้กลายเป็นสีใส ทะลุไปด้านหลัง ให้มองเห็น Camera
ซึ่ง BitmapData ก็จะสามารถ ปรับให้เป็น alpha chanel ได้อยุ่แล้ว
ด้วย thresHold อีกแล้ว แหม มันช่างมีประโยชน์หลายด้านซะจริง
b.threshold( b , b.rect , new Point , "==" , 0xFF000000 , 0x00000000 , 0xFFFFFFFF );
โค้ดนี้หมายความว่า สีดำทั้งหมด จงมีค่า alpha chanel เป็น 0 หรือโปร่งใสทะลุด้านหลัง นั่นเอง

จะเห็นว่า พื้นสีดำทั้งหมด ถูกตัดออกไป กลายเป็น transparent ที่มองเห็นทะลุไปยัง Video ด้านหลัง
แต่เราก็ยังเห็นขอบการ์ด ที่เป็นสีดำอยู่ เพราะว่ามันเป็นสี ดำอมเขียว จากการเบลอเพื่อลด noise นั่นเอง
มันไม่เป็นสีดำสนิท มันจึงไม่หายไปจากการ thresHold ข้างบน
ผมจึงคิดว่า งั้นผม thresHold อีกรอบ เพื่อแปลงให้กลายเป็นสีเขียวให้หมดเลยแล้วกัน
b.threshold( b , b.rect , new Point , ">" , 0x00000000 , 0xFF00FF00 , 0xFF000000 );
คำสั่งด้านบน แปลได้ว่า ทำการเปลี่ยนสี ที่ไม่ได้โปร่งใสทั้งหมด ไปเป็นสีเขียว

ก็จะได้ผลออกมา สำเร็จสำหรับการ Detect สีแล้วครับ
เราสามารถใช้ Methode อื่นๆของ BitmapData เพื่อนำมันไปใช้งานก็ได้
เช่น getColorBoundRect เพื่อหา่ว่า กรอบสีเขียว อยู่ตรงไหนของหน้าจอ
ใช้เพื่อพัฒนาเกมก็ได้ครับ โดยอาจจะใช้การ์ด เป็นตัวควบคุมเกมแทนที่จะเป็นเมาส์ก็ได้
แถมครับ กระป๋องเครื่องดื่มยี่ห้อหนึ่ง ก็สีเขียวใช้ได้นะครับ 555555

