ช่วงนี้เล่นกับกล้อง 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

Download Sourcecode ที่นี่ เป็นไฟล์ Flash CS4