Friday, April 16, 2010

Learning OpenCV: The rest of Transformation

ยังเหลือ Transform อีกสองแบบที่กล่าวไ้ว้คือ Integral Images กับ Distance Transform ซึ่งมองภาพไม่ออก เอาไว้ช่วยในการคำนวณล้วนๆ (ประมาณเดียวกับ transform ช่วงหลังๆ ) เข้าเรื่องเลยดีกว่า

Integral Images 

เป็นการ transform โดยการคำนวณค่าผลรวม, ผลรวมของกำลังสอง, ผลรวมของภาพที่่หมุนแล้ว (ไม่รู้เรื่องใช่มะ ดูภาพดีกว่า)

ทางซ้ายจะเป็น matrix ทางขวาจะเป็น matrix ที่เป็นค่ารวม (สังเกตว่า จะมีขนาดใหญ่กว่า) ค่าในแถวบน และหลักซ้ายจะเท่าเดิม  ส่วนค่าใน pixel ใดๆ จะได้มาจากผลรวมทั้งหมดจากแถวแรก ถึงแถวมัน จากหลักแรกถึงหลักมัน เช่น 25 นี่ได้มาจาก 1+2+2+20  80 ได้มากจาก 1+2+5+2+20+50 แต่จริงแล้วในการคำนวณค่า 80 ในแถวสองไม่จำเป็นต้องบวกหมดทั้งหกตัว จะใช้แค่ 50(ตัวมันเอง)+25(ซ้ายของมัน)+8(บนของมัน)-3(ซ้ายบน) ดังนั้นเวลาคำนวณ ตารางนี้ใช้เวลาเป็น O(N)

ประโยชน์หนึ่งในการคำนวณ integral image คือถ้าเราต้องการหาผลรวมในพื้นที่ (เพื่อทำค่าเฉลี่ย ความแปรปรวน) ในส่วนหนึ่งของ image ยกตัวอย่างคือ พื้นที่ที่ล้อมรอบด้วยวงเส้นทึบ ปกติการคำนวณจะใช้ระยะเวลาขึ้นกับขนาดพื้นที่ที่ต้องการคำนวณ O(N^2) ผลการคำนวณจากด้านซ้ายจะได้
20+50+20+50+100+50+20+50+20 = 380 แต่ในการคำนวณด้านขวาจะใช้ตัวเลขสี่ตัวเท่านั้นตลอดไม่ขึ้นกับขนาด ทำให้ O(1) 398-10-9+1 =380 (คงไม่งง) ฟังก์ชันของ OpenCV คือ


void cvIntegral(
    const CvArr* image,
    CvArr* sum,
    CvArr* sqsum = NULL,
    CvArr* tilted_sum = NULL
);

image คือภาพต้นฉบับ
sum คือผลลัพธ์แบบตารางในรูปด้านขวา
sqsum คือคล้ายๆ อันที่แล้วที่ยกกำลังสองก่อนบวก
tilted_sum คือหมุน 45 องศา(คือการ transpose หรือเปล่า) แล้ว sum



Distance Transform

มาถึงการ transform แบบสุดท้าย (ในหนังสือ) นิยามของการ transform แบบนี้คือในภาพผลลัพธ์ค่าที่ได้ในจุด x,y จะเป็นระยะห่างระหว่างจุด x,y ในภาพต้นฉบับกับจุด pixel ที่เป็น 0 ที่ใกล้ที่สุด (ลองคิดดูว่าถ้าหา edge detection เช่น canny แล้วกลับค่าให้ edge เป็น 0 ส่วนที่ไม่ใช่ edge มีค่า แล้วทำ distance transform ค่ายิ่งมากก็น่าจะเป็น centroid ของวัตถุ)

ในการคำนวณของ OpenCV จะใช้วิธีคำนวณระยะจริงๆ หรือว่าจะใช้ kernel ในการช่วยก็ได้


Void cvDistTransform(
    const CvArr* src,
    CvArr* dst,
    int distance_type = CV_DIST_L2,
    int mask_size = 3,
    const float* kernel = NULL,
    CvArr* labels = NULL
);

src, dst คือต้นฉบับและผลลัพธ์
distance_type คือวิธีการคำนวณหาระยะ (CV_DIST_L2 หมายถึงคำนวณระยะแบบ Cartesian)
mask_size คือขนาดของ mask ถ้าไม่ต้องการใช้ mask ใส่ CV_DIST_MASK_PRECISE
kernel ในกรณีที่เราจะทำ mask เอง (distance_type = CV_DIST_USER)
labels เป็นตัวเก็บไว้ว่าระยะห่างที่ใกล้ที่สุดที่คำนวณมาแต่ละ pixel ในผลลัพธ์คำนวณเทียบกับจุด zero ใดในภาพต้นฉบับ

No comments:

Post a Comment