基於細胞自動機Cellular Automata(CA)的區域生長

本來沒想研究這個,但Nvidia NPP的《NVIDIA 2D Image And Signal Performance Primitives》這個模塊的NPP Image Processing部分的Filtering Functions中的computer vision部分有連通域標記以及分水嶺分割現成的函數。但是當我google這個函數的內容時,卻發現根本沒普通人使用,只彈出nvidia官網對這個函數的介紹:

如果有很多人使用過NPP的這個函數,怎麼可能就2條結果。我內心有點忐忑是不是這部分內容並不好用。

其中官網介紹這個分水嶺分割是基於論文《Effiffifficient 2D and 3D Watershed on Graphics Processing Unit: Block-Asynchronous Approches Based on Cellular automata》然後我就查關於CA的內容,然後看到了有CA的區域生長,對於粘連目標,選每個目標的中心爲種子點,分別區域生長是不是就把這個粘連目標分割開了。想這樣做。

代碼:https://github.com/KiriteeGak/region-growing-by-cellular-automata 其參考的論文是Vezhnevets, Vladimir, and Vadim Konouchine. "GrowCut: Interactive multi-label ND image segmentation by cellular automata." proc. of Graphicon. Vol. 1. 2005. 我的理解如下:

class RegionGrowing(object):
    def region_growing(self, image_array, file_path, cutoff_threshold, iterations):
        size = np.shape(image_array)
		#圖像矩陣的數組
        image_array_map = {(r, c): pixel for r, each_row in enumerate(image_array) for c, pixel in
                           enumerate(each_row)}
		#種子點狀態初始化爲1,其他設置爲0??
        seeds_map = {(r, c): 1 for (r, c) in self._seed_points(file_path)}
		#默認要經過50次迭代
        for it in range(0, iterations):
			#先複製上一次的種子點的狀態
            _update_seeds_map = seeds_map.copy()
            for pixel_coord, strength in seeds_map.iteritems():
				#更新每個點的鄰域的值
                _temp_weights_neighbors_pixel = self._neighborhood_weighting(pixel_coord, image_array_map,
                                                                             seeds_map, cutoff_threshold, size)
				#更新種子點的座標和值
                _update_seeds_map = self._update_weights(_temp_weights_neighbors_pixel, _update_seeds_map)
			#將更新後的種子點作爲下一次迭代開始的種子點
            seeds_map = _update_seeds_map
        self._save_image(self._make_binary_image(np.shape(image_array), seeds_map))

    @staticmethod
    def _seed_points(file_path):
        fid = open(file_path, 'rb')
        return [list(map(lambda x: int(x.replace(' ', '')), line.strip().split(','))) for line in fid]

    @staticmethod
	#更新每個點的鄰域的值後,更新種子點的統計(包括種子點座標和值)
    def _update_weights(_temp_weights_neighbors_pixel, _update_seeds_map):
        for pixel_address, strength in _temp_weights_neighbors_pixel.iteritems():
			#如果原來的種子點統計內沒有這個點,那要把這個點新加進去。
            if pixel_address not in _update_seeds_map:
                _update_seeds_map[pixel_address] = strength
			#如果原來的種子點內有這個點,已經是種子點,但原來這個種子點的值小於現在的值,那麼這個將種子點統計內這個點的值進行更新
            elif pixel_address in _update_seeds_map and _update_seeds_map[pixel_address] < strength:
                _update_seeds_map[pixel_address] = strength
            else:
                pass
        return _update_seeds_map

	#更新每個像素點的鄰域點的值
    def _neighborhood_weighting(self, coord, image_map, seeds_map, threshold, canvas_size):
		#所有像素點還是種子點???應該是所有像素點的具體座標[r,c],圖像尺寸大小[max_r,max_c]
        [[r, c], [max_r, max_c]] = [coord, canvas_size]
        _temp_weights = {}
		#計算所有像素點3X3鄰域
        for i in range(-1, 2, 1):
            for j in range(-1, 2, 1):
                pixel_key = (r, c)
				#中心點不用計算,判斷越界
                if [i, j] != [0, 0] and 0 <= r + i < max_r and 0 <= c + j < max_c:
					#如果這個中心點是種子點
                    if pixel_key in seeds_map:
						#計算這個中心點與鄰域的strength
                        trans_strength = self._calculate_strength(image_map[pixel_key],
                                                                  image_map[(r + i, c + j)],
                                                                  seeds_map[pixel_key], threshold)
                    else:
						##計算這個中心點與鄰域的strength
                        trans_strength = self._calculate_strength(image_map[pixel_key],
                                                                  image_map[(r + i, c + j)], 0,
                                                                  threshold)
                    if trans_strength != 0:
						#對這個點的鄰域進行更新
                        _temp_weights[(r + i, c + j)] = trans_strength
        return _temp_weights

    @staticmethod
	#中心點,待計算的鄰域點,種子點,閾值默認是0.5
    def _calculate_strength(dat1, dat2, strength, threshold):
        if dat1 != 0 or dat2 != 0:
            strength_trans = strength * (1 - (abs(dat1 - dat2) / max(dat1, dat2)))
            if strength_trans >= threshold:
                return strength_trans
        return 0

然後準備按照這個理解實現C++版本,我的註釋部分即每個像素點都要計算3X3鄰域然後更新鄰域的值,那豈不是一個鄰域會被相鄰的幾個中心點更新很多次?這樣不是很浪費時間嗎??另外一點,我按照這個註釋運行完C++結果,這是以[300,300]爲種子點迭代50、100、200、300、400、450、480次的結果:

可以看到需要迭代很多次輪廓纔是完全的。是不是因此網上給出的python代碼才選了不止一個種子點,而是5個然後迭代幾十次就夠了!!!因爲單一種子點耗時太長是嗎。

論文中提到對於多個目標的分割也是可以應用的,但我看論文中多個目標時選的種子點更多,我覺得這個算法應用有點狹窄,最起碼現在我有點不想使用了。

但是我還是要試試對於粘連輪廓分割怎樣,是否可以達到分水嶺的效果

這是我的待分割原圖,其實分水嶺的效果已經分割得可以。然後以下分別是迭代10、15、20次的結果

可以看到不適合我的應用,因爲我的目標大小不一。

看來還是要去理解關於CA的分水嶺算法那篇論文。

 

 

 

 

 

另外感覺這個博客平臺越來越功利了,現在只爲了賺錢而存在,不想在這裏記錄了,想轉簡書或博客園去。

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