Commit f275c01cd45055b03de7194d7a4eae38b24938ee
1 parent
fad1a72c
finalized the preliminary functions for classification
Showing
2 changed files
with
63 additions
and
49 deletions
Show diff stats
python/classify.py
@@ -9,31 +9,37 @@ import numpy | @@ -9,31 +9,37 @@ import numpy | ||
9 | import colorsys | 9 | import colorsys |
10 | import sklearn | 10 | import sklearn |
11 | import sklearn.metrics | 11 | import sklearn.metrics |
12 | -from scipy import misc | ||
13 | -from envi import envi | 12 | +import scipy |
13 | +import scipy.misc | ||
14 | +import envi | ||
14 | 15 | ||
15 | #generate a 2D color class map using a stack of binary class images | 16 | #generate a 2D color class map using a stack of binary class images |
17 | +#input: C is a C x Y x X binary image | ||
18 | +#output: an RGB color image with a unique color for each class | ||
16 | def classcolor2(C): | 19 | def classcolor2(C): |
17 | 20 | ||
18 | #determine the number of classes | 21 | #determine the number of classes |
19 | - nc = C.shape[-1] | 22 | + nc = C.shape[0] |
23 | + | ||
24 | + s = C.shape[1:] | ||
25 | + s = numpy.append(s, 3) | ||
20 | 26 | ||
21 | #generate an RGB image | 27 | #generate an RGB image |
22 | - RGB = numpy.zeros((C.shape[0], C.shape[1], 3), dtype=numpy.ubyte) | 28 | + RGB = numpy.zeros(s, dtype=numpy.ubyte) |
23 | 29 | ||
24 | #for each class | 30 | #for each class |
25 | for c in range(0, nc): | 31 | for c in range(0, nc): |
26 | hsv = (c * 1.0 / nc, 1, 1) | 32 | hsv = (c * 1.0 / nc, 1, 1) |
27 | color = numpy.asarray(colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2])) * 255 | 33 | color = numpy.asarray(colorsys.hsv_to_rgb(hsv[0], hsv[1], hsv[2])) * 255 |
28 | - RGB[C[:, :, c], :] = color | 34 | + RGB[C[c, ...], :] = color |
29 | 35 | ||
30 | return RGB | 36 | return RGB |
31 | 37 | ||
32 | #create a function that loads a set of class images as a stack of binary masks | 38 | #create a function that loads a set of class images as a stack of binary masks |
33 | #input: list of class image names | 39 | #input: list of class image names |
34 | -#output: X x Y x C stack of binary mask images | 40 | +#output: C x Y x X binary image specifying class/pixel membership |
35 | #example: image2class(("class_coll.bmp", "class_epith.bmp")) | 41 | #example: image2class(("class_coll.bmp", "class_epith.bmp")) |
36 | -def image2class(*masks): | 42 | +def image2class(masks): |
37 | #get num of mask file names | 43 | #get num of mask file names |
38 | num_masks = len(masks) | 44 | num_masks = len(masks) |
39 | 45 | ||
@@ -42,53 +48,26 @@ def image2class(*masks): | @@ -42,53 +48,26 @@ def image2class(*masks): | ||
42 | print("Usage example: image2class(('class_coll.bmp', 'class_epith.bmp'))") | 48 | print("Usage example: image2class(('class_coll.bmp', 'class_epith.bmp'))") |
43 | return | 49 | return |
44 | 50 | ||
45 | - #load first mask to get dimensions -- assuming all masks have same dimensions | ||
46 | - mask = misc.imread(masks[0], flatten=True).astype(numpy.bool) | ||
47 | - | ||
48 | - mask_stack = numpy.zeros((mask.shape[0], mask.shape[1], num_masks), dtype=numpy.bool) | ||
49 | - mask_stack[:,:,0] = mask | ||
50 | - | ||
51 | - #load the rest of masks | ||
52 | - for i in range(1, num_masks): | ||
53 | - mask_stack[:,:,i] = misc.imread(masks[i], flatten=True).astype(numpy.bool) | ||
54 | - | ||
55 | - return mask_stack | ||
56 | - | 51 | + classimages = [] |
52 | + for m in masks: | ||
53 | + img = scipy.misc.imread(m, flatten=True).astype(numpy.bool) | ||
54 | + classimages.append(img) | ||
57 | 55 | ||
58 | -#create a set of feature/target pairs for classification | ||
59 | -#input: envi file object, stack of class masks, list of class names | ||
60 | -#output: feature matrix (features x pixels), target matrix (1 x pixels) | ||
61 | -#example: generate_training(("class_coll.bmp", "class_epith.bmp"), (1, 2)) | ||
62 | -def generate_training(filename, mask_stack): | ||
63 | - #create envi file object | ||
64 | - E = envi(filename) | 56 | + result = numpy.stack(classimages) |
57 | + sum_images = numpy.sum(result.astype(numpy.uint32), 0) | ||
65 | 58 | ||
66 | - # get number of classes | ||
67 | - C = mask_stack.shape[2] | 59 | + #identify and remove redundant pixels |
60 | + bad_idx = sum_images > 1 | ||
61 | + result[:, bad_idx] = 0 | ||
68 | 62 | ||
69 | - #get number of annotated pixels | ||
70 | - num_pixels = 0 | ||
71 | - for i in range(0,C): | ||
72 | - num_pixels += numpy.count_nonzero(mask_stack[:,:,i]) | 63 | + return result |
73 | 64 | ||
74 | - feature_matrix = numpy.zeros((E.header.bands, num_pixels), dtype=E.header.data_type) | ||
75 | - target_matrix = numpy.zeros((1, num_pixels)) | ||
76 | 65 | ||
77 | - #load masks and append the corresponding pixels to feature matrix and labels to target_matrix | ||
78 | - idx = 0 | ||
79 | - for i in range(0,C): | ||
80 | - mask = E.loadmask(mask_stack[:,:,i]) | ||
81 | - feature_matrix[:, idx:idx + mask.shape[1]] = mask | ||
82 | - target_matrix[idx:idx+mask.shape[1]] = (i+1) | ||
83 | - idx += mask.shape[1] | ||
84 | - | ||
85 | - return feature_matrix, target_matrix | ||
86 | - | ||
87 | -#create a class mask stack from an X x Y x C probability image | ||
88 | -#input: X x Y x C image giving the probability P(c |x,y) | ||
89 | -#output: X x Y x C binary class image | 66 | +#create a class mask stack from an C x Y x X probability image |
67 | +#input: C x Y x X image giving the probability P(c |x,y) | ||
68 | +#output: C x Y x X binary class image | ||
90 | def prob2class(prob_image): | 69 | def prob2class(prob_image): |
91 | - class_image = numpy.zeros_like(prob_image) | 70 | + class_image = numpy.zeros(prob_image.shape, dtype=numpy.bool) |
92 | #get nonzero indices | 71 | #get nonzero indices |
93 | nnz_idx = numpy.transpose(numpy.nonzero(numpy.sum(prob_image, axis=0))) | 72 | nnz_idx = numpy.transpose(numpy.nonzero(numpy.sum(prob_image, axis=0))) |
94 | 73 | ||
@@ -98,8 +77,20 @@ def prob2class(prob_image): | @@ -98,8 +77,20 @@ def prob2class(prob_image): | ||
98 | class_image[idx_max_prob, idx[0], idx[1]] = 1 | 77 | class_image[idx_max_prob, idx[0], idx[1]] = 1 |
99 | 78 | ||
100 | return class_image | 79 | return class_image |
80 | + | ||
101 | #calculate an ROC curve given a probability image and mask of "True" values | 81 | #calculate an ROC curve given a probability image and mask of "True" values |
102 | -def image2roc(P, t_vals, mask=[]): | 82 | +#input: |
83 | +# P is a Y x X probability image specifying P(c | x,y) | ||
84 | +# t_vals is a Y x X binary image specifying points where x,y = c | ||
85 | +# mask is a mask specifying all pixels to be considered (positives and negatives) | ||
86 | +# use this mask to limit analysis to regions of the image that have been classified | ||
87 | +#output: fpr, tpr, thresholds | ||
88 | +# fpr is the false-positive rate (x-axis of an ROC curve) | ||
89 | +# tpr is the true-positive rate (y-axis of an ROC curve) | ||
90 | +# thresholds stores the threshold associated with each point on the ROC curve | ||
91 | +# | ||
92 | +#note: the AUC can be calculated as auc = sklearn.metrics.auc(fpr, tpr) | ||
93 | +def prob2roc(P, t_vals, mask=[]): | ||
103 | 94 | ||
104 | if not P.shape == t_vals.shape: | 95 | if not P.shape == t_vals.shape: |
105 | print("ERROR: the probability and mask images must be the same shape") | 96 | print("ERROR: the probability and mask images must be the same shape") |
@@ -127,3 +118,7 @@ def image2roc(P, t_vals, mask=[]): | @@ -127,3 +118,7 @@ def image2roc(P, t_vals, mask=[]): | ||
127 | labels = numpy.concatenate((Lp, Ln)) | 118 | labels = numpy.concatenate((Lp, Ln)) |
128 | 119 | ||
129 | return sklearn.metrics.roc_curve(labels, scores) | 120 | return sklearn.metrics.roc_curve(labels, scores) |
121 | + | ||
122 | +#Function to convert a set of class labels to a matrix of neuron responses for an ANN | ||
123 | + | ||
124 | +#Function CNN extraction function | ||
130 | \ No newline at end of file | 125 | \ No newline at end of file |
python/envi.py
@@ -278,6 +278,25 @@ class envi: | @@ -278,6 +278,25 @@ class envi: | ||
278 | 278 | ||
279 | return band | 279 | return band |
280 | 280 | ||
281 | + #create a set of feature/target pairs for classification | ||
282 | + #input: envi file object, stack of class masks C x Y x X | ||
283 | + #output: feature matrix (features x pixels), target matrix (1 x pixels) | ||
284 | + #example: generate_training(("class_coll.bmp", "class_epith.bmp"), (1, 2)) | ||
285 | + def loadtrain(self, classimages): | ||
286 | + | ||
287 | + # get number of classes | ||
288 | + C = classimages.shape[0] | ||
289 | + | ||
290 | + F = [] | ||
291 | + T = [] | ||
292 | + for c in range(0, C): | ||
293 | + f = self.loadmask(classimages[c, :, :]) #load the feature matrix for class c | ||
294 | + t = numpy.ones((f.shape[1])) * (c+1) #generate a target array | ||
295 | + F.append(f) | ||
296 | + T.append(t) | ||
297 | + | ||
298 | + return numpy.concatenate(F, 1).transpose(), numpy.concatenate(T) | ||
299 | + | ||
281 | 300 | ||
282 | def __del__(self): | 301 | def __del__(self): |
283 | self.file.close() | 302 | self.file.close() |
284 | \ No newline at end of file | 303 | \ No newline at end of file |