1
|
|
|
"""!
|
2
|
|
|
|
3
|
|
|
@brief Utils that are used by modules of pyclustering.
|
4
|
|
|
|
5
|
|
|
@authors Andrei Novikov ([email protected])
|
6
|
|
|
@date 2014-2018
|
7
|
|
|
@copyright GNU Public License
|
8
|
|
|
|
9
|
|
|
@cond GNU_PUBLIC_LICENSE
|
10
|
|
|
PyClustering is free software: you can redistribute it and/or modify
|
11
|
|
|
it under the terms of the GNU General Public License as published by
|
12
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
13
|
|
|
(at your option) any later version.
|
14
|
|
|
|
15
|
|
|
PyClustering is distributed in the hope that it will be useful,
|
16
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
17
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
18
|
|
|
GNU General Public License for more details.
|
19
|
|
|
|
20
|
|
|
You should have received a copy of the GNU General Public License
|
21
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
22
|
|
|
@endcond
|
23
|
|
|
|
24
|
|
|
"""
|
25
|
|
|
|
26
|
|
|
import time;
|
27
|
|
|
import numpy;
|
28
|
|
|
|
29
|
|
|
from PIL import Image;
|
30
|
|
|
from numpy import array;
|
31
|
|
|
|
32
|
|
|
import matplotlib.pyplot as plt;
|
33
|
|
|
from mpl_toolkits.mplot3d import Axes3D;
|
34
|
|
|
|
35
|
|
|
from sys import platform as _platform;
|
36
|
|
|
|
37
|
|
|
|
38
|
|
|
## The number \f$pi\f$ is a mathematical constant, the ratio of a circle's circumference to its diameter.
|
39
|
|
|
pi = 3.1415926535;
|
40
|
|
|
|
41
|
|
|
|
42
|
|
|
def read_sample(filename):
|
43
|
|
|
"""!
|
44
|
|
|
@brief Returns data sample from simple text file.
|
45
|
|
|
@details This function should be used for text file with following format:
|
46
|
|
|
@code
|
47
|
|
|
point_1_coord_1 point_1_coord_2 ... point_1_coord_n
|
48
|
|
|
point_2_coord_1 point_2_coord_2 ... point_2_coord_n
|
49
|
|
|
... ...
|
50
|
|
|
@endcode
|
51
|
|
|
|
52
|
|
|
@param[in] filename (string): Path to file with data.
|
53
|
|
|
|
54
|
|
|
@return (list) Points where each point represented by list of coordinates.
|
55
|
|
|
|
56
|
|
|
"""
|
57
|
|
|
|
58
|
|
|
file = open(filename, 'r');
|
59
|
|
|
|
60
|
|
|
sample = [[float(val) for val in line.split()] for line in file if len(line.strip()) > 0];
|
61
|
|
|
|
62
|
|
|
file.close();
|
63
|
|
|
return sample;
|
64
|
|
|
|
65
|
|
|
|
66
|
|
|
def read_image(filename):
|
67
|
|
|
"""!
|
68
|
|
|
@brief Returns image as N-dimension (depends on the input image) matrix, where one element of list describes pixel.
|
69
|
|
|
|
70
|
|
|
@param[in] filename (string): Path to image.
|
71
|
|
|
|
72
|
|
|
@return (list) Pixels where each pixel described by list of RGB-values.
|
73
|
|
|
|
74
|
|
|
"""
|
75
|
|
|
|
76
|
|
|
with Image.open(filename) as image_source:
|
77
|
|
|
data = [pixel for pixel in image_source.getdata()];
|
78
|
|
|
return data;
|
79
|
|
|
|
80
|
|
|
return [];
|
81
|
|
|
|
82
|
|
|
|
83
|
|
|
def rgb2gray(image_rgb_array):
|
84
|
|
|
"""!
|
85
|
|
|
@brief Returns image as 1-dimension (gray colored) matrix, where one element of list describes pixel.
|
86
|
|
|
@details Luma coding is used for transformation and that is calculated directly from gamma-compressed primary intensities as a weighted sum:
|
87
|
|
|
|
88
|
|
|
\f[Y = 0.2989R + 0.587G + 0.114B\f]
|
89
|
|
|
|
90
|
|
|
@param[in] image_rgb_array (list): Image represented by RGB list.
|
91
|
|
|
|
92
|
|
|
@return (list) Image as gray colored matrix, where one element of list describes pixel.
|
93
|
|
|
|
94
|
|
|
@code
|
95
|
|
|
colored_image = read_image(file_name);
|
96
|
|
|
gray_image = rgb2gray(colored_image);
|
97
|
|
|
@endcode
|
98
|
|
|
|
99
|
|
|
@see read_image()
|
100
|
|
|
|
101
|
|
|
"""
|
102
|
|
|
|
103
|
|
|
image_gray_array = [0.0] * len(image_rgb_array);
|
104
|
|
|
for index in range(0, len(image_rgb_array), 1):
|
105
|
|
|
image_gray_array[index] = float(image_rgb_array[index][0]) * 0.2989 + float(image_rgb_array[index][1]) * 0.5870 + float(image_rgb_array[index][2]) * 0.1140;
|
106
|
|
|
|
107
|
|
|
return image_gray_array;
|
108
|
|
|
|
109
|
|
|
|
110
|
|
|
def stretch_pattern(image_source):
|
111
|
|
|
"""!
|
112
|
|
|
@brief Returns stretched content as 1-dimension (gray colored) matrix with size of input image.
|
113
|
|
|
|
114
|
|
|
@param[in] image_source (Image): PIL Image instance.
|
115
|
|
|
|
116
|
|
|
@return (list, Image) Stretched image as gray colored matrix and source image.
|
117
|
|
|
|
118
|
|
|
"""
|
119
|
|
|
wsize, hsize = image_source.size;
|
120
|
|
|
|
121
|
|
|
# Crop digit exactly
|
122
|
|
|
(ws, hs, we, he) = gray_pattern_borders(image_source);
|
123
|
|
|
image_source = image_source.crop((ws, hs, we, he));
|
124
|
|
|
|
125
|
|
|
# Stretch it to initial sizes
|
126
|
|
|
image_source = image_source.resize((wsize, hsize), Image.ANTIALIAS);
|
127
|
|
|
|
128
|
|
|
# Transform image to simple array
|
129
|
|
|
data = [pixel for pixel in image_source.getdata()];
|
130
|
|
|
image_pattern = rgb2gray(data);
|
131
|
|
|
|
132
|
|
|
return (image_pattern, image_source);
|
133
|
|
|
|
134
|
|
|
|
135
|
|
|
def gray_pattern_borders(image):
|
136
|
|
|
"""!
|
137
|
|
|
@brief Returns coordinates of gray image content on the input image.
|
138
|
|
|
|
139
|
|
|
@param[in] image (Image): PIL Image instance that is processed.
|
140
|
|
|
|
141
|
|
|
@return (tuple) Returns coordinates of gray image content as (width_start, height_start, width_end, height_end).
|
142
|
|
|
|
143
|
|
|
"""
|
144
|
|
|
|
145
|
|
|
width, height = image.size;
|
146
|
|
|
|
147
|
|
|
width_start = width;
|
148
|
|
|
width_end = 0;
|
149
|
|
|
height_start = height;
|
150
|
|
|
height_end = 0;
|
151
|
|
|
|
152
|
|
|
row, col = 0, 0;
|
153
|
|
|
for pixel in image.getdata():
|
154
|
|
|
value = float(pixel[0]) * 0.2989 + float(pixel[1]) * 0.5870 + float(pixel[2]) * 0.1140;
|
155
|
|
|
|
156
|
|
|
if (value < 128):
|
157
|
|
|
if (width_end < col):
|
158
|
|
|
width_end = col;
|
159
|
|
|
|
160
|
|
|
if (height_end < row):
|
161
|
|
|
height_end = row;
|
162
|
|
|
|
163
|
|
|
if (width_start > col):
|
164
|
|
|
width_start = col;
|
165
|
|
|
|
166
|
|
|
if (height_start > row):
|
167
|
|
|
height_start = row;
|
168
|
|
|
|
169
|
|
|
col += 1;
|
170
|
|
|
if (col >= width):
|
171
|
|
|
col = 0;
|
172
|
|
|
row += 1;
|
173
|
|
|
|
174
|
|
|
return (width_start, height_start, width_end + 1, height_end + 1);
|
175
|
|
|
|
176
|
|
|
|
177
|
|
|
def average_neighbor_distance(points, num_neigh):
|
178
|
|
|
"""!
|
179
|
|
|
@brief Returns average distance for establish links between specified number of nearest neighbors.
|
180
|
|
|
|
181
|
|
|
@param[in] points (list): Input data, list of points where each point represented by list.
|
182
|
|
|
@param[in] num_neigh (uint): Number of neighbors that should be used for distance calculation.
|
183
|
|
|
|
184
|
|
|
@return (double) Average distance for establish links between 'num_neigh' in data set 'points'.
|
185
|
|
|
|
186
|
|
|
"""
|
187
|
|
|
|
188
|
|
|
if (num_neigh > len(points) - 1):
|
189
|
|
|
raise NameError('Impossible to calculate average distance to neighbors when number of object is less than number of neighbors.');
|
190
|
|
|
|
191
|
|
|
dist_matrix = [ [ 0.0 for i in range(len(points)) ] for j in range(len(points)) ];
|
192
|
|
|
for i in range(0, len(points), 1):
|
193
|
|
|
for j in range(i + 1, len(points), 1):
|
194
|
|
|
distance = euclidean_distance(points[i], points[j]);
|
195
|
|
|
dist_matrix[i][j] = distance;
|
196
|
|
|
dist_matrix[j][i] = distance;
|
197
|
|
|
|
198
|
|
|
dist_matrix[i] = sorted(dist_matrix[i]);
|
199
|
|
|
|
200
|
|
|
total_distance = 0;
|
201
|
|
|
for i in range(0, len(points), 1):
|
202
|
|
|
# start from 0 - first element is distance to itself.
|
203
|
|
|
for j in range(0, num_neigh, 1):
|
204
|
|
|
total_distance += dist_matrix[i][j + 1];
|
205
|
|
|
|
206
|
|
|
return ( total_distance / (num_neigh * len(points)) );
|
207
|
|
|
|
208
|
|
|
|
209
|
|
|
def centroid(points, indexes = None):
|
210
|
|
|
"""!
|
211
|
|
|
@brief Calculate centroid of input set of points.
|
212
|
|
|
|
213
|
|
|
@param[in] points (list): Set of points for centroid calculation.
|
214
|
|
|
@param[in] indexes (list): Indexes of objects in input set of points that will be taken into account during centroid calculation.
|
215
|
|
|
|
216
|
|
|
@return (list) centroid on the set of points where each element of list is corresponding to value in its dimension.
|
217
|
|
|
|
218
|
|
|
"""
|
219
|
|
|
|
220
|
|
|
dimension = len(points[0]);
|
221
|
|
|
centroid_value = [0.0] * dimension;
|
222
|
|
|
|
223
|
|
|
range_points = None;
|
224
|
|
|
if (indexes is None):
|
225
|
|
|
range_points = range(len(points));
|
226
|
|
|
else:
|
227
|
|
|
range_points = indexes;
|
228
|
|
|
|
229
|
|
|
for index_point in range_points:
|
230
|
|
|
centroid_value = list_math_addition(centroid_value, points[index_point]);
|
231
|
|
|
|
232
|
|
|
centroid_value = list_math_division_number(centroid_value, len(range_points));
|
233
|
|
|
return centroid_value;
|
234
|
|
|
|
235
|
|
|
|
236
|
|
|
def median(points, indexes = None):
|
237
|
|
|
"""!
|
238
|
|
|
@brief Calculate geometric median of input set of points using Euclidian distance.
|
239
|
|
|
|
240
|
|
|
@param[in] points (list): Set of points for median calculation.
|
241
|
|
|
@param[in] indexes (list): Indexes of objects in input set of points that will be taken into account during median calculation.
|
242
|
|
|
|
243
|
|
|
@return (uint) index of point in input set that corresponds to median.
|
244
|
|
|
|
245
|
|
|
"""
|
246
|
|
|
|
247
|
|
|
index_median = None;
|
248
|
|
|
distance = float('Inf');
|
249
|
|
|
|
250
|
|
|
range_points = None;
|
251
|
|
|
if (indexes is None):
|
252
|
|
|
range_points = range(len(points));
|
253
|
|
|
else:
|
254
|
|
|
range_points = indexes;
|
255
|
|
|
|
256
|
|
|
for index_candidate in range_points:
|
257
|
|
|
distance_candidate = 0.0;
|
258
|
|
|
for index in range_points:
|
259
|
|
|
distance_candidate += euclidean_distance_square(points[index_candidate], points[index]);
|
260
|
|
|
|
261
|
|
|
if (distance_candidate < distance):
|
262
|
|
|
distance = distance_candidate;
|
263
|
|
|
index_median = index_candidate;
|
264
|
|
|
|
265
|
|
|
return index_median;
|
266
|
|
|
|
267
|
|
|
|
268
|
|
|
def euclidean_distance(a, b):
|
269
|
|
|
"""!
|
|
|
|
|
270
|
|
|
@brief Calculate Euclidean distance between vector a and b.
|
271
|
|
|
@details The Euclidean between vectors (points) a and b is calculated by following formula:
|
272
|
|
|
|
273
|
|
|
\f[
|
274
|
|
|
dist(a, b) = \sqrt{ \sum_{i=0}^{N}(b_{i} - a_{i})^{2}) };
|
275
|
|
|
\f]
|
276
|
|
|
|
277
|
|
|
Where N is a length of each vector.
|
278
|
|
|
|
279
|
|
|
@param[in] a (list): The first vector.
|
280
|
|
|
@param[in] b (list): The second vector.
|
281
|
|
|
|
282
|
|
|
@return (double) Euclidian distance between two vectors.
|
283
|
|
|
|
284
|
|
|
@note This function for calculation is faster then standard function in ~100 times!
|
285
|
|
|
|
286
|
|
|
"""
|
287
|
|
|
|
288
|
|
|
distance = euclidean_distance_square(a, b);
|
289
|
|
|
return distance**(0.5);
|
290
|
|
|
|
291
|
|
|
|
292
|
|
|
def euclidean_distance_square(a, b):
|
293
|
|
|
"""!
|
294
|
|
|
@brief Calculate square Euclidian distance between vector a and b.
|
295
|
|
|
|
296
|
|
|
@param[in] a (list): The first vector.
|
297
|
|
|
@param[in] b (list): The second vector.
|
298
|
|
|
|
299
|
|
|
@return (double) Square Euclidian distance between two vectors.
|
300
|
|
|
|
301
|
|
|
"""
|
302
|
|
|
|
303
|
|
|
if ( ((type(a) == float) and (type(b) == float)) or ((type(a) == int) and (type(b) == int)) ):
|
304
|
|
|
return (a - b)**2.0;
|
305
|
|
|
|
306
|
|
|
distance = 0.0;
|
307
|
|
|
for i in range(0, len(a)):
|
308
|
|
|
distance += (a[i] - b[i])**2.0;
|
309
|
|
|
|
310
|
|
|
return distance;
|
311
|
|
|
|
312
|
|
|
|
313
|
|
|
def manhattan_distance(a, b):
|
314
|
|
|
"""!
|
315
|
|
|
@brief Calculate Manhattan distance between vector a and b.
|
316
|
|
|
|
317
|
|
|
@param[in] a (list): The first cluster.
|
318
|
|
|
@param[in] b (list): The second cluster.
|
319
|
|
|
|
320
|
|
|
@return (double) Manhattan distance between two vectors.
|
321
|
|
|
|
322
|
|
|
"""
|
323
|
|
|
|
324
|
|
|
if ( ((type(a) == float) and (type(b) == float)) or ((type(a) == int) and (type(b) == int)) ):
|
325
|
|
|
return abs(a - b);
|
326
|
|
|
|
327
|
|
|
distance = 0.0;
|
328
|
|
|
dimension = len(a);
|
329
|
|
|
|
330
|
|
|
for i in range(0, dimension):
|
331
|
|
|
distance += abs(a[i] - b[i]);
|
332
|
|
|
|
333
|
|
|
return distance;
|
334
|
|
|
|
335
|
|
|
|
336
|
|
|
def average_inter_cluster_distance(cluster1, cluster2, data = None):
|
337
|
|
|
"""!
|
338
|
|
|
@brief Calculates average inter-cluster distance between two clusters.
|
339
|
|
|
@details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
|
340
|
|
|
or by list of indexes of points from the data (represented by list of points), in this case
|
341
|
|
|
data should be specified.
|
342
|
|
|
|
343
|
|
|
@param[in] cluster1 (list): The first cluster where each element can represent index from the data or object itself.
|
344
|
|
|
@param[in] cluster2 (list): The second cluster where each element can represent index from the data or object itself.
|
345
|
|
|
@param[in] data (list): If specified than elements of clusters will be used as indexes,
|
346
|
|
|
otherwise elements of cluster will be considered as points.
|
347
|
|
|
|
348
|
|
|
@return (double) Average inter-cluster distance between two clusters.
|
349
|
|
|
|
350
|
|
|
"""
|
351
|
|
|
|
352
|
|
|
distance = 0.0;
|
353
|
|
|
|
354
|
|
|
if (data is None):
|
355
|
|
|
for i in range(len(cluster1)):
|
356
|
|
|
for j in range(len(cluster2)):
|
357
|
|
|
distance += euclidean_distance_square(cluster1[i], cluster2[j]);
|
358
|
|
|
else:
|
359
|
|
|
for i in range(len(cluster1)):
|
360
|
|
|
for j in range(len(cluster2)):
|
361
|
|
|
distance += euclidean_distance_square(data[ cluster1[i]], data[ cluster2[j]]);
|
362
|
|
|
|
363
|
|
|
distance /= float(len(cluster1) * len(cluster2));
|
364
|
|
|
return distance ** 0.5;
|
365
|
|
|
|
366
|
|
|
|
367
|
|
|
def average_intra_cluster_distance(cluster1, cluster2, data = None):
|
368
|
|
|
"""!
|
369
|
|
|
@brief Calculates average intra-cluster distance between two clusters.
|
370
|
|
|
@details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
|
371
|
|
|
or by list of indexes of points from the data (represented by list of points), in this case
|
372
|
|
|
data should be specified.
|
373
|
|
|
|
374
|
|
|
@param[in] cluster1 (list): The first cluster.
|
375
|
|
|
@param[in] cluster2 (list): The second cluster.
|
376
|
|
|
@param[in] data (list): If specified than elements of clusters will be used as indexes,
|
377
|
|
|
otherwise elements of cluster will be considered as points.
|
378
|
|
|
|
379
|
|
|
@return (double) Average intra-cluster distance between two clusters.
|
380
|
|
|
|
381
|
|
|
"""
|
382
|
|
|
|
383
|
|
|
distance = 0.0;
|
384
|
|
|
|
385
|
|
|
for i in range(len(cluster1) + len(cluster2)):
|
386
|
|
|
for j in range(len(cluster1) + len(cluster2)):
|
387
|
|
|
first_point = None;
|
388
|
|
|
second_point = None;
|
389
|
|
|
|
390
|
|
|
if (data is None):
|
391
|
|
|
# the first point
|
392
|
|
|
if (i < len(cluster1)): first_point = cluster1[i];
|
393
|
|
|
else: first_point = cluster2[i - len(cluster1)];
|
394
|
|
|
|
395
|
|
|
# the second point
|
396
|
|
|
if (j < len(cluster1)): second_point = cluster1[j];
|
397
|
|
|
else: second_point = cluster2[j - len(cluster1)];
|
398
|
|
|
|
399
|
|
|
else:
|
400
|
|
|
# the first point
|
401
|
|
|
if (i < len(cluster1)): first_point = data[ cluster1[i] ];
|
402
|
|
|
else: first_point = data[ cluster2[i - len(cluster1)] ];
|
403
|
|
|
|
404
|
|
|
if (j < len(cluster1)): second_point = data[ cluster1[j] ];
|
405
|
|
|
else: second_point = data[ cluster2[j - len(cluster1)] ];
|
406
|
|
|
|
407
|
|
|
|
408
|
|
|
|
409
|
|
|
distance += euclidean_distance_square(first_point, second_point);
|
410
|
|
|
|
411
|
|
|
distance /= float( (len(cluster1) + len(cluster2)) * (len(cluster1) + len(cluster2) - 1.0) );
|
412
|
|
|
return distance ** 0.5;
|
413
|
|
|
|
414
|
|
|
|
415
|
|
|
def variance_increase_distance(cluster1, cluster2, data = None):
|
416
|
|
|
"""!
|
417
|
|
|
@brief Calculates variance increase distance between two clusters.
|
418
|
|
|
@details Clusters can be represented by list of coordinates (in this case data shouldn't be specified),
|
419
|
|
|
or by list of indexes of points from the data (represented by list of points), in this case
|
420
|
|
|
data should be specified.
|
421
|
|
|
|
422
|
|
|
@param[in] cluster1 (list): The first cluster.
|
423
|
|
|
@param[in] cluster2 (list): The second cluster.
|
424
|
|
|
@param[in] data (list): If specified than elements of clusters will be used as indexes,
|
425
|
|
|
otherwise elements of cluster will be considered as points.
|
426
|
|
|
|
427
|
|
|
@return (double) Average variance increase distance between two clusters.
|
428
|
|
|
|
429
|
|
|
"""
|
430
|
|
|
|
431
|
|
|
# calculate local sum
|
432
|
|
|
member_cluster1 = None;
|
433
|
|
|
member_cluster2 = None;
|
434
|
|
|
|
435
|
|
|
if (data is None):
|
436
|
|
|
member_cluster1 = [0.0] * len(cluster1[0]);
|
437
|
|
|
member_cluster2 = [0.0] * len(cluster2[0]);
|
438
|
|
|
|
439
|
|
|
else:
|
440
|
|
|
member_cluster1 = [0.0] * len(data[0]);
|
441
|
|
|
member_cluster2 = [0.0] * len(data[0]);
|
442
|
|
|
|
443
|
|
|
for i in range(len(cluster1)):
|
444
|
|
|
if (data is None):
|
445
|
|
|
member_cluster1 = list_math_addition(member_cluster1, cluster1[i]);
|
446
|
|
|
else:
|
447
|
|
|
member_cluster1 = list_math_addition(member_cluster1, data[ cluster1[i] ]);
|
448
|
|
|
|
449
|
|
|
|
450
|
|
|
for j in range(len(cluster2)):
|
451
|
|
|
if (data is None):
|
452
|
|
|
member_cluster2 = list_math_addition(member_cluster2, cluster2[j]);
|
453
|
|
|
else:
|
454
|
|
|
member_cluster2 = list_math_addition(member_cluster2, data[ cluster2[j] ]);
|
455
|
|
|
|
456
|
|
|
member_cluster_general = list_math_addition(member_cluster1, member_cluster2);
|
457
|
|
|
member_cluster_general = list_math_division_number(member_cluster_general, len(cluster1) + len(cluster2));
|
458
|
|
|
|
459
|
|
|
member_cluster1 = list_math_division_number(member_cluster1, len(cluster1));
|
460
|
|
|
member_cluster2 = list_math_division_number(member_cluster2, len(cluster2));
|
461
|
|
|
|
462
|
|
|
# calculate global sum
|
463
|
|
|
distance_general = 0.0;
|
464
|
|
|
distance_cluster1 = 0.0;
|
465
|
|
|
distance_cluster2 = 0.0;
|
466
|
|
|
|
467
|
|
|
for i in range(len(cluster1)):
|
468
|
|
|
if (data is None):
|
469
|
|
|
distance_cluster1 += euclidean_distance_square(cluster1[i], member_cluster1);
|
470
|
|
|
distance_general += euclidean_distance_square(cluster1[i], member_cluster_general);
|
471
|
|
|
|
472
|
|
|
else:
|
473
|
|
|
distance_cluster1 += euclidean_distance_square(data[ cluster1[i]], member_cluster1);
|
474
|
|
|
distance_general += euclidean_distance_square(data[ cluster1[i]], member_cluster_general);
|
475
|
|
|
|
476
|
|
|
for j in range(len(cluster2)):
|
477
|
|
|
if (data is None):
|
478
|
|
|
distance_cluster2 += euclidean_distance_square(cluster2[j], member_cluster2);
|
479
|
|
|
distance_general += euclidean_distance_square(cluster2[j], member_cluster_general);
|
480
|
|
|
|
481
|
|
|
else:
|
482
|
|
|
distance_cluster2 += euclidean_distance_square(data[ cluster2[j]], member_cluster2);
|
483
|
|
|
distance_general += euclidean_distance_square(data[ cluster2[j]], member_cluster_general);
|
484
|
|
|
|
485
|
|
|
return distance_general - distance_cluster1 - distance_cluster2;
|
486
|
|
|
|
487
|
|
|
|
488
|
|
|
def calculate_ellipse_description(covariance, scale = 2.0):
|
489
|
|
|
"""!
|
490
|
|
|
@brief Calculates description of ellipse using covariance matrix.
|
491
|
|
|
|
492
|
|
|
@param[in] covariance (numpy.array): Covariance matrix for which ellipse area should be calculated.
|
493
|
|
|
@param[in] scale (float): Scale of the ellipse.
|
494
|
|
|
|
495
|
|
|
@return (float, float, float) Return ellipse description: angle, width, height.
|
496
|
|
|
|
497
|
|
|
"""
|
498
|
|
|
|
499
|
|
|
eigh_values, eigh_vectors = numpy.linalg.eigh(covariance);
|
500
|
|
|
order = eigh_values.argsort()[::-1];
|
501
|
|
|
|
502
|
|
|
values, vectors = eigh_values[order], eigh_vectors[order];
|
503
|
|
|
angle = numpy.degrees(numpy.arctan2(*vectors[:,0][::-1]));
|
504
|
|
|
|
505
|
|
|
if (0.0 in values):
|
506
|
|
|
return 0, 0, 0;
|
507
|
|
|
|
508
|
|
|
width, height = 2.0 * scale * numpy.sqrt(values);
|
509
|
|
|
return angle, width, height;
|
510
|
|
|
|
511
|
|
|
|
512
|
|
|
def data_corners(data, data_filter = None):
|
513
|
|
|
"""!
|
514
|
|
|
@brief Finds maximum and minimum corner in each dimension of the specified data.
|
515
|
|
|
|
516
|
|
|
@param[in] data (list): List of points that should be analysed.
|
517
|
|
|
@param[in] data_filter (list): List of indexes of the data that should be analysed,
|
518
|
|
|
if it is 'None' then whole 'data' is analysed to obtain corners.
|
519
|
|
|
|
520
|
|
|
@return (list) Tuple of two points that corresponds to minimum and maximum corner (min_corner, max_corner).
|
521
|
|
|
|
522
|
|
|
"""
|
523
|
|
|
|
524
|
|
|
dimensions = len(data[0]);
|
525
|
|
|
|
526
|
|
|
bypass = data_filter;
|
527
|
|
|
if (bypass is None):
|
528
|
|
|
bypass = range(len(data));
|
529
|
|
|
|
530
|
|
|
maximum_corner = data[bypass[0]][:];
|
531
|
|
|
minimum_corner = data[bypass[0]][:];
|
532
|
|
|
|
533
|
|
|
for index_point in bypass:
|
534
|
|
|
for index_dimension in range(dimensions):
|
535
|
|
|
if (data[index_point][index_dimension] > maximum_corner[index_dimension]):
|
536
|
|
|
maximum_corner[index_dimension] = data[index_point][index_dimension];
|
537
|
|
|
|
538
|
|
|
if (data[index_point][index_dimension] < minimum_corner[index_dimension]):
|
539
|
|
|
minimum_corner[index_dimension] = data[index_point][index_dimension];
|
540
|
|
|
|
541
|
|
|
return (minimum_corner, maximum_corner);
|
542
|
|
|
|
543
|
|
|
|
544
|
|
|
def norm_vector(vector):
|
545
|
|
|
"""!
|
546
|
|
|
@brief Calculates norm of an input vector that is known as a vector length.
|
547
|
|
|
|
548
|
|
|
@param[in] vector (list): The input vector whose length is calculated.
|
549
|
|
|
|
550
|
|
|
@return (double) vector norm known as vector length.
|
551
|
|
|
|
552
|
|
|
"""
|
553
|
|
|
|
554
|
|
|
length = 0.0;
|
555
|
|
|
for component in vector:
|
556
|
|
|
length += component * component;
|
557
|
|
|
|
558
|
|
|
length = length ** 0.5;
|
559
|
|
|
|
560
|
|
|
return length;
|
561
|
|
|
|
562
|
|
|
|
563
|
|
|
def unit_vector(vector):
|
564
|
|
|
"""!
|
565
|
|
|
@brief Calculates unit vector.
|
566
|
|
|
@details Unit vector calculates of an input vector involves two steps.
|
567
|
|
|
The first, calculate vector length. The second,
|
568
|
|
|
divide each vector component by the obtained length.
|
569
|
|
|
|
570
|
|
|
@param[in] vector (list): The input vector that is used for unit vector calculation.
|
571
|
|
|
|
572
|
|
|
"""
|
573
|
|
|
|
574
|
|
|
length = norm_vector(vector);
|
575
|
|
|
unit_vector_instance = [];
|
576
|
|
|
|
577
|
|
|
for component in vector:
|
578
|
|
|
unit_vector_instance.append(component / length);
|
579
|
|
|
|
580
|
|
|
return unit_vector_instance;
|
581
|
|
|
|
582
|
|
|
|
583
|
|
|
def heaviside(value):
|
584
|
|
|
"""!
|
585
|
|
|
@brief Calculates Heaviside function that represents step function.
|
586
|
|
|
@details If input value is greater than 0 then returns 1, otherwise returns 0.
|
587
|
|
|
|
588
|
|
|
@param[in] value (double): Argument of Heaviside function.
|
589
|
|
|
|
590
|
|
|
@return (double) Value of Heaviside function.
|
591
|
|
|
|
592
|
|
|
"""
|
593
|
|
|
if (value > 0.0):
|
594
|
|
|
return 1.0;
|
595
|
|
|
|
596
|
|
|
return 0.0;
|
597
|
|
|
|
598
|
|
|
|
599
|
|
|
def timedcall(executable_function, *args):
|
600
|
|
|
"""!
|
601
|
|
|
@brief Executes specified method or function with measuring of execution time.
|
602
|
|
|
|
603
|
|
|
@param[in] executable_function (pointer): Pointer to function or method.
|
604
|
|
|
@param[in] args (*): Arguments of called function or method.
|
605
|
|
|
|
606
|
|
|
@return (tuple) Execution time and result of execution of function or method (execution_time, result_execution).
|
607
|
|
|
|
608
|
|
|
"""
|
609
|
|
|
|
610
|
|
|
time_start = time.clock();
|
611
|
|
|
result = executable_function(*args);
|
612
|
|
|
time_end = time.clock();
|
613
|
|
|
|
614
|
|
|
return (time_end - time_start, result);
|
615
|
|
|
|
616
|
|
|
|
617
|
|
|
def extract_number_oscillations(osc_dyn, index = 0, amplitude_threshold = 1.0):
|
618
|
|
|
"""!
|
619
|
|
|
@brief Extracts number of oscillations of specified oscillator.
|
620
|
|
|
|
621
|
|
|
@param[in] osc_dyn (list): Dynamic of oscillators.
|
622
|
|
|
@param[in] index (uint): Index of oscillator in dynamic.
|
623
|
|
|
@param[in] amplitude_threshold (double): Amplitude threshold when oscillation is taken into account, for example,
|
624
|
|
|
when oscillator amplitude is greater than threshold then oscillation is incremented.
|
625
|
|
|
|
626
|
|
|
@return (uint) Number of oscillations of specified oscillator.
|
627
|
|
|
|
628
|
|
|
"""
|
629
|
|
|
|
630
|
|
|
number_oscillations = 0;
|
631
|
|
|
waiting_differential = False;
|
632
|
|
|
threshold_passed = False;
|
633
|
|
|
high_level_trigger = True if (osc_dyn[0][index] > amplitude_threshold) else False;
|
634
|
|
|
|
635
|
|
|
for values in osc_dyn:
|
636
|
|
|
if ( (values[index] >= amplitude_threshold) and (high_level_trigger is False) ):
|
637
|
|
|
high_level_trigger = True;
|
638
|
|
|
threshold_passed = True;
|
639
|
|
|
|
640
|
|
|
elif ( (values[index] < amplitude_threshold) and (high_level_trigger is True) ):
|
641
|
|
|
high_level_trigger = False;
|
642
|
|
|
threshold_passed = True;
|
643
|
|
|
|
644
|
|
|
if (threshold_passed is True):
|
645
|
|
|
threshold_passed = False;
|
646
|
|
|
if (waiting_differential is True and high_level_trigger is False):
|
647
|
|
|
number_oscillations += 1;
|
648
|
|
|
waiting_differential = False;
|
649
|
|
|
|
650
|
|
|
else:
|
651
|
|
|
waiting_differential = True;
|
652
|
|
|
|
653
|
|
|
return number_oscillations;
|
654
|
|
|
|
655
|
|
|
|
656
|
|
View Code Duplication |
def allocate_sync_ensembles(dynamic, tolerance = 0.1, threshold = 1.0, ignore = None):
|
|
|
|
|
657
|
|
|
"""!
|
658
|
|
|
@brief Allocate clusters in line with ensembles of synchronous oscillators where each
|
659
|
|
|
synchronous ensemble corresponds to only one cluster.
|
660
|
|
|
|
661
|
|
|
@param[in] dynamic (dynamic): Dynamic of each oscillator.
|
662
|
|
|
@param[in] tolerance (double): Maximum error for allocation of synchronous ensemble oscillators.
|
663
|
|
|
@param[in] threshold (double): Amlitude trigger when spike is taken into account.
|
664
|
|
|
@param[in] ignore (bool): Set of indexes that shouldn't be taken into account.
|
665
|
|
|
|
666
|
|
|
@return (list) Grours (lists) of indexes of synchronous oscillators, for example,
|
667
|
|
|
[ [index_osc1, index_osc3], [index_osc2], [index_osc4, index_osc5] ].
|
668
|
|
|
|
669
|
|
|
"""
|
670
|
|
|
|
671
|
|
|
descriptors = [ [] for _ in range(len(dynamic[0])) ];
|
672
|
|
|
|
673
|
|
|
# Check from the end for obtaining result
|
674
|
|
|
for index_dyn in range(0, len(dynamic[0]), 1):
|
675
|
|
|
if ((ignore is not None) and (index_dyn in ignore)):
|
676
|
|
|
continue;
|
677
|
|
View Code Duplication |
|
|
|
|
|
678
|
|
|
time_stop_simulation = len(dynamic) - 1;
|
679
|
|
|
active_state = False;
|
680
|
|
|
|
681
|
|
|
if (dynamic[time_stop_simulation][index_dyn] > threshold):
|
682
|
|
|
active_state = True;
|
683
|
|
|
|
684
|
|
|
# if active state is detected, it means we don't have whole oscillatory period for the considered oscillator, should be skipped.
|
685
|
|
|
if (active_state is True):
|
686
|
|
|
while ( (dynamic[time_stop_simulation][index_dyn] > threshold) and (time_stop_simulation > 0) ):
|
687
|
|
|
time_stop_simulation -= 1;
|
688
|
|
|
|
689
|
|
|
# if there are no any oscillation than let's consider it like noise
|
690
|
|
|
if (time_stop_simulation == 0):
|
691
|
|
|
continue;
|
692
|
|
|
|
693
|
|
|
# reset
|
694
|
|
|
active_state = False;
|
695
|
|
|
|
696
|
|
|
desc = [0, 0, 0]; # end, start, average time of oscillation
|
697
|
|
|
for t in range(time_stop_simulation, 0, -1):
|
698
|
|
|
if ( (dynamic[t][index_dyn] > threshold) and (active_state is False) ):
|
699
|
|
|
desc[0] = t;
|
700
|
|
|
active_state = True;
|
701
|
|
|
elif ( (dynamic[t][index_dyn] < threshold) and (active_state is True) ):
|
702
|
|
|
desc[1] = t;
|
703
|
|
|
active_state = False;
|
704
|
|
|
|
705
|
|
|
break;
|
706
|
|
|
|
707
|
|
|
if (desc == [0, 0, 0]):
|
708
|
|
|
continue;
|
709
|
|
|
|
710
|
|
|
desc[2] = desc[1] + (desc[0] - desc[1]) / 2.0;
|
711
|
|
|
descriptors[index_dyn] = desc;
|
712
|
|
|
|
713
|
|
|
|
714
|
|
|
# Cluster allocation
|
715
|
|
|
sync_ensembles = [];
|
716
|
|
|
desc_sync_ensembles = [];
|
717
|
|
|
|
718
|
|
|
for index_desc in range(0, len(descriptors), 1):
|
719
|
|
|
if (descriptors[index_desc] == []):
|
720
|
|
|
continue;
|
721
|
|
|
|
722
|
|
|
if (len(sync_ensembles) == 0):
|
723
|
|
|
desc_ensemble = descriptors[index_desc];
|
724
|
|
|
reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
|
725
|
|
|
|
726
|
|
|
desc_ensemble[0] = desc_ensemble[2] + reducer;
|
727
|
|
|
desc_ensemble[1] = desc_ensemble[2] - reducer;
|
728
|
|
|
|
729
|
|
|
desc_sync_ensembles.append(desc_ensemble);
|
730
|
|
|
sync_ensembles.append([ index_desc ]);
|
731
|
|
|
else:
|
732
|
|
|
oscillator_captured = False;
|
733
|
|
|
for index_ensemble in range(0, len(sync_ensembles), 1):
|
734
|
|
|
if ( (desc_sync_ensembles[index_ensemble][0] > descriptors[index_desc][2]) and (desc_sync_ensembles[index_ensemble][1] < descriptors[index_desc][2])):
|
735
|
|
|
sync_ensembles[index_ensemble].append(index_desc);
|
736
|
|
|
oscillator_captured = True;
|
737
|
|
|
break;
|
738
|
|
|
|
739
|
|
|
if (oscillator_captured is False):
|
740
|
|
|
desc_ensemble = descriptors[index_desc];
|
741
|
|
|
reducer = (desc_ensemble[0] - desc_ensemble[1]) * tolerance;
|
742
|
|
|
|
743
|
|
|
desc_ensemble[0] = desc_ensemble[2] + reducer;
|
744
|
|
|
desc_ensemble[1] = desc_ensemble[2] - reducer;
|
745
|
|
|
|
746
|
|
|
desc_sync_ensembles.append(desc_ensemble);
|
747
|
|
|
sync_ensembles.append([ index_desc ]);
|
748
|
|
|
|
749
|
|
|
return sync_ensembles;
|
750
|
|
|
|
751
|
|
|
|
752
|
|
|
def draw_clusters(data, clusters, noise = [], marker_descr = '.', hide_axes = False, axes = None, display_result = True):
|
753
|
|
|
"""!
|
754
|
|
|
@brief Displays clusters for data in 2D or 3D.
|
755
|
|
|
|
756
|
|
|
@param[in] data (list): Points that are described by coordinates represented.
|
757
|
|
|
@param[in] clusters (list): Clusters that are represented by lists of indexes where each index corresponds to point in data.
|
758
|
|
|
@param[in] noise (list): Points that are regarded to noise.
|
759
|
|
|
@param[in] marker_descr (string): Marker for displaying points.
|
760
|
|
|
@param[in] hide_axes (bool): If True - axes is not displayed.
|
761
|
|
|
@param[in] axes (ax) Matplotlib axes where clusters should be drawn, if it is not specified (None) then new plot will be created.
|
762
|
|
|
@param[in] display_result (bool): If specified then matplotlib axes will be used for drawing and plot will not be shown.
|
763
|
|
|
|
764
|
|
|
@return (ax) Matplotlib axes where drawn clusters are presented.
|
765
|
|
|
|
766
|
|
|
"""
|
767
|
|
|
# Get dimension
|
768
|
|
|
dimension = 0;
|
769
|
|
|
if ( (data is not None) and (clusters is not None) ):
|
770
|
|
|
dimension = len(data[0]);
|
771
|
|
|
elif ( (data is None) and (clusters is not None) ):
|
772
|
|
|
dimension = len(clusters[0][0]);
|
773
|
|
|
else:
|
774
|
|
|
raise NameError('Data or clusters should be specified exactly.');
|
775
|
|
|
|
776
|
|
|
"Draw clusters"
|
777
|
|
|
colors = [ 'red', 'blue', 'darkgreen', 'brown', 'violet',
|
778
|
|
|
'deepskyblue', 'darkgrey', 'lightsalmon', 'deeppink', 'yellow',
|
779
|
|
|
'black', 'mediumspringgreen', 'orange', 'darkviolet', 'darkblue',
|
780
|
|
|
'silver', 'lime', 'pink', 'gold', 'bisque' ];
|
781
|
|
|
|
782
|
|
|
if (len(clusters) > len(colors)):
|
783
|
|
|
raise NameError('Impossible to represent clusters due to number of specified colors.');
|
784
|
|
|
|
785
|
|
|
fig = plt.figure();
|
786
|
|
|
|
787
|
|
|
if (axes is None):
|
788
|
|
|
# Check for dimensions
|
789
|
|
|
if ((dimension) == 1 or (dimension == 2)):
|
790
|
|
|
axes = fig.add_subplot(111);
|
791
|
|
View Code Duplication |
elif (dimension == 3):
|
|
|
|
|
792
|
|
|
axes = fig.gca(projection='3d');
|
793
|
|
|
else:
|
794
|
|
|
raise NameError('Drawer supports only 2d and 3d data representation');
|
795
|
|
|
|
796
|
|
|
color_index = 0;
|
797
|
|
|
for cluster in clusters:
|
798
|
|
|
color = colors[color_index];
|
799
|
|
|
for item in cluster:
|
800
|
|
|
if (dimension == 1):
|
801
|
|
|
if (data is None):
|
802
|
|
|
axes.plot(item[0], 0.0, color = color, marker = marker_descr);
|
803
|
|
|
else:
|
804
|
|
|
axes.plot(data[item][0], 0.0, color = color, marker = marker_descr);
|
805
|
|
|
|
806
|
|
|
if (dimension == 2):
|
807
|
|
|
if (data is None):
|
808
|
|
|
axes.plot(item[0], item[1], color = color, marker = marker_descr);
|
809
|
|
|
else:
|
810
|
|
|
axes.plot(data[item][0], data[item][1], color = color, marker = marker_descr);
|
811
|
|
|
|
812
|
|
View Code Duplication |
elif (dimension == 3):
|
|
|
|
|
813
|
|
|
if (data is None):
|
814
|
|
|
axes.scatter(item[0], item[1], item[2], c = color, marker = marker_descr);
|
815
|
|
|
else:
|
816
|
|
|
axes.scatter(data[item][0], data[item][1], data[item][2], c = color, marker = marker_descr);
|
817
|
|
|
|
818
|
|
|
color_index += 1;
|
819
|
|
|
|
820
|
|
|
for item in noise:
|
821
|
|
|
if (dimension == 1):
|
822
|
|
|
if (data is None):
|
823
|
|
|
axes.plot(item[0], 0.0, 'w' + marker_descr);
|
824
|
|
|
else:
|
825
|
|
|
axes.plot(data[item][0], 0.0, 'w' + marker_descr);
|
826
|
|
|
|
827
|
|
|
if (dimension == 2):
|
828
|
|
|
if (data is None):
|
829
|
|
|
axes.plot(item[0], item[1], 'w' + marker_descr);
|
830
|
|
|
else:
|
831
|
|
|
axes.plot(data[item][0], data[item][1], 'w' + marker_descr);
|
832
|
|
|
|
833
|
|
|
elif (dimension == 3):
|
834
|
|
|
if (data is None):
|
835
|
|
|
axes.scatter(item[0], item[1], item[2], c = 'w', marker = marker_descr);
|
836
|
|
|
else:
|
837
|
|
|
axes.scatter(data[item][0], data[item][1], data[item][2], c = 'w', marker = marker_descr);
|
838
|
|
|
|
839
|
|
|
axes.grid(True);
|
840
|
|
|
|
841
|
|
|
if (hide_axes is True):
|
842
|
|
|
axes.xaxis.set_ticklabels([]);
|
843
|
|
|
axes.yaxis.set_ticklabels([]);
|
844
|
|
|
|
845
|
|
|
if (dimension == 3):
|
846
|
|
|
axes.zaxis.set_ticklabels([]);
|
847
|
|
|
|
848
|
|
|
if (display_result is True):
|
849
|
|
|
plt.show();
|
850
|
|
|
|
851
|
|
|
return axes;
|
852
|
|
|
|
853
|
|
|
|
854
|
|
|
def draw_dynamics(t, dyn, x_title = None, y_title = None, x_lim = None, y_lim = None, x_labels = True, y_labels = True, separate = False, axes = None):
|
855
|
|
|
"""!
|
856
|
|
|
@brief Draw dynamics of neurons (oscillators) in the network.
|
857
|
|
|
@details It draws if matplotlib is not specified (None), othewise it should be performed manually.
|
858
|
|
|
|
859
|
|
|
@param[in] t (list): Values of time (used by x axis).
|
860
|
|
|
@param[in] dyn (list): Values of output of oscillators (used by y axis).
|
861
|
|
|
@param[in] x_title (string): Title for Y.
|
862
|
|
|
@param[in] y_title (string): Title for X.
|
863
|
|
|
@param[in] x_lim (double): X limit.
|
864
|
|
|
@param[in] y_lim (double): Y limit.
|
865
|
|
|
@param[in] x_labels (bool): If True - shows X labels.
|
866
|
|
|
@param[in] y_labels (bool): If True - shows Y labels.
|
867
|
|
|
@param[in] separate (list): Consists of lists of oscillators where each such list consists of oscillator indexes that will be shown on separated stage.
|
868
|
|
|
@param[in] axes (ax): If specified then matplotlib axes will be used for drawing and plot will not be shown.
|
869
|
|
|
|
870
|
|
|
@return (ax) Axes of matplotlib.
|
871
|
|
|
|
872
|
|
|
"""
|
873
|
|
|
|
874
|
|
|
number_lines = 0;
|
875
|
|
|
|
876
|
|
|
stage_xlim = None;
|
877
|
|
|
if (x_lim is not None):
|
878
|
|
|
stage_xlim = x_lim;
|
879
|
|
|
elif (len(t) > 0):
|
880
|
|
|
stage_xlim = [0, t[len(t) - 1]];
|
881
|
|
|
|
882
|
|
|
if ( (isinstance(separate, bool) is True) and (separate is True) ):
|
883
|
|
|
if (isinstance(dyn[0], list) is True):
|
884
|
|
|
number_lines = len(dyn[0]);
|
885
|
|
|
else:
|
886
|
|
|
number_lines = 1;
|
887
|
|
|
|
888
|
|
|
elif (isinstance(separate, list) is True):
|
889
|
|
|
number_lines = len(separate);
|
890
|
|
|
|
891
|
|
|
else:
|
892
|
|
|
number_lines = 1;
|
893
|
|
|
|
894
|
|
|
dysplay_result = False;
|
895
|
|
|
if (axes is None):
|
896
|
|
|
dysplay_result = True;
|
897
|
|
|
(fig, axes) = plt.subplots(number_lines, 1);
|
898
|
|
|
|
899
|
|
|
# Check if we have more than one dynamic
|
900
|
|
|
if (isinstance(dyn[0], list) is True):
|
901
|
|
|
num_items = len(dyn[0]);
|
902
|
|
|
for index in range(0, num_items, 1):
|
903
|
|
|
y = [item[index] for item in dyn];
|
904
|
|
|
|
905
|
|
|
if (number_lines > 1):
|
906
|
|
|
index_stage = -1;
|
907
|
|
|
|
908
|
|
|
# Find required axes for the y
|
909
|
|
|
if (isinstance(separate, bool) is True):
|
910
|
|
|
index_stage = index;
|
911
|
|
|
|
912
|
|
|
elif (isinstance(separate, list) is True):
|
913
|
|
|
for index_group in range(0, len(separate), 1):
|
914
|
|
|
if (index in separate[index_group]):
|
915
|
|
|
index_stage = index_group;
|
916
|
|
|
break;
|
917
|
|
|
|
918
|
|
|
if (index_stage != -1):
|
919
|
|
|
if (index_stage != number_lines - 1):
|
920
|
|
|
axes[index_stage].get_xaxis().set_visible(False);
|
921
|
|
|
|
922
|
|
|
axes[index_stage].plot(t, y, 'b-', linewidth = 0.5);
|
923
|
|
|
set_ax_param(axes[index_stage], x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
|
924
|
|
|
|
925
|
|
|
else:
|
926
|
|
|
axes.plot(t, y, 'b-', linewidth = 0.5);
|
927
|
|
|
set_ax_param(axes, x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
|
928
|
|
|
else:
|
929
|
|
|
axes.plot(t, dyn, 'b-', linewidth = 0.5);
|
930
|
|
|
set_ax_param(axes, x_title, y_title, stage_xlim, y_lim, x_labels, y_labels, True);
|
931
|
|
|
|
932
|
|
|
if (dysplay_result is True):
|
933
|
|
|
plt.show();
|
934
|
|
|
|
935
|
|
|
return axes;
|
936
|
|
|
|
937
|
|
|
|
938
|
|
|
def set_ax_param(ax, x_title = None, y_title = None, x_lim = None, y_lim = None, x_labels = True, y_labels = True, grid = True):
|
939
|
|
|
"""!
|
940
|
|
|
@brief Sets parameters for matplotlib ax.
|
941
|
|
|
|
942
|
|
|
@param[in] ax (Axes): Axes for which parameters should applied.
|
943
|
|
|
@param[in] x_title (string): Title for Y.
|
944
|
|
|
@param[in] y_title (string): Title for X.
|
945
|
|
|
@param[in] x_lim (double): X limit.
|
946
|
|
|
@param[in] y_lim (double): Y limit.
|
947
|
|
|
@param[in] x_labels (bool): If True - shows X labels.
|
948
|
|
|
@param[in] y_labels (bool): If True - shows Y labels.
|
949
|
|
|
@param[in] grid (bool): If True - shows grid.
|
950
|
|
|
|
951
|
|
|
"""
|
952
|
|
|
from matplotlib.font_manager import FontProperties;
|
953
|
|
|
from matplotlib import rcParams;
|
954
|
|
|
|
955
|
|
|
if (_platform == "linux") or (_platform == "linux2"):
|
956
|
|
|
rcParams['font.sans-serif'] = ['Liberation Serif'];
|
957
|
|
|
else:
|
958
|
|
|
rcParams['font.sans-serif'] = ['Arial'];
|
959
|
|
|
|
960
|
|
|
rcParams['font.size'] = 12;
|
961
|
|
|
|
962
|
|
|
surface_font = FontProperties();
|
963
|
|
|
if (_platform == "linux") or (_platform == "linux2"):
|
964
|
|
|
surface_font.set_name('Liberation Serif');
|
965
|
|
|
else:
|
966
|
|
|
surface_font.set_name('Arial');
|
967
|
|
|
|
968
|
|
|
surface_font.set_size('12');
|
969
|
|
|
|
970
|
|
|
if (y_title is not None): ax.set_ylabel(y_title, fontproperties = surface_font);
|
971
|
|
|
if (x_title is not None): ax.set_xlabel(x_title, fontproperties = surface_font);
|
972
|
|
|
|
973
|
|
|
if (x_lim is not None): ax.set_xlim(x_lim[0], x_lim[1]);
|
974
|
|
|
if (y_lim is not None): ax.set_ylim(y_lim[0], y_lim[1]);
|
975
|
|
|
|
976
|
|
|
if (x_labels is False): ax.xaxis.set_ticklabels([]);
|
977
|
|
|
if (y_labels is False): ax.yaxis.set_ticklabels([]);
|
978
|
|
|
|
979
|
|
|
ax.grid(grid);
|
980
|
|
|
|
981
|
|
|
|
982
|
|
|
def draw_dynamics_set(dynamics, xtitle = None, ytitle = None, xlim = None, ylim = None, xlabels = False, ylabels = False):
|
983
|
|
|
"""!
|
984
|
|
|
@brief Draw lists of dynamics of neurons (oscillators) in the network.
|
985
|
|
|
|
986
|
|
|
@param[in] dynamics (list): List of network outputs that are represented by values of output of oscillators (used by y axis).
|
987
|
|
|
@param[in] xtitle (string): Title for Y.
|
988
|
|
|
@param[in] ytitle (string): Title for X.
|
989
|
|
|
@param[in] xlim (double): X limit.
|
990
|
|
|
@param[in] ylim (double): Y limit.
|
991
|
|
|
@param[in] xlabels (bool): If True - shows X labels.
|
992
|
|
|
@param[in] ylabels (bool): If True - shows Y labels.
|
993
|
|
|
|
994
|
|
|
"""
|
995
|
|
|
# Calculate edge for confortable representation.
|
996
|
|
|
number_dynamics = len(dynamics);
|
997
|
|
|
if (number_dynamics == 1):
|
998
|
|
|
draw_dynamics(dynamics[0][0], dynamics[0][1], xtitle, ytitle, xlim, ylim, xlabels, ylabels);
|
999
|
|
|
return;
|
1000
|
|
|
|
1001
|
|
|
number_cols = int(numpy.ceil(number_dynamics ** 0.5));
|
1002
|
|
|
number_rows = int(numpy.ceil(number_dynamics / number_cols));
|
1003
|
|
|
|
1004
|
|
|
real_index = 0, 0;
|
1005
|
|
|
double_indexer = True;
|
1006
|
|
|
if ( (number_cols == 1) or (number_rows == 1) ):
|
1007
|
|
|
real_index = 0;
|
1008
|
|
|
double_indexer = False;
|
1009
|
|
|
|
1010
|
|
|
(_, axarr) = plt.subplots(number_rows, number_cols);
|
1011
|
|
|
#plt.setp([ax for ax in axarr], visible = False);
|
1012
|
|
|
|
1013
|
|
|
for dynamic in dynamics:
|
1014
|
|
|
axarr[real_index] = draw_dynamics(dynamic[0], dynamic[1], xtitle, ytitle, xlim, ylim, xlabels, ylabels, axes = axarr[real_index]);
|
1015
|
|
|
#plt.setp(axarr[real_index], visible = True);
|
1016
|
|
|
|
1017
|
|
|
if (double_indexer is True):
|
1018
|
|
|
real_index = real_index[0], real_index[1] + 1;
|
1019
|
|
|
if (real_index[1] >= number_cols):
|
1020
|
|
|
real_index = real_index[0] + 1, 0;
|
1021
|
|
|
else:
|
1022
|
|
|
real_index += 1;
|
1023
|
|
|
|
1024
|
|
|
plt.show();
|
1025
|
|
|
|
1026
|
|
|
|
1027
|
|
|
def draw_image_color_segments(source, clusters, hide_axes = True):
|
1028
|
|
|
"""!
|
1029
|
|
|
@brief Shows image segments using colored image.
|
1030
|
|
|
@details Each color on result image represents allocated segment. The first image is initial and other is result of segmentation.
|
1031
|
|
|
|
1032
|
|
|
@param[in] source (string): Path to image.
|
1033
|
|
|
@param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
|
1034
|
|
|
consists of indexes of pixel from source image.
|
1035
|
|
|
@param[in] hide_axes (bool): If True then axes will not be displayed.
|
1036
|
|
|
|
1037
|
|
|
"""
|
1038
|
|
|
|
1039
|
|
|
image_source = Image.open(source);
|
1040
|
|
|
image_size = image_source.size;
|
1041
|
|
|
|
1042
|
|
|
(fig, axarr) = plt.subplots(1, 2);
|
1043
|
|
|
|
1044
|
|
|
plt.setp([ax for ax in axarr], visible = False);
|
1045
|
|
|
|
1046
|
|
|
available_colors = [ (0, 162, 232), (34, 177, 76), (237, 28, 36),
|
1047
|
|
|
(255, 242, 0), (0, 0, 0), (237, 28, 36),
|
1048
|
|
|
(255, 174, 201), (127, 127, 127), (185, 122, 87),
|
1049
|
|
|
(200, 191, 231), (136, 0, 21), (255, 127, 39),
|
1050
|
|
|
(63, 72, 204), (195, 195, 195), (255, 201, 14),
|
1051
|
|
|
(239, 228, 176), (181, 230, 29), (153, 217, 234),
|
1052
|
|
|
(112, 146, 180) ];
|
1053
|
|
|
|
1054
|
|
|
image_color_segments = [(255, 255, 255)] * (image_size[0] * image_size[1]);
|
1055
|
|
|
|
1056
|
|
|
for index_segment in range(len(clusters)):
|
1057
|
|
|
for index_pixel in clusters[index_segment]:
|
1058
|
|
|
image_color_segments[index_pixel] = available_colors[index_segment];
|
1059
|
|
|
|
1060
|
|
|
stage = array(image_color_segments, numpy.uint8);
|
1061
|
|
|
stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
|
1062
|
|
|
image_cluster = Image.fromarray(stage, 'RGB');
|
1063
|
|
|
|
1064
|
|
|
axarr[0].imshow(image_source, interpolation = 'none');
|
1065
|
|
|
axarr[1].imshow(image_cluster, interpolation = 'none');
|
1066
|
|
|
|
1067
|
|
|
for i in range(2):
|
1068
|
|
|
plt.setp(axarr[i], visible = True);
|
1069
|
|
|
|
1070
|
|
|
if (hide_axes is True):
|
1071
|
|
|
axarr[i].xaxis.set_ticklabels([]);
|
1072
|
|
|
axarr[i].yaxis.set_ticklabels([]);
|
1073
|
|
|
axarr[i].xaxis.set_ticks_position('none');
|
1074
|
|
|
axarr[i].yaxis.set_ticks_position('none');
|
1075
|
|
|
|
1076
|
|
|
plt.show();
|
1077
|
|
|
|
1078
|
|
|
|
1079
|
|
|
def draw_image_mask_segments(source, clusters, hide_axes = True):
|
1080
|
|
|
"""!
|
1081
|
|
|
@brief Shows image segments using black masks.
|
1082
|
|
|
@details Each black mask of allocated segment is presented on separate plot.
|
1083
|
|
|
The first image is initial and others are black masks of segments.
|
1084
|
|
|
|
1085
|
|
|
@param[in] source (string): Path to image.
|
1086
|
|
|
@param[in] clusters (list): List of clusters (allocated segments of image) where each cluster
|
1087
|
|
|
consists of indexes of pixel from source image.
|
1088
|
|
|
@param[in] hide_axes (bool): If True then axes will not be displayed.
|
1089
|
|
|
|
1090
|
|
|
"""
|
1091
|
|
|
if (len(clusters) == 0):
|
1092
|
|
|
print("Warning: Nothing to draw - list of clusters is empty.")
|
1093
|
|
|
return;
|
1094
|
|
|
|
1095
|
|
|
image_source = Image.open(source);
|
1096
|
|
|
image_size = image_source.size;
|
1097
|
|
|
|
1098
|
|
|
# Calculate edge for confortable representation.
|
1099
|
|
|
number_clusters = len(clusters) + 1; # show with the source image
|
1100
|
|
|
|
1101
|
|
|
number_cols = int(numpy.ceil(number_clusters ** 0.5));
|
1102
|
|
|
number_rows = int(numpy.ceil(number_clusters / number_cols));
|
1103
|
|
|
|
1104
|
|
|
|
1105
|
|
|
real_index = 0, 0;
|
1106
|
|
|
double_indexer = True;
|
1107
|
|
|
if ( (number_cols == 1) or (number_rows == 1) ):
|
1108
|
|
|
real_index = 0;
|
1109
|
|
|
double_indexer = False;
|
1110
|
|
|
|
1111
|
|
|
(fig, axarr) = plt.subplots(number_rows, number_cols);
|
1112
|
|
|
plt.setp([ax for ax in axarr], visible = False);
|
1113
|
|
|
|
1114
|
|
|
axarr[real_index].imshow(image_source, interpolation = 'none');
|
1115
|
|
|
plt.setp(axarr[real_index], visible = True);
|
1116
|
|
|
|
1117
|
|
|
if (hide_axes is True):
|
1118
|
|
|
axarr[real_index].xaxis.set_ticklabels([]);
|
1119
|
|
|
axarr[real_index].yaxis.set_ticklabels([]);
|
1120
|
|
|
axarr[real_index].xaxis.set_ticks_position('none');
|
1121
|
|
|
axarr[real_index].yaxis.set_ticks_position('none');
|
1122
|
|
|
|
1123
|
|
|
if (double_indexer is True):
|
1124
|
|
|
real_index = 0, 1;
|
1125
|
|
|
else:
|
1126
|
|
|
real_index += 1;
|
1127
|
|
|
|
1128
|
|
|
for cluster in clusters:
|
1129
|
|
|
stage_cluster = [(255, 255, 255)] * (image_size[0] * image_size[1]);
|
1130
|
|
|
for index in cluster:
|
1131
|
|
|
stage_cluster[index] = (0, 0, 0);
|
1132
|
|
|
|
1133
|
|
|
stage = array(stage_cluster, numpy.uint8);
|
1134
|
|
|
stage = numpy.reshape(stage, (image_size[1], image_size[0]) + ((3),)); # ((3),) it's size of RGB - third dimension.
|
1135
|
|
|
|
1136
|
|
|
image_cluster = Image.fromarray(stage, 'RGB');
|
1137
|
|
|
|
1138
|
|
|
axarr[real_index].imshow(image_cluster, interpolation = 'none');
|
1139
|
|
|
plt.setp(axarr[real_index], visible = True);
|
1140
|
|
|
|
1141
|
|
|
if (hide_axes is True):
|
1142
|
|
|
axarr[real_index].xaxis.set_ticklabels([]);
|
1143
|
|
|
axarr[real_index].yaxis.set_ticklabels([]);
|
1144
|
|
|
|
1145
|
|
|
axarr[real_index].xaxis.set_ticks_position('none');
|
1146
|
|
|
axarr[real_index].yaxis.set_ticks_position('none');
|
1147
|
|
|
|
1148
|
|
|
if (double_indexer is True):
|
1149
|
|
|
real_index = real_index[0], real_index[1] + 1;
|
1150
|
|
|
if (real_index[1] >= number_cols):
|
1151
|
|
|
real_index = real_index[0] + 1, 0;
|
1152
|
|
|
else:
|
1153
|
|
|
real_index += 1;
|
1154
|
|
|
|
1155
|
|
|
|
1156
|
|
|
plt.show();
|
1157
|
|
|
|
1158
|
|
|
|
1159
|
|
|
def linear_sum(list_vector):
|
1160
|
|
|
"""!
|
1161
|
|
|
@brief Calculates linear sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
|
1162
|
|
|
|
1163
|
|
|
@param[in] list_vector (list): Input vector.
|
1164
|
|
|
|
1165
|
|
|
@return (list|double) Linear sum of vector that can be represented by list in case of multidimensional elements.
|
1166
|
|
|
|
1167
|
|
|
"""
|
1168
|
|
|
dimension = 1;
|
1169
|
|
|
linear_sum = 0.0;
|
1170
|
|
|
list_representation = (type(list_vector[0]) == list);
|
1171
|
|
|
|
1172
|
|
|
if (list_representation is True):
|
1173
|
|
|
dimension = len(list_vector[0]);
|
1174
|
|
|
linear_sum = [0] * dimension;
|
1175
|
|
|
|
1176
|
|
|
for index_element in range(0, len(list_vector)):
|
1177
|
|
|
if (list_representation is True):
|
1178
|
|
|
for index_dimension in range(0, dimension):
|
1179
|
|
|
linear_sum[index_dimension] += list_vector[index_element][index_dimension];
|
1180
|
|
|
else:
|
1181
|
|
|
linear_sum += list_vector[index_element];
|
1182
|
|
|
|
1183
|
|
|
return linear_sum;
|
1184
|
|
|
|
1185
|
|
|
|
1186
|
|
|
def square_sum(list_vector):
|
1187
|
|
|
"""!
|
1188
|
|
|
@brief Calculates square sum of vector that is represented by list, each element can be represented by list - multidimensional elements.
|
1189
|
|
|
|
1190
|
|
|
@param[in] list_vector (list): Input vector.
|
1191
|
|
|
|
1192
|
|
|
@return (double) Square sum of vector.
|
1193
|
|
|
|
1194
|
|
|
"""
|
1195
|
|
|
|
1196
|
|
|
square_sum = 0.0;
|
1197
|
|
|
list_representation = (type(list_vector[0]) == list);
|
1198
|
|
|
|
1199
|
|
|
for index_element in range(0, len(list_vector)):
|
1200
|
|
|
if (list_representation is True):
|
1201
|
|
|
square_sum += sum(list_math_multiplication(list_vector[index_element], list_vector[index_element]));
|
1202
|
|
|
else:
|
1203
|
|
|
square_sum += list_vector[index_element] * list_vector[index_element];
|
1204
|
|
|
|
1205
|
|
|
return square_sum;
|
1206
|
|
|
|
1207
|
|
|
|
1208
|
|
|
def list_math_subtraction(a, b):
|
1209
|
|
|
"""!
|
1210
|
|
|
@brief Calculates subtraction of two lists.
|
1211
|
|
|
@details Each element from list 'a' is subtracted by element from list 'b' accordingly.
|
1212
|
|
|
|
1213
|
|
|
@param[in] a (list): List of elements that supports mathematical subtraction.
|
1214
|
|
|
@param[in] b (list): List of elements that supports mathematical subtraction.
|
1215
|
|
|
|
1216
|
|
|
@return (list) Results of subtraction of two lists.
|
1217
|
|
|
|
1218
|
|
|
"""
|
1219
|
|
|
return [a[i] - b[i] for i in range(len(a))];
|
1220
|
|
|
|
1221
|
|
|
|
1222
|
|
|
def list_math_substraction_number(a, b):
|
1223
|
|
|
"""!
|
1224
|
|
|
@brief Calculates subtraction between list and number.
|
1225
|
|
|
@details Each element from list 'a' is subtracted by number 'b'.
|
1226
|
|
|
|
1227
|
|
|
@param[in] a (list): List of elements that supports mathematical subtraction.
|
1228
|
|
|
@param[in] b (list): Value that supports mathematical subtraction.
|
1229
|
|
|
|
1230
|
|
|
@return (list) Results of subtraction between list and number.
|
1231
|
|
|
|
1232
|
|
|
"""
|
1233
|
|
|
return [a[i] - b for i in range(len(a))];
|
1234
|
|
|
|
1235
|
|
|
|
1236
|
|
|
def list_math_addition(a, b):
|
1237
|
|
|
"""!
|
1238
|
|
|
@brief Addition of two lists.
|
1239
|
|
|
@details Each element from list 'a' is added to element from list 'b' accordingly.
|
1240
|
|
|
|
1241
|
|
|
@param[in] a (list): List of elements that supports mathematic addition..
|
1242
|
|
|
@param[in] b (list): List of elements that supports mathematic addition..
|
1243
|
|
|
|
1244
|
|
|
@return (list) Results of addtion of two lists.
|
1245
|
|
|
|
1246
|
|
|
"""
|
1247
|
|
|
return [a[i] + b[i] for i in range(len(a))];
|
1248
|
|
|
|
1249
|
|
|
|
1250
|
|
|
def list_math_addition_number(a, b):
|
1251
|
|
|
"""!
|
1252
|
|
|
@brief Addition between list and number.
|
1253
|
|
|
@details Each element from list 'a' is added to number 'b'.
|
1254
|
|
|
|
1255
|
|
|
@param[in] a (list): List of elements that supports mathematic addition.
|
1256
|
|
|
@param[in] b (double): Value that supports mathematic addition.
|
1257
|
|
|
|
1258
|
|
|
@return (list) Result of addtion of two lists.
|
1259
|
|
|
|
1260
|
|
|
"""
|
1261
|
|
|
return [a[i] + b for i in range(len(a))];
|
1262
|
|
|
|
1263
|
|
|
|
1264
|
|
|
def list_math_division_number(a, b):
|
1265
|
|
|
"""!
|
1266
|
|
|
@brief Division between list and number.
|
1267
|
|
|
@details Each element from list 'a' is divided by number 'b'.
|
1268
|
|
|
|
1269
|
|
|
@param[in] a (list): List of elements that supports mathematic division.
|
1270
|
|
|
@param[in] b (double): Value that supports mathematic division.
|
1271
|
|
|
|
1272
|
|
|
@return (list) Result of division between list and number.
|
1273
|
|
|
|
1274
|
|
|
"""
|
1275
|
|
|
return [a[i] / b for i in range(len(a))];
|
1276
|
|
|
|
1277
|
|
|
|
1278
|
|
|
def list_math_division(a, b):
|
1279
|
|
|
"""!
|
1280
|
|
|
@brief Division of two lists.
|
1281
|
|
|
@details Each element from list 'a' is divided by element from list 'b' accordingly.
|
1282
|
|
|
|
1283
|
|
|
@param[in] a (list): List of elements that supports mathematic division.
|
1284
|
|
|
@param[in] b (list): List of elements that supports mathematic division.
|
1285
|
|
|
|
1286
|
|
|
@return (list) Result of division of two lists.
|
1287
|
|
|
|
1288
|
|
|
"""
|
1289
|
|
|
return [a[i] / b[i] for i in range(len(a))];
|
1290
|
|
|
|
1291
|
|
|
|
1292
|
|
|
def list_math_multiplication_number(a, b):
|
1293
|
|
|
"""!
|
1294
|
|
|
@brief Multiplication between list and number.
|
1295
|
|
|
@details Each element from list 'a' is multiplied by number 'b'.
|
1296
|
|
|
|
1297
|
|
|
@param[in] a (list): List of elements that supports mathematic division.
|
1298
|
|
|
@param[in] b (double): Number that supports mathematic division.
|
1299
|
|
|
|
1300
|
|
|
@return (list) Result of division between list and number.
|
1301
|
|
|
|
1302
|
|
|
"""
|
1303
|
|
|
return [a[i] * b for i in range(len(a))];
|
1304
|
|
|
|
1305
|
|
|
|
1306
|
|
|
def list_math_multiplication(a, b):
|
1307
|
|
|
"""!
|
1308
|
|
|
@brief Multiplication of two lists.
|
1309
|
|
|
@details Each element from list 'a' is multiplied by element from list 'b' accordingly.
|
1310
|
|
|
|
1311
|
|
|
@param[in] a (list): List of elements that supports mathematic multiplication.
|
1312
|
|
|
@param[in] b (list): List of elements that supports mathematic multiplication.
|
1313
|
|
|
|
1314
|
|
|
@return (list) Result of multiplication of elements in two lists.
|
1315
|
|
|
|
1316
|
|
|
"""
|
1317
|
|
|
return [a[i] * b[i] for i in range(len(a))];
|
1318
|
|
|
|
Escape sequences in Python are generally interpreted according to rules similar to standard C. Only if strings are prefixed with
r
orR
are they interpreted as regular expressions.The escape sequence that was used indicates that you might have intended to write a regular expression.
Learn more about the available escape sequences. in the Python documentation.