Một chủ đề khá là hay mà hôm nay Ngô Tôn .IT đem đến cho các bạn đó là Tính khoảng cách từ Camera đến một đối tượng với OpenCV.
Sự giống nhau giữa hình tam giác và khoảng cách giữa camera với một vật thể
Để xác định khoảng cách từ máy ảnh đến một vật thể, chúng ta sẽ sử dụng tính tương tự của tam giác. Sự tương tự của tam giác như sau:
Giả sử chúng ta có một điểm đánh dấu hoặc đối tượng có chiều rộng đã biết W. Sau đó, chúng ta đặt điểm đánh dấu này cách máy ảnh một khoảng D. Chúng ta chụp ảnh đối tượng bằng máy ảnh của mình và sau đó đo chiều rộng tính bằng pixel P. Điều này cho phép chúng ta tính được tiêu cự F của máy ảnh:
F = (P x D) / W
Ví dụ: Giả sử mình đặt một mảnh giấy tiêu chuẩn có kích thước 8,5 x 11 inch (theo chiều ngang: W = 11), D = 24 inch trước máy ảnh và chụp ảnh. Khi đo chiều rộng của mảnh giấy trong hình ảnh, mình nhận thấy rằng chiều rộng cảm nhận được của tờ giấy là P = 248 pixel.
Tiêu cự F sau đó là:
F = (248 x 24) / 11 = 543,45
Khi tiếp tục di chuyển máy ảnh của mình cả gần và xa hơn đối tượng, mình có thể áp dụng phép đồng dạng tam giác để xác định khoảng cách của đối tượng đến máy ảnh:
D’= (W x F) / P
Cụ thể, giả sử mình di chuyển máy ảnh của mình ra xa điểm đánh dấu 36 inch và chụp ảnh cùng một mảnh giấy. Thông qua xử lý hình ảnh tự động, mình có thể xác định rằng chiều rộng cảm nhận được của mảnh giấy hiện là 168 pixel. Bây giờ chúng ta nhận được:
D’= (11 x 543,45) / 168 = 35,58 ~ 36 inch
Tính khoảng cách từ Camera đến một đối tượng với OpenCV
Bây giờ, chúng ta sẽ triển khai code python để tính khoảng cách từ camera tới vật thể với OpenCV. Ở phần này, mình kết hợp giữa việc nhận diện khuôn mặt và đo khoảng cách từ camera tới mặt người luôn.
Khai báo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
import cv2 # variables # distance from camera to object(face) measured Known_distance = 72.2 # Centimeter # mine is 14.3 something, measure your face width, are google it Known_width = 14.3 # Centimeter # Colors >>> BGR Format(BLUE, GREEN, RED) GREEN = (0, 255, 0) RED = (0, 0, 255) BLACK = (0, 0, 0) YELLOW = (0, 255, 255) WHITE = (255, 255, 255) CYAN = (255, 255, 0) MAGENTA = (255, 0, 242) GOLDEN = (32, 218, 165) LIGHT_BLUE = (255, 9, 2) PURPLE = (128, 0, 128) CHOCOLATE = (30, 105, 210) PINK = (147, 20, 255) ORANGE = (0, 69, 255) fonts = cv2.FONT_HERSHEY_COMPLEX fonts2 = cv2.FONT_HERSHEY_SCRIPT_SIMPLEX fonts3 = cv2.FONT_HERSHEY_COMPLEX_SMALL fonts4 = cv2.FONT_HERSHEY_TRIPLEX |
1 2 3 4 5 6 7 8 9 10 |
# Camera Object cap = cv2.VideoCapture(0) # Number According to your Camera Distance_level = 0 # Define the codec and create VideoWriter object fourcc = cv2.VideoWriter_fourcc(*'XVID') out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (640, 480)) # face detector object face_detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") |
1 2 3 4 5 6 7 8 9 10 11 12 13 |
def FocalLength(measured_distance, real_width, width_in_rf_image): # Function Description (Doc String) ''' This Function Calculate the Focal Length(distance between lens to CMOS sensor), it is simple constant we can find by using MEASURED_DISTACE, REAL_WIDTH(Actual width of object) and WIDTH_OF_OBJECT_IN_IMAGE :param1 Measure_Distance(int): It is distance measured from object to the Camera while Capturing Reference image :param2 Real_Width(int): It is Actual width of object, in real world (like My face width is = 5.7 Inches) :param3 Width_In_Image(int): It is object width in the frame /image in our case in the reference image(found by Face detector) :retrun Focal_Length(Float): ''' focal_length = (width_in_rf_image * measured_distance) / real_width return focal_length |
1 2 3 4 5 6 7 8 9 10 11 12 |
def Distance_finder(Focal_Length, real_face_width, face_width_in_frame): ''' This Function simply Estimates the distance between object and camera using arguments(Focal_Length, Actual_object_width, Object_width_in_the_image) :param1 Focal_length(float): return by the Focal_Length_Finder function :param2 Real_Width(int): It is Actual width of object, in real world (like My face width is = 5.7 Inches) :param3 object_Width_Frame(int): width of object in the image(frame in our case, using Video feed) :return Distance(float) : distance Estimated ''' distance = (real_face_width * Focal_Length)/face_width_in_frame return distance |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
def face_data(image, CallOut, Distance_level): ''' This function Detect face and Draw Rectangle and display the distance over Screen :param1 Image(Mat): simply the frame :param2 Call_Out(bool): If want show Distance and Rectangle on the Screen or not :param3 Distance_Level(int): which change the line according the Distance changes(Intractivate) :return1 face_width(int): it is width of face in the frame which allow us to calculate the distance and find focal length :return2 face(list): length of face and (face paramters) :return3 face_center_x: face centroid_x coordinate(x) :return4 face_center_y: face centroid_y coordinate(y) ''' face_width = 0 face_x, face_y = 0, 0 face_center_x = 0 face_center_y = 0 gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) faces = face_detector.detectMultiScale(gray_image, 1.3, 5) for (x, y, h, w) in faces: line_thickness = 2 # print(len(faces)) LLV = int(h*0.12) # print(LLV) # cv2.rectangle(image, (x, y), (x+w, y+h), BLACK, 1) cv2.line(image, (x, y+LLV), (x+w, y+LLV), (GREEN), line_thickness) cv2.line(image, (x, y+h), (x+w, y+h), (GREEN), line_thickness) cv2.line(image, (x, y+LLV), (x, y+LLV+LLV), (GREEN), line_thickness) cv2.line(image, (x+w, y+LLV), (x+w, y+LLV+LLV), (GREEN), line_thickness) cv2.line(image, (x, y+h), (x, y+h-LLV), (GREEN), line_thickness) cv2.line(image, (x+w, y+h), (x+w, y+h-LLV), (GREEN), line_thickness) face_width = w face_center = [] # Drwaing circle at the center of the face face_center_x = int(w/2)+x face_center_y = int(h/2)+y if Distance_level < 10: Distance_level = 10 # cv2.circle(image, (face_center_x, face_center_y),5, (255,0,255), 3 ) if CallOut == True: # cv2.line(image, (x,y), (face_center_x,face_center_y ), (155,155,155),1) cv2.line(image, (x, y-11), (x+180, y-11), (ORANGE), 28) cv2.line(image, (x, y-11), (x+180, y-11), (YELLOW), 20) cv2.line(image, (x, y-11), (x+Distance_level, y-11), (GREEN), 18) # cv2.circle(image, (face_center_x, face_center_y),2, (255,0,255), 1 ) # cv2.circle(image, (x, y),2, (255,0,255), 1 ) # face_x = x # face_y = y return face_width, faces, face_center_x, face_center_y |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
import cv2 # variables # distance from camera to object(face) measured Known_distance = 72.2 # Centimeter # mine is 14.3 something, measure your face width, are google it Known_width = 14.3 # Centimeter # Colors >>> BGR Format(BLUE, GREEN, RED) GREEN = (0, 255, 0) RED = (0, 0, 255) BLACK = (0, 0, 0) YELLOW = (0, 255, 255) WHITE = (255, 255, 255) CYAN = (255, 255, 0) MAGENTA = (255, 0, 242) GOLDEN = (32, 218, 165) LIGHT_BLUE = (255, 9, 2) PURPLE = (128, 0, 128) CHOCOLATE = (30, 105, 210) PINK = (147, 20, 255) ORANGE = (0, 69, 255) fonts = cv2.FONT_HERSHEY_COMPLEX fonts2 = cv2.FONT_HERSHEY_SCRIPT_SIMPLEX fonts3 = cv2.FONT_HERSHEY_COMPLEX_SMALL fonts4 = cv2.FONT_HERSHEY_TRIPLEX # Camera Object cap = cv2.VideoCapture(0) # Number According to your Camera Distance_level = 0 # Define the codec and create VideoWriter object fourcc = cv2.VideoWriter_fourcc(*'XVID') out = cv2.VideoWriter('output.mp4', fourcc, 30.0, (640, 480)) # face detector object face_detector = cv2.CascadeClassifier("haarcascade_frontalface_default.xml") # focal length finder function def FocalLength(measured_distance, real_width, width_in_rf_image): # Function Discrption (Doc String) ''' This Function Calculate the Focal Length(distance between lens to CMOS sensor), it is simple constant we can find by using MEASURED_DISTACE, REAL_WIDTH(Actual width of object) and WIDTH_OF_OBJECT_IN_IMAGE :param1 Measure_Distance(int): It is distance measured from object to the Camera while Capturing Reference image :param2 Real_Width(int): It is Actual width of object, in real world (like My face width is = 5.7 Inches) :param3 Width_In_Image(int): It is object width in the frame /image in our case in the reference image(found by Face detector) :retrun Focal_Length(Float): ''' focal_length = (width_in_rf_image * measured_distance) / real_width return focal_length # distance estimation function def Distance_finder(Focal_Length, real_face_width, face_width_in_frame): ''' This Function simply Estimates the distance between object and camera using arguments(Focal_Length, Actual_object_width, Object_width_in_the_image) :param1 Focal_length(float): return by the Focal_Length_Finder function :param2 Real_Width(int): It is Actual width of object, in real world (like My face width is = 5.7 Inches) :param3 object_Width_Frame(int): width of object in the image(frame in our case, using Video feed) :return Distance(float) : distance Estimated ''' distance = (real_face_width * Focal_Length)/face_width_in_frame return distance # face detection Fauction def face_data(image, CallOut, Distance_level): ''' This function Detect face and Draw Rectangle and display the distance over Screen :param1 Image(Mat): simply the frame :param2 Call_Out(bool): If want show Distance and Rectangle on the Screen or not :param3 Distance_Level(int): which change the line according the Distance changes(Intractivate) :return1 face_width(int): it is width of face in the frame which allow us to calculate the distance and find focal length :return2 face(list): length of face and (face paramters) :return3 face_center_x: face centroid_x coordinate(x) :return4 face_center_y: face centroid_y coordinate(y) ''' face_width = 0 face_x, face_y = 0, 0 face_center_x = 0 face_center_y = 0 gray_image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) faces = face_detector.detectMultiScale(gray_image, 1.3, 5) for (x, y, h, w) in faces: line_thickness = 2 # print(len(faces)) LLV = int(h*0.12) # print(LLV) # cv2.rectangle(image, (x, y), (x+w, y+h), BLACK, 1) cv2.line(image, (x, y+LLV), (x+w, y+LLV), (GREEN), line_thickness) cv2.line(image, (x, y+h), (x+w, y+h), (GREEN), line_thickness) cv2.line(image, (x, y+LLV), (x, y+LLV+LLV), (GREEN), line_thickness) cv2.line(image, (x+w, y+LLV), (x+w, y+LLV+LLV), (GREEN), line_thickness) cv2.line(image, (x, y+h), (x, y+h-LLV), (GREEN), line_thickness) cv2.line(image, (x+w, y+h), (x+w, y+h-LLV), (GREEN), line_thickness) face_width = w face_center = [] # Drwaing circle at the center of the face face_center_x = int(w/2)+x face_center_y = int(h/2)+y if Distance_level < 10: Distance_level = 10 # cv2.circle(image, (face_center_x, face_center_y),5, (255,0,255), 3 ) if CallOut == True: # cv2.line(image, (x,y), (face_center_x,face_center_y ), (155,155,155),1) cv2.line(image, (x, y-11), (x+180, y-11), (ORANGE), 28) cv2.line(image, (x, y-11), (x+180, y-11), (YELLOW), 20) cv2.line(image, (x, y-11), (x+Distance_level, y-11), (GREEN), 18) # cv2.circle(image, (face_center_x, face_center_y),2, (255,0,255), 1 ) # cv2.circle(image, (x, y),2, (255,0,255), 1 ) # face_x = x # face_y = y return face_width, faces, face_center_x, face_center_y # reading reference image from directory ref_image = cv2.imread("Ref_image.png") ref_image_face_width, _, _, _ = face_data(ref_image, False, Distance_level) Focal_length_found = FocalLength( Known_distance, Known_width, ref_image_face_width) print(Focal_length_found) cv2.imshow("ref_image", ref_image) while True: _, frame = cap.read() # calling face_data function # Distance_leve =0 face_width_in_frame, Faces, FC_X, FC_Y = face_data( frame, True, Distance_level) # finding the distance by calling function Distance finder for (face_x, face_y, face_w, face_h) in Faces: if face_width_in_frame != 0: Distance = Distance_finder( Focal_length_found, Known_width, face_width_in_frame) Distance = round(Distance, 2) # Drwaing Text on the screen Distance_level = int(Distance) cv2.putText(frame, f"Distance {Distance} cm", (face_x-6, face_y-6), fonts, 0.5, (BLACK), 2) cv2.imshow("frame", frame) out.write(frame) if cv2.waitKey(1) == ord("q"): break cap.release() # out.release() cv2.destroyAllWindows() |
Xong, chạy lên xem kết quả thế nào!
1 |
$python camera_distance.py |
Leave a Reply