Thursday, April 15, 2010

Learning OpenCV: Convolution (cont)

Canny Edge Detection
แนวคิดของ canny edge detection คือการหาอนุพันธ์เพื่่อหาจุดสุดสุดและต่ำสุดของข้อมูล โดยเชื่่อว่าจุดนั้นคือ edge (ดังที่ได้อธิบายไปในตอนที่แล้ว) ที่นี้ข้อแตกต่างก็คือ ในขณะที่ Laplace ใช้ผลรวมของสองแกน x,y ของ canny หาอนุพันธ์ของแกนสี่แกน (มุมเฉียงอีกสองแกน) วิธีการเรียกก็เหมือนกับ Laplace


void cvCanny(
    const CvArr* img,
    CvArr* edges,
    double lowThresh,
    double highThresh,
    int apertureSize = 3
);

ค่าที่ได้จะเป็น array ของ edge ที่ detect ได้ (ซึ่งสามารถสร้างเป็น contour)
โดยถ้าค่า gradient ที่ได้มากว่า highThresh จะถือว่าเป็น edge ถ้านอ้ยว่า lowThresh จะถือว่าไม่ใช่ edge ค่าที่อยู่ระหว่างนั้น ให้ดู pixel ข้างเคียง โดยถือว่าเป็น edge ถ้าข้อมูลข้างเคียงสูงกว่า highThresh

Hough Transform
เป็นการค้นหาเส้นตรงที่อยู่ในรูปภาพโดยมีแนวคิดที่ว่า จุดใดๆ ในภาพอาจจะเป็นส่วนหนึ่งของเส้นตรงได้
ดังนั้นถ้ากำหนดพารามิเตอร์ของเส้นตรงเช่น เส้นตรงกำหนดได้ด้วย a,b (ความชัน กับ จุดตัดแกน x) ดังนั้น จุดในระนาบ x,y ใดๆ ในภาพก็สามารถ map ไปยังระนาบ a,b ได้ (แน่นอนไม่ได้เป็น one-to-one แต่เป็น one-to-many) เมื่อ map หลายจุดเข้าไป ในระนาบ จุดที่ซ้ำกัน ก็ให้รวมค่าซะ (เราเรียก plane นี้ว่า accumulator plane) ค่าที่เป็น local maximum (เนื่องจากจุดใกล้ๆ เป็นเส้นตรงที่คล้ายๆ กันไม่จำเป็นต้องนับทั้งหมด) ก็ถือว่าเป็นเส้นที่อยู่ในภาพ แต่จริงๆ แล้วการใช้ระนาบ a,b ไม่ค่อยเวิร์คเพราะว่าค่า a มีได้อนันต์ จึงใช้วิธีกำหนดเส้นโดยใช้ polar (ρ,θ) (จริงๆ polar กำหนดเส้นตรงไม่ได้ แต่ในที่นี้กำหนดให้เส้นตรงนั้นตั้งฉากกับมุม θ) ใน OpenCV จะคำนวณค่า local max ในระนาบ   (ρ,θ) ออกมาให้

OpenCV สนับสนุน Hough Line Transform สองชนิด ชนิดแรกคือ standard Hough Transform (SHT) ดังที่กล่าวไปแล้ว อีกชนิด คือ progressive probabilistic Hough Transform (PPHT) โดยใช้ความน่าจะเป็นในการคำนวณ local max โดยไม่จำเป็นต้องคำนวณทุกจุดใน accumulator plane เพราะถือว่าถ้า local max สูงพอ การคำนวณขาดไปบางจุดค่า local max ก็ยังต้องสูงอยู่ดี

CvSeq* cvHoughLines2(

    CvArr* image,
    void* line_storage,
    int method,
    double rho,
    double theta,
    int threshold,
    double param1 = 0,
    double param2 = 0
);

image คือรูปภาพที่จะ transform
line_storage คือที่เก็บผลลัพธ์ อาจจะเป็น memory storage หรือ CvArr ขนาด Nx1 (N คือขนาดมากสุดของผลลัพธ์)
method คือ CV_HOUGH_STANDARD, CV_HOUGH_PROBABILISTIC, หรือ CV_HOUGH_
MULTI_SCALE สำหรับ SHT, PPHT และ multi scale variant ของ SHT
rho, theta คือขนาดของของระนาบ  (ρ,θ)
threshold  คือขนาดที่กำหนดไว้ เพื่อที่จะนับว่าเป็น line ข้อควรระวังคือค่านี้ไม่ normalize เพราะฉะนั้นในกรณีที่รูปภาพใหญ่ขึ้นสำหรับ SHT ค่านี้อาจจะต้องตั้งสูงขึ้น

SHT ไม่ใช้ค่า param1, param2
PPHT ใช้ค่า param1 สำหรับความยาวสั้นสุดของเส้นผลลัพธ์ (สั้นมากก็ไม่น่าจะนับเป็นเส้นตรง) param2 จะเป็นระยะห่างต่ำสุดที่จะไม่เชื่อมเส้นตรงสองเส้นที่อยู่ในแนวเดียวกันด้วยกัน(คือถ้าระยะห่างสั้นกว่า param2 ก็เขื่อมเส้นตรงสองเส้นนี้เป็นเส้นเดียวกันซะ)
multi scale HT ค่า param1, param2 จะใช้ในการ กำหนดรายละเอียดการค้นหาเส้น โดยหลังจากคำนวณหาเส้นแล้วจะทำการ ตรวจสอบผลของเส้นโดยการ เพิ่ม resolution ใน rho ด้วย param1 และ theta ด้วย param2 (อันนี้หนังสืออธิบายว่าผลลัพธ์สุดท้าย rho จะเท่ากับ rho/param1 และ theta จะเท่ากับ theta/param2 ถ้าละเอียดขึ้นมันน่าจะคูณกันนี่นา)

ค่าที่ return จากฟังก์ชัน ขึ้นอยู่กับ พารามิเตอร์ line_storage ถ้าเป็น matrix array ฟังก์ชันจะคืนค่า NULL แล้วค่าของ  (ρ,θ) จะอยู่ใน 2 channel ใน array ถ้าเป็น PPHT จะเป็น array 4 channel เก็บค่า x,y ของจุดต้นและจุดปลายของเส้น ทั้งสองกรณีขนาด rows จะถูกอัพเดตโดยฟังกช์นเองเพื่อให้สอดคล้องกับจำนวนเส้นที่หาได้

ในกรณีที่ line_storage เป็น memory storage ฟังก์ชันจะคืนค่า pointer ไปยัง sequence ตัวอย่างการอ้างถึง line ที่สนใจใน sequence

float* line = (float*) cvGetSeqElem( return_line , i );

return_line คือ pointer ของ sequence ที่ได้มาจากฟังก์ชัน line[0] จะเป็นค่า ρ line[1] จะเป็นค่า θ (สำหรับ SHT, MSHT ในกรณีของ PPHT ต้อง casting ให้เป็น CvPoint)


Hough Circle Transform

เป็นแนวคิดในการหาวงกลมคล้ายๆ กับ การหา Hough Line Transform แต่เปลี่ยนจาก accumulate plane เป็น accumulate volume เพราะต้องการค่า x,y และ r แต่จริงๆ แล้ว OpenCV ไม่ได้ใช้วิธีนี้แต่ใช้วิธีที่เรียกว่า Hough Gradient Method

วิธีการของ Hough Gradient Method คือ นำภาพมาผ่านกระบวนการ edge detection ก่อน(คือ cvCanny) ต่อมาทุกๆ จุดที่ไม่ใช้ 0 ใน edge image จะมาดูค่า gradient (ที่ได้มาจากอนุพันธ์ลำดับแรกใน cvSobel) โดย

  1. ทุกจุดบนเส้นนี้(เส้นที่ผ่านจุดใน edge มีความชันทีได้มากจาก gradient โดยมีระยะห่างต่ำสุดและสูงสุดกำหนดไว้ จะถูกเพิ่มใน  accumulator)
  2. จุดที่เป็น local max ใน accumulator น่าจะมีสิทธิ์ เป็นจุดศูนย์กลาง นำมาเรียงจากมากไปน้อย
  3. ไล่ขนาดวงกลมไปเพื่อให้ตัดจุดที่ไม่ใช้ 0 ใน canny image วงกลมที่ตัดจุดมากพอและห่างจากวงกลมที่เคยหาได้แล้ว จะถูกเก็บ
  4. ไล่ไปจนครบทุก candidate ที่จะเป็น center


CvSeq* cvHoughCircles(

    CvArr* image,

    void* circle_storage,
    int method,
    double dp,
    double min_dist,
    double param1 = 100,
    double param2 = 300,
    int min_radius = 0,
    int max_radius = 0
)

image หมายถึง ภาพต้นฉบับ
circle_storage ถ้าเป็น CvArr จะได้ค่ามาเป็น array 3 channel แทน x,y,r ตามลำดับและฟังก์ชันจะคืนค่า NULL ถ้าเป็น memory storage ฟังก์ชันจะคืนค่า pointer ไปยัง sequence (คล้ายๆ อันที่แล้ว)
dp จะกำหนดตัวหารเพื่อลดขนาดของ accumulator image
min_dist เป็นค่าระยะห่างต่ำสุดที่จะแยกว่าวงกลมสองวงเป็นคนละวง
param1 คือ cvCanny threshold (จริงๆ canny ต้องการ threshold สองค่า แต่ cvHoughtCircles จะกำหนดค่าที่สองให้เป็ฯ param1/2)
param2 คือ accumulator threshold
min_radius, max_radius คือรัศมีของวงกลมที่เล็กสุดและใหญ่สุด

No comments:

Post a Comment