1. 程式人生 > >2019年11月24日開發手記

2019年11月24日開發手記

提取外切輪廓目前沒有什麼現成的API可用,所以也得自己寫一個,考慮到經過濾波後的目標影象為點集狀態,所以打算採用聚類演算法,經過比較,決定選擇聚類演算法中的k-means演算法作為運動區域中心點檢測演算法。
k-means演算法:
演算法步驟:
(1) 首先我們選擇一些類/組,並隨機初始化它們各自的中心點。中心點是與每個資料點向量長度相同的位置。這需要我們提前預知類的數量(即中心點的數量)。
(2) 計算每個資料點到中心點的距離,資料點距離哪個中心點最近就劃分到哪一類中。
(3) 計算每一類中中心點作為新的中心點。
(4) 重複以上步驟,直到每一類中心在每次迭代後變化不大為止。也可以多次隨機初始化中心點,然後選擇執行結果最好的一個。
程式碼例程:
# -*- coding:utf-8 -*-
import re
import math
import numpy as np
import pylab as pl
data = \
"1,0.697,0.46,Y,\
2,0.774,0.376,Y,\
3,0.634,0.264,Y,\
4,0.608,0.318,Y,\
5,0.556,0.215,Y,\
6,0.403,0.237,Y,\
7,0.481,0.149,Y,\
8,0.437,0.211,Y,\
9,0.666,0.091,N,\
10,0.243,0.267,N,\
11,0.245,0.057,N,\
12,0.343,0.099,N,\
13,0.639,0.161,N,\
14,0.657,0.198,N,\
15,0.36,0.37,N,\
16,0.593,0.042,N,\
17,0.719,0.103,N"
#定義一個西瓜類,四個屬性,分別是編號,密度,含糖率,是否好瓜
class watermelon:
    def __init__(self, properties):
        self.number = properties[0]
        self.density = float(properties[1])
        self.sweet = float(properties[2])
        self.good = properties[3]

#資料簡單處理
a = re.split(',|\n|\t', data.strip(" "))
dataset = []     #dataset:資料集
for i in range(int(len(a)/4)):
    temp = tuple(a[i * 4: i * 4 + 4])
    dataset.append(watermelon(temp))

#計算歐幾里得距離,a,b分別為兩個元組
def dist(a, b):
    return math.sqrt(math.pow(a[0]-b[0], 2)+math.pow(a[1]-b[1], 2))


#演算法模型
def k_means(k, dataset, max_iter):
    U = np.random.choice(dataset, k)#從a中隨機選取3個值
    U = [(wm.density, wm.sweet) for wm in U]    #均值向量列表
    C = [[] for i in range(k)]      #初始化分類列表
    U_update = []                   #均值向量更新列表
    while max_iter > 0:
        #分類
        for i in dataset:
            temp = np.argmin([dist((i.density, i.sweet), U[j]) for j in range(len(U))])#返回最小值的下標
            C[temp].append(i)
        #更新均值向量
        for i in range(k):
            ui_density = 0.0
            ui_sweet = 0.0
            for j in C[i]:
                ui_density += j.density
                ui_sweet += j.sweet
            U_update.append((ui_density/len(C[i]), ui_sweet/len(C[i])))#求得均值
        #每五次輸出一次分類圖
        if max_iter % 5 == 0:
            draw(C, U)
        #比較U和U_update
        if U == U_update:
            break
        U = U_update
        U_update = []
        C = [[] for i in range(k)]
        max_iter -= 1

    return C, U

#畫圖
def draw(C, U):
    colValue = ['r', 'y', 'g', 'b', 'c', 'k', 'm']
    for i in range(len(C)):
        coo_X = []    #x座標列表
        coo_Y = []    #y座標列表
        for j in range(len(C[i])):
            coo_X.append(C[i][j].density)
            coo_Y.append(C[i][j].sweet)
        pl.scatter(coo_X, coo_Y, marker='x', color=colValue[i%len(C)], label=str(i))
    #展示均值向量
    U_x = []
    U_y = []
    for i in U:
        U_x.append(i[0])
        U_y.append(i[1])
    pl.scatter(U_x, U_y, marker='.', color=colValue[6], label="avg_vector")
    pl.legend(loc='upper right')
    pl.show()

C, U = k_means(3, dataset, 30)
draw(C, U)
輸出結果:

第一張圖是最開始初始化的樣子,均值向量和樣本點重合。 第二張圖為最後聚類結果。

目前的打算是先用cv2.findContours得到輪廓的點集,再用k-means演算法得到每個輪廓的幾何中心,繼而根據得到的多個幾何中心繪製矩形,從而得到目標外切輪廓