-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathfeatures.py
executable file
·220 lines (179 loc) · 7.69 KB
/
features.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/usr/bin/env python3
import numpy as np
from colorsys import *
import matplotlib as mpl
from scipy.stats import skew
from sklearn import preprocessing
from skimage import filters, color
from PIL import Image, ImageStat, ImageFilter
from skimage.feature import greycomatrix, greycoprops
def colorAvg(im, mask):
"""Takes in a string containing an image file name, returns the average red, blue, and green
values for all the pixels in that image."""
imStats = ImageStat.Stat(im, mask)
(redAv, greenAv, blueAv) = imStats.mean
return redAv, greenAv, blueAv
def colorVariance(im, mask):
'''Calculates the diversity in color using a hue histogram'''
# load image pixels
pix = im.load()
width, height = im.size
mask_arr = np.asarray(mask)
# create empty histogram to be filled with frequencies
histogram = [0]*360
pixelHue = 0
for i in range(width):
for j in range(height):
if mask_arr[j,i]:
(r,g,b) = pix[i,j] #pull out the current r,g,b values
(h,s,v) = rgb_to_hsv(r/255.,g/255.,b/255.)
pixelHue = int(360*h)
#build histogram
histogram[pixelHue] += 1
#print histogram
# calculate standard deviation of histogram
return np.std(histogram)
def countEdgePixels(im, mask):
''' counts the number of pixels that make up the edges of features'''
# define threshold for edges
threshold = 150
# open image and filter
im2 = im.filter(ImageFilter.FIND_EDGES)
im2 = im2.convert("L")
mask_arr = np.asarray(mask)
# load pixels and count edge pixels
pix = im2.load()
pixels = 0
for x in range(0,im.size[0]):
for y in range(0, im.size[1]):
if pix[x,y] > threshold and mask_arr[y,x]:
pixels += 1
return float(pixels) / (im.size[0]*im.size[1])
def textureAnalysis(im, mask):
''' determines the proportion of the image that has texture'''
# define texture threshold and grid size
threshold = 100
n = 7
# open image
width, height = im.size
# loop across image
count = 0
pixList = []
for i in range(0,width-n,n):
for j in range(0,height-n,n):
# divide into small grids
box = (i,j,i+n,j+n)
imTemp = im.crop(box)
# calculate intensity from RGB data
pixels = list(imTemp.getdata())
intensity = [pixels[i][0]+pixels[i][1]+pixels[i][2] for i in range(len(pixels))]
# count as high texture if difference in intensity is
# greater than threshold
if ((max(intensity) - min(intensity)) > threshold):
count += 1
pixList += [(i,j)]
# calculate the percentage of high texture grids
if width/n == 0: #if width is less than n something is wrong! Check the width and make sure n is a reasonable value.
print(width)
raw_input('Oops')
return float(count)/((width/n)*(height/n))
def yellowFast(im, mask):
"""counts the number of a given color pixels in the given image."""
# im = Image.open(imageName)
#define HSV value ranges for yellow
#for now just base of Hue - refine for actual yellows seen in field?
minHue = 20/360.
maxHue = 150/360.
minSat = 5/360.
# maxSat = 0.4
minV = 190/360.
width, height = im.size #find the size of the image
count = 0 #initialize a counter for yellow pixels.
rgbList = list(im.getdata())
maskList = list(mask.getdata())
hsvList = map(hsv, rgbList)
for i, (h,s,v) in zip(maskList, hsvList):
if i and minHue <h and h<maxHue and minSat<s and minV<v:
count += 1
totalPix = width*height
portion = float(count)/totalPix
return portion
def hsv(colors):
r,g,b=colors
return rgb_to_hsv(r/255., g/255., b/255.)
def glcm(im, mask=None, offset=None, features=['contrast', 'dissimilarity', 'homogeneity', 'energy', 'correlation', 'ASM']):
"""Calculate the grey level co-occurrence matrices and output values for
contrast, dissimilarity, homogeneity, energy, correlation, and ASM in a list"""
newIm = im
if not isinstance(im, np.ndarray):
newIm = np.array(im.convert('L')) #Convert to a grey scale image
# TODO: check if you want the offset to be a list or just a single value
if offset is None:
# we assume the pixel of interest is in the center of the rectangular image
# and calculate the offset as half of the radius (or 1/4th of the diameter)
offset = [int(s/4) for s in newIm.shape[::-1]] * 2
elif type(offset) == int:
offset = [offset] * 4
# calculate the glcm and then average over all 4 angles
# glcm = np.mean(greycomatrix(newIm, offset, [0, np.pi/4, np.pi/2, 3*np.pi/4])[mask], axis=(-1, -2), keepdims=True)
glcm = np.mean(greycomatrix(newIm, offset, [0, np.pi/4, np.pi/2, 3*np.pi/4]), axis=(-1, -2), keepdims=True)
returns = []
for feature in features:
#Compute all of the grey co occurrence features.
metric = greycoprops(glcm, feature)[0][0]
if np.isnan(metric):
metric = 0
returns.append([metric])
return np.concatenate(tuple(returns), 0) #concatenate into one list along axis 0 and return
def colorMoment(im, mask):
"""Calculates the 2nd and 3rd color moments of the input image and returns values in a list."""
#The first color moment is the mean. This is already considered as a metric for
#the red, green, and blue channels, so this is not included here.
#Only the 2nd and 3rd moments will be calculated here.
newIm = mpl.colors.rgb_to_hsv(im) #convert to HSV space
#Pull out each channel from the image to analyze seperately.
HChannel = newIm[:,:,0]
SChannel = newIm[:,:,1]
VChannel = newIm[:,:,2]
#2nd moment = standard deviation.
Hstd = np.std(HChannel)
Sstd = np.std(SChannel)
Vstd = np.std(VChannel)
#3rd Moment = "Skewness". Calculate the skew, which gives an array.
#Then take the mean of that array to get a single value for each channel.
Hskew = np.mean(skew(HChannel))
Sskew = np.mean(skew(SChannel))
Vskew = np.mean(skew(VChannel))
return [Hstd, Sstd, Vstd, Hskew, Sskew, Vskew] #return all of the metrics.
def sameness(im, segments, center=None, radius=None):
"""
how many of the segments near center are of the same species as it?
segments can be a mask where pixels are labeled by their class
or segments can be a list of 2-element tuples: (segments, class_label)
where "segments" is a list of types: imantics.Polygon.points
"""
h, w = im.shape[:1]
# first, get a circular mask around the center
if center is None: # use the middle of the image
center = (int(w/2), int(h/2))
if radius is None: # use the smallest distance between the center and image walls
radius = min(center[0], center[1], w-center[0], h-center[1])
Y, X = np.ogrid[:h, :w]
dist_from_center = np.sqrt((X - center[0])**2 + (Y-center[1])**2)
mask = dist_from_center <= radius
new_segments = {}
if type(segments) is list:
# now, figure out which segments are in the mask
for i in range(len(segments)):
# get the centroid of each segment
centroid = tuple(np.around(segments[i][0].mean(axis=0)).astype(np.uint))[::-1]
# use the segment only if its centroid is in the mask
if segments[i][1] not in new_segments:
new_segments[segments[i][1]] = 0
if mask[centroid]:
new_segments[segments[i][1]] += 1
else:
# add up all of the pixels in the mask
for i in np.unique(segments):
new_segments[i] = np.sum(segments[mask] == i)
return new_segments