概述
在圖像處理時,可能不可避免的需要計算圖像中目標體的中心點,因而本片文章重點講如何用傳統圖像處理方式來計算圖像中目標體的中心。
方案
剛開始在考慮這個問題時其實也考慮了很多方法,比如找出物體最大座標與最小座標然後取平均值、直接求平均值等。這些方法比較容易想到但有一定的侷限性,比如只能用在幾何規則的圖形上邊,比如矩形、圓形等。但實際的目標體可能具有各種各樣的形狀,因而應用範圍可能大大的受到限制。
最終在查閱相關資料後採用的方式是求邊緣+對邊緣求中心點。
具體思路如下:
- 首先計算出圖像目標體的邊緣,當前已經有許多成熟的計算方法,爲了提高計算速度,我們在計算邊緣時,只需要邊緣點即可,具體的邊緣輪廓並不需要。
- 求出邊緣點之後,可以求出邊緣點的中心距進而求出中心點的座標。
結合我們工程上的實際應用場景:
我們在項目中需要計算的圖片如下所示:
圖片中有多個目標體,因而需要分不同的情況對多個目標體分別計算。
在計算的過程中爲了減少其他目標體的干擾,會做一個像素替換工作,比如在計算主體時只保留主體的像素,其他像素都變成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