遺傳算法優化otsu分割問題python實現

杭電模式識別課程設計作業

最大類間方差法(Otsu)

詳見https://www.cnblogs.com/xiaomanon/p/4110006.html,這裏就不去贅述了。

遺傳算法策略

關於遺傳算法的詳解什麼的,可以參考其他的類似文章,下面講講我自己的策略

種羣編碼策略

二進制,優點在於方便理解,缺點在於python對於二進制數的處理有點雞肋。

選擇策略

輪盤選擇法(有待改進)
缺點在於:若變異產生了一個新的最大值(更接近於最優值),但是其種羣數量就只有1,遠遠比不上當前的最大值(離最優值遠一點)。若直接使用輪盤選擇法,那麼這個新變異出來的值就會被覆蓋掉。
改進:強制將上一代中最大的值進行保留,這樣使得種羣不會退化

交換策略

從基因一半往後的位置開始交換,這樣有利於保持當前的最優值,使得交換之後的種羣不至於很差。(待改進,對於最大值個體不進行交換操作)

  • Step1:產生一個概率,若小於交換概率,那麼進行step2往後,否則處理下一個個體;

  • Step1:將種羣的population亂序排列,取出前一半作爲father,後一半作爲mother;

    Step2:產生half-end的隨機位置,然後從father和mother中各選出一位出來配對,交換。

變異策略

對於每一個個體,對於任意位置產生一位變異,但是變異概率不宜設計的很大,這樣會導致種羣不容易收斂。(待改進,對於最大值個體不進行變異操作)

  • Step1:產生一個概率,若小於變異的概率,那麼進行step2往後,否則處理下一個個體;
  • Step2:產生一個隨機位置start-end;
  • Step3:將該個體位置對應的隨機位置的值取反。

停止條件

需滿足以下其一即可:
1:滿足種羣迭代的最大值;
2:種羣的0.98的個體都指向同一個值。

核心代碼

otsu

import cv2
import numpy as np
import matplotlib.pyplot as plt

GRAY_SCALE = 256


def otsuth(img, threshold):
    image_gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY)

    # fg_pro = np.zeros((1, GRAY_SCALE))
    # bg_pro = np.zeros((1, GRAY_SCALE))

    # fg_sum = 0
    # bg_sum = 0
    # for col in image_gray:
    #     for pix in col:
    #         if pix > threshold:
    #             fg_pro[0, pix] += 1
    #             fg_sum += pix
    #         else:
    #             bg_pro[0, pix] += 1
    #             bg_sum += pix
    #
    # if fg_sum != 0:
    #     fg_pro = fg_pro / fg_sum
    # if bg_sum != 0:
    #     bg_pro = bg_pro / bg_sum

    fg_pix = image_gray > threshold
    bg_pix = image_gray <= threshold

    w0 = float(np.sum(fg_pix)) / image_gray.size
    w1 = float(np.sum(bg_pix)) / image_gray.size
    
    u0 = 0
    u1 = 0
    if np.sum(fg_pix) != 0:
        u0 = np.sum(image_gray * fg_pix) / np.sum(fg_pix)
    if np.sum(bg_pix) != 0:
        u1 = np.sum(image_gray * bg_pix) / np.sum(bg_pix)

    val = w0 * w1 * (u0 - u1) * (u0 - u1)
    return val


if __name__ == '__main__':
    file_path = 'images\ship.jpg'
    image = cv2.imread(file_path)
    cv2.imshow('origin_img', image)
    image_gray = cv2.cvtColor(image, cv2.COLOR_RGB2GRAY)

    image_gray[image_gray > 143] = 255

    cv2.imshow('IM_OTSU', image_gray)
    cv2.waitKey(-1)

將該函數傳入GA model,當做適應度函數

GA

def selection(self):
    """
            通過輪盤法選擇(改進算法),保留最大值對應的個體,防止種羣退化
    :return:
    """
    # 計算每一個個體的適應度值
    fitness_list = self.calculate_fitness_list()
    max_fitness = max(fitness_list)

    # step1:將最大值和其餘的值分離,並且將populations重新賦值
    new_populations = []
    remain_populations = []
    for i in range(len(fitness_list)):
        if fitness_list[i] == max_fitness:
            new_populations.append(self.populations[i])
        else:
            remain_populations.append(self.populations[i])

    # 將剩下的populations賦值給model
    self.new_populations = new_populations
    self.populations = remain_populations
    fitness_list = self.calculate_fitness_list()

    # step2:計算概率
    fitness_sum = 0.0
    for fit in fitness_list:
        fitness_sum += fit
    fitness_pro = []
    for i in range(len(fitness_list)):
        fitness_pro.append(fitness_list[i] / fitness_sum)

    # step3:計算剩餘人口的輪盤選擇概率
    pro_sum = 0.0
    for i in range(1, len(fitness_pro)):
        pro_sum += fitness_pro[i]
        fitness_pro[i] = pro_sum
    # 在計算中由於浮點數計算會存在誤差導致最後的概率之和不爲1,這裏糾正
    fitness_pro[-1] = 1

    next_generations = []
    # step4:輪盤選擇出與剩下的人數相等的population
    for i in range(len(remain_populations)):
        # 產生一個0 - 1的概率
        pro = random.uniform(0, 1)

        # 可優化(先計算完輪盤選擇的全部概率分佈,歸結子問題),見上
        if pro <= fitness_pro[0]:
            next_generations.append(self.populations[0])
            continue
        for j in range(self.population_num - 1):
            if fitness_pro[j] < pro < fitness_pro[j + 1]:
                next_generations.append(self.populations[j + 1])
                break
    self.populations = next_generations

def crossover(self):
    """
            種羣交叉,,先reshuffle截取前面一半用作父親,後面用作母親
    :return:
    """
    # # todo delete
    # gen = self.statistics()
    # print('before cross:', gen)

    # reshuffle
    # self.populations = random.shuffle(self.populations)
    random.shuffle(self.populations)

    half = int(len(self.populations) / 2)
    fathers = self.populations[:half]
    mothers = self.populations[half:]

    next_generations = []
    for i in range(half):
        father = fathers[i]
        mother = mothers[i]

        pro = random.uniform(0, 1)
        if pro < self.crossover_pro:
            # todo 位置從一半開始,防止每一次變化過大(待優化,每一次迭代需要將最大的值保留下來,這樣能保證種羣不會退化)
            index = random.randint(self.ga_length / 2, self.ga_length)
            child_a = father[:index] + mother[index:]
            child_b = mother[:index] + father[index:]

            next_generations.append(child_a)
            next_generations.append(child_b)
        else:
            next_generations.append(father)
            next_generations.append(mother)

    if len(self.populations) % 2 != 0:
        next_generations.append(self.populations[-1])

    self.populations = next_generations

    # # todo delete
    # gen = self.statistics()
    # print('after cross:', gen)

def variation(self):
    """
            變異,沒用到左移右移以及取反操作,python無bit類型數據結構
    :return:
    """
    # # todo delete
    # gen = self.statistics()
    # print('before variation:', gen)

    length = len(self.populations)
    for i in range(length):
        pop = self.populations[i]
        # TypeError: 'str' object does not support item assignment
        pop = list(pop)
        pro = random.uniform(0, 1)
        # todo
        if pro < self.variation_pro:  # self.variation_pro
            index = random.randint(0, self.ga_length - 1)
            j = pop[index]
            if int(j) == 0:
                pop[index] = 1
            else:
                pop[index] = 0
            string = "".join('%s' % s for s in pop)
            self.populations[i] = string

    for p in self.new_populations:
        self.populations.append(p)

以上實現爲:選擇、交叉、變異 各步驟。

代碼下載

https://download.csdn.net/download/qq_26479655/10811021

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章