def segment(self): flattened = flatten_image_matrix(self.image_matrix) # n X 1 def component_joint_prob(i): a = 0.5 * np.log(2 * np.pi * self.variances[i]) b = (np.square(flattened - self.means[i])) / (2 * self.variances[i]) c = np.log(self.mixing_coefficients[i]) return -a - b + c z = np.array([ component_joint_prob(i) for i in range(self.num_components) ]) # 3 X n gamma_nk_denom = logsumexp(z, axis=0) gamma_component = [] for k in range(self.num_components): gamma_nk_nomin = z[k] gamma_nk = np.exp(gamma_nk_nomin - gamma_nk_denom) gamma_component.append(gamma_nk) idx = np.argmax(np.array(gamma_component), axis=0) #print(idx) image_matrix = np.array([self.means[i] for i in idx.flatten()]) update_image_values = unflatten_image_matrix( image_matrix, self.image_matrix.shape[1]) return update_image_values
def likelihood(self): """Assign a log likelihood to the trained model based on the following formula for posterior probability: ln(Pr(X | mixing, mean, stdev)) = sum((n=1 to N), ln(sum((k=1 to K), mixing_k * N(x_n | mean_k,stdev_k)))) returns: log_likelihood = float [0,1] """ flattened = flatten_image_matrix(self.image_matrix) # n X 1 def component_joint_prob(i): a = 0.5 * np.log(2 * np.pi * self.variances[i]) b = ((flattened - self.means[i])**2) / (2 * self.variances[i]) c = np.log(self.mixing_coefficients[i]) return -a - b + c z = np.array([ component_joint_prob(i) for i in range(self.num_components) ]) # 3 X n likelihood = logsumexp(z, axis=0).sum() return likelihood raise NotImplementedError()
def initialize_training(self): """ Initialize the training process by setting each component mean to a random pixel's value (without replacement), each component variance to 1, and each component mixing coefficient to a uniform value (e.g. 4 components -> [0.25,0.25,0.25,0.25]). NOTE: this should be called before train_model() in order for tests to execute correctly. """ # TODO: finish this self.variances = np.array(self.num_components * [1]) self.flat_values = np.array(flatten_image_matrix(self.image_matrix)) self.flat_values.shape = (len(self.flat_values), ) ##set the mean to a random val in the data set initial_means_index = np.array( sample(range(len(self.flat_values)), self.num_components)) self.means = np.array(self.flat_values[initial_means_index]) self.mixing_coefficients = np.array(self.num_components * [1 / float(self.num_components)]) self.mixing_coefficients = np.log(self.mixing_coefficients)
def __init__(self, image_matrix, num_components, means=None): """ Initialize a Gaussian mixture model. params: image_matrix = (grayscale) numpy.nparray[numpy.nparray[float]] num_components = int """ self.image_matrix = image_matrix self.num_components = num_components self.flat_values = np.array(flatten_image_matrix(self.image_matrix)) self.flat_values.shape = (len(self.flat_values), ) if (means is None): self.means = [0] * num_components else: self.means = means self.means = np.array(self.means) self.variances = [0] * num_components self.mixing_coefficients = [0] * num_components if (len(self.image_matrix.shape) == 3): self.height, self.width, self.depth = self.image_matrix.shape else: self.height, self.width = self.image_matrix.shape self.depth = 1
def test_gmm_best_segment(self): """ Calculate the best segment generated by the GMM and compare the subsequent likelihood of a reference segmentation. Note: this test will take a while to run. returns: best_seg = np.ndarray[np.ndarray[float]] """ image_file = 'images/party_spock.png' image_matrix = image_to_matrix(image_file) image_matrix_flat = flatten_image_matrix(image_matrix) num_components = 3 gmm = GaussianMixtureModel(image_matrix, num_components) gmm.initialize_training() iters = 10 # generate best segment from 10 iterations # and extract its likelihood best_seg = gmm.best_segment(iters) matrix_to_image(best_seg, 'images/best_segment_spock.png') best_likelihood = gmm.likelihood() # extract likelihood from reference image ref_image_file = 'images/party_spock%d_baseline.png' % num_components ref_image = image_to_matrix(ref_image_file, grays=True) gmm_ref = GaussianMixtureModel(ref_image, num_components) ref_vals = ref_image.flatten() ref_means = list(set(ref_vals)) ref_variances = np.zeros(num_components) ref_mixing = np.zeros(num_components) for i in range(num_components): relevant_vals = ref_vals[ref_vals == ref_means[i]] ref_mixing[i] = float(len(relevant_vals)) / float(len(ref_vals)) ref_mask = ref_vals == ref_means[i] ref_variances[i] = np.mean( (image_matrix_flat[ref_mask] - ref_means[i])**2) gmm_ref.means = ref_means gmm_ref.variances = ref_variances gmm_ref.mixing_coefficients = ref_mixing ref_likelihood = gmm_ref.likelihood() # compare best likelihood and reference likelihood likelihood_diff = best_likelihood - ref_likelihood print "Reference" print gmm_ref.means print gmm_ref.variances print gmm_ref.mixing_coefficients print best_likelihood print ref_likelihood print likelihood_diff likelihood_thresh = 8e4 self.assertTrue(likelihood_diff >= likelihood_thresh, msg=("Image segmentation failed to improve baseline " "by at least %.2f" % likelihood_thresh))
def k_means_cluster(image_values, k=3, initial_means=None): """ Separate the provided RGB values into k separate clusters using the k-means algorithm, then return an updated version of the image with the original values replaced with the corresponding cluster values. params: image_values = numpy.ndarray[numpy.ndarray[numpy.ndarray[float]]] k = int initial_means = numpy.ndarray[numpy.ndarray[float]] or None returns: updated_image_values = numpy.ndarray[numpy.ndarray[numpy.ndarray[float]]] """ # TODO: finish this function if (len(image_values.shape) == 3): height, width, depth = image_values.shape else: height, width = image_values.shape depth = 1 flat_values = flatten_image_matrix(image_values) #if flat_values.shape[1]==1: # flat_values.shape=(len(flat_values,),) if initial_means == None: initial_means_index = np.array(sample(range(len(flat_values)), k)) means = np.array(flat_values[initial_means_index]) else: means = np.array(initial_means) diff_max = 10 count = 0 while count < 100: ###insert while condition here for testing whether the clustering has converged dist = [] for i in range(k): dist.append(np.sqrt(((flat_values - means[i])**2).sum(axis=1))) dist = np.array(dist) cluster_indices = dist.T.argmin(axis=1) new_means = [] diff = [] for i in range(k): new_means.append(flat_values[cluster_indices == i].mean(axis=0)) diff.append(np.sqrt(((new_means[i] - means[i])**2).sum(axis=0))) diff_max = max(diff) if diff_max < .01: count += 1 means = new_means[:] for i in range(k): flat_values[cluster_indices == i] = flat_values[cluster_indices == i].mean(axis=0).T new_values = unflatten_image_matrix(flat_values, width) return new_values
def k_means_cluster(image_values, k=3, initial_means=None): """ Separate the provided RGB values into k separate clusters using the k-means algorithm, then return an updated version of the image with the original values replaced with the corresponding cluster values. params: image_values = numpy.ndarray[numpy.ndarray[numpy.ndarray[float]]] k = int initial_means = numpy.ndarray[numpy.ndarray[float]] or None returns: updated_image_values = numpy.ndarray[numpy.ndarray[numpy.ndarray[float]]] """ # TODO: finish this function original_dim = image_values.shape flatten_image = flatten_image_matrix(image_values) dim = flatten_image.shape indicator_indices = np.zeros((dim[0], 1)) distance = np.zeros((dim[0], k)) if initial_means == None: means_indices = np.random.choice(dim[0], k, replace=False) means = flatten_image[means_indices, :].copy() else: means = initial_means previous_means = np.zeros(means.shape) while not np.array_equal(means, previous_means): indicator = np.zeros((dim[0], k)) previous_means = means.copy() for i in range(k): distance[:, i] = np.sum(np.power( np.subtract(flatten_image, means[i, :]), 2), axis=1, dtype=float) indicator_indices = np.argmin(distance, axis=1) indx = np.arange(dim[0]).reshape(-1, 1) indicator_indices = indicator_indices.reshape(-1, 1) indicator[indx, indicator_indices] = 1 for i in range(k): means[i, :] = np.divide( np.sum(np.multiply(indicator[:, i].reshape(-1, 1), flatten_image), axis=0, dtype=float), np.sum(indicator[:, i], dtype=float)) for i in range(k): indices = np.where(indicator[:, i] == 1) flatten_image[indices, :] = means[i, :] updated_image_values = unflatten_image_matrix(flatten_image, original_dim[1]) return updated_image_values
def k_means_cluster(image_values, k=3, initial_means=None): """ Separate the provided RGB values into k separate clusters using the k-means algorithm, then return an updated version of the image with the original values replaced with the corresponding cluster values. params: image_values = numpy.ndarray[numpy.ndarray[numpy.ndarray[float]]] k = int initial_means = numpy.ndarray[numpy.ndarray[float]] or None returns: updated_image_values = numpy.ndarray[numpy.ndarray[numpy.ndarray[float]]] """ # raise NotImplementedError() #1. If initial is None, create a random initial point list from data dim = [ np.size(image_values, 0), np.size(image_values, 1), np.size(image_values, 2) ] if initial_means == None: initial_means = getInitialMeans(image_values, k, dim) #2. Loop through initial list and subtract it from the data points sz = 1 for i in range(len(dim) - 1): sz = sz * dim[i] arr_reshaped = flatten_image_matrix( image_values) #np.reshape(image_values,(sz,dim[len(dim)-1])) #3. Square sum and root results to get distance from eack k point kArray = getDistances(arr_reshaped, initial_means, k) #4. Build array containing dataset for each k kImage, kIndexes, kMeans = getMeans(arr_reshaped, kArray, k) #5. Test for convergence and compile new image data to return while (getConvergence(kMeans, initial_means) == False): initial_means = kMeans kArray = getDistances(arr_reshaped, initial_means, k) kImage, kIndexes, kMeans = getMeans(arr_reshaped, kArray, k) arr_orig = np.ndarray(shape=(sz, dim[len(dim) - 1])) for i in range(k): arr_orig[kIndexes[i]] = kMeans[i] arr_orig = unflatten_image_matrix(arr_orig, dim[0]) return arr_orig
def k_means_cluster(image_values, k=3, initial_means=None): import random if image_values.ndim == 3: row, col, depth = image_values.shape else: row, col = image_values.shape depth = 1 if initial_means == None: initial_means = np.zeros((k, depth)) random_rows = random.sample(range(row), k) random_cols = random.sample(range(col), k) for count in range(k): initial_means[count] = image_values[random_rows[count]][ random_cols[count]] distance = np.zeros((k, row * col)) image_values_flat = flatten_image_matrix(image_values) means = initial_means prev_cluster_allocation = np.zeros((1, row * col)) while (1): for cluster_number in range(k): distance[cluster_number] = np.linalg.norm(image_values_flat - means[cluster_number], axis=1) cluster_allocation = np.argmin(distance, axis=0) if (np.array_equal(prev_cluster_allocation, cluster_allocation)): break else: prev_cluster_allocation = cluster_allocation for cluster_number in range(k): indices = np.where(cluster_allocation == cluster_number) means[cluster_number] = np.mean(image_values_flat[[indices]][0], axis=0) for cluster_number in range(k): indices = np.where(cluster_allocation == cluster_number) image_values_flat[[indices]] = means[cluster_number] updated_image_values = unflatten_image_matrix(image_values_flat, col) return updated_image_values
def k_means_cluster(image_values, k=3, initial_means=None): """ Separate the provided RGB values into k separate clusters using the k-means algorithm, then return an updated version of the image with the original values replaced with the corresponding cluster values. params: image_values = numpy.ndarray[numpy.ndarray[numpy.ndarray[float]]] k = int initial_means = numpy.ndarray[numpy.ndarray[float]] or None returns: updated_image_values = numpy.ndarray[numpy.ndarray[numpy.ndarray[float]]] """ flattened = flatten_image_matrix(image_values) count = 0 # M step, random prototype vector if initial_means == None: initial_means_idx = np.random.choice(flattened.shape[0], k, replace=False) mu_k = flattened[initial_means_idx] else: mu_k = initial_means while count < 1: # E step - updating r_nk squared_dist = np.array([ np.square(flattened - mu_k[i]).sum(axis=1) for i in range(mu_k.shape[0]) ]) r_nk = np.argmin(squared_dist, axis=0) # M step - update mu_k clusters = np.array([flattened[(r_nk == i)] for i in range(k)]) mu_k_prev = mu_k mu_k = np.array([clusters[i].mean(axis=0) for i in range(k)]) # Convergence test if np.array_equal(mu_k_prev, mu_k): count += 1 image_matrix = np.array([mu_k[k] for k in r_nk]) updated_image_values = unflatten_image_matrix(image_matrix, image_values.shape[1]) return updated_image_values raise NotImplementedError()
def __init__(self, image_matrix, num_components, means=None): """ Initialize a Gaussian mixture model. params: image_matrix = (grayscale) numpy.nparray[numpy.nparray[float]] num_components = int """ self.image_matrix = image_matrix self.flatten_image = flatten_image_matrix(self.image_matrix) self.num_components = num_components if (means is None): self.means = np.zeros(num_components) else: self.means = means self.variances = np.zeros(num_components) self.mixing_coefficients = np.zeros(num_components) ind = np.where(self.flatten_image < 0) self.flatten_image[ind] = 10e-9
def initialize_training(self): """ Initialize the training process by setting each component mean using some algorithm that you think might give better means to start with, each component variance to 1, and each component mixing coefficient to a uniform value (e.g. 4 components -> [0.25,0.25,0.25,0.25]). [You can feel free to modify the variance and mixing coefficient initializations too if that works well.] """ flattened = flatten_image_matrix(self.image_matrix) count = 0 k = self.num_components # M step, random prototype vector initial_means_idx = np.random.choice(flattened.shape[0], k, replace=False) mu_k = flattened[initial_means_idx] while count < 1: # E step - updating r_nk squared_dist = np.array([ np.square(flattened - mu_k[i]).sum(axis=1) for i in range(mu_k.shape[0]) ]) r_nk = np.argmin(squared_dist, axis=0) # M step - update mu_k clusters = np.array([flattened[r_nk == i] for i in range(k)]) mu_k_prev = mu_k mu_k = np.array([clusters[i].mean(axis=0) for i in range(k)]) # Convergence test if np.array_equal(mu_k_prev, mu_k): count += 1 #print(1/self.num_components) mixing_coefficient = 1.0 / self.num_components self.means = mu_k.tolist() self.variances = [1] * self.num_components self.mixing_coefficients = [mixing_coefficient] * self.num_components
def initialize_training(self): """ Initialize the training process by setting each component mean to a random pixel's value (without replacement), each component variance to 1, and each component mixing coefficient to a uniform value (e.g. 4 components -> [0.25,0.25,0.25,0.25]). NOTE: this should be called before train_model() in order for tests to execute correctly. """ flattened = flatten_image_matrix(self.image_matrix) rand_idx = np.random.choice(flattened.shape[0], self.num_components, replace=False) self.means = flattened[rand_idx] #.tolist() self.variances = np.ones(self.num_components) self.mixing_coefficients = np.full(self.num_components, 1 / self.num_components)
def __init__(self, image_matrix, num_components, means=None): """ Initialize a Gaussian mixture model. params: image_matrix = (grayscale) numpy.nparray[numpy.nparray[float]] num_components = int """ # self.image_matrix = image_matrix # self.num_components = num_components # if(means is None): # self.means = [0]*num_components # else: # self.means = means # self.variances = [0]*num_components # self.mixing_coefficients = [0]*num_components self.image_matrix = image_matrix self.num_components = num_components self.means = np.array( [0] * num_components) if means is None else np.array(means) self.variances = np.array([0] * num_components) self.mixing_coefficients = np.array([0] * num_components) self.flatten_image = flatten_image_matrix(self.image_matrix)
def initialize_training(self): """ Initialize the training process by setting each component mean to a random pixel's value (without replacement), each component variance to 1, and each component mixing coefficient to a uniform value (e.g. 4 components -> [0.25,0.25,0.25,0.25]). NOTE: this should be called before train_model() in order for tests to execute correctly. """ # TODO: finish this flatten_image = flatten_image_matrix(self.image_matrix) dim = flatten_image.shape indx = np.random.choice(dim[0], self.num_components, replace=0) self.means = flatten_image[indx] self.variances = np.ones(self.num_components, dtype=float) self.mixing_coefficients = np.multiply( np.ones(self.num_components, dtype=float), 1 / float(self.num_components))
def train_model(self, convergence_function=default_convergence): """ Train the mixture model using the expectation-maximization algorithm. Since each Gaussian is a combination of mean and variance, this will fill self.means and self.variances, plus self.mixing_coefficients, with the values that maximize the overall model likelihood. params: convergence_function = function, returns True if convergence is reached """ flattened = flatten_image_matrix(self.image_matrix) # n X 1 conv_ctr = 0 #print(self.means) #print(self.variances) def component_joint_prob(i): a = 0.5 * np.log(2 * np.pi * self.variances[i]) b = (np.square(flattened - self.means[i])) / (2 * self.variances[i]) c = np.log(self.mixing_coefficients[i]) return -a - b + c while conv_ctr < 10: prev_likelihood = self.likelihood() #+ 150 prev_variables = np.array([ np.array(self.means).tolist(), np.array(self.variances).tolist(), np.array(self.mixing_coefficients).tolist() ]) # formulae Bishop equation 9.23 z = np.array([ component_joint_prob(i) for i in range(self.num_components) ]) # 3 X n gamma_nk_denom = logsumexp(z, axis=0) for k in range(self.num_components): # E step gamma_nk_nomin = z[k] #print(z[k]) gamma_nk = np.exp(gamma_nk_nomin - gamma_nk_denom) N_k = gamma_nk.sum() #print("array_constant_N_k") #print(N_k) # M step mu_k = np.multiply(gamma_nk, flattened).sum() / N_k x_muk = flattened - mu_k sigma_k = np.multiply( gamma_nk, np.square(x_muk)).sum() / N_k #covar matrix #print(gamma_nk.shape) #print("diff shape") #print((flattened - mu_k).shape) pi_k = N_k / flattened.shape[0] # update self.means[k] = mu_k self.variances[k] = sigma_k self.mixing_coefficients[k] = pi_k new_likelihood = self.likelihood() #+ 150 new_variables = np.array([ np.array(self.means).tolist(), np.array(self.variances).tolist(), np.array(self.mixing_coefficients).tolist() ]) if convergence_function == default_convergence: conv_ctr, converge_bool = convergence_function(prev_likelihood, new_likelihood, conv_ctr, conv_ctr_cap=10) else: conv_ctr, converge_bool = convergence_function(prev_variables, new_variables, conv_ctr, conv_ctr_cap=10)