標識多個物體並返回物體中心座標方法的實現
阿新 • • 發佈:2020-12-27
概述
在影象處理時,可能不可避免的需要計算影象中目標體的中心點,因而本片文章重點講如何用傳統影象處理方式來計算影象中目標體的中心。
方案
剛開始在考慮這個問題時其實也考慮了很多方法,比如找出物體最大座標與最小座標然後取平均值、直接求平均值等。這些方法比較容易想到但有一定的侷限性,比如只能用在幾何規則的圖形上邊,比如矩形、圓形等。但實際的目標體可能具有各種各樣的形狀,因而應用範圍可能大大的受到限制。
最終在查閱相關資料後採用的方式是求邊緣+對邊緣求中心點。
具體思路如下:
- 首先計算出影象目標體的邊緣,當前已經有許多成熟的計算方法,為了提高計算速度,我們在計算邊緣時,只需要邊緣點即可,具體的邊緣輪廓並不需要。
- 求出邊緣點之後,可以求出邊緣點的中心距進而求出中心點的座標。
結合我們工程上的實際應用場景:
我們在專案中需要計算的圖片如下所示:
圖片中有多個目標體,因而需要分不同的情況對多個目標體分別計算。
在計算的過程中為了減少其他目標體的干擾,會做一個畫素替換工作,比如在計算主體時只保留主體的畫素,其他畫素都變成0。
然後求出主體的邊緣,進而求出目標的中心。
在求目標的中心時,為了提高計算速度,我們使用了一個OpenCV求中心距的函式:moments()
中心矩
openCV提供moments()函式來計算影象的中心矩,最高計算到三階
空間矩本質上是面積或者質量,因此可以通過一階矩來計算中心:
程式碼
import imutils import datetime import cv2 import numpy as np def getCenter(image, pixel): gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 灰度 gray = np.where(gray==pixel,240,0) gray = np.asarray(gray,dtype='uint8') blurred = cv2.medianBlur(gray,5,0) # 在閾值影象中查詢輪廓 cnts = cv2.findContours(blurred.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 找到白色對應的邊界點的集合 cnts = imutils.grab_contours(cnts) results = [] # 計算輪廓中心 for c in cnts: M = cv2.moments(c) # 防止出現除以0異常 if M["m00"] == 0: continue cX = int(M["m10"] / M["m00"]) cY = int(M["m01"] / M["m00"]) # 在影象上繪製形狀的輪廓和中心 # cv2.drawContours(image, [c], -1, (0, 255, 0), 2) # cv2.circle(image, (cX, cY), 3, (72, 61, 139), -1) # cv2.putText(image, "center", (cX - 20, cY - 20), # cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 2) results.append([cX,cY]) return results def getObjectCenter(image_path): image = cv2.imread(image_path) # 只考慮圖片上有一架無人機 loadCenterList = getCenter(image,240) roterCenterList = getCenter(image,80) bodayCenterList = getCenter(image,160) # 獲取唯一負載 loadCenter = [] bodayCenter = [] if len(loadCenterList) >=1: loadCenter = loadCenterList[len(loadCenterList) - 1] cv2.circle(image, (loadCenter[0], loadCenter[1]), 5, (128, 225, 0), -1) # 獲取主體中心座標(修正前) if len(bodayCenterList) >=1: bodayCenter = bodayCenterList[len(bodayCenterList) - 1] # 畫出主體座標 if bodayCenter !=[]: # 對座標進行簡單修正 if loadCenter != []: bodayCenter[1] -= int((abs(loadCenter[1] - bodayCenter[1])) * 0.5) cv2.circle(image, (bodayCenter[0], bodayCenter[1]), 5, (0, 225, 118), -1) # 畫出旋翼中心 for roterCenter in roterCenterList: cv2.circle(image, (roterCenter[0], roterCenter[1]), 5, (168, 0, 108), -1) return image