Completed
Push — master ( 9abba1...044b53 )
by Andrei
03:00
created

rgb2gray()   B

Complexity

Conditions 2

Size

Total Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

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