---
# System prepended metadata

title: Finding Ducks

---

# Finding Ducks
資工四 410711225 連昱婷
## :memo:方法及步驟
我是使用之前機器學習的「貝氏定理辨識鳶尾花」為基礎來做這次的作業，這次一樣是使用**Anaconda**的環境來做。

測試資料集我是利用小畫家匯入圖片，根據鴨子和非鴨子取色出RGB紀錄於csv檔，再從csv檔匯入檔案做訓練模型。數據包含鴨子與非鴨子共30筆。

貝氏定理：
概率論中的一個定理,描述在已知一些條件下某事件的發生機率。
公式 ***P(A|B) = (P(A)P(B|A)) / P(B)***
>其中 A 以及 B 為隨機事件,且 P(B)不為零。P(A|B)是指在事件 B 發生的情況下事件 A 發生的概率。
  P( A | B ):已知 B 發生後,A 的條件機率,被稱作 A 的後驗概率
  P( B | A ) :已知 A 發生後,B 的條件機率
  P(A):A 的先驗概率(不考慮任何 B 方面因素)
  P(B):B 的先驗概率(不考慮任何 A 方面因素)

1. 處理數據
   載入圖片轉換成像素數據做處理
1. 提取特徵結果
   提取訓練數據之屬性特徵，以便做計算並預測結果
1. 預測結果：
   擷取部分圖片作為測試用預測模型的訓練結果
1. 結果輸出：
   將結果以圖片輸出評估模組的準確度
   
## :memo:數據

label：鴨子(1)/非鴨子(2)

| R    | G    | B    | label |
| ---- | ---- | ---- | --- |
|231|246|253|1
|233|250|255|1
|234|248|251|1
|236|251|255|1
|238|	250|	250|	1
|240|	249|	255|	1
|233|	233|	245|	1
|241|	241|	249|	1
|243|	251|	254|	1
|242|	250|	253|	1
|246|	251|	255|	1
|236|	247|	253|	1
|242|	247|	253|	1
|239|	244|	248|	1
|239|	250|	255|	1
|208|	230|	244|	1
|248|	252|	255|	1
|239|	247|	250|	1
|244|	251|	255|	1
|236|	243|	251|	1
|40|	60|	51|	2
|150|	150|	152|	2
|86|	96|	106|	2
|87|	142|	158|	2
|57|	90|	35|	2
|96|	95|	109|	2
|108|	99|	92|	2
|135|	142|	148|	2




## :memo:程式碼
```python=
import csv
import cv2
import numpy as np
from PIL import Image
import pandas as pd
import math

def loadcsv(file):
    data = csv.reader(open(file, "r"))  #文件以讀取(r)方式打開
    dataset = list(data)  #每行存入list
    for i in range(1, len(dataset)):  #將標題過濾所以range從1開始
        dataset[i] = [float(x) for x in dataset[i]]  #轉成數字的data存入dataset
    return dataset

#將圖片轉為RGB表示
def loadduck(file):
    img = Image.open(file)
    pixels = img.convert("RGB")
    indices_arr = np.moveaxis(np.indices(img.size), 0, 2)
    color_arr = np.array(pixels.getdata()).reshape(img.size + (3,))
    arr = np.dstack((indices_arr, color_arr)).reshape((-1, 5))
    #轉dataframe
    df = pd.DataFrame(arr, columns=["x", "y", "red", "green", "blue"])
    df.drop(['x', 'y'], axis=1, inplace=True)
    data = df.to_numpy().astype(float).tolist()
    return data

#標準差
def SD(num):  
    avg = sum(num) / float(len(num))
    variance = sum([pow(x - avg, 2) for x in num]) / float(len(num) - 1)
    sd = math.sqrt(variance)
    return sd

#計算每個屬性的平均值和標準差
def summarize(dataset):  
    summaries = [(sum(attribute) / float(len(attribute)), SD(attribute)) for attribute in zip(*dataset[1:-2])]   
    #因serial id和label不需要
    #zip->資料樣本可以照屬性分組為一個個清單，然後對每個屬性計算平均值、標準差
    del summaries[-1]
    return summaries

#將每個lable分類，分別存入seperated
#最後一個屬性-1為類別值，返回一個類別值到資料樣本清單的映射
def class_separate(dataset):  
    separate = {}
    for i in range(len(dataset)):
        vector = dataset[i]
        if (vector[-1] not in separate):  #如果vector[-1](最後一欄即label)沒有在seperate裡的話，加入
            separate[vector[-1]] = []
        separate[vector[-1]].append(vector)
    return separate

#將訓練資料集按照類別label進行劃分，計算每個屬性的特徵
def class_summarize(dataset):
    separate = class_separate(dataset)
    summaries = {}
    for class_value, instances in separate.items():
        summaries[class_value] = summarize(instances)
    return summaries

#高斯概率密度函數
def probability(x, mean, SD):  
    exponent = math.exp(-(math.pow(x - mean, 2) / (2 * math.pow(SD, 2))))
    prob = (1 / (math.sqrt(2 * math.pi) * SD)) * exponent
    return prob

#給定一個資料樣本，它所屬每個類別的概率，可以通過將其屬性概率相乘得到，結果是一個類值到概率的映射
def class_probability(summaries, inputv):  
    prob = {}
    for class_value, class_summ in summaries.items():
        prob[class_value] = 1
        for i in range(len(class_summ)):
            mean, SD = class_summ[i]
            x = inputv[i]
            prob[class_value] *= probability(x, mean, SD)
    return prob

#計算一個資料樣本屬於每個類的概率，找到最大的概率值，並返回關聯的類別
def predict(summaries, inputv):  
    prob = class_probability(summaries, inputv)
    best_label, best_prob = None, -1
    for class_value, probability in prob.items():
        if best_label is None or best_prob < probability:
            best_prob = probability
            best_label = class_value
    return best_label

#透過對測試資料集中每個資料樣本的預測，可以評估模型精準度
def get_predict(summaries, testSet): 
    prediction = []
    for i in range(len(testSet)):
        result = predict(summaries, testSet[i])
        prediction.append(result)
    return prediction

#建立新的圖片結果
def create(prediction,imgpath):
    imgs = []
    for i in range(len(prediction)):
        #判斷是否為鴨子，是的話印出白色，否則黑色
        if(int(prediction[i]) == 1):
            imgs.append([255,255,255])
        else:
            imgs.append([0,0,0])
    
    img = cv2.imread(imgpath)
    size = img.shape
    imgs = np.array(imgs)
    array = np.reshape(imgs, (size[0], -1))
    new = Image.fromarray(array)
    new = new.resize((392, 393))
    new.show()
    print("OK")

def main():
    imgpath = "4.jpg" #輸入要辨識的圖片檔
    train = loadcsv("train.csv")  #輸入data
    train = train[1:]  #將標題過濾
    test = loadduck(imgpath)    #將圖片轉為RGB表示
    summ = class_summarize(train)   #將按照label進行劃分的資料集存入summ
    pred = get_predict(summ, test)   #預測
    create(pred,imgpath)    #建立出圖片結果

main()
```

## :memo:執行成果

:point_down: 圖一
>![](https://i.imgur.com/IyefZYi.jpg =60%x) 
>![](https://i.imgur.com/LjCEO7r.png =60%x)

:point_down: 圖二
>![](https://i.imgur.com/0QgRfka.jpg =60%x)
>![](https://i.imgur.com/iLtz5uF.png =60%x)

:point_down: 圖三
>![](https://i.imgur.com/3gAUhwP.jpg =60%x)
>![](https://i.imgur.com/5Ef9SUQ.png =60%x)

:point_down: 圖四
>![](https://i.imgur.com/KXv279T.jpg =60%x)
>![](https://i.imgur.com/mURo61B.png =60%x)
 
:point_down:全圖
>![](https://i.imgur.com/0goHJAQ.png)


## :memo:結果討論
首先我用小畫家選取鴨子和非鴨子查看色碼並記錄起來(如上述數據表格)
鴨子圖片的部分則是轉成RGB以array形式存取再進行分類
因原先的圖片過於龐大，因此我先切部分圖片作為測試(圖一至圖四)
再使用原圖片來訓練，否則單次測試時間會過於冗長
由上述執行結果圖片來看辨識度不錯，白色部分為鴨子，黑色部分為非鴨子
這次的作業讓我對貝氏分類更為熟悉對於應用的層面更廣泛、實用性也更高
整體來說是份蠻有趣的作業