Completed
Push — master ( 31b63e...5ba327 )
by Andrei
01:29
created

stretch_pattern()   A

Complexity

Conditions 2

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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