def main(): p = opt.ArgumentParser(description=""" Recodes an image based on Level 1 bag-of-things model. This means that the original image has been already re-coded in terms of a Level 0 bag-of-things model and the resulting coding is fed into this program. The input data is a file containing the region coordinates and the histogram of level 0 features for each window in the original image that is to be recoded. The output will preserve the same structure: regions and assigned label. """) p.add_argument('in_data', action='store', help='level 0-coded image') p.add_argument('out_data', action='store', help='resulting level 1-coded image') p.add_argument('l1_model', action='store', help='level-0 codebook model file') args = p.parse_args() with ModelPersistence(args.l1_model, 'r', format='pickle') as mp: l1_model = mp with ModelPersistence(args.in_data, 'r', format='pickle') as d: data = d['bag_l1'] block_codes = \ Parallel(n_jobs=cpu_count()) \ ( delayed(worker_nearest_medoid)(p, l1_model['codebook']['cluster_centers_']) for p in data['hist_l0'] ) with ModelPersistence(args.out_data, 'c', format='pickle') as d: d['l1_codes'] = block_codes d['regs'] = data['regs'] return
def worker(img_name, desc, wnd_size, idx): # consider the images already in H-intensity try: im_h = imread(img_name) except IOError as e: return None # -image bag growing # bag for current image: bag = grow_bag_from_new_image(im_h, desc, (wnd_size, wnd_size), 1000000, sampling_strategy='sliding', it_step=(wnd_size, wnd_size), discard_empty=True) # save the bag: with ModelPersistence('.'.join(img_name.split('.')[:-1]) + '_bag_gabor_l0.pkl', 'c', format='pickle') as d: d['bag_l0'] = bag return bag[desc.name], idx, bag['regs']
def worker(img_name, desc, wnd_size, nwindows): # consider the images already in H-intensity try: im_h = imread(img_name) except IOError as e: return None # -preprocessing #if im.ndim == 3: # im_h, _ = rgb2he(im, normalize=True) # im_h = equalize_adapthist(im_h) # im_h = rescale_intensity(im_h, out_range=(0,255)) # im_h = im_h.astype(np.uint8) #else: # return None # -image bag growing # bag for current image: bag = grow_bag_from_new_image(im_h, desc, (wnd_size, wnd_size), nwindows, sampling_strategy='random', discard_empty=True) # save the bag: with ModelPersistence('.'.join(img_name.split('.')[:-1]) + '_bag_l0.pkl', 'c', format='pickle') as d: d['bag_l0'] = bag return bag[desc.name]
def main(): p = opt.ArgumentParser(description=""" Prints information about a codebook. """) p.add_argument('cbk', action='store', help='codebook file name') p.add_argument('-s', '--size', action='store_true', help='print codebook size', default=True) p.add_argument('-n', '--norm', action='store_true', help='print 1 if data was normalized', default=False) args = p.parse_args() cbk_file = args.cbk with ModelPersistence(cbk_file, 'r', format='pickle') as mp: codebook = mp['codebook'] standardize = mp['standardize'] if args.size: print(codebook.cluster_centers_.shape[0]) if args.norm: print("1" if standardize else "0") return
def main(): p = opt.ArgumentParser(description=""" Emphasizes the patches with a given code (from BoT) by reducing the contrast of the rest of the image. """ ) p.add_argument('image', action='store', help='image file name') p.add_argument('res_image', action='store', help='name of the resulting image') p.add_argument('bot_result', action='store', help='a file with BoT coding for regions') p.add_argument('bot_code', action='store', help='the code of the regions to be emphasized', type=int) p.add_argument('-g', '--gamma', action='store', nargs=1, type=float, help='the gamma level of the background regions', default=0.2) args = p.parse_args() img = skimage.io.imread(args.image) regs = [] with ModelPersistence(args.bot_result, 'r', format='pickle') as d: block_codes = d['l1_codes'] regs = d['regs'] #print(block_codes) #print(args.bot_code) # filter regions of interest: roi = [ regs[k] for k in np.where(np.array(block_codes, dtype=np.int) == args.bot_code)[0] ] #print(roi) img = enhance_patches(img, roi, _gamma=args.gamma) skimage.io.imsave(args.res_image, img) return
def worker(img_name, desc, wnd_size, l0_model): try: im_h = imread(img_name) except IOError as e: return None # assume H-plane is given in the image # -preprocessing # if im.ndim == 3: # im_h, _ = rgb2he(im, normalize=True) # im_h = equalize_adapthist(im_h) # im_h = rescale_intensity(im_h, out_range=(0,255)) # im_h = im_h.astype(np.uint8) # else: # return None print("...start on", img_name) bag = [] wnd = [] itw1 = sliding_window(im_h.shape, (wnd_size, wnd_size), start=(0, 0), step=(wnd_size, wnd_size)) for w1 in itw1: # for each "large window": # -divide it in windows of level-0 size # -classify these "small windows" # -build the histogram of codeblock frequencies wnd1 = im_h[w1[0]:w1[1], w1[2]:w1[3]] if wnd1.sum() < wnd_size**2 / 100: continue # not enough non-zero pixels itw0 = sliding_window( wnd1.shape, (l0_model['window_size'], l0_model['window_size']), start=(0, 0), step=(l0_model['window_size'], l0_model['window_size'])) ldesc = [] for w0 in itw0: ldesc.append(desc.compute(wnd1[w0[0]:w0[1], w0[2]:w0[3]])) X = np.vstack(ldesc) y = l0_model['codebook'].predict(X) h = np.zeros(l0_model['codebook'].cluster_centers_.shape[0] ) # histogram of code blocks for k in range(y.size): h[y[k]] += 1.0 h /= y.size # frequencies bag.append(h) # add it to the bag wnd.append(w1) # end for all "large windows" with ModelPersistence('.'.join(img_name.split('.')[:-1]) + '_bag_l1.pkl', 'c', format='pickle') as d: d['bag_l1'] = dict([('hist_l0', bag), ('regs', wnd)]) print('...end on', img_name) return bag
def main(): p = opt.ArgumentParser(description=""" Builds a codebook based on a set of local descriptors, previously computed. """) p.add_argument('config', action='store', help='a configuration file') args = p.parse_args() cfg_file = args.config parser = SafeConfigParser() parser.read(cfg_file) if not parser.has_section('codebook'): raise ValueError('"codebook" section is mandatory') codebook_size = ast.literal_eval(parser.get('codebook', 'size')) if isinstance(codebook_size, list): # expect 3 values: if len(codebook_size) != 3: raise ValueError('Wrong codebook size specification') codebook_size = np.linspace(*codebook_size, dtype=np.int32) elif isinstance(codebook_size, int): if codebook_size <= 0: raise ValueError('Wrong codebook size specification') else: raise ValueError('Wrong codebook size specification') verbose = False if parser.has_option('codebook', 'verbose'): verbose = parser.getboolean('codebook', 'verbose') standardize = True if parser.has_option('codebook', 'standardize_features'): standardize = parser.getboolean('codebook', 'standardize_features') result_file = 'output.dat' if parser.has_option('codebook', 'result'): result_file = parser.get('codebook', 'result') # Read the various features: big_bag = {} img_names = [] # for each region, the image it belongs to all_regs = [] # all regions descriptors = [] for desc_name in [ 'gabor', 'haar', 'identity', 'stats', 'hist', 'hog', 'lbp' ]: if not parser.has_option('codebook', desc_name): continue feat_files = [] with open(parser.get('codebook', desc_name), 'r') as f: feat_files = f.readlines() if len(feat_files) == 0: raise UserWarning('No files specified for ' + desc_name + ' feature.') if verbose: print('Reading', desc_name) descriptors.append(desc_name) desc_values = [ ] # all values for this descriptor will be concatenated in a single list for f in feat_files: f = f.strip() if len(f) == 0: continue if verbose: print('\t', f) bag = read_bag(f, desc_name) desc_values.extend(bag[desc_name]) if len(big_bag) == 0: # since the image names and regions are the same (in the same order too) for all # feature types (gabor, haar,...) it makes sense to add them only once, when the "big_bag" # is still empty (for the 1st feature type read) img_names.extend([f] * len( bag[desc_name])) # for each feature, add the image name all_regs.extend(bag['regs']) if len(big_bag) == 0: # 1st time store some values: big_bag['regs'] = all_regs big_bag['fname'] = img_names big_bag[desc_name] = desc_values if verbose: print("Read", len(descriptors), "feature type(s) with a total of", len(big_bag['regs']), "regions.") print('\nBuilding codebook:') desc = [np.array(bag[dn_]) for dn_ in descriptors] # ensures a strict ordering X = np.hstack(desc) # put all feature vectors in an array Xm = np.zeros((X.shape[1], ), dtype=np.float32) Xs = np.ones((X.shape[1], ), dtype=np.float32) if standardize: # make sure each variable (column) is mean-centered and has unit standard deviation Xm = np.mean(X, axis=0) Xs = np.std(X, axis=0) Xs[np.isclose(Xs, 1e-16)] = 1.0 X = (X - Xm) / Xs if not isinstance(codebook_size, int): # try to estimate a suitable codebook size based on gap statistic: codebook_size, _ = gap(X, Ks=codebook_size, Wstar=None, B=20) if verbose: print("\tBest codebook size:", codebook_size) rng = np.random.RandomState(0) vq = MiniBatchKMeans(n_clusters=codebook_size, random_state=rng, batch_size=500, compute_labels=False, verbose=True) # vector quantizer vq.fit(X) with ModelPersistence(result_file, 'c', format='pickle') as d: d['codebook'] = vq d['shift'] = Xm d['scale'] = Xs d['standardize'] = standardize return True
def main(): p = opt.ArgumentParser(description=""" Classifies regions of an image (based on SURF) using a pre-built model (codebook). """) p.add_argument('in_file', action='store', help='image file name') p.add_argument('out_file', action='store', help='file to store the resulting classification') p.add_argument('model', action='store', help='file containing the model') p.add_argument('-a', '--annot', action='store', help='annotation file name', default=None) p.add_argument('-t', '--threshold', action='store', type=int, default=5000, help='Hessian threshold for SURF features.') p.add_argument('-x', action='store', help='image name with patches classified', default=None) p.add_argument('-v', '--verbose', action='store_true', help='verbose?') args = p.parse_args() th = args.threshold if args.verbose: print("Image:", args.in_file) img = cv2.imread(args.in_file) mask = None with ModelPersistence(args.model, 'r', format='pickle') as mp: codebook = mp['codebook'] Xm = mp['shift'] Xs = mp['scale'] standardize = mp['standardize'] avg_dist = mp['avg_dist_to_centroid'] sd_dist = mp['stddev_dist_to_centroid'] if args.annot is not None: coords = np.fromfile(args.annot, dtype=int, sep=' ') # x y - values coords = np.reshape(coords, (coords.size/2, 2), order='C') # get the bounding box: xmin, ymin = coords.min(axis=0) xmax, ymax = coords.max(axis=0) img = img[ymin:ymax+3, xmin:xmax+3, :] # keep only the region of interest if args.verbose: print("\t...building mask") mask = np.zeros(img.shape[0:2], dtype=np.uint8) r, c = skimage.draw.polygon(coords[:,1]-ymin, coords[:,0]-xmin) # adapt to new image... mask[r,c] = 1 # everything outside the region is black if args.verbose: print("\t...H&E extraction") img_h, _ = rgb2he(img, normalize=True) # get the H- component img_h = equalize_adapthist(img_h) img_h = rescale_intensity(img_h, out_range=(0,255)) # make sure the dtype is right for image and the mask: OpenCV is sensitive to data type img_h = img_h.astype(np.uint8) if mask is not None: img_h *= mask if args.verbose: print("\t...feature detection and computation") feat = cv2.xfeatures2d.SURF_create(hessianThreshold=th) keyp, desc = feat.detectAndCompute(img_h, mask) if args.verbose: print("\t...", str(len(keyp)), "features extracted") X = np.hstack(desc) X = np.reshape(X, (len(desc), desc[0].size), order='C') if standardize: # make sure each variable (column) is mean-centered and has unit standard deviation X = (X - Xm) / Xs if args.verbose: print("\t...classification") # instead of the following, allow for "no label": y0 = codebook.predict(X).tolist() y = np.zeros(X.shape[0], dtype=np.int) - 1 d = np.zeros((X.shape[0], codebook.cluster_centers_.shape[0])) for k in range(0, codebook.cluster_centers_.shape[0]): d[:, k] = np.linalg.norm(X - codebook.cluster_centers_[k, :], axis=1) for i in range(0, d.shape[0]): # find the closest centroid among those that have a distance < 3*SD j = np.where(d[i, :] < avg_dist + 3.0*sd_dist)[0] if np.any(j): y[i] = j[np.argmin(d[i, j])] # the label of the closest centroid #if np.any(y < 0): # y = y[y >= 0] if args.verbose: print("\t...of", str(X.shape[0]), "patches,", str(y.size), "where assigned a label") with open(args.out_file, mode='w') as fout: for k in range(len(y)): s = '\t'.join([str(np.round(keyp[k].pt[0])), str(np.round(keyp[k].pt[1])), str(np.round(keyp[k].size)), str(y[k]), str(y0[k])]) + '\n' fout.write(s) if args.x is not None: # construct a representation of the image based on the class labels img = adjust_gamma(img, 0.2) # dim the image for k in range(len(y)): x, y = keyp[k].pt x = int(np.round(x)) y = int(np.round(y)) r = int(np.round(keyp[k].size)) img[y-int(r/2):y+int(r/2), x-int(r/2):x+int(r/2), :] = (10, (10+2*k)%256, k%256) cv2.imwrite(args.x, img)
def main(): p = opt.ArgumentParser(description=""" Constructs a dictionary for image representation based on Gabor wavelet local descriptors. The dictionary is built from a set of images given as a list in an input file. """) p.add_argument( 'img_path', action='store', help='path to image files - all images in the folder will be used') p.add_argument( 'img_ext', action='store', help='extension of the image files (e.g. "jpg" or "png") - NO DOT!') p.add_argument('out_file', action='store', help='resulting model file name') p.add_argument('codebook_size', action='store', help='codebook size', type=int) p.add_argument('-w', '--window', action='store', help='local window size', type=int, default=16) args = p.parse_args() #--------- # data data_path = args.img_path img_ext = args.img_ext wnd_size = args.window img_files = glob.glob(data_path + '/*.' + img_ext) if len(img_files) == 0: return #--------- # Gabor tmp = np.array([0.0, np.pi / 4.0, np.pi / 2.0, 3.0 * np.pi / 4.0], dtype=np.double) tmp2 = np.array([3.0 / 4.0, 3.0 / 8.0, 3.0 / 16.0], dtype=np.double) tmp3 = np.array([1.0, 2 * np.sqrt(2.0)], dtype=np.double) local_descriptor = GaborDescriptor(theta=tmp, freq=tmp2, sigma=tmp3) ## Process: sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) # unbuferred output desc_vectors = [] # a list of local descriptor vectors res = \ Parallel(n_jobs=cpu_count()) \ ( delayed(worker)(img_files[i], local_descriptor, wnd_size, i) for i in np.arange(len(img_files)) ) desc_vectors = [r_[0] for r_ in res] idx = np.array([r_[1] for r_ in res]) wnd = np.array([r_[2] for r_ in res]) res = None print('Vector quantization:') print('-prepare...') X = np.vstack(desc_vectors) print('-cluster...') rng = np.random.RandomState(0) vq = MiniBatchKMeans(n_clusters=args.codebook_size, random_state=rng, n_init=10, batch_size=5000, compute_labels=True, verbose=False) # vector quantizer vq.fit(X) print('OK') print('Saving model...', end='') # compute the average distance and std.dev. of the points in each cluster: avg_dist = np.zeros(args.codebook_size) sd_dist = np.zeros(args.codebook_size) for k in range(0, args.codebook_size): d = numpy.linalg.norm(X[vq.labels_ == k, :] - vq.cluster_centers_[k, :], axis=1) avg_dist[k] = d.mean() sd_dist[k] = d.std() with ModelPersistence(args.out_file, 'c', format='pickle') as d: d['codebook'] = vq d['avg_dist_to_centroid'] = avg_dist d['stddev_dist_to_centroid'] = sd_dist d['window_size'] = wnd_size print('OK') return
def main(): p = opt.ArgumentParser(description=""" Computes textural and tissue descriptors from an RGB image (of an H&E slide). """) p.add_argument('img_file', action='store', help='RGB image file') p.add_argument('model_file', action='store', help='Models file') p.add_argument('--meta', action='store_true', help='store meta information associated with the results') args = p.parse_args() img_file = args.img_file model_file = args.model_file base_name = os.path.basename(img_file).split('.') if len(base_name) > 1: # at least 1 suffix .ext base_name.pop() # drop the extension base_name = '.'.join( base_name) # reassemble the rest of the list into file name img = skimage.io.imread(img_file) with ModelPersistence(model_file, 'r', format='pickle') as d: rgb_models = d['models'] desc = extract_descriptors_he(img, rgb_models) if args.meta: r = ET.Element('meta', attrib={'processor': 'wsi_extract_descriptors'}) t = ET.SubElement(r, 'file') t.text = img_file t = ET.SubElement(r, 'parameters') t1 = ET.SubElement(t, 'rgb_models') t1.text = args.model_file t = ET.SubElement(r, 'result') t1 = ET.SubElement(t, 'descriptors') t2 = ET.SubElement(t1, 'bin_prop_connective') t2.text = str(desc['bin_prop_connective']) t2 = ET.SubElement(t1, 'bin_prop_chromatin') t2.text = str(desc['bin_prop_chromatin']) t2 = ET.SubElement(t1, 'bin_prop_fat') t2.text = str(desc['bin_prop_fat']) t2 = ET.SubElement(t1, 'bin_compact_chromatin') t2.text = str(desc['bin_compact_chromatin']) t2 = ET.SubElement(t1, 'bin_compact_connective') t2.text = str(desc['bin_compact_connective']) t2 = ET.SubElement(t1, 'bin_compact_fat') t2.text = str(desc['bin_compact_fat']) t2 = ET.SubElement(t1, 'grey_gabor') t2.text = str(desc['grey_gabor']) t2 = ET.SubElement(t1, 'grey_lbp') t2.text = str(desc['grey_lbp']) t2 = ET.SubElement(t1, 'grey_glcm') t2.text = str(desc['grey_glcm']) t2 = ET.SubElement(t1, 'h_gabor') t2.text = str(desc['h_gabor']) t2 = ET.SubElement(t1, 'h_lbp') t2.text = str(desc['h_lbp']) t2 = ET.SubElement(t1, 'h_glcm') t2.text = str(desc['h_glcm']) t2 = ET.SubElement(t1, 'e_gabor') t2.text = str(desc['e_gabor']) t2 = ET.SubElement(t1, 'e_lbp') t2.text = str(desc['e_lbp']) t2 = ET.SubElement(t1, 'e_glcm') t2.text = str(desc['e_glcm']) raw_txt = ET.tostring(r, 'utf-8') reparsed = minidom.parseString(raw_txt) pp_txt = reparsed.toprettyxml(indent=' ') meta_file = open(base_name + '_desc.meta.xml', 'w') meta_file.write(pp_txt) return
def main(): p = opt.ArgumentParser(description=""" Assigns the regions of an image to the clusters of a codebook. """) p.add_argument('image', action='store', help='image file name') p.add_argument('config', action='store', help='a configuration file') p.add_argument( '-r', '--roi', action='store', nargs=4, type=int, help= 'region of interest from the image as: row_min row_max col_min col_max', default=None) args = p.parse_args() img_file = args.image cfg_file = args.config image_orig = skimage.io.imread(img_file) if image_orig.ndim == 3: im_h, _, _ = rgb2he2(image_orig) if args.roi is None: roi = (0, im_h.shape[0] - 1, 0, im_h.shape[1] - 1) else: roi = args.roi # Process configuration file: parser = SafeConfigParser() parser.read(cfg_file) if not parser.has_section('data'): raise RuntimeError('Section [data] is mandatory') wsize = (32, 32) if parser.has_option('data', 'window_size'): wsize = ast.literal_eval(parser.get('data', 'window_size')) if not parser.has_option('data', 'model'): raise RuntimeError('model file name is missing in [data] section') model_file = parser.get('data', 'model') with ModelPersistence(model_file, 'r', format='pickle') as mp: codebook = mp['codebook'] Xm = mp['shift'] Xs = mp['scale'] standardize = mp['standardize'] if parser.has_option('data', 'output'): out_file = parser.get('data', 'output') else: out_file = 'output.dat' descriptors = read_local_descriptors_cfg(parser) # For the moment, it is assumed tha only one type of local descriptors is # used - no composite feature vectors. This will change in the future but, # for the moment only the first type of descriptor in "descriptors" list # is used, and the codebook is assumed to be constructed using the same. desc = descriptors[0] print(img_file) print(wsize) print(roi[0], roi[1], roi[2], roi[3]) w_offset = (0, 0) if isinstance(desc, HaarLikeDescriptor): # this one works on integral images image = intg_image(im_h) # the sliding window should also be increased by 1: w_offset = (1, 1) wsize = (wsize[0] + w_offset[0], wsize[1] + w_offset[1]) else: image = im_h itw = sliding_window_on_regions(image.shape, [tuple(roi)], wsize, step=wsize) wnd = [] labels = [] buff_size = 10000 # every <buff_size> patches we do a classification X = np.zeros((buff_size, codebook.cluster_centers_[0].shape[0])) k = 0 if standardize: # placed here, to avoid testing inside the loop for r in itw: # adjust if needed: r2 = (r[0], r[1] - w_offset[1], r[2], r[3] - w_offset[0]) wnd.append(r2) X[k, :] = desc.compute(image[r[0]:r[1], r[2]:r[3]]) k += 1 if k == buff_size: X = (X - Xm) / Xs labels.extend(codebook.predict(X).tolist()) k = 0 # reset the block else: for r in itw: # adjust if needed: r2 = (r[0], r[1] - w_offset[1], r[2], r[3] - w_offset[0]) wnd.append(r2) X[k, :] = desc.compute(image[r[0]:r[1], r[2]:r[3]]) k += 1 if k == buff_size: labels.extend(codebook.predict(X).tolist()) k = 0 # reset the block if k != 0: # it means some data is accumulated in X but not yet classified if standardize: X[0:k + 1, ] = (X[0:k + 1, ] - Xm) / Xs labels.extend(codebook.predict(X[0:k + 1, ]).tolist()) with open(out_file, 'w') as f: n = len(wnd) # total number of descriptors of this type for k in range(n): s = '\t'.join([str(x_) for x_ in wnd[k]]) + '\t' + str(labels[k]) + '\n' f.write(s)
def main(): p = opt.ArgumentParser(description=""" Constructs a dictionary for image representation based on histograms of codeblocks (Gabor wavelet local descriptors) over larger neighborhoods. The dictionary is built from a set of images given as a list in an input file. """) p.add_argument( 'img_path', action='store', help='path to image files - all images in the folder will be used') p.add_argument( 'img_ext', action='store', help='extension of the image files (e.g. "jpg" or "png") - NO DOT!') p.add_argument('l0_model', action='store', help='level-0 codebook model file') p.add_argument('out_file', action='store', help='resulting model file name') p.add_argument('codebook_size', action='store', help='codebook size', type=int) p.add_argument('-w', '--window', action='store', help='local window size (default: 512)', type=int, default=512) args = p.parse_args() #--------- # data data_path = args.img_path img_ext = args.img_ext wnd_size = args.window with ModelPersistence(args.l0_model, 'r', format='pickle') as mp: l0_model = mp img_files = glob.glob(data_path + '/*.' + img_ext) if len(img_files) == 0: return #--------- # Gabor tmp = np.array([0.0, np.pi / 4.0, np.pi / 2.0, 3.0 * np.pi / 4.0], dtype=np.double) tmp2 = np.array([3.0 / 4.0, 3.0 / 8.0, 3.0 / 16.0], dtype=np.double) tmp3 = np.array([1.0, 2 * np.sqrt(2.0)], dtype=np.double) local_descriptor = GaborDescriptor(theta=tmp, freq=tmp2, sigma=tmp3) ## Process: sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0) # unbuferred output desc_vectors = [] # a list of local descriptor vectors print('Computing level 0 coding...') desc_vectors = \ Parallel(n_jobs=cpu_count()) \ ( delayed(worker)(img_name, local_descriptor, wnd_size, l0_model) for img_name in img_files ) print('OK') print('Vector quantization:') print('-prepare...') X = np.vstack(desc_vectors) # each row is a histogram np.save('X_bag_level1.dat', X) print('-compute pairwise distances...') n = X.shape[0] pdist = Parallel(n_jobs=cpu_count())( delayed(worker_chisq_M)(X[i, :], X[i + 1:n, :]) for i in np.arange(0, n - 1)) # make the list flat: pdist = np.array(list(itertools.chain.from_iterable(pdist))) #for i in np.arange(0, X.shape[0]-1): # for j in np.arange(i+1, X.shape[0]): # pdist.append(dist.chisq(X[i,:], X[j,:])) pdist = np.array(pdist) np.save('X_pdist_level1.data.npy', pdist) print('-cluster (k-medoids)...') meds = kmedoids(pdist, nclusters=args.codebook_size, npass=20) labels = np.unique( meds[0] ) # also the indexes of vectors from X that became cluster centers (medoids) vq = {} vq['cluster_centers_'] = X[labels, :] vq['labels_'] = labels vq['distance'] = 'chisq' print('OK') print('Saving model...', end='') # compute the average distance and std.dev. of the points in each cluster: avg_dist = np.zeros(args.codebook_size) sd_dist = np.zeros(args.codebook_size) for k in range(0, args.codebook_size): idx = np.where(meds[0] == labels[k])[0] d = [] for i in idx: d.append(dist.chisq(X[i, :], vq['cluster_centers_'][k, :])) avg_dist[k] = np.array(d).mean() sd_dist[k] = np.array(d).std() print('K-medoids summary:') print('-avg. dist: ', avg_dist) print('-std. dev. dist: ', sd_dist) with ModelPersistence(args.out_file, 'c', format='pickle') as d: d['codebook'] = vq d['avg_dist_to_centroid'] = avg_dist d['stddev_dist_to_centroid'] = sd_dist print('OK') return
def main(): p = opt.ArgumentParser(description=""" Extracts features from annotated regions and constructs a codebook of a given size. """) p.add_argument( 'in_file', action='store', help='a file with image file, annotation file and label (0/1)') p.add_argument('out_file', action='store', help='resulting model file name') #p.add_argument('codebook_size', action='store', help='codebook size', type=int) p.add_argument('-t', '--threshold', action='store', type=int, default=5000, help='Hessian threshold for SURF features.') p.add_argument( '-s', '--standardize', action='store_true', default=False, help='should the features be standardized before codebook construction?' ) p.add_argument('-v', '--verbose', action='store_true', help='verbose?') args = p.parse_args() th = args.threshold all_image_names, all_descriptors = [], [] all_roi = [] y = [] unique_image_names = [] with open(args.in_file, mode='r') as fin: for l in fin.readlines(): l = l.strip() if len(l) == 0: break img_file, annot_file, lbl = [ z_ for z_ in l.split() ][0:3] # file names: image and its annotation and label y.append(int(lbl)) if args.verbose: print("Image:", img_file) img = cv2.imread(img_file) coords = np.fromfile(annot_file, dtype=int, sep=' ') # x y - values coords = np.reshape(coords, (coords.size / 2, 2), order='C') # get the bounding box: xmin, ymin = coords.min(axis=0) xmax, ymax = coords.max(axis=0) if args.verbose: print("\t...H&E extraction") img = img[ymin:ymax + 2, xmin:xmax + 2, :] # keep only the region of interest img_h, _ = rgb2he(img, normalize=True) # get the H- component img_h = equalize_adapthist(img_h) img_h = rescale_intensity(img_h, out_range=(0, 255)) # make sure the dtype is right for image and the mask: OpenCV is sensitive to data type img_h = img_h.astype(np.uint8) if args.verbose: print("\t...building mask") mask = np.zeros(img_h.shape, dtype=np.uint8) r, c = skimage.draw.polygon(coords[:, 1] - ymin, coords[:, 0] - xmin) # adapt to new image... mask[r, c] = 1 # everything outside the region is black if args.verbose: print("\t...feature detection and computation") img_h *= mask feat = cv2.xfeatures2d.SURF_create(hessianThreshold=th) keyp, desc = feat.detectAndCompute(img_h, mask) if args.verbose: print("\t...", str(len(keyp)), "features extracted") all_descriptors.extend(desc) all_image_names.extend([img_file] * len(keyp)) unique_image_names.append(img_file) # end for X = np.hstack(all_descriptors) X = np.reshape(X, (len(all_descriptors), all_descriptors[0].size), order='C') if args.standardize: # make sure each variable (column) is mean-centered and has unit standard deviation Xm = np.mean(X, axis=0) Xs = np.std(X, axis=0) Xs[np.isclose(Xs, 1e-16)] = 1.0 X = (X - Xm) / Xs y = np.array(y, dtype=int) rng = np.random.RandomState(0) acc = [] # will keep accuracy of the classifier vqs = [] # all quantizers, to find the best for k in np.arange(10, 121, 10): # Method: # -generate a codebook with k codewords # -re-code the data # -compute frequencies # -estimate classification on best 10 features if args.verbose: print("\nK-means clustering (k =", str(k), ")") print("\t...with", str(X.shape[0]), "points") #-codebook and re-coding vq = MiniBatchKMeans(n_clusters=k, random_state=rng, batch_size=500, compute_labels=True, verbose=False) # vector quantizer vq.fit(X) vqs.append(vq) #-codeword frequencies frq = np.zeros((len(unique_image_names), k)) for i in range(vq.labels_.size): frq[unique_image_names.index(all_image_names[i]), vq.labels_[i]] += 1.0 for i in range(len(unique_image_names)): if frq[i, :].sum() > 0: frq[i, :] /= frq[i, :].sum() if args.verbose: print("...\tfeature selection (t-test)") pv = np.ones(k) for i in range(k): _, pv[i] = ttest_ind(frq[y == 0, i], frq[y == 1, i]) idx = np.argsort(pv) # order of the p-values if args.verbose: print("\t...classification performance estimation") clsf = LDA(solver='lsqr', shrinkage='auto').fit(frq[:, idx[:10]], y) # keep top 10 features acc.append(clsf.score(frq[:, idx[:10]], y)) acc = np.array(acc) k = np.arange(10, 121, 10)[acc.argmax()] # best k if args.verbose: print("\nOptimal codebook size:", str(k)) # final codebook: vq = vqs[acc.argmax()] # compute the average distance and std.dev. of the points in each cluster: avg_dist = np.zeros(k) sd_dist = np.zeros(k) for k in range(0, k): d = numpy.linalg.norm(X[vq.labels_ == k, :] - vq.cluster_centers_[k, :], axis=1) avg_dist[k] = d.mean() sd_dist[k] = d.std() with ModelPersistence(args.out_file, 'c', format='pickle') as d: d['codebook'] = vq d['shift'] = Xm d['scale'] = Xs d['standardize'] = args.standardize d['avg_dist_to_centroid'] = avg_dist d['stddev_dist_to_centroid'] = sd_dist return True
def main(): p = opt.ArgumentParser(description=""" Extracts features from annotated regions and constructs a codebook of a given size. """) p.add_argument('in_file', action='store', help='a file with pairs of image and annotation files') p.add_argument('out_file', action='store', help='resulting model file name') p.add_argument('codebook_size', action='store', help='codebook size', type=int) p.add_argument('-t', '--threshold', action='store', type=int, default=5000, help='Hessian threshold for SURF features.') p.add_argument( '-s', '--standardize', action='store_true', default=False, help='should the features be standardized before codebook construction?' ) p.add_argument('-x', action='store_true', help='save the image patches closes to the code blocks?') p.add_argument('-v', '--verbose', action='store_true', help='verbose?') args = p.parse_args() th = args.threshold all_key_points, all_descriptors, all_image_names = [], [], [] all_roi = [] with open(args.in_file, mode='r') as fin: for l in fin.readlines(): l = l.strip() if len(l) == 0: break img_file, annot_file = [ z_ for z_ in l.split() ][0:2] # file names: image and its annotation if args.verbose: print("Image:", img_file) img = cv2.imread(img_file) coords = np.fromfile(annot_file, dtype=int, sep=' ') # x y - values coords = np.reshape(coords, (coords.size / 2, 2), order='C') # get the bounding box: xmin, ymin = coords.min(axis=0) xmax, ymax = coords.max(axis=0) if args.verbose: print("\t...H&E extraction") img = img[ymin:ymax + 2, xmin:xmax + 2, :] # keep only the region of interest img_h, _ = rgb2he(img, normalize=True) # get the H- component img_h = equalize_adapthist(img_h) img_h = rescale_intensity(img_h, out_range=(0, 255)) # make sure the dtype is right for image and the mask: OpenCV is sensitive to data type img_h = img_h.astype(np.uint8) if args.verbose: print("\t...building mask") mask = np.zeros(img_h.shape, dtype=np.uint8) r, c = skimage.draw.polygon(coords[:, 1] - ymin, coords[:, 0] - xmin) # adapt to new image... mask[r, c] = 1 # everything outside the region is black if args.verbose: print("\t...feature detection and computation") img_h *= mask feat = cv2.xfeatures2d.SURF_create(hessianThreshold=th) keyp, desc = feat.detectAndCompute(img_h, mask) if args.verbose: print("\t...", str(len(keyp)), "features extracted") all_descriptors.extend(desc) if args.x: # only needed if saving patches: all_key_points.extend(keyp) all_image_names.extend([img_file] * len(keyp)) all_roi.extend([(xmin, xmax, ymin, ymax)] * len(keyp)) # end for if args.verbose: print("\nK-means clustering") X = np.hstack(all_descriptors) X = np.reshape(X, (len(all_descriptors), all_descriptors[0].size), order='C') if args.standardize: # make sure each variable (column) is mean-centered and has unit standard deviation Xm = np.mean(X, axis=0) Xs = np.std(X, axis=0) Xs[np.isclose(Xs, 1e-16)] = 1.0 X = (X - Xm) / Xs if args.verbose: print("\t...with", str(X.shape[0]), "points") rng = np.random.RandomState(0) vq = MiniBatchKMeans(n_clusters=args.codebook_size, random_state=rng, batch_size=500, compute_labels=True, verbose=False) # vector quantizer vq.fit(X) # compute the average distance and std.dev. of the points in each cluster: avg_dist = np.zeros(args.codebook_size) sd_dist = np.zeros(args.codebook_size) for k in range(0, args.codebook_size): d = numpy.linalg.norm(X[vq.labels_ == k, :] - vq.cluster_centers_[k, :], axis=1) avg_dist[k] = d.mean() sd_dist[k] = d.std() with ModelPersistence(args.out_file, 'c', format='pickle') as d: d['codebook'] = vq d['shift'] = Xm d['scale'] = Xs d['standardize'] = args.standardize d['avg_dist_to_centroid'] = avg_dist d['stddev_dist_to_centroid'] = sd_dist if args.x: # find the closest patches to each centroid: idx = np.zeros(args.codebook_size, dtype=np.int) d = np.zeros(X.shape[0]) for k in range(0, args.codebook_size): for i in range(0, X.shape[0]): d[i] = numpy.linalg.norm(X[i, :] - vq.cluster_centers_[k, :]) idx[k] = d.argmin( ) # the index of the closest patch to k-th centroid for k in range(0, args.codebook_size): i = idx[k] x, y = all_key_points[i].pt x = int(np.round(x)) y = int(np.round(y)) r = all_key_points[i].size # diameter of the region img = cv2.imread(all_image_names[i]) print("Image:", all_image_names[i], "\tPatch (row_min->max, col_min->max):", str(y + all_roi[i][2] - int(r / 2)), str(y + all_roi[i][2] + int(r / 2)), str(x + all_roi[i][0] - int(r / 2)), str(x + all_roi[i][0] + int(r / 2))) patch = img[y + all_roi[i][2] - int(r / 2):y + all_roi[i][2] + int(r / 2), x + all_roi[i][0] - int(r / 2):x + all_roi[i][0] + int(r / 2), :] cv2.imwrite('codeblock_' + str(k) + '.png', patch) return True