1. 程式人生 > >Python-opencv之目標定位

Python-opencv之目標定位

最近團隊準備參加一個無人機比賽,大致的規則是這樣的:固定翼飛機從跑道起飛,然後在空中轉體360°,通過GPS粗定位飛行至一個高13米左右,寬6米左右八字形框前(距離約50米左右),這時依靠計算機視覺的方法,讓飛機準確的穿過去。(之後還有其他的動作,但是第一步大體就是這樣)。

    初步的方案:①通過機載攝像機獲取影象序列

                        ②選取關鍵幀進行處理,獲得框的中心點影象座標

                        ③將框的中心點影象座標與影象中心座標進行比較,將偏差資訊反饋給控制系統,使其自動調節

    關鍵點:如何準確地定位目標框,並提取出框中心點影象座標。

——————————————————————————————————————————————

以下是一個比較簡單的實現方案

    通過顏色提取出大致區域->形態學處理->輪廓提取->利用輪廓大小關係找到目標框->獲得中心點資訊並比較反饋

程式碼部分

# coding:UTF-8
import cv2
import numpy as np


class Detect:
    def __init__(self, path):
        # 原始影象資訊
        self.ori_img = cv2.imread(path)
        self.gray = cv2.cvtColor(self.ori_img, cv2.COLOR_BGR2GRAY)
        self.hsv = cv2.cvtColor(self.ori_img, cv2.COLOR_BGR2HSV)
        # 獲得原始影象行列
        rows, cols = self.ori_img.shape[:2]
        # 工作影象
        self.work_img = cv2.resize(self.ori_img, (cols / 4, rows / 4))
        self.work_gray = cv2.resize(self.gray, (cols / 4, rows / 4))
        self.work_hsv = cv2.resize(self.hsv, (cols / 4, rows / 4))

    # 顏色區域提取
    def color_area(self):
        # 提取紅色區域(暫定框的顏色為紅色)
        low_red = np.array([156, 43, 46])
        high_red = np.array([180, 255, 255])
        mask = cv2.inRange(self.work_hsv, low_red, high_red)
        red = cv2.bitwise_and(self.work_hsv, self.work_hsv, mask=mask)
        return red

    # 形態學處理
    def good_thresh_img(self, img):
        # hsv空間變換到gray空間
        img = cv2.cvtColor(img, cv2.COLOR_HSV2BGR)
        img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
        # 閾值處理
        _, thresh = cv2.threshold(img, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
        # 做一些形態學操作,去一些小物體干擾
        img_morph = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, (3, 3))
        cv2.erode(img_morph, (3, 3), img_morph, iterations=2)
        cv2.dilate(img_morph, (3, 3), img_morph, iterations=2)
        return img_morph

    # 矩形四角點提取
    def key_points_tap(self, img):
        img_cp = img.copy()
        # 按結構樹模式找所有輪廓
        cnts, _ = cv2.findContours(img_cp, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
        # 按區域大小排序,找到第二大輪廓
        cnt_second = sorted(cnts, key=cv2.contourArea, reverse=True)[1]
        # 找輪廓的最小外接矩形((point), (w, h))
        box = cv2.minAreaRect(cnt_second)
        # ->(points)->(l_ints)
        return np.int0(cv2.cv.BoxPoints(box))

    # 畫出關鍵輪廓的最校外接矩形
    def key_cnt_draw(self, points):
        mask = np.zeros(self.work_gray.shape, np.uint8)
        cv2.drawContours(mask, [points], -1, 255, 2)
        return mask

    # 目標框影象中心點提取
    def center_point_cal(self, points):
        pt1_x, pt1_y = points[0, 0], points[0, 1]
        pt3_x, pt3_y = points[2, 0], points[2, 1]
        center_x, center_y = (pt1_x + pt3_x) / 2, (pt1_y + pt3_y) / 2
        return center_x, center_y

    # 中心點比較,進行反饋
    def feedback(self, rect_center_point):
        # 獲取矩形框中心
        rect_center_point_x, rect_center_point_y = rect_center_point[0], rect_center_point[1]
        # 得到影象中心
        rows, cols = self.work_img.shape[:2]
        img_center_x, img_center_y = cols / 2, rows / 2
        # 相對x、y
        delta_x = rect_center_point_x - img_center_x
        delta_y = rect_center_point_y - img_center_y
        # 條件判斷
        print '-------------------'
        if delta_x > 0:
            print '->right'
        elif delta_x < 0:
            print 'left <-'
        else:
            print 'v_hold'

        if delta_y < 0:
            print '+up'
        elif delta_y > 0:
            print '-down'
        else:
            print 'h_hold'

    # 執行主函式
    def img_process_main(self):
        # 找到紅色區域
        red = self.color_area()
        # 處理得到一個比較好的二值圖
        img_morph = self.good_thresh_img(red)
        # 獲取矩形框的四個關鍵點
        points = self.key_points_tap(img_morph)
        # 找到矩形中心點
        rect_center_point = self.center_point_cal(points)
        # 畫出關鍵輪廓(除錯用,並沒有什麼卯月)
        cnt_img = self.key_cnt_draw(points)
        # 反饋資訊
        self.feedback(rect_center_point)

        # 顯示影象
        cv2.imshow('ori', self.work_img)
        cv2.imshow('red', red)
        cv2.imshow('good_thresh', img_morph)
        cv2.imshow('cnts', cnt_img)
        cv2.waitKey(0)

        cv2.destroyAllWindows()


if __name__ == '__main__':
    root_path = './201655'
    img_index = 0
    while True:
        img_index += 1
        img_path = root_path + '/' + str(img_index) + '.bmp'
        d = Detect(img_path)
        d.img_process_main()

實際效果

    實驗用的是等比例縮小做的模型。