示例#1
0
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
示例#2
0
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']
示例#3
0
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]
示例#4
0
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
示例#7
0
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
示例#8
0
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)
示例#9
0
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
示例#10
0
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
示例#11
0
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
示例#13
0
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
示例#14
0
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