def extract_windows(cls, img, block_size): """Extracts blocks of size (block_size x block_size) from the micrograph. Blocks are extracted with steps of size (block_size) Args: img: Micrograph image. block_size: required block size. Returns: 3D Matrix of blocks. For example, img[0] is the first block. """ # keep only the portion of the image that can be split into blocks with no remainder img = xp.asarray( img[:-(img.shape[0] % block_size), :-(img.shape[1] % block_size)]) dim3_size = np.sqrt(np.prod(img.shape) // (block_size**2)).astype(int) img = xp.reshape(img, (block_size, dim3_size, block_size, dim3_size), 'F') img = xp.transpose(img, (0, 2, 1, 3)) img = xp.reshape( img, (img.shape[0] * img.shape[1], img.shape[2] * img.shape[3]), 'F') return img
def query_score(self, show_progress=True): """Calculates score for each query image. Extracts query images and reference windows. Computes the cross-correlation between these windows, and applies a threshold to compute a score for each query image. Args: show_progress: Whether to show a progress bar Returns: Matrix containing a score for each query image. """ micro_img = xp.asarray(self.im) logger.info('Extracting query images') query_box = PickerHelper.extract_query(micro_img, self.query_size // 2) logger.info('Extracting query images complete') query_box = xp.conj(xp.fft2(query_box, axes=(2, 3))) reference_box = PickerHelper.extract_references(micro_img, self.query_size, self.container_size) reference_size = PickerHelper.reference_size(micro_img, self.container_size) conv_map = xp.zeros((reference_size, query_box.shape[0], query_box.shape[1])) def _work(index): reference_box_i = xp.fft2(reference_box[index], axes=(0, 1)) window_t = xp.multiply(reference_box_i, query_box) cc = xp.ifft2(window_t, axes=(2, 3)) return index, cc.real.max((2, 3)) - cc.real.mean((2, 3)) n_works = reference_size n_threads = config.apple.conv_map_nthreads pbar = tqdm(total=reference_size, disable=not show_progress) # Ideally we'd like something like 'SerialExecutor' to enable easy debugging # but for now do an if-else if n_threads > 1: with futures.ThreadPoolExecutor(n_threads) as executor: to_do = [executor.submit(_work, i) for i in range(n_works)] for future in futures.as_completed(to_do): i, res = future.result() conv_map[i, :, :] = res pbar.update(1) else: for i in range(n_works): _, conv_map[i, :, :] = _work(i) pbar.update(1) pbar.close() conv_map = xp.transpose(conv_map, (1, 2, 0)) min_val = xp.min(conv_map) max_val = xp.max(conv_map) thresh = min_val + (max_val - min_val) / config.apple.response_thresh_norm_factor return xp.asnumpy(xp.sum(conv_map >= thresh, axis=2))
def extract_query(cls, img, block_size): """Extract all query images from the micrograph. windows are extracted with steps of size (block_size/2) Args: img: Micrograph image. block_size: Query images must be of size (block_size x block_size). Returns: 4D Matrix of query images. """ # keep only the portion of the image that can be split into blocks with no remainder blocks = xp.asarray( img[:-(img.shape[0] % block_size), :-(img.shape[1] % block_size)]) dim3_size = np.sqrt(np.prod(blocks.shape) // (block_size**2)).astype(int) blocks = xp.reshape(blocks, (block_size, dim3_size, block_size, dim3_size), 'F') blocks = xp.transpose(blocks, (0, 2, 1, 3)) blocks = xp.reshape(blocks, (blocks.shape[0], blocks.shape[1], -1), 'F') blocks = xp.concatenate( (blocks, xp.concatenate( (blocks[:, :, 1:], xp.reshape(blocks[:, :, 0], (blocks.shape[0], blocks.shape[1], 1), 'F')), axis=2)), axis=0) temp = xp.concatenate( (blocks[:, :, int(np.floor(2 * img.shape[1] / 2 / block_size)):], blocks[:, :, 0:int(np.floor(2 * img.shape[1] / 2 / block_size))]), axis=2) blocks = xp.concatenate((blocks, temp), axis=1) blocks = xp.reshape(blocks, (2 * block_size, 2 * block_size, int(np.floor(2 * img.shape[0] / 2 / block_size)), int(np.floor(2 * img.shape[1] / 2 / block_size))), 'F') blocks = blocks[:, :, 0:blocks.shape[2] - 1, 0:blocks.shape[3] - 1] blocks = xp.transpose(blocks, (2, 3, 0, 1)) return blocks
def run_svm(self, score): """ Trains and uses an SVM classifier. Trains an SVM classifier to distinguish between noise and particle projections based on mean intensity and variance. Every possible window in the micrograph is then classified as either noise or particle, resulting in a segmentation of the micrograph. Args: score: Matrix containing a score for each query image. Returns: Segmentation of the micrograph into noise and particle projections. """ micro_img = xp.asarray(self.im) particle_windows = np.floor(self.tau1) non_noise_windows = np.ceil(self.tau2) bw_mask_p, bw_mask_n = Picker.get_maps(self, score, micro_img, particle_windows, non_noise_windows) x, y = PickerHelper.get_training_set(micro_img, bw_mask_p, bw_mask_n, self.query_size) x = xp.asnumpy(x) y = xp.asnumpy(y) scaler = preprocessing.StandardScaler() scaler.fit(x) x = scaler.transform(x) classify = svm.SVC(C=1, kernel=config.apple.svm_kernel, gamma=config.apple.svm_gamma, class_weight='balanced') classify.fit(x, y) mean_all, std_all = PickerHelper.moments(micro_img, self.query_size) mean_all = xp.asnumpy(mean_all) std_all = xp.asnumpy(std_all) mean_all = mean_all[self.query_size - 1:-(self.query_size - 1), self.query_size - 1:-(self.query_size - 1)] std_all = std_all[self.query_size - 1:-(self.query_size - 1), self.query_size - 1:-(self.query_size - 1)] mean_all = np.reshape(mean_all, (np.prod(mean_all.shape), 1), 'F') std_all = np.reshape(std_all, (np.prod(std_all.shape), 1), 'F') cls_input = np.concatenate((mean_all, std_all), axis=1) cls_input = scaler.transform(cls_input) # compute classification for all possible windows in micrograph segmentation = classify.predict(cls_input) _segmentation_shape = int(np.sqrt(segmentation.shape[0])) segmentation = np.reshape(segmentation, (_segmentation_shape, _segmentation_shape), 'F') return segmentation.copy()
def extract_references(cls, img, query_size, container_size): """Chooses and extracts reference images from the micrograph. Args: img: Micrograph image. query_size: Reference images must be of the same size of query images, i.e. (query_size x query_size). container_size: Containers are large regions used to select reference images. The size of each region is (container_size x container_size) Returns: 3D Matrix of reference images. windows[0] is the first reference window. """ img = xp.asarray(img) num_containers_row = img.shape[0] // container_size num_containers_col = img.shape[1] // container_size windows = xp.zeros( (cls.reference_size(img, container_size), query_size, query_size)) win_idx = 0 mean_all, std_all = cls.moments(img, query_size) for y_contain in range(num_containers_row): for x_contain in range(num_containers_col): temp = img[y_contain * container_size:min(img.shape[0], (y_contain + 1) * container_size), x_contain * container_size:min(img.shape[1], (x_contain + 1) * container_size)] y_start = y_contain * container_size + query_size - 1 y_end = min(mean_all.shape[0] - query_size, (y_contain + 1) * container_size) x_start = x_contain * container_size + query_size - 1 x_end = min(mean_all.shape[1] - query_size, (x_contain + 1) * container_size) mean_contain = mean_all[y_start:y_end, x_start:x_end] std_contain = std_all[y_start:y_end, x_start:x_end] ind = xp.argmax(mean_contain) if ind.size == 1: y, x = xp.unravel_index(ind, mean_contain.shape) windows[win_idx, :, :] = temp[y:y + query_size, x:x + query_size] win_idx += 1 ind = xp.argmin(mean_contain) if ind.size == 1: y, x = xp.unravel_index(ind, mean_contain.shape) windows[win_idx, :, :] = temp[y:y + query_size, x:x + query_size] win_idx += 1 ind = xp.argmax(std_contain) if ind.size == 1: y, x = xp.unravel_index(ind, std_contain.shape) windows[win_idx, :, :] = temp[y:y + query_size, x:x + query_size] win_idx += 1 ind = xp.argmin(std_contain) if ind.size == 1: y, x = xp.unravel_index(ind, std_contain.shape) windows[win_idx, :, :] = temp[y:y + query_size, x:x + query_size] win_idx += 1 return windows