Computer VisionMachine LearningOpenCVPython

Machine Learning ใช้ Python และ OpenCV ทำระบบ Measuring Size ขนาดของวัตถุในภาพ

การทำ Machine Learning โดยใช้ OpenCV ในการวัด (Measuring) ขนาดของ Machine Learning ใช้ Python และ OpenCV ทำระบบ Measuring Size ขนาดของวัตถุในภาพ ด้วย Python

บทเรียนการทำ Machine Learning โดยใช้ OpenCV ในการวัด (Measuring) ขนาดของ Machine Learning ใช้ Python และ OpenCV ทำระบบ Measuring Size ขนาดของวัตถุในภาพ ด้วย Python

เห็น Tutorial มากมายจากใน Medium หรือ PyImageSearch ทำนู่นนี่มากมายทั้งระบบล้ำๆ รอบนี้ก็เลยอยากลองหาวิธีการทำมาตรวัด Measure หรือระบบ Measuring ขนาด กว้าง x ยาว ของวัตถุที่ปรากฏในภาพถ่ายบ้างหน่อยก็เลยลองทำ Tutorial ที่ย่อย และยำใหม่ให้เข้าใจง่ายตามเดิม

บทเรียนก่อนหน้านี้:

สำหรับระบบให้เราลองสร้างไฟล์ Python ชื่อว่าอะไรก็ได้เช่น test.py หลังจากนั้นสร้างโฟลเดอร์ชื่อ images ขึ้นมาใส่ภาพสิ่งของที่วางบนพื้นเรียบๆ หน่อย

ไฟล์ชื่อ sample.jpeg

ในภาพมี เหรียญ 10 บาท เหรียญ 5 บาท ขวดเจลแอลกอฮอล 70% ทำใช้เองในมหาวิทยาลัย และ Apple Pencil ในภาพเราต้องกำหนด Reference Image ไว้ชิ้นหนึ่งทางด้านซ้ายซึ่งผมเลือก เหรียญ 10 ดังนั้นต้องหาข้อมูลสำหรับ Carlibrate หน่อยคือ ขนาด 2.6cm x 2.6cm (เส้นผ่าศูนย์กลาง 2.6 cm) ตีเป็นนิ้ว (Inch) คือ 1.02362205

โอเคให้ประกาศ Header ดังนี้:

from scipy.spatial import distance as dist
from imutils import perspective
from imutils import contours
import numpy as np
import imutils
from cv2 import cv2

พิจารณา ฟังก์ชันของ scipy เป็นการหาระยะห่างระหว่างจุดหรือหลักของ Euclidean distance (งานวิจัยผมค่อนข้างเยอะเลยที่ใช้กฏนี้) Python สำหรับ Machine Learning นั้นพระเอกหลักของบทเรียนนี้คือการคำนวณระยะห่างระหว่างจุดข้อมูล Euclidean distance (วิธีการเพื่อนบ้านใกล้สุด k ตัว, วิธีการ k เฉลี่ย) ซึ่งคำสั่ง  Module ทั้งหลายอยู่ใน scipy.spatial.distance ของ scipy ที่เราประกาศนี่แหละ และบทเรียนนี้เลือก distance มาใช้

ในระบบนี้จะทำงานส่วนของ imutils เราจะเรียกใช้ perspective และ contours ของมันมาใช้ ถามว่าคืออะไรให้นึกถึง library ของการประมวลผลรูปภาพ (image processing) เช่น กลับภาพ Flip หมุนภาพ Rotate หรือ ปรับขนาด Scale Image และวาก contours (เส้นรูปร่าง) มาใช้ในงานนี้

ทำการวางฟังก์ชัน midpoint ไว้หาจุดกึ่งกลางที่เราจะทำเส้นตัดของเส้นผ่านศูนย์กลางแนวดิ่งและระนาบ

def midpoint(ptA, ptB):
	return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)

โหลดไฟล์รูปภาพของเรา “images/sample.jpeg” ทำกแปลงเป็นสีเทาและทำ Gaussian Blur เบลอเล็กน้อย

image = cv2.imread("./images/sample.jpeg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)

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

edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)

ค้นหาส่วนที่ทำ contours ภาพร่างจากกระบวนการข้างต้น

cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
	cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)

จับ Contours มาประมวลผล จัดเรียงภาพร่างจากซ้ายไปขวา กำหนดตัวแปรการเปรียบเทียบ Calibration ซึ่งมาจากการคำนวณของ Pixels ต่อ Metric หน่วยที่คำนวณ เก็บลงตัวแปรชื่อเดียวกัน

(cnts, _) = contours.sort_contours(cnts)
pixelsPerMetric = None

contours สีที่ต่างกันเพื่อหาภาพร่างของวัตถุ ซึ่งถ้าเจอจุดสีต่างใน contours ให้วาด แต่ถ้าหาก Contours ไม่ได้ใหญ่มากเป็นจุดต่างเล็กๆ ก็ไม่ต้องนำมาพิจารณาเก็บค่า

for c in cnts:
	if cv2.contourArea(c) < 100:
		continue

คำนวณเพื่อสร้าง Box จากขอบเขตของภาพ contours ที่ได้ กำหนดทิศทางการหมุน scipy ของรูปร่างวัตถุ

orig = image.copy()
	box = cv2.minAreaRect(c)
	box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
	box = np.array(box, dtype="int")

เรียงลำดับ Point ที่จับได้รูปร่าง contours ซ้ายบน ขวาบน ล่างขวา ล่างซ้าย เรียงเสร็จจากนั้นวาดโครงร่างของขอบเขตที่หมุน ด้วย Box ครอบตำแหน่งทั้งหมดของวัตถุ:

box = perspective.order_points(box)
	cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 64), 2)

สี RGB(0,255,64) สีเขียวความหนาสัก 2 ทำการ Loop หา Original Point จาก contours ในวัตถุอื่นๆ ไปเรื่อยๆ เพื่อสร้าง Box ให้กับมัน

for (x, y) in box:
		cv2.circle(orig, (int(x), int(y)), 5, (0, 255, 64), -1)

	(tl, tr, br, bl) = box
	(tltrX, tltrY) = midpoint(tl, tr)
	(blbrX, blbrY) = midpoint(bl, br)

	(tlblX, tlblY) = midpoint(tl, bl)
	(trbrX, trbrY) = midpoint(tr, br)

ทีนี้มาทำเส้นตัดศูนย์กลางอ้างอิง midpoint กันดีกว่า ใช้เส้นบางหน่อย:

cv2.circle(orig, (int(tltrX), int(tltrY)), 0, (0, 255, 64), 0)
cv2.circle(orig, (int(blbrX), int(blbrY)), 0, (0, 255, 64), 0)
cv2.circle(orig, (int(tlblX), int(tlblY)), 0, (0, 255, 64), 0)
cv2.circle(orig, (int(trbrX), int(trbrY)), 0, (0, 255, 64), 0)

วาดเส้นขาวตัดกลางทั้งแกนระนาบ และแนวดิ่งเลย

cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)),
		(255, 255, 255), 1)
cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)),
		(255, 255, 255), 1)

คำนวณหาระยะห่างระหว่างจุดด้วย Euclidian Distance:

dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY))
	dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))

	if pixelsPerMetric is None:
		pixelsPerMetric = dB / 1.02362205

	dimA = dA / pixelsPerMetric
	dimB = dB / pixelsPerMetric

ได้ค่า dimA,dimB ที่คำนวณจาก pixelsPerMetric อ้างอิง ขนาดของเหรียญ 10 บาท (1.02362205 นิ้วหรือ 2.6 เซ็นติเมตร โดยประมาณ) เอามาคำนวณค่า inch นิ้วเป็น cm (คูณกับ 2.54) วาด Text ให้กับภาพ

cv2.putText(orig, "{:.2f}cm".format(dimA * 2.54),
		(int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX,
		0.65, (255, 255, 255), 2)
cv2.putText(orig, "{:.2f}cm".format(dimB * 2.54),
		(int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX,
		0.65, (255, 255, 255), 2)

แสดง Output มาหน่อยดีกว่าเพื่อกดอะไรก็ได้ก็จะย้ายตำแหน่ง contours ไปเรื่อยๆ

cv2.imshow("Measuring_Size_Image", orig)
cv2.waitKey(0)

ดังนั้นภาพรวม source code นี้จะเป็นแบบนี้:

from scipy.spatial import distance as dist
from imutils import perspective
from imutils import contours
import numpy as np
import imutils
import cv2

def midpoint(ptA, ptB):
	return ((ptA[0] + ptB[0]) * 0.5, (ptA[1] + ptB[1]) * 0.5)

image = cv2.imread("./images/sample.jpeg")
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
gray = cv2.GaussianBlur(gray, (7, 7), 0)

edged = cv2.Canny(gray, 50, 100)
edged = cv2.dilate(edged, None, iterations=1)
edged = cv2.erode(edged, None, iterations=1)

cnts = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL,
	cv2.CHAIN_APPROX_SIMPLE)
cnts = imutils.grab_contours(cnts)

(cnts, _) = contours.sort_contours(cnts)
pixelsPerMetric = None

for c in cnts:
	if cv2.contourArea(c) < 100:
		continue

	orig = image.copy()
	box = cv2.minAreaRect(c)
	box = cv2.cv.BoxPoints(box) if imutils.is_cv2() else cv2.boxPoints(box)
	box = np.array(box, dtype="int")

	box = perspective.order_points(box)
	cv2.drawContours(orig, [box.astype("int")], -1, (0, 255, 64), 2)

	for (x, y) in box:
		cv2.circle(orig, (int(x), int(y)), 5, (0, 255, 64), -1)

	(tl, tr, br, bl) = box
	(tltrX, tltrY) = midpoint(tl, tr)
	(blbrX, blbrY) = midpoint(bl, br)

	(tlblX, tlblY) = midpoint(tl, bl)
	(trbrX, trbrY) = midpoint(tr, br)

	cv2.circle(orig, (int(tltrX), int(tltrY)), 0, (0, 255, 64), 0)
	cv2.circle(orig, (int(blbrX), int(blbrY)), 0, (0, 255, 64), 0)
	cv2.circle(orig, (int(tlblX), int(tlblY)), 0, (0, 255, 64), 0)
	cv2.circle(orig, (int(trbrX), int(trbrY)), 0, (0, 255, 64), 0)

	cv2.line(orig, (int(tltrX), int(tltrY)), (int(blbrX), int(blbrY)),
		(255, 255, 255), 1)
	cv2.line(orig, (int(tlblX), int(tlblY)), (int(trbrX), int(trbrY)),
		(255, 255, 255), 1)

	dA = dist.euclidean((tltrX, tltrY), (blbrX, blbrY))
	dB = dist.euclidean((tlblX, tlblY), (trbrX, trbrY))

	if pixelsPerMetric is None:
		pixelsPerMetric = dB / 1.02362205

	dimA = dA / pixelsPerMetric
	dimB = dB / pixelsPerMetric

	cv2.putText(orig, "{:.2f}cm".format(dimA * 2.54),
		(int(tltrX - 15), int(tltrY - 10)), cv2.FONT_HERSHEY_SIMPLEX,
		0.65, (255, 255, 255), 2)
	cv2.putText(orig, "{:.2f}cm".format(dimB * 2.54),
		(int(trbrX + 10), int(trbrY)), cv2.FONT_HERSHEY_SIMPLEX,
		0.65, (255, 255, 255), 2)

	# show output
	cv2.imshow("Measuring_Size_Image", orig)
	cv2.waitKey(0)

แสดงผลหน่อยจะได้แบบนี้ กด Next ไปเรื่อยๆ

หมดละ คราวนี้ก็ลองไปทำอะไรกับของที่มันใหญ่ๆ กว่านี้ล่ะกันนะครับ

Asst. Prof. Banyapon Poolsawas

อาจารย์ประจำสาขาวิชาการออกแบบเชิงโต้ตอบ และการพัฒนาเกม วิทยาลัยครีเอทีฟดีไซน์ & เอ็นเตอร์เทนเมนต์เทคโนโลยี มหาวิทยาลัยธุรกิจบัณฑิตย์ ผู้ก่อตั้ง บริษัท Daydev Co., Ltd, (เดย์เดฟ จำกัด)

Related Articles

Back to top button

Adblock Detected

เราตรวจพบว่าคุณใช้ Adblock บนบราวเซอร์ของคุณ,กรุณาปิดระบบ Adblock ก่อนเข้าอ่าน Content ของเรานะครับ, ถือว่าช่วยเหลือกัน