Esempio n. 1
0
	def construct_gcgraph(self, lam):
		vertex_count = self.cols*self.rows
		edge_count = 2*(4*vertex_count - 3*(self.rows + self.cols) + 2)
		self.graph = GCGraph(vertex_count, edge_count)
		for y in range(self.rows):
			for x in range(self.cols):
				vertex_index = self.graph.add_vertex() # add-node and return its index
				color = self.img[y, x]
				if self._mask[y, x] == self._GC_PR_BGD or self._mask[y, x] == self._GC_PR_FGD:
					fromSource = -np.log(self.BGD_GMM.prob_pixel_GMM(color))
					toSink = -np.log(self.FGD_GMM.prob_pixel_GMM(color))
				elif self._mask[y, x] == self._GC_BGD:
					fromSource = 0
					toSink = lam
				else:
					fromSource = lam
					toSink = 0
				self.graph.add_term_weights(vertex_index, fromSource, toSink)

				if x > 0:
					w = self.left_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-1, w, w)
				if x > 0 and y > 0:
					w = self.upleft_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols-1, w, w)
				if y > 0:
					w = self.up_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols, w, w)
				if x < self.cols - 1 and y > 0:
					w = self.upright_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols+1, w, w)
Esempio n. 2
0
    def construct_gcgraph(self, lam):
        '''Construct a GCGraph with the Gibbs Energy'''
        '''The vertexs of the graph are the pixels, and the edges are constructed by two parts,
			the first part of which are the edges that connect each vertex with Sink Point(the background) and the Source Point(the foreground),
			and the weight of which is the first term in Gibbs Energy;
			the second part of the edges are those that connect each vertex with its neighbourhoods,
			and the weight of which is the second term in Gibbs Energy.'''
        vertex_count = self.cols * self.rows
        edge_count = 2 * (4 * vertex_count - 3 * (self.rows + self.cols) + 2)
        self.graph = GCGraph(vertex_count, edge_count)
        for y in range(self.rows):
            for x in range(self.cols):
                vertex_index = self.graph.add_vertex(
                )  # add-node and return its index
                color = self.img[y, x]
                # '''Set t-weights: Calculate the weight of each vertex with Sink node and Source node'''
                if self._mask[y, x] == self._GC_PR_BGD or self._mask[
                        y, x] == self._GC_PR_FGD:
                    # For each vertex, calculate the first term of G.E. as it be the BGD or FGD, and set them respectively as weight to t/s.
                    fromSource = -np.log(self.BGD_GMM.prob_pixel_GMM(color))
                    toSink = -np.log(self.FGD_GMM.prob_pixel_GMM(color))
                    # print(np.exp(-fromSource), np.exp(-toSink))
                    # print(fromSource)
                elif self._mask[y, x] == self._GC_BGD:
                    # For the vertexs that are Background pixels, t-weight with Source = 0, with Sink = lam
                    fromSource = 0
                    toSink = lam
                else:
                    # GC_FGD
                    fromSource = lam
                    toSink = 0
                # print(fromSource, toSink)
                self.graph.add_term_weights(vertex_index, fromSource, toSink)
                '''Set n-weights and n-link, Calculate the weights between two neighbour vertexs, which is also the second term in Gibbs Energy(the smooth term)'''
                if x > 0:
                    w = self.left_weight[y, x]
                    self.graph.add_edges(vertex_index, vertex_index - 1, w, w)
                if x > 0 and y > 0:
                    w = self.upleft_weight[y, x]
                    self.graph.add_edges(vertex_index,
                                         vertex_index - self.cols - 1, w, w)
                if y > 0:
                    w = self.up_weight[y, x]
                    self.graph.add_edges(vertex_index,
                                         vertex_index - self.cols, w, w)
                if x < self.cols - 1 and y > 0:
                    w = self.upright_weight[y, x]
                    self.graph.add_edges(vertex_index,
                                         vertex_index - self.cols + 1, w, w)
Esempio n. 3
0
    def construct_gcgraph(self, lam):
        '''Construct a GCGraph with the Gibbs Energy'''
        '''The vertexs of the graph are the pixels, and the edges are constructed by two parts,
		the first part of which are the edges that connect each vertex with Sink Point(the background) and the Source Point(the foreground),
		and the weight of which is the first term in Gibbs Energy;
		the second part of the edges are those that connect each vertex with its neighbourhoods,
		and the weight of which is the second term in Gibbs Energy.'''
        print('Constructing grabcut graph...')
        vertex_count = self.cols * self.rows * self.depth
        edge_count = 2 * (9 * self.rows * self.cols * self.depth - 5 *
                          (self.rows * self.cols + self.rows * self.depth +
                           self.cols * self.depth) + 2 *
                          (self.rows + self.cols + self.depth)
                          )  #有向图的边数,每两个顶点之间有正反两条边
        self.graph = GCGraph(vertex_count, edge_count)
        [
            self._construct_gcgraph(lam, z, y, x) for z in range(self.depth)
            for y in range(self.rows) for x in range(self.cols)
        ]
Esempio n. 4
0
	def construct_gcgraph(self, lam):
		'''Construct a GCGraph with the Gibbs Energy'''
		'''The vertexs of the graph are the pixels, and the edges are constructed by two parts,
			the first part of which are the edges that connect each vertex with Sink Point(the background) and the Source Point(the foreground),
			and the weight of which is the first term in Gibbs Energy;
			the second part of the edges are those that connect each vertex with its neighbourhoods,
			and the weight of which is the second term in Gibbs Energy.'''
		vertex_count = self.cols*self.rows
		edge_count = 2*(4*vertex_count - 3*(self.rows + self.cols) + 2)
		self.graph = GCGraph(vertex_count, edge_count)
		for y in range(self.rows):
			for x in range(self.cols):
				vertex_index = self.graph.add_vertex() # add-node and return its index
				color = self.img[y, x]
				# '''Set t-weights: Calculate the weight of each vertex with Sink node and Source node'''
				if self._mask[y, x] == self._GC_PR_BGD or self._mask[y, x] == self._GC_PR_FGD:
					# For each vertex, calculate the first term of G.E. as it be the BGD or FGD, and set them respectively as weight to t/s.
					fromSource = -np.log(self.BGD_GMM.prob_pixel_GMM(color))
					toSink = -np.log(self.FGD_GMM.prob_pixel_GMM(color))
					# print(np.exp(-fromSource), np.exp(-toSink))
					# print(fromSource)
				elif self._mask[y, x] == self._GC_BGD:
					# For the vertexs that are Background pixels, t-weight with Source = 0, with Sink = lam
					fromSource = 0
					toSink = lam
				else:
					# GC_FGD
					fromSource = lam
					toSink = 0
				# print(fromSource, toSink)
				self.graph.add_term_weights(vertex_index, fromSource, toSink)

				'''Set n-weights and n-link, Calculate the weights between two neighbour vertexs, which is also the second term in Gibbs Energy(the smooth term)'''
				if x > 0:
					w = self.left_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-1, w, w)
				if x > 0 and y > 0:
					w = self.upleft_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols-1, w, w)
				if y > 0:
					w = self.up_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols, w, w)
				if x < self.cols - 1 and y > 0:
					w = self.upright_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols+1, w, w)
Esempio n. 5
0
class GCClient:
	def __init__(self, img, k):
		self.k = k # The number of components in each GMM model
		self.img = np.asarray(img, dtype = np.float32)
		self.img2 = img
		self.rows, self.cols = get_size(img)
		self.gamma = 50
		self.lam = 9*self.gamma
		self.beta = 0
		self._BLUE = [255,0,0]        # rectangle color
		self._RED = [0,0,255]         # PR BG
		self._GREEN = [0,255,0]       # PR FG
		self._BLACK = [0,0,0]         # sure BG
		self._WHITE = [255,255,255]   # sure FG
		self._DRAW_BG = {'color':self._BLACK, 'val':0}
		self._DRAW_FG = {'color':self._WHITE, 'val':1}
		self._DRAW_PR_FG = {'color':self._GREEN, 'val':3}
		self._DRAW_PR_BG = {'color':self._RED, 'val':2}
		self._rect = [0, 0, 1, 1]
		self._drawing = False         # flag for drawing curves
		self._rectangle = False       # flag for drawing rect
		self._rect_over = False       # flag to check if rect drawn
		self._thickness = 5           # brush thickness
		self._GC_BGD = 0	#{'color' : BLACK, 'val' : 0}
		self._GC_FGD = 1	#{'color' : WHITE, 'val' : 1}
		self._GC_PR_BGD = 2	#{'color' : GREEN, 'val' : 3}
		self._GC_PR_FGD = 3	#{'color' : RED, 'val' : 2}
		self.calc_beta()
		self.calc_nearby_weight()
		self._DRAW_VAL = None
		self._mask = np.zeros([self.rows, self.cols], dtype = np.uint8) # Init the mask
		self._mask[:, :] = self._GC_BGD

	def calc_beta(self):
		beta = 0
		self._left_diff = self.img[:, 1:] - self.img[:, :-1] # Left-difference
		self._upleft_diff = self.img[1:, 1:] - self.img[:-1, :-1] # Up-Left difference
		self._up_diff = self.img[1:, :] - self.img[:-1, :] # Up-difference
		self._upright_diff = self.img[1:, :-1] - self.img[:-1, 1:] # Up-Right difference
		beta = (self._left_diff*self._left_diff).sum() + (self._upleft_diff*self._upleft_diff).sum() \
			+ (self._up_diff*self._up_diff).sum() + (self._upright_diff*self._upright_diff).sum() # According to the formula
		self.beta = 1/(2*beta/(4*self.cols*self.rows - 3*self.cols - 3*self.rows + 2))

	def calc_nearby_weight(self):
		self.left_weight = np.zeros([self.rows, self.cols])
		self.upleft_weight = np.zeros([self.rows, self.cols])
		self.up_weight = np.zeros([self.rows, self.cols])
		self.upright_weight = np.zeros([self.rows, self.cols])
		for y in range(self.rows):
			for x in range(self.cols):
				color = self.img[y, x]
				if x >= 1:
					diff = color - self.img[y, x-1]
					self.left_weight[y, x] = self.gamma*np.exp(-self.beta*(diff*diff).sum())
				if x >= 1 and y >= 1:
					diff = color - self.img[y-1, x-1]
					self.upleft_weight[y, x] = self.gamma/np.sqrt(2) * np.exp(-self.beta*(diff*diff).sum())
				if y >= 1:
					diff = color - self.img[y-1, x]
					self.up_weight[y, x] = self.gamma*np.exp(-self.beta*(diff*diff).sum())
				if x+1 < self.cols and y >= 1:
					diff = color - self.img[y-1, x+1]
					self.upright_weight[y, x] = self.gamma/np.sqrt(2)*np.exp(-self.beta*(diff*diff).sum())
		
	def init_mask(self, event, x, y, flags, param):
		if event == cv2.EVENT_RBUTTONDOWN:
			self._rectangle = True
			self._ix,self._iy = x,y

		elif event == cv2.EVENT_MOUSEMOVE:
		    if self._rectangle == True:
		    	self.img = self.img2.copy()
		    	cv2.rectangle(self.img,(self._ix,self._iy),(x,y),self._BLUE,2)
		    	self._rect = [min(self._ix,x),min(self._iy,y),abs(self._ix-x),abs(self._iy-y)]
		    	self.rect_or_mask = 0

		elif event == cv2.EVENT_RBUTTONUP:
			self._rectangle = False
			self._rect_over = True
			cv2.rectangle(self.img,(self._ix,self._iy),(x,y),self._BLUE,2)
			self._rect = [min(self._ix,x),min(self._iy,y),abs(self._ix-x),abs(self._iy-y)]
			self.rect_or_mask = 0
			self._mask[self._rect[1]+self._thickness:self._rect[1]+self._rect[3]-self._thickness, self._rect[0]+self._thickness:self._rect[0]+self._rect[2]-self._thickness] = self._GC_PR_FGD

		if event == cv2.EVENT_LBUTTONDOWN:
			if self._rect_over == False:
			    print("Continue")
			else:
				self._drawing == True
				cv2.circle(self.img, (x, y), self._thickness, self._DRAW_VAL['color'], -1)
				cv2.circle(self._mask, (x, y), self._thickness, self._DRAW_VAL['val'], -1)

		elif event == cv2.EVENT_MOUSEMOVE:
			if self._drawing == True:
				cv2.circle(self.img, (x, y), self._thickness, self._DRAW_VAL['color'], -1)
				cv2.circle(self._mask, (x, y), self._thickness, self._DRAW_VAL['val'], -1)

		elif event == cv2.EVENT_LBUTTONUP:
			if self._drawing == True:
				self._drawing = False
				cv2.circle(self.img, (x, y), self._thickness, self._DRAW_VAL['color'], -1)
				cv2.circle(self._mask, (x, y), self._thickness, self._DRAW_VAL['val'], -1)

	def init_with_kmeans(self):
		print(self.cols*self.rows)
		print(len(list(np.where(self._mask == 0))[1]))
		max_iter = 2 # Max-iteration count for Kmeans
		self._bgd = np.where(np.logical_or(self._mask == self._GC_BGD, self._mask == self._GC_PR_BGD)) # Find the places where pixels in the mask MAY belong to BGD.
		self._fgd = np.where(np.logical_or(self._mask == self._GC_FGD, self._mask == self._GC_PR_FGD)) # Find the places where pixels in the mask MAY belong to FGD.
		self._BGDpixels = self.img[self._bgd]
		self._FGDpixels = self.img[self._fgd]
		KMB = kmeans(self._BGDpixels, dim = 3, n = 5, max_iter = max_iter) # The Background Model by kmeans
		KMF = kmeans(self._FGDpixels, dim = 3, n = 5, max_iter = max_iter) # The Foreground Model by kmeans
		KMB.run()
		KMF.run()
		self._BGD_by_components = KMB.output()
		self._FGD_by_components = KMF.output()
		self.BGD_GMM = GMM() # The GMM Model for BGD
		self.FGD_GMM = GMM() # The GMM Model for FGD
		for ci in range(5):
			for pixel in self._BGD_by_components[ci]:
				self.BGD_GMM.add_pixel(pixel, ci)
			for pixel in self._FGD_by_components[ci]:
				self.FGD_GMM.add_pixel(pixel, ci)
		self.BGD_GMM.learning()
		self.FGD_GMM.learning()

	def assign_GMM_components(self):
		self.components_index = np.zeros([self.rows, self.cols], dtype = np.uint)
		for y in range(self.rows):
			for x in range(self.cols):
				pixel = self.img[y, x]
				self.components_index[y, x] = self.BGD_GMM.most_likely_pixel_component(pixel) if (self._mask[y, x] \
					== self._GC_BGD or self._mask[y, x] == self._GC_PR_BGD) else self.FGD_GMM.most_likely_pixel_component(pixel)

	def _assign_GMM_components(self):
		self.components_index = np.zeros([self.rows, self.cols], dtype = np.uint)
		self.components_index[self._bgd] = [i[0] for i in self.BGD_GMM.vec_pix_comp(self.img[self._bgd])]
		self.components_index[self._fgd] = [i[0] for i in self.FGD_GMM.vec_pix_comp(self.img[self._fgd])]

	def learn_GMM_parameters(self):
		for ci in range(5):
			bgd_ci = np.where(np.logical_and(self.components_index == ci, np.logical_or(self._mask == self._GC_BGD, self._mask == self._GC_PR_BGD)))
			fgd_ci = np.where(np.logical_and(self.components_index == ci, np.logical_or(self._mask == self._GC_FGD, self._mask == self._GC_PR_FGD)))
			for pixel in self.img[bgd_ci]:
				self.BGD_GMM.add_pixel(pixel, ci)
			for pixel in self.img[fgd_ci]:
				self.FGD_GMM.add_pixel(pixel, ci)
		self.BGD_GMM.learning()
		self.FGD_GMM.learning()

	def construct_gcgraph(self, lam):
		vertex_count = self.cols*self.rows
		edge_count = 2*(4*vertex_count - 3*(self.rows + self.cols) + 2)
		self.graph = GCGraph(vertex_count, edge_count)
		for y in range(self.rows):
			for x in range(self.cols):
				vertex_index = self.graph.add_vertex() # add-node and return its index
				color = self.img[y, x]
				if self._mask[y, x] == self._GC_PR_BGD or self._mask[y, x] == self._GC_PR_FGD:
					fromSource = -np.log(self.BGD_GMM.prob_pixel_GMM(color))
					toSink = -np.log(self.FGD_GMM.prob_pixel_GMM(color))
				elif self._mask[y, x] == self._GC_BGD:
					fromSource = 0
					toSink = lam
				else:
					fromSource = lam
					toSink = 0
				self.graph.add_term_weights(vertex_index, fromSource, toSink)

				if x > 0:
					w = self.left_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-1, w, w)
				if x > 0 and y > 0:
					w = self.upleft_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols-1, w, w)
				if y > 0:
					w = self.up_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols, w, w)
				if x < self.cols - 1 and y > 0:
					w = self.upright_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols+1, w, w)

	def estimate_segmentation(self):
		a =  self.graph.max_flow()
		for y in range(self.rows):
			for x in range(self.cols):
				if self._mask[y, x] == self._GC_PR_BGD or self._mask[y, x] == self._GC_PR_FGD:
					if self.graph.insource_segment(y*self.cols+x): # Vertex Index
						self._mask[y, x] = self._GC_PR_FGD
					else:
						self._mask[y, x] = self._GC_PR_BGD

	def iter(self, n):
		for i in range(n):
			self.assign_GMM_components()
			self.learn_GMM_parameters()
			self.construct_gcgraph(self.lam)
			self.estimate_segmentation()

	def run(self):
		self.init_with_kmeans()
		self.iter(1)

	def _smoothing(self):
		for y in range(1, self.rows-2):
			for x in range(1, self.cols-2):
				a = self._mask[x-1, y]
				b = self._mask[x+1, y]
				c = self._mask[x, y-1]
				d = self._mask[x, y+1]
				if a==b==3 or a==c==3 or a==d==3 or b==c==3 or b==d==3 or c==d==3:
					self._mask[x, y] = 3

	def show(self, output):
		FGD = np.where((self._mask == 1) + (self._mask == 3), 255, 0).astype('uint8')
		output = cv2.bitwise_and(self.img2, self.img2, mask = FGD)
		return output
Esempio n. 6
0
class GCClient:
    '''The engine of grabcut'''
    def __init__(self, img, k):
        self.k = k  # The number of components in each GMM model
        self.img3d = np.asarray(img, dtype=np.float32)
        self.img3d_2 = self.img3d.copy()
        self.depth, self.rows, self.cols = img.shape
        print('img.shape:', img.shape)
        self.gamma = 50
        self.lam = 28 * self.gamma
        self.beta = 0

        self._BLACK = 0  # sure BG
        self._GRAY1 = 80  # PR BG
        self._GRAY2 = 160  # PR FG
        self._WHITE = 255  # sure FG
        self._GC_BGD = 0
        self._GC_FGD = 1
        self._GC_PR_BGD = 2
        self._GC_PR_FGD = 3
        self._DRAW_BG = {'color': self._BLACK, 'val': self._GC_BGD}
        self._DRAW_FG = {'color': self._WHITE, 'val': self._GC_FGD}
        self._DRAW_PR_BG = {'color': self._GRAY1, 'val': self._GC_PR_BGD}
        self._DRAW_PR_FG = {'color': self._GRAY2, 'val': self._GC_PR_FGD}

        # setting up flags
        self._rect1 = [0, 0, 1, 1, -1]  #[x1,x2, y1,y2, z1] 矩形1左上角的坐标以及宽、高、深度坐标
        self._rect2 = [0, 0, 1, 1, -1]  #[x1,x2, y1,y2, z2] 矩形2左上角的坐标以及宽、高、深度坐标
        self._cube = [0, 0, 1, 1, 0, 1]  #[x1,x2, y1,y2, z1,z2]前景存在的立方体的坐标
        self._drawing = False  # flag for drawing curves
        self._rectangle1 = False  # flag for drawing rectangle1
        self._rectangle2 = False  # flag for drawing rectangle2
        self._rect1_over = False  # flag to check if rect drawn
        self._rect2_over = False  # flag to check if rect drawn
        self._rect_or_mask = 0  # flag for selecting rect or mask mode

        self._thickness = 2  # brush thickness
        self._DRAW_VAL = None  #color of brush
        self._mask = np.zeros([self.depth, self.rows, self.cols],
                              dtype=np.uint8)  # Init the mask
        self._mask[:, :, :] = self._GC_BGD
        self._mask3d = self._mask.astype('float32')

        self.calc_nearby_weight()

    def calc_nearby_weight(self):
        '''STEP 1:'''
        '''Calculate Beta -- The Exp Term of Smooth Parameter in Gibbs Energy'''
        '''beta = 1/(2*average(sqrt(||pixel[i] - pixel[j]||)))'''
        '''Beta is used to adjust the difference of two nearby pixels in high or low contrast rate'''
        '''STEP 2:'''
        '''Calculate the weight of the edge of each pixel with its nearby pixel, as each pixel is regarded
		as a vertex of the graph'''
        '''The weight of each direction is saved in a image the same size of the original image'''
        '''weight = gamma * 1/distance<m,n> * exp(-beta*(diff*diff))'''

        #diff1 = self.img3d[:-1,:-1,:-1] - self.img3d[1:,1:,1:]
        diff2 = self.img3d[:-1, :-1, :] - self.img3d[1:, 1:, :]
        #diff3 = self.img3d[:-1,:-1,1:] - self.img3d[1:,1:,:-1]
        diff4 = self.img3d[:-1, :, :-1] - self.img3d[1:, :, 1:]
        diff5 = self.img3d[:-1, :, :] - self.img3d[1:, :, :]
        diff6 = self.img3d[:-1, :, 1:] - self.img3d[1:, :, :-1]
        #diff7 = self.img3d[:-1,1:,:-1] - self.img3d[1:,:-1,1:]
        diff8 = self.img3d[:-1, 1:, :] - self.img3d[1:, :-1, :]
        #diff9 = self.img3d[:-1,1:,1:] - self.img3d[1:,:-1,:-1]
        diff10 = self.img3d[:, :-1, :-1] - self.img3d[:, 1:, 1:]
        diff11 = self.img3d[:, :-1, :] - self.img3d[:, 1:, :]
        diff12 = self.img3d[:, :-1, 1:] - self.img3d[:, 1:, :-1]
        diff13 = self.img3d[:, :, :-1] - self.img3d[:, :, 1:]

        beta = (diff2*diff2).sum() + (diff4*diff4).sum() + (diff5*diff5).sum() \
         + (diff6*diff6).sum() + (diff8*diff8).sum() + (diff10*diff10).sum() \
         + (diff11*diff11).sum() + (diff12*diff12).sum() + (diff13*diff13).sum()
        self.beta = (9 * self.rows * self.cols * self.depth -
                     5 * self.rows * self.cols - 5 * self.rows * self.depth -
                     5 * self.cols * self.depth + 2 * self.rows +
                     2 * self.cols + 2 * self.depth) / (2 * beta)
        print('self.beta:', self.beta)

        # Use the formula to calculate the weight
        self.weight2 = self.gamma * 0.707 * np.exp(-self.beta *
                                                   (diff2 * diff2))
        self.weight4 = self.gamma * 0.707 * np.exp(-self.beta *
                                                   (diff4 * diff4))
        self.weight5 = self.gamma * np.exp(-self.beta * (diff5 * diff5))
        self.weight6 = self.gamma * 0.707 * np.exp(-self.beta *
                                                   (diff6 * diff6))
        self.weight8 = self.gamma * 0.707 * np.exp(-self.beta *
                                                   (diff8 * diff8))
        self.weight10 = self.gamma * 0.707 * np.exp(-self.beta *
                                                    (diff10 * diff10))
        self.weight11 = self.gamma * np.exp(-self.beta * (diff11 * diff11))
        self.weight12 = self.gamma * 0.707 * np.exp(-self.beta *
                                                    (diff12 * diff12))
        self.weight13 = self.gamma * np.exp(-self.beta * (diff13 * diff13))

    '''The following function is derived from the sample of opencv sources'''

    def init_mask(self, event, x, y, flags, param):
        '''Init the mask with interactive movements'''
        '''Notice: the elements in the mask should be within the follows:
			"GC_BGD":The pixel belongs to background;
			"GC_FGD":The pixel belongs to foreground;
			"GC_PR_BGD":The pixel MAY belongs to background;
			"GC_PR_FGD":The pixel MAY belongs to foreground;'''

        # Draw Rectangle1
        if self._rect1_over == False:
            if event == cv2.EVENT_LBUTTONDOWN:
                self._rectangle1 = True
                self._ix, self._iy = x, y

            elif event == cv2.EVENT_LBUTTONUP:
                if self._rectangle1 == True:
                    self._rect1_over = True
                    self._rectangle1 == False
                    cv2.rectangle(self.img3d_2[index1], (self._ix, self._iy),
                                  (x, y), self._WHITE, self._thickness)
                    self._rect1 = [
                        min(self._ix, x),
                        max(self._ix, x),
                        min(self._iy, y),
                        max(self._iy, y), index1
                    ]

        elif self._rect2_over == False:
            if event == cv2.EVENT_LBUTTONDOWN:
                if index1 == self._rect1[4]:
                    print('Please change the depth to draw anather rectangle!')
                else:
                    self._rectangle2 = True
                    self._ix, self._iy = x, y

            elif event == cv2.EVENT_LBUTTONUP:
                if self._rectangle2 == True:
                    self._rect2_over = True
                    cv2.rectangle(self.img3d_2[index1], (self._ix, self._iy),
                                  (x, y), self._WHITE, self._thickness)
                    self._rect2 = [
                        min(self._ix, x),
                        max(self._ix, x),
                        min(self._iy, y),
                        max(self._iy, y), index1
                    ]
                    self._cube = [
                        min(self._rect1[0], self._rect2[0]),
                        max(self._rect1[1], self._rect2[1]),
                        min(self._rect1[2], self._rect2[2]),
                        max(self._rect1[3], self._rect2[3]),
                        min(self._rect1[4], self._rect2[4]),
                        max(self._rect1[4], self._rect2[4])
                    ]
                    print('self._cube:', self._cube)
                    self._mask[self._cube[4]:self._cube[5] + 1,
                               self._cube[2]:self._cube[3],
                               self._cube[0]:self._cube[1]] = self._GC_PR_FGD
                    self._mask3d = self._mask.astype('float32')

        # Notice : The x and y axis in CV2 are inversed to those in numpy.
        elif self._DRAW_VAL:
            if event == cv2.EVENT_LBUTTONDOWN:
                self._drawing = True
                cv2.circle(self.img3d_2[index1], (x, y), self._thickness,
                           self._DRAW_VAL['color'], -1)
                cv2.circle(self._mask3d[index1], (x, y), self._thickness,
                           self._DRAW_VAL['val'], -1)

            elif event == cv2.EVENT_MOUSEMOVE:
                if self._drawing == True:
                    cv2.circle(self.img3d_2[index1], (x, y), self._thickness,
                               self._DRAW_VAL['color'], -1)
                    cv2.circle(self._mask3d[index1], (x, y), self._thickness,
                               self._DRAW_VAL['val'], -1)

            elif event == cv2.EVENT_LBUTTONUP:
                if self._drawing == True:
                    self._drawing = False
                    cv2.circle(self.img3d_2[index1], (x, y), self._thickness,
                               self._DRAW_VAL['color'], -1)
                    cv2.circle(self._mask3d[index1], (x, y), self._thickness,
                               self._DRAW_VAL['val'], -1)

    def init_with_kmeans(self):
        '''Initialise the BGDGMM and FGDGMM, which are respectively background-model and foreground-model,
			using kmeans algorithm'''
        print('init with k-means processing...')
        self._mask = self._mask3d.astype('uint8')
        max_iter = 3  # Max-iteration count for Kmeans
        '''In the following two indexings, the np.logical_or is needed in place of or'''
        self._bgd = np.where(
            np.logical_or(self._mask == self._GC_BGD,
                          self._mask == self._GC_PR_BGD)
        )  # Find the places where pixels in the mask MAY belong to BGD.
        self._fgd = np.where(
            np.logical_or(self._mask == self._GC_FGD,
                          self._mask == self._GC_PR_FGD)
        )  # Find the places where pixels in the mask MAY belong to FGD.
        self._BGDpixels = self.img3d[self._bgd]
        self._FGDpixels = self.img3d[self._fgd]
        KMB = kmeans(self._BGDpixels, n=self.k,
                     max_iter=max_iter)  # The Background Model by kmeans
        KMF = kmeans(self._FGDpixels, n=self.k,
                     max_iter=max_iter)  # The Foreground Model by kmeans
        KMB.run()
        KMF.run()
        self._BGD_by_components = KMB.output()
        self._FGD_by_components = KMF.output()
        self.BGD_GMM = GMM()  # The GMM Model for BGD
        self.FGD_GMM = GMM()  # The GMM Model for FGD
        self.BGD_GMM.learning(self._BGD_by_components)
        self.FGD_GMM.learning(self._FGD_by_components)
        print('BGD_GMM:', '\nweights:\n', list(self.BGD_GMM.weights),
              '\nmeans:\n', list(self.BGD_GMM.means), '\nvars:\n',
              list(self.BGD_GMM.vars), '\n')
        print('FGD_GMM:', '\nweights:\n', list(self.FGD_GMM.weights),
              '\nmeans:\n', list(self.FGD_GMM.means), '\nvars:\n',
              list(self.FGD_GMM.vars), '\n')

    '''The first step of the iteration in the paper: Assign components of GMMs to pixels,
		(the kn in the paper), which is saved in self.components_index'''

    def assign_GMM_components(self):
        print('Refreshing GMM components...')
        self._mask = self._mask3d.astype('uint8')
        self.components_index = np.asarray([
            self.BGD_GMM.most_likely_pixel_component(pixel) if
            (mask == self._GC_BGD) or (mask == self._GC_PR_BGD) else
            self.FGD_GMM.most_likely_pixel_component(pixel)
            for mat1, mat2 in zip(self.img3d, self._mask)
            for row1, row2 in zip(mat1, mat2)
            for pixel, mask in zip(row1, row2)
        ],
                                           dtype='uint8').reshape(
                                               self.depth, self.rows,
                                               self.cols)

    '''The second step in the iteration: Learn the parameters from GMM models'''

    def learn_GMM_parameters(self):
        '''Calculate the parameters of each component of GMM'''
        print('Learning GMM parameters...')
        self._BGD_by_components = []
        self._FGD_by_components = []

        for ci in range(self.k):
            # The places where the pixel belongs to the ci_th model and background model.
            bgd_ci = np.where(
                np.logical_and(
                    self.components_index == ci,
                    np.logical_or(self._mask == self._GC_BGD,
                                  self._mask == self._GC_PR_BGD)))
            self._BGD_by_components.append(self.img3d[bgd_ci])
            # The places where the pixel belongs to the ci_th model and foreground model.
            fgd_ci = np.where(
                np.logical_and(
                    self.components_index == ci,
                    np.logical_or(self._mask == self._GC_FGD,
                                  self._mask == self._GC_PR_FGD)))
            self._FGD_by_components.append(self.img3d[fgd_ci])

        self.BGD_GMM.learning(self._BGD_by_components)
        self.FGD_GMM.learning(self._FGD_by_components)
        print('BGD_GMM:', '\nweights:\n', list(self.BGD_GMM.weights),
              '\nmeans:\n', list(self.BGD_GMM.means), '\nvars:\n',
              list(self.BGD_GMM.vars), '\n')
        print('FGD_GMM:', '\nweights:\n', list(self.FGD_GMM.weights),
              '\nmeans:\n', list(self.FGD_GMM.means), '\nvars:\n',
              list(self.FGD_GMM.vars), '\n')

    def _construct_gcgraph(self, lam, z, y, x):
        '''Set t-weights: Calculate the weight of each vertex with Sink node and Source node'''
        vertex_index = self.graph.add_vertex()  # add-node and return its index
        color = self.img3d[z, y, x]
        if self._mask[z, y, x] == self._GC_PR_BGD or self._mask[
                z, y, x] == self._GC_PR_FGD:
            # For each vertex, calculate the first term of G.E. as it be the BGD or FGD, and set them respectively as weight to t/s.
            fromSource = -np.log(self.BGD_GMM.prob_pixel_GMM(color))
            toSink = -np.log(self.FGD_GMM.prob_pixel_GMM(color))

        elif self._mask[z, y, x] == self._GC_BGD:
            # For the vertexs that are Background pixels, t-weight with Source = 0, with Sink = lam
            fromSource = 0
            toSink = lam
        else:
            fromSource = lam
            toSink = 0
        self.graph.add_term_weights(vertex_index, fromSource, toSink)
        '''Set n-weights and n-link, Calculate the weights between two neighbour vertexs, which is also the second term in Gibbs Energy(the smooth term)'''
        if z > 0:
            w = self.weight5[z - 1, y, x]
            self.graph.add_edges(vertex_index,
                                 vertex_index - (self.rows * self.cols), w, w)
            if y > 0:
                w = self.weight2[z - 1, y - 1, x]
                self.graph.add_edges(
                    vertex_index,
                    vertex_index - (self.rows * self.cols + self.cols), w, w)
            if x > 0:
                w = self.weight4[z - 1, y, x - 1]
                self.graph.add_edges(
                    vertex_index, vertex_index - (self.rows * self.cols + 1),
                    w, w)
            if x < self.cols - 1:
                w = self.weight6[z - 1, y, x]
                self.graph.add_edges(
                    vertex_index, vertex_index - (self.rows * self.cols - 1),
                    w, w)
            if y < self.rows - 1:
                w = self.weight8[z - 1, y, x]
                self.graph.add_edges(
                    vertex_index,
                    vertex_index - (self.rows * self.cols - self.cols), w, w)
        if y > 0:
            w = self.weight11[z, y - 1, x]
            self.graph.add_edges(vertex_index, vertex_index - self.cols, w, w)
            if x > 0:
                w = self.weight10[z, y - 1, x - 1]
                self.graph.add_edges(vertex_index,
                                     vertex_index - (self.cols + 1), w, w)
            if x < self.cols - 1:
                w = self.weight12[z, y - 1, x]
                self.graph.add_edges(vertex_index,
                                     vertex_index - (self.cols - 1), w, w)
        if x > 0:
            w = self.weight13[z, y, x - 1]
            self.graph.add_edges(vertex_index, vertex_index - 1, w, w)

    def construct_gcgraph(self, lam):
        '''Construct a GCGraph with the Gibbs Energy'''
        '''The vertexs of the graph are the pixels, and the edges are constructed by two parts,
		the first part of which are the edges that connect each vertex with Sink Point(the background) and the Source Point(the foreground),
		and the weight of which is the first term in Gibbs Energy;
		the second part of the edges are those that connect each vertex with its neighbourhoods,
		and the weight of which is the second term in Gibbs Energy.'''
        print('Constructing grabcut graph...')
        vertex_count = self.cols * self.rows * self.depth
        edge_count = 2 * (9 * self.rows * self.cols * self.depth - 5 *
                          (self.rows * self.cols + self.rows * self.depth +
                           self.cols * self.depth) + 2 *
                          (self.rows + self.cols + self.depth)
                          )  #有向图的边数,每两个顶点之间有正反两条边
        self.graph = GCGraph(vertex_count, edge_count)
        [
            self._construct_gcgraph(lam, z, y, x) for z in range(self.depth)
            for y in range(self.rows) for x in range(self.cols)
        ]

    def estimate_segmentation(self):
        print('Estimating segmentation...')
        a = self.graph.max_flow()
        self._mask = np.asarray([
            self._GC_PR_FGD if
            self.graph.insource_segment(self.rows * self.cols * z +
                                        self.cols * y + x) else self._GC_PR_BGD
            for z in range(self.depth) for y in range(self.rows)
            for x in range(self.cols)
        ]).reshape(self.depth, self.rows, self.cols)

    def iter(self, n):
        for i in range(n):
            self.assign_GMM_components()
            self.learn_GMM_parameters()
            self.construct_gcgraph(self.lam)
            self.estimate_segmentation()

    def run(self):
        self.init_with_kmeans()
        self.construct_gcgraph(self.lam)
        self.estimate_segmentation()
Esempio n. 7
0
class GCClient:
	'''The engine of grabcut'''
	def __init__(self, img, k):
		self.k = k # The number of components in each GMM model

		self.img = np.asarray(img, dtype = np.float32)
		self.img2 = img
		self.rows, self.cols = get_size(img)
		self.gamma = 50
		self.lam = 9*self.gamma
		self.beta = 0

		self._BLUE = [255,0,0]        # rectangle color
		self._RED = [0,0,255]         # PR BG
		self._GREEN = [0,255,0]       # PR FG
		self._BLACK = [0,0,0]         # sure BG
		self._WHITE = [255,255,255]   # sure FG

		self._DRAW_BG = {'color':self._BLACK, 'val':0}
		self._DRAW_FG = {'color':self._WHITE, 'val':1}
		self._DRAW_PR_FG = {'color':self._GREEN, 'val':3}
		self._DRAW_PR_BG = {'color':self._RED, 'val':2}

		# setting up flags
		self._rect = [0, 0, 1, 1]
		self._drawing = False         # flag for drawing curves
		self._rectangle = False       # flag for drawing rect
		self._rect_over = False       # flag to check if rect drawn
		# se;f._rect_or_mask = 100      # flag for selecting rect or mask mode
		# self._value = DRAW_FG         # drawing initialized to FG
		self._thickness = 3           # brush thickness
		
		self._GC_BGD = 0	#{'color' : BLACK, 'val' : 0}
		self._GC_FGD = 1	#{'color' : WHITE, 'val' : 1}
		self._GC_PR_BGD = 2	#{'color' : GREEN, 'val' : 3}
		self._GC_PR_FGD = 3	#{'color' : RED, 'val' : 2}
		self.calc_beta()
		self.calc_nearby_weight()

		self._DRAW_VAL = None

		self._mask = np.zeros([self.rows, self.cols], dtype = np.uint8) # Init the mask
		self._mask[:, :] = self._GC_BGD


	def calc_beta(self):
		'''Calculate Beta -- The Exp Term of Smooth Parameter in Gibbs Energy'''
		'''beta = 1/(2*average(sqrt(||pixel[i] - pixel[j]||)))'''
		'''Beta is used to adjust the difference of two nearby pixels in high or low contrast rate'''
		beta = 0
		self._left_diff = self.img[:, 1:] - self.img[:, :-1] # Left-difference
		self._upleft_diff = self.img[1:, 1:] - self.img[:-1, :-1] # Up-Left difference
		self._up_diff = self.img[1:, :] - self.img[:-1, :] # Up-difference
		self._upright_diff = self.img[1:, :-1] - self.img[:-1, 1:] # Up-Right difference
		beta = (self._left_diff*self._left_diff).sum() + (self._upleft_diff*self._upleft_diff).sum() \
			+ (self._up_diff*self._up_diff).sum() + (self._upright_diff*self._upright_diff).sum() # According to the formula
		self.beta = 1/(2*beta/(4*self.cols*self.rows - 3*self.cols - 3*self.rows + 2))
		# print(self.beta) # According to the paper

	@timeit
	def calc_nearby_weight(self):
		'''Calculate the weight of the edge of each pixel with its nearby pixel, as each pixel is regarded
			as a vertex of the graph'''
		'''The weight of each direction is saved in a image the same size of the original image'''
		'''weight = gamma*exp(-beta*(diff*diff))'''
		self.left_weight = np.zeros([self.rows, self.cols])
		self.upleft_weight = np.zeros([self.rows, self.cols])
		self.up_weight = np.zeros([self.rows, self.cols])
		self.upright_weight = np.zeros([self.rows, self.cols])
		# Use the formula to calculate the weight
		# for y in range(self.rows):
		# 	for x in range(self.cols):
		# 		color = self.img[y, x]
		# 		if x >= 1:
		# 			diff = color - self.img[y, x-1]
		# 			diff.shape = (1, 3)
		# 			self.left_weight[y, x] = self.gamma*np.exp(-self.beta*np.dot(diff, np.transpose(diff)))
		# 		if x >= 1 and y >= 1:
		# 			diff = color - self.img[y-1, x-1]
		# 			diff.shape = (1, 3)
		# 			self.upleft_weight[y, x] = self.gamma/np.sqrt(2) * np.exp(-self.beta*np.dot(diff, np.transpose(diff)))
		# 		if y >= 1:
		# 			diff = color - self.img[y-1, x]
		# 			diff.shape = (1, 3)
		# 			self.up_weight[y, x] = self.gamma*np.exp(-self.beta*np.dot(diff, np.transpose(diff)))
		# 		if x+1 < self.cols and y >= 1:
		# 			diff = color - self.img[y-1, x+1]
		# 			diff.shape = (1, 3)
		# 			self.upright_weight[y, x] = self.gamma/np.sqrt(2)*np.exp(-self.beta*np.dot(diff, np.transpose(diff)))
		
		# Use the formula to calculate the weight
		for y in range(self.rows):
			for x in range(self.cols):
				color = self.img[y, x]
				if x >= 1:
					diff = color - self.img[y, x-1]
					# print(np.exp(-self.beta*(diff*diff).sum()))
					self.left_weight[y, x] = self.gamma*np.exp(-self.beta*(diff*diff).sum())
				if x >= 1 and y >= 1:
					diff = color - self.img[y-1, x-1]
					self.upleft_weight[y, x] = self.gamma/np.sqrt(2) * np.exp(-self.beta*(diff*diff).sum())
				if y >= 1:
					diff = color - self.img[y-1, x]
					self.up_weight[y, x] = self.gamma*np.exp(-self.beta*(diff*diff).sum())
				if x+1 < self.cols and y >= 1:
					diff = color - self.img[y-1, x+1]
					self.upright_weight[y, x] = self.gamma/np.sqrt(2)*np.exp(-self.beta*(diff*diff).sum())
		

		# self.left_weight[:, 1:] = self.gamma*np.exp(-self.beta*((self._left_diff*self._left_diff).sum()))
		# self.upleft_weight[1:, 1:] = self.gamma*np.exp(-self.beta*((self._upleft_diff*self._upleft_diff).sum()))
		# self.up_weight[1:, :] = self.gamma*np.exp(-self.beta*(self._up_diff*self._up_diff).sum())
		# self.upright_weight = self.gamma*np.exp(-self.beta*(self._upright_diff*self._upright_diff).sum())

	
	'''The following function is derived from the sample of opencv sources'''
	def init_mask(self, event, x, y, flags, param):
		'''Init the mask with interactive movements'''
		'''Notice: the elements in the mask should be within the follows:
			"GC_BGD":The pixel belongs to background;
			"GC_FGD":The pixel belongs to foreground;
			"GC_PR_BGD":The pixel MAY belongs to background;
			"GC_PR_FGD":The pixel MAY belongs to foreground;'''

		# Draw Rectangle
		if event == cv2.EVENT_RBUTTONDOWN:
			self._rectangle = True
			self._ix,self._iy = x,y

		elif event == cv2.EVENT_MOUSEMOVE:
		    if self._rectangle == True:
		    	self.img = self.img2.copy()
		    	cv2.rectangle(self.img,(self._ix,self._iy),(x,y),self._BLUE,2)
		    	self._rect = [min(self._ix,x),min(self._iy,y),abs(self._ix-x),abs(self._iy-y)]
		    	self.rect_or_mask = 0

		elif event == cv2.EVENT_RBUTTONUP:
			self._rectangle = False
			self._rect_over = True
			cv2.rectangle(self.img,(self._ix,self._iy),(x,y),self._BLUE,2)
			self._rect = [min(self._ix,x),min(self._iy,y),abs(self._ix-x),abs(self._iy-y)]
			# print(" Now press the key 'n' a few times until no further change \n")
			self.rect_or_mask = 0
			self._mask[self._rect[1]+self._thickness:self._rect[1]+self._rect[3]-self._thickness, self._rect[0]+self._thickness:self._rect[0]+self._rect[2]-self._thickness] = self._GC_PR_FGD


		# Notice : The x and y axis in CV2 are inversed to those in numpy.

		if event == cv2.EVENT_LBUTTONDOWN:
			if self._rect_over == False:
				print('Draw a rectangle on the image first.')
			else:
				self._drawing == True
				cv2.circle(self.img, (x, y), self._thickness, self._DRAW_VAL['color'], -1)
				cv2.circle(self._mask, (x, y), self._thickness, self._DRAW_VAL['val'], -1)

		elif event == cv2.EVENT_MOUSEMOVE:
			if self._drawing == True:
				cv2.circle(self.img, (x, y), self._thickness, self._DRAW_VAL['color'], -1)
				cv2.circle(self._mask, (x, y), self._thickness, self._DRAW_VAL['val'], -1)

		elif event == cv2.EVENT_LBUTTONUP:
			if self._drawing == True:
				self._drawing = False
				cv2.circle(self.img, (x, y), self._thickness, self._DRAW_VAL['color'], -1)
				cv2.circle(self._mask, (x, y), self._thickness, self._DRAW_VAL['val'], -1)

	# @timeit
	def init_with_kmeans(self):
		print(self.cols*self.rows)
		print(len(list(np.where(self._mask == 0))[1]))
		'''Initialise the BGDGMM and FGDGMM, which are respectively background-model and foreground-model,
			using kmeans algorithm'''
		max_iter = 2 # Max-iteration count for Kmeans
		'''In the following two indexings, the np.logical_or is needed in place of or'''
		self._bgd = np.where(np.logical_or(self._mask == self._GC_BGD, self._mask == self._GC_PR_BGD)) # Find the places where pixels in the mask MAY belong to BGD.
		self._fgd = np.where(np.logical_or(self._mask == self._GC_FGD, self._mask == self._GC_PR_FGD)) # Find the places where pixels in the mask MAY belong to FGD.
		self._BGDpixels = self.img[self._bgd]
		self._FGDpixels = self.img[self._fgd]
		KMB = kmeans(self._BGDpixels, dim = 3, n = self.k, max_iter = max_iter) # The Background Model by kmeans
		KMF = kmeans(self._FGDpixels, dim = 3, n = self.k, max_iter = max_iter) # The Foreground Model by kmeans
		KMB.run()
		KMF.run()
		# self._BGD_types = KMB.output()
		# self._FGD_types = KMF.output()
		# print(self._BGD_types)
		self._BGD_by_components = KMB.output()
		self._FGD_by_components = KMF.output()
		self.BGD_GMM = GMM() # The GMM Model for BGD
		self.FGD_GMM = GMM() # The GMM Model for FGD
		'''Add the pixels by components to GMM'''
		for ci in range(self.k):
			# print(len(self._BGD_by_components[ci]))
			# print(self._BGD_by_components[ci])
			for pixel in self._BGD_by_components[ci]:
				# pixel = np.asarray([j for j in pixel], dtype = np.float32)
				self.BGD_GMM.add_pixel(pixel, ci)
			for pixel in self._FGD_by_components[ci]:
				self.FGD_GMM.add_pixel(pixel, ci)
		# for ci in range(self.k):
		# 	bgd_index = np.where(self._BGD_types == ci)
		# 	fgd_index = np.where(self._FGD_types == ci)
		# 	for pixel in self.img[bgd_index]:
		# 		self.BGD_GMM.add_pixel(pixel, ci)
		# 	for pixel in self.img[fgd_index]:
		# 		self.FGD_GMM.add_pixel(pixel, ci)
		self.BGD_GMM.learning()
		self.FGD_GMM.learning()

	'''The first step of the iteration in the paper: Assign components of GMMs to pixels,
		(the kn in the paper), which is saved in self.components_index'''
	# @timeit
	def assign_GMM_components(self):
		self.components_index = np.zeros([self.rows, self.cols], dtype = np.uint)
		# self.components_index[self._bgd] = [i[0] for i in self.BGD_GMM.vec_pix_comp(self.img[self._bgd])]
		# self.components_index[self._fgd] = [i[0] for i in self.FGD_GMM.vec_pix_comp(self.img[self._fgd])]
		for y in range(self.rows):
			for x in range(self.cols):
				pixel = self.img[y, x]
				self.components_index[y, x] = self.BGD_GMM.most_likely_pixel_component(pixel) if (self._mask[y, x] \
					== self._GC_BGD or self._mask[y, x] == self._GC_PR_BGD) else self.FGD_GMM.most_likely_pixel_component(pixel)

	# @timeit
	def _assign_GMM_components(self):
		self.components_index = np.zeros([self.rows, self.cols], dtype = np.uint)
		self.components_index[self._bgd] = [i[0] for i in self.BGD_GMM.vec_pix_comp(self.img[self._bgd])]
		self.components_index[self._fgd] = [i[0] for i in self.FGD_GMM.vec_pix_comp(self.img[self._fgd])]



	'''The second step in the iteration: Learn the parameters from GMM models'''
	# @timeit
	def learn_GMM_parameters(self):
		for ci in range(self.k):
			# The places where the pixel belongs to the ci_th model and background model.
			bgd_ci = np.where(np.logical_and(self.components_index == ci, np.logical_or(self._mask == self._GC_BGD, self._mask == self._GC_PR_BGD)))
			fgd_ci = np.where(np.logical_and(self.components_index == ci, np.logical_or(self._mask == self._GC_FGD, self._mask == self._GC_PR_FGD)))
			for pixel in self.img[bgd_ci]:
				self.BGD_GMM.add_pixel(pixel, ci)
			for pixel in self.img[fgd_ci]:
				self.FGD_GMM.add_pixel(pixel, ci)
		self.BGD_GMM.learning()
		self.FGD_GMM.learning()

	# @timeit
	def construct_gcgraph(self, lam):
		'''Construct a GCGraph with the Gibbs Energy'''
		'''The vertexs of the graph are the pixels, and the edges are constructed by two parts,
			the first part of which are the edges that connect each vertex with Sink Point(the background) and the Source Point(the foreground),
			and the weight of which is the first term in Gibbs Energy;
			the second part of the edges are those that connect each vertex with its neighbourhoods,
			and the weight of which is the second term in Gibbs Energy.'''
		vertex_count = self.cols*self.rows
		edge_count = 2*(4*vertex_count - 3*(self.rows + self.cols) + 2)
		self.graph = GCGraph(vertex_count, edge_count)
		for y in range(self.rows):
			for x in range(self.cols):
				vertex_index = self.graph.add_vertex() # add-node and return its index
				color = self.img[y, x]
				# '''Set t-weights: Calculate the weight of each vertex with Sink node and Source node'''
				if self._mask[y, x] == self._GC_PR_BGD or self._mask[y, x] == self._GC_PR_FGD:
					# For each vertex, calculate the first term of G.E. as it be the BGD or FGD, and set them respectively as weight to t/s.
					fromSource = -np.log(self.BGD_GMM.prob_pixel_GMM(color))
					toSink = -np.log(self.FGD_GMM.prob_pixel_GMM(color))
					# print(np.exp(-fromSource), np.exp(-toSink))
					# print(fromSource)
				elif self._mask[y, x] == self._GC_BGD:
					# For the vertexs that are Background pixels, t-weight with Source = 0, with Sink = lam
					fromSource = 0
					toSink = lam
				else:
					# GC_FGD
					fromSource = lam
					toSink = 0
				# print(fromSource, toSink)
				self.graph.add_term_weights(vertex_index, fromSource, toSink)

				'''Set n-weights and n-link, Calculate the weights between two neighbour vertexs, which is also the second term in Gibbs Energy(the smooth term)'''
				if x > 0:
					w = self.left_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-1, w, w)
				if x > 0 and y > 0:
					w = self.upleft_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols-1, w, w)
				if y > 0:
					w = self.up_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols, w, w)
				if x < self.cols - 1 and y > 0:
					w = self.upright_weight[y, x]
					self.graph.add_edges(vertex_index, vertex_index-self.cols+1, w, w)

	# @timeit
	def estimate_segmentation(self):
		a =  self.graph.max_flow()
		for y in range(self.rows):
			for x in range(self.cols):
				if self._mask[y, x] == self._GC_PR_BGD or self._mask[y, x] == self._GC_PR_FGD:
					if self.graph.insource_segment(y*self.cols+x): # Vertex Index
						self._mask[y, x] = self._GC_PR_FGD
					else:
						# print(y, x)
						self._mask[y, x] = self._GC_PR_BGD

	def iter(self, n):
		for i in range(n):
			self.assign_GMM_components()
			self.learn_GMM_parameters()
			self.construct_gcgraph(self.lam)
			self.estimate_segmentation()
			# self._smoothing()

	def run(self):
		self.init_with_kmeans()
		self.iter(1)

	def _smoothing(self):
		for y in range(1, self.rows-2):
			for x in range(1, self.cols-2):
				# if self._mask[x-1, y] == self._mask[x+1, y] == self._mask[x, y-1] == self._mask[x, y+1]:
				a = self._mask[x-1, y]
				b = self._mask[x+1, y]
				c = self._mask[x, y-1]
				d = self._mask[x, y+1]
				if a==b==3 or a==c==3 or a==d==3 or b==c==3 or b==d==3 or c==d==3:
					self._mask[x, y] = 3

	def show(self, output):
		# 
		# FGD = np.where(np.logical_and(np.logical_or(self._mask == 1, self._mask == 3), self._mask0 == 3))
		# FGD = np.where(np.logical_or(self._mask==1, self._mask==3))
		FGD = np.where((self._mask == 1) + (self._mask == 3), 255, 0).astype('uint8')
		# output[FGD] = self.img[FGD]
		# output = output.astype(np.uint8)
		output = cv2.bitwise_and(self.img2, self.img2, mask = FGD)
		# print('Press N to continue')
		return output
Esempio n. 8
0
class GCClient:
    '''The engine of grabcut'''
    def __init__(self, img, k):
        self.k = k  # The number of components in each GMM model

        self.img = np.asarray(img, dtype=np.float32)
        self.img2 = img
        self.rows, self.cols = get_size(img)  # 이미지 사이즈
        self.gamma = 50
        self.lam = 9 * self.gamma
        self.beta = 0

        self._BLUE = [255, 0, 0]  # rectangle color
        self._RED = [0, 0, 255]  # PR BG
        self._GREEN = [0, 255, 0]  # PR FG
        self._BLACK = [0, 0, 0]  # sure BG
        self._WHITE = [255, 255, 255]  # sure FG

        self._DRAW_BG = {'color': self._BLACK, 'val': 0}
        self._DRAW_FG = {'color': self._WHITE, 'val': 1}
        self._DRAW_PR_FG = {'color': self._GREEN, 'val': 3}
        self._DRAW_PR_BG = {'color': self._RED, 'val': 2}

        # setting up flags
        self._rect = [0, 0, 1, 1]
        self._drawing = False  # flag for drawing curves
        self._rectangle = False  # flag for drawing rect
        self._rect_over = False  # flag to check if rect drawn
        # se;f._rect_or_mask = 100      # flag for selecting rect or mask mode
        # self._value = DRAW_FG         # drawing initialized to FG
        self._thickness = 3  # brush thickness

        self._GC_BGD = 0  #{'color' : BLACK, 'val' : 0}
        self._GC_FGD = 1  #{'color' : WHITE, 'val' : 1}
        self._GC_PR_BGD = 2  #{'color' : GREEN, 'val' : 3}
        self._GC_PR_FGD = 3  #{'color' : RED, 'val' : 2}
        self.calc_beta()
        self.calc_nearby_weight()

        self._DRAW_VAL = None

        self._mask = np.zeros([self.rows, self.cols],
                              dtype=np.uint8)  # Init the mask
        self._mask[:, :] = self._GC_BGD

        flag = False

        ## jaejin add

        self._rectangle = False
        self._rect_over = True
        self._rect = [0, 0, self.cols, self.rows]  # 전체선택.
        self.rect_or_mask = 0
        self._mask[self._rect[1] + self._thickness:self._rect[1] +
                   self._rect[3] - self._thickness,
                   self._rect[0] + self._thickness:self._rect[0] +
                   self._rect[2] - self._thickness] = self._GC_PR_FGD

        # --- ---- - --- - - --- - - -- end  시작하자마자 선택됨

    ## _mask 바꾸면 될듯함

    def calc_beta(self):
        '''Calculate Beta -- The Exp Term of Smooth Parameter in Gibbs Energy'''
        '''beta = 1/(2*average(sqrt(||pixel[i] - pixel[j]||)))'''
        '''Beta is used to adjust the difference of two nearby pixels in high or low contrast rate'''
        beta = 0
        self._left_diff = self.img[:, 1:] - self.img[:, :-1]  # Left-difference
        self._upleft_diff = self.img[
            1:, 1:] - self.img[:-1, :-1]  # Up-Left difference
        self._up_diff = self.img[1:, :] - self.img[:-1, :]  # Up-difference
        self._upright_diff = self.img[
            1:, :-1] - self.img[:-1, 1:]  # Up-Right difference
        beta = (self._left_diff*self._left_diff).sum() + (self._upleft_diff*self._upleft_diff).sum() \
         + (self._up_diff*self._up_diff).sum() + (self._upright_diff*self._upright_diff).sum() # According to the formula
        self.beta = 1 / (
            2 * beta /
            (4 * self.cols * self.rows - 3 * self.cols - 3 * self.rows + 2))
        # print(self.beta) # According to the paper

    @timeit
    def calc_nearby_weight(self):
        '''Calculate the weight of the edge of each pixel with its nearby pixel, as each pixel is regarded
			as a vertex of the graph'''
        '''The weight of each direction is saved in a image the same size of the original image'''
        '''weight = gamma*exp(-beta*(diff*diff))'''
        self.left_weight = np.zeros([self.rows, self.cols])
        self.upleft_weight = np.zeros([self.rows, self.cols])
        self.up_weight = np.zeros([self.rows, self.cols])
        self.upright_weight = np.zeros([self.rows, self.cols])
        # Use the formula to calculate the weight
        # for y in range(self.rows):
        # 	for x in range(self.cols):
        # 		color = self.img[y, x]
        # 		if x >= 1:
        # 			diff = color - self.img[y, x-1]
        # 			diff.shape = (1, 3)
        # 			self.left_weight[y, x] = self.gamma*np.exp(-self.beta*np.dot(diff, np.transpose(diff)))
        # 		if x >= 1 and y >= 1:
        # 			diff = color - self.img[y-1, x-1]
        # 			diff.shape = (1, 3)
        # 			self.upleft_weight[y, x] = self.gamma/np.sqrt(2) * np.exp(-self.beta*np.dot(diff, np.transpose(diff)))
        # 		if y >= 1:
        # 			diff = color - self.img[y-1, x]
        # 			diff.shape = (1, 3)
        # 			self.up_weight[y, x] = self.gamma*np.exp(-self.beta*np.dot(diff, np.transpose(diff)))
        # 		if x+1 < self.cols and y >= 1:
        # 			diff = color - self.img[y-1, x+1]
        # 			diff.shape = (1, 3)
        # 			self.upright_weight[y, x] = self.gamma/np.sqrt(2)*np.exp(-self.beta*np.dot(diff, np.transpose(diff)))

        # Use the formula to calculate the weight
        for y in range(self.rows):
            for x in range(self.cols):
                color = self.img[y, x]
                if x >= 1:
                    diff = color - self.img[y, x - 1]
                    # print(np.exp(-self.beta*(diff*diff).sum()))
                    self.left_weight[y, x] = self.gamma * np.exp(
                        -self.beta * (diff * diff).sum())
                if x >= 1 and y >= 1:
                    diff = color - self.img[y - 1, x - 1]
                    self.upleft_weight[y,
                                       x] = self.gamma / np.sqrt(2) * np.exp(
                                           -self.beta * (diff * diff).sum())
                if y >= 1:
                    diff = color - self.img[y - 1, x]
                    self.up_weight[y, x] = self.gamma * np.exp(
                        -self.beta * (diff * diff).sum())
                if x + 1 < self.cols and y >= 1:
                    diff = color - self.img[y - 1, x + 1]
                    self.upright_weight[y,
                                        x] = self.gamma / np.sqrt(2) * np.exp(
                                            -self.beta * (diff * diff).sum())

        # self.left_weight[:, 1:] = self.gamma*np.exp(-self.beta*((self._left_diff*self._left_diff).sum()))
        # self.upleft_weight[1:, 1:] = self.gamma*np.exp(-self.beta*((self._upleft_diff*self._upleft_diff).sum()))
        # self.up_weight[1:, :] = self.gamma*np.exp(-self.beta*(self._up_diff*self._up_diff).sum())
        # self.upright_weight = self.gamma*np.exp(-self.beta*(self._upright_diff*self._upright_diff).sum())

    '''The following function is derived from the sample of opencv sources'''

    def init_mask(self, event, x, y, flags, param):
        '''Init the mask with interactive movements'''
        '''Notice: the elements in the mask should be within the follows:
			"GC_BGD":The pixel belongs to background;
			"GC_FGD":The pixel belongs to foreground;
			"GC_PR_BGD":The pixel MAY belongs to background;
			"GC_PR_FGD":The pixel MAY belongs to foreground;'''

        # Draw Rectangle
        if event == cv2.EVENT_RBUTTONDOWN:  # 오른쪽 마우스 눌렀을때
            self._rectangle = True
            self._ix, self._iy = x, y

        elif event == cv2.EVENT_MOUSEMOVE:
            if self._rectangle == True:
                self.img = self.img2.copy()
                cv2.rectangle(
                    self.img, (self._ix, self._iy), (x, y), self._BLUE, 2
                )  # 사각형 그리기 cv2.rectangle(img, start, end, color, thickness) 이미지 시작좌표 종료좌표 color 선의두께
                self._rect = [
                    min(self._ix, x),
                    min(self._iy, y),
                    abs(self._ix - x),
                    abs(self._iy - y)
                ]
                self.rect_or_mask = 0

        elif event == cv2.EVENT_RBUTTONUP:
            self._rectangle = False
            self._rect_over = True
            cv2.rectangle(self.img, (self._ix, self._iy), (x, y), self._BLUE,
                          2)
            self._rect = [
                min(self._ix, x),
                min(self._iy, y),
                abs(self._ix - x),
                abs(self._iy - y)
            ]  # x와 ix 작은값  y와 iy 작은값  ix-x 의 절대값 , iy-y의 절대값

            # add jaejin
            self._rect = [0, 0, self.cols, self.rows]  # 전체선택.

            # ---
            # print(" Now press the key 'n' a few times until no further change \n")

            self.rect_or_mask = 0
            self._mask[self._rect[1] + self._thickness:self._rect[1] +
                       self._rect[3] - self._thickness,
                       self._rect[0] + self._thickness:self._rect[0] +
                       self._rect[2] - self._thickness] = self._GC_PR_FGD
            ## _mask 바꾸면 될듯함

        # Notice : The x and y axis in CV2 are inversed to those in numpy.

        if event == cv2.EVENT_LBUTTONDOWN:  # 왼쪽마우스 눌렸을때
            if self._rect_over == False:
                print('Draw a rectangle on the image first.')
            else:
                self._drawing == True
                cv2.circle(self.img, (x, y), self._thickness,
                           self._DRAW_VAL['color'], -1)
                cv2.circle(self._mask, (x, y), self._thickness,
                           self._DRAW_VAL['val'], -1)

        elif event == cv2.EVENT_MOUSEMOVE:
            if self._drawing == True:
                cv2.circle(self.img, (x, y), self._thickness,
                           self._DRAW_VAL['color'], -1)
                cv2.circle(self._mask, (x, y), self._thickness,
                           self._DRAW_VAL['val'], -1)

        elif event == cv2.EVENT_LBUTTONUP:
            if self._drawing == True:
                self._drawing = False
                cv2.circle(self.img, (x, y), self._thickness,
                           self._DRAW_VAL['color'], -1)
                cv2.circle(self._mask, (x, y), self._thickness,
                           self._DRAW_VAL['val'], -1)

    # @timeit
    def init_with_kmeans(self):
        print(self.cols * self.rows)
        print(len(list(np.where(self._mask == 0))[1]))
        '''Initialise the BGDGMM and FGDGMM, which are respectively background-model and foreground-model,
			using kmeans algorithm'''
        max_iter = 2  # Max-iteration count for Kmeans
        '''In the following two indexings, the np.logical_or is needed in place of or'''
        self._bgd = np.where(
            np.logical_or(self._mask == self._GC_BGD,
                          self._mask == self._GC_PR_BGD)
        )  # Find the places where pixels in the mask MAY belong to BGD.
        self._fgd = np.where(
            np.logical_or(self._mask == self._GC_FGD,
                          self._mask == self._GC_PR_FGD)
        )  # Find the places where pixels in the mask MAY belong to FGD.
        self._BGDpixels = self.img[self._bgd]
        self._FGDpixels = self.img[self._fgd]
        KMB = kmeans(self._BGDpixels, dim=3, n=self.k,
                     max_iter=max_iter)  # The Background Model by kmeans
        KMF = kmeans(self._FGDpixels, dim=3, n=self.k,
                     max_iter=max_iter)  # The Foreground Model by kmeans
        KMB.run()
        KMF.run()
        # self._BGD_types = KMB.output()
        # self._FGD_types = KMF.output()
        # print(self._BGD_types)
        self._BGD_by_components = KMB.output()
        self._FGD_by_components = KMF.output()
        self.BGD_GMM = GMM()  # The GMM Model for BGD
        self.FGD_GMM = GMM()  # The GMM Model for FGD
        '''Add the pixels by components to GMM'''
        for ci in range(self.k):
            # print(len(self._BGD_by_components[ci]))
            # print(self._BGD_by_components[ci])
            for pixel in self._BGD_by_components[ci]:
                # pixel = np.asarray([j for j in pixel], dtype = np.float32)
                self.BGD_GMM.add_pixel(pixel, ci)
            for pixel in self._FGD_by_components[ci]:
                self.FGD_GMM.add_pixel(pixel, ci)
        # for ci in range(self.k):
        # 	bgd_index = np.where(self._BGD_types == ci)
        # 	fgd_index = np.where(self._FGD_types == ci)
        # 	for pixel in self.img[bgd_index]:
        # 		self.BGD_GMM.add_pixel(pixel, ci)
        # 	for pixel in self.img[fgd_index]:
        # 		self.FGD_GMM.add_pixel(pixel, ci)
        self.BGD_GMM.learning()
        self.FGD_GMM.learning()

    '''The first step of the iteration in the paper: Assign components of GMMs to pixels,
		(the kn in the paper), which is saved in self.components_index'''

    # @timeit
    def assign_GMM_components(self):
        self.components_index = np.zeros([self.rows, self.cols], dtype=np.uint)
        # self.components_index[self._bgd] = [i[0] for i in self.BGD_GMM.vec_pix_comp(self.img[self._bgd])]
        # self.components_index[self._fgd] = [i[0] for i in self.FGD_GMM.vec_pix_comp(self.img[self._fgd])]
        for y in range(self.rows):
            for x in range(self.cols):
                pixel = self.img[y, x]
                self.components_index[y, x] = self.BGD_GMM.most_likely_pixel_component(pixel) if (self._mask[y, x] \
                 == self._GC_BGD or self._mask[y, x] == self._GC_PR_BGD) else self.FGD_GMM.most_likely_pixel_component(pixel)

    # @timeit
    def _assign_GMM_components(self):
        self.components_index = np.zeros([self.rows, self.cols], dtype=np.uint)
        self.components_index[self._bgd] = [
            i[0] for i in self.BGD_GMM.vec_pix_comp(self.img[self._bgd])
        ]
        self.components_index[self._fgd] = [
            i[0] for i in self.FGD_GMM.vec_pix_comp(self.img[self._fgd])
        ]

    '''The second step in the iteration: Learn the parameters from GMM models'''

    # @timeit
    def learn_GMM_parameters(self):
        for ci in range(self.k):
            # The places where the pixel belongs to the ci_th model and background model.
            bgd_ci = np.where(
                np.logical_and(
                    self.components_index == ci,
                    np.logical_or(self._mask == self._GC_BGD,
                                  self._mask == self._GC_PR_BGD)))
            fgd_ci = np.where(
                np.logical_and(
                    self.components_index == ci,
                    np.logical_or(self._mask == self._GC_FGD,
                                  self._mask == self._GC_PR_FGD)))
            for pixel in self.img[bgd_ci]:
                self.BGD_GMM.add_pixel(pixel, ci)
            for pixel in self.img[fgd_ci]:
                self.FGD_GMM.add_pixel(pixel, ci)
        self.BGD_GMM.learning()
        self.FGD_GMM.learning()

    # @timeit
    def construct_gcgraph(self, lam):
        '''Construct a GCGraph with the Gibbs Energy'''
        '''The vertexs of the graph are the pixels, and the edges are constructed by two parts,
			the first part of which are the edges that connect each vertex with Sink Point(the background) and the Source Point(the foreground),
			and the weight of which is the first term in Gibbs Energy;
			the second part of the edges are those that connect each vertex with its neighbourhoods,
			and the weight of which is the second term in Gibbs Energy.'''
        vertex_count = self.cols * self.rows
        edge_count = 2 * (4 * vertex_count - 3 * (self.rows + self.cols) + 2)
        self.graph = GCGraph(vertex_count, edge_count)
        for y in range(self.rows):
            for x in range(self.cols):
                vertex_index = self.graph.add_vertex(
                )  # add-node and return its index
                color = self.img[y, x]
                # '''Set t-weights: Calculate the weight of each vertex with Sink node and Source node'''
                if self._mask[y, x] == self._GC_PR_BGD or self._mask[
                        y, x] == self._GC_PR_FGD:
                    # For each vertex, calculate the first term of G.E. as it be the BGD or FGD, and set them respectively as weight to t/s.
                    fromSource = -np.log(self.BGD_GMM.prob_pixel_GMM(color))
                    toSink = -np.log(self.FGD_GMM.prob_pixel_GMM(color))
                    # print(np.exp(-fromSource), np.exp(-toSink))
                    # print(fromSource)
                elif self._mask[y, x] == self._GC_BGD:
                    # For the vertexs that are Background pixels, t-weight with Source = 0, with Sink = lam
                    fromSource = 0
                    toSink = lam
                else:
                    # GC_FGD
                    fromSource = lam
                    toSink = 0
                # print(fromSource, toSink)
                self.graph.add_term_weights(vertex_index, fromSource, toSink)
                '''Set n-weights and n-link, Calculate the weights between two neighbour vertexs, which is also the second term in Gibbs Energy(the smooth term)'''
                if x > 0:
                    w = self.left_weight[y, x]
                    self.graph.add_edges(vertex_index, vertex_index - 1, w, w)
                if x > 0 and y > 0:
                    w = self.upleft_weight[y, x]
                    self.graph.add_edges(vertex_index,
                                         vertex_index - self.cols - 1, w, w)
                if y > 0:
                    w = self.up_weight[y, x]
                    self.graph.add_edges(vertex_index,
                                         vertex_index - self.cols, w, w)
                if x < self.cols - 1 and y > 0:
                    w = self.upright_weight[y, x]
                    self.graph.add_edges(vertex_index,
                                         vertex_index - self.cols + 1, w, w)

    # @timeit
    def estimate_segmentation(self):
        a = self.graph.max_flow()
        for y in range(self.rows):
            for x in range(self.cols):
                if self._mask[y, x] == self._GC_PR_BGD or self._mask[
                        y, x] == self._GC_PR_FGD:
                    if self.graph.insource_segment(y * self.cols +
                                                   x):  # Vertex Index
                        self._mask[y, x] = self._GC_PR_FGD
                    else:
                        # print(y, x)
                        self._mask[y, x] = self._GC_PR_BGD

    def iter(self, n):
        for i in range(n):
            self.assign_GMM_components()
            self.learn_GMM_parameters()
            self.construct_gcgraph(self.lam)
            self.estimate_segmentation()
            # self._smoothing()

    def run(self):
        self.init_with_kmeans()
        self.iter(1)

    def _smoothing(self):
        for y in range(1, self.rows - 2):
            for x in range(1, self.cols - 2):
                # if self._mask[x-1, y] == self._mask[x+1, y] == self._mask[x, y-1] == self._mask[x, y+1]:
                a = self._mask[x - 1, y]
                b = self._mask[x + 1, y]
                c = self._mask[x, y - 1]
                d = self._mask[x, y + 1]
                if a == b == 3 or a == c == 3 or a == d == 3 or b == c == 3 or b == d == 3 or c == d == 3:
                    self._mask[x, y] = 3

    def show(self, output):
        #
        # FGD = np.where(np.logical_and(np.logical_or(self._mask == 1, self._mask == 3), self._mask0 == 3))
        # FGD = np.where(np.logical_or(self._mask==1, self._mask==3))
        FGD = np.where((self._mask == 1) + (self._mask == 3), 255,
                       0).astype('uint8')
        # output[FGD] = self.img[FGD]
        # output = output.astype(np.uint8)
        output = cv2.bitwise_and(self.img2, self.img2, mask=FGD)
        # print('Press N to continue')
        return output