การวัดระยะวัตถุในภาพด้วย Python

Kritthanit Malathong
2 min readDec 11, 2020

--

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

โจทย์ปัญหาในบทความนี้

หัวใจหลักของการเขียนโปรแกรมนี้อยู่ที่หลักการคำนวณนะครับ หากผู้อ่านเข้าใจหลักการแล้ว สามารถนำไปเขียนด้วยภาษาอะไรก็ได้ ไม่จำเป็นว่าต้องเป็น Python อย่างเดียวเท่านั้น

ก่อนอื่นเรามาดูสมการที่เราจะใช้ในการเขียนโปรแกรมนี้กันก่อนเลยครับ

W คือความกว้างของวัตถุ มีหน่วยเป็นเมตร (หรือจะใช้ความสูงก็ได้นะครับ)

P คือความกว้างของวัตถุที่ภายในภาพ มีหน่วยเป็นพิกเซล

D คือระยะห่างระหว่างกล้องกับวัตถุ มีหน่วยเป็นเมตร

F คือระยะโฟกัสของภาพ มีหน่วยเป็นพิกเซล (ซึ่งก็คือค่าที่เราต้องการหา)

เมื่อเขียนออกมาเป็นภาพจะได้ประมาณนี้ครับ

ภาพ top view ของระบบ

เมื่อเราใช้การเปรียบเทียบค่าเราจะได้สมการดังนี้

D/F = W/P

F = (D*P)/W

ยกตัวอย่างเช่น ถ้าหากเราตั้งกล้องห่างจากวัตถุ 1 เมตร และวัตถุสมมุติว่าเป็นกระดาษ A4 ที่วางในแนวตั้ง ระยะโฟกัสจะเป็นเท่าใด

วิธีทำ

D = 1 meter

F = ?

W = 0.21 meter (กระดาษ A4 มีขนาด 210 x 297 มิลลิเมตร และเนื่องจากเราวางกระดาษในแนวตั้ง ความกว้างจึงเท่ากับ 210 มิลลิเมตร)

P = ?

จริงๆ P เป็นตัวแปรที่เราทราบค่านะครับ เพียงแต่ก่อนที่จะบอกว่ามันเป็นเท่าไร ผมขออธิบายที่ไปที่มาของมันก่อนแล้วกัน หรือก็คือ ทำยังไงเราถึงจะทราบค่า P

วิธีการก็คือ ถ่ายภาพวัตถุนั้นออกมาแล้ววัดระยะวัตถุนั้นจากภายในภาพ ถ้าหากกล้องของเรา หรือวัตถุของเราจะอยู่ตำแหน่งเดิมตลอด เราก็สามารถวัดความกว้างวัดถุด้วยมือเราได้เองครับ (เอามาเปิดในโปรแกรม paint แล้วลากเม้าส์ไปยังจุดที่เราต้องการ ด้านล่างซ้ายจะแสดงค่าตำแหน่ง ในรูปแบบ [x, y])

แต่ถ้าหากเราต้องขยับกล้อง หรือวัตถุ เราก็ต้องเขียนโปรแกรมให้มันตรวจจับวัตถุนั้น แล้วให้โปรแกรมคำนวณขนาดออกมาให้อีกที

ตัวอย่างภาพกระดาษ A4

ในตัวอย่างนี้เราจะใช้ค่า P = 737 px ตามที่แสดงในรูปด้านบนนะครับ ดังนั้นระยะโฟกัส F จึงเท่ากับ

F = (D*P)/W = ( 1m*737px)/0.21m

F = 3509.52 px

แล้วถ้าหากเราขยับกล้องออกไปละ จะเกิดอะไรขึ้น?

เมื่อเราขยับกล้องห่างออกไปหรือใกล้เข้ามา ตัวแปร D ก็จะไม่ใช่ตัวแปรที่เราทราบค่าอีกต่อไป แต่เราทราบค่า F หรือระยะโฟกัสของกล้องแล้ว (ซึ่งค่านี้จะไม่เปลี่ยนแปลง หากเราไม่ได้ปรับกล้องใหม่) ดังนั้นเราจึงสามารถคำนวณหาค่า D ใหม่ได้

D = (F*W)/P

อย่างเช่น ถ้าเราขยับกล้องออกห่างจากกระดาษ จะทำให้ขนาดกระดาษในภาพเล็กลง สมมุติว่าจาก 737 px เหลือแค่ 500 px เราก็จะได้ว่า…

D = (3509.52px * 0.21m)/500px = 1.474 meter

หรือก็คือเราขยับกล้องจากระยะ 1 เมตรออกมาที่ระยะ 1.474 เมตร นั่นเอง

เอาละครับ เมื่อเข้าใจหลักการกันแล้ว เรามาลองเขียนโปรแกรมกันเลยดีกว่า

ในตัวอย่างนี้ผมจะวางกระดาษไว้บนพื้นนะครับ จากนั้นจะเริ่มถ่ายวีดีโอที่ระดับความสูง 75 เซนติเมตร จากนั้นจะค่อยๆ ยกกล้องขึ้นเรื่อยๆ ผลลัพธ์จะเป็นอย่างไรมาดูกันครับ

ก่อนอื่นเราต้องเขียนฟังก์ชันหาตำแหน่งของกระดาษ (object) ภายในภาพก่อนครับ

def find_marker(image):
# convert the image to grayscale, blur it, and detect edges
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
blurred = cv2.GaussianBlur(gray, (5, 5), 0)
thresh = cv2.threshold(blurred, 200, 255, cv2.THRESH_BINARY)[1]
edged = cv2.Canny(thresh, 10, 30)

# find the contours in the edged image and keep the largest one;
cnts = cv2.findContours(edged.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)
c = max(cnts, key = cv2.contourArea)
# compute the bounding box of the of the paper region and return it
return cv2.minAreaRect(c)

โดยฟังก์ชันนี้จะรับภาพเข้ามา แล้วแปลงเป็นภาพสีเทา จากนั้นค่อยแปลงเป็นภาพขาวดำ แล้วใช้ฟังก์ชัน canny เพื่อหาขอบของกระดาษในภาพ เมื่อได้รูปภาพที่มีแต่เส้นขอบแล้ว ก็ใช้คำสั่ง findContours เพื่อค้นหา object ภายในภาพ (object ภายในภาพคือส่วนที่เป็นสีขาว ส่วนพื้นหลังจะเป็นสีดำ) จากนั้นจึงเลือก object ที่มีขนาดใหญ่ที่สุด ในภาพครับ (เพราะเรารู้ว่ากระดาษ คือ object ที่ใหญ่สุดในภาพ)

เมื่อได้ขนาดความกว้างของกระดาษภายในภาพแล้ว ก็นำมาคำนวณตามสมการที่ได้อธิบายไปข้างบนครับ

def distance_to_camera(knownWidth, focalLength, perWidth):
# compute and return the distance from the maker to the camera
return (knownWidth * focalLength) / perWidth

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

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

ตัวอย่างผลรัน

--

--

No responses yet