classify.py 4.07 KB
# -*- coding: utf-8 -*-
"""
Created on Sun Jul 23 16:04:33 2017

@author: david
"""

import numpy
import colorsys
import sklearn
import sklearn.metrics
from scipy import misc
from envi import envi

#generate a 2D color class map using a stack of binary class images
def classcolor2(C):
    
    #determine the number of classes
    nc = C.shape[-1]

    #generate an RGB image
    RGB = numpy.zeros((C.shape[0], C.shape[1], 3), dtype=numpy.ubyte)
    
    #for each class
    for c in range(0, nc):
        hsv = (c * 1.0 / nc, 1, 1)
        color = numpy.asarray(colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2])) * 255
        RGB[C[:, :, c], :] = color
    
    return RGB

#create a function that loads a set of class images as a stack of binary masks
#input: list of class image names
#output: X x Y x C stack of binary mask images
#example: image2class(("class_coll.bmp", "class_epith.bmp"))
def image2class(*masks):
    #get num of mask file names
    num_masks = len(masks)

    if num_masks == 0:
        print("ERROR: mask filenames not provided")
        print("Usage example: image2class(('class_coll.bmp', 'class_epith.bmp'))")
        return

    #load first mask to get dimensions -- assuming all masks have same dimensions
    mask = misc.imread(masks[0], flatten=True).astype(numpy.bool)

    mask_stack = numpy.zeros((mask.shape[0], mask.shape[1], num_masks), dtype=numpy.bool)
    mask_stack[:,:,0] = mask

    #load the rest of masks
    for i in range(1, num_masks):
        mask_stack[:,:,i] = misc.imread(masks[i], flatten=True).astype(numpy.bool)

    return mask_stack


#create a set of feature/target pairs for classification
#input: envi file object, stack of class masks, list of class names
#output: feature matrix (features x pixels), target matrix (1 x pixels)
#example: generate_training(("class_coll.bmp", "class_epith.bmp"), (1, 2))
def generate_training(filename, mask_stack):
    #create envi file object
    E = envi(filename)

    # get number of classes
    C = mask_stack.shape[2]

    #get number of annotated pixels
    num_pixels = 0
    for i in range(0,C):
        num_pixels += numpy.count_nonzero(mask_stack[:,:,i])

    feature_matrix = numpy.zeros((E.header.bands, num_pixels), dtype=E.header.data_type)
    target_matrix = numpy.zeros((1, num_pixels))

    #load masks and append the corresponding pixels to feature matrix and labels to target_matrix
    idx = 0
    for i in range(0,C):
        mask = E.loadmask(mask_stack[:,:,i])
        feature_matrix[:, idx:idx + mask.shape[1]] = mask
        target_matrix[idx:idx+mask.shape[1]] = (i+1)
        idx += mask.shape[1]

    return feature_matrix, target_matrix

#create a class mask stack from an X x Y x C probability image
#input: X x Y x C image giving the probability P(c |x,y)
#output: X x Y x C binary class image
def prob2class(prob_image):
    class_image = numpy.zeros_like(prob_image)
    #get nonzero indices
    nnz_idx = numpy.transpose(numpy.nonzero(numpy.sum(prob_image, axis=0)))
    
    #set pixel corresponding to max probability to 1
    for idx in nnz_idx:
        idx_max_prob = numpy.argmax(prob_image[:, idx[0], idx[1]])
        class_image[idx_max_prob, idx[0], idx[1]] = 1

    return class_image
#calculate an ROC curve given a probability image and mask of "True" values
def image2roc(P, t_vals, mask=[]):
    
    if not P.shape == t_vals.shape:
        print("ERROR: the probability and mask images must be the same shape")
        return
    
    #if a mask image isn't provided, create one for the entire image
    if mask == []:
        mask = numpy.ones(t_vals.shape, dtype=numpy.bool)
    
    #create masks for the positive and negative probability scores
    mask_p = t_vals
    mask_n = mask - mask * t_vals
    
    #calculate the indices for the positive and negative scores
    idx_p = numpy.nonzero(mask_p)
    idx_n = numpy.nonzero(mask_n)
    
    Pp = P[idx_p]
    Pn = P[idx_n]

    Lp = numpy.ones((Pp.shape), dtype=numpy.bool)
    Ln = numpy.zeros((Pn.shape), dtype=numpy.bool)
    
    scores = numpy.concatenate((Pp, Pn))
    labels = numpy.concatenate((Lp, Ln))
    
    return sklearn.metrics.roc_curve(labels, scores)