Chickaboo/Advanced-MIDI-Renderer
1
1#! /usr/bin/python3
2
3r'''############################################################################
4################################################################################
5#
6#
7# Tegridy Plots Python Module (TPLOTS)
8# Version 1.0
9#
10# Project Los Angeles
11#
12# Tegridy Code 2025
13#
14# https://github.com/asigalov61/tegridy-tools
15#
16#
17################################################################################
18#
19# Copyright 2024 Project Los Angeles / Tegridy Code
20#
21# Licensed under the Apache License, Version 2.0 (the "License");
22# you may not use this file except in compliance with the License.
23# You may obtain a copy of the License at
24#
25# http://www.apache.org/licenses/LICENSE-2.0
26#
27# Unless required by applicable law or agreed to in writing, software
28# distributed under the License is distributed on an "AS IS" BASIS,
29# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
30# See the License for the specific language governing permissions and
31# limitations under the License.
32#
33################################################################################
34################################################################################
35#
36# Critical dependencies
37#
38# !pip install numpy==1.24.4
39# !pip install scipy
40# !pip install matplotlib
41# !pip install networkx
42# !pip3 install scikit-learn
43#
44################################################################################
45#
46# Future critical dependencies
47#
48# !pip install umap-learn
49# !pip install alphashape
50#
51################################################################################
52'''
53
54################################################################################
55# Modules imports
56################################################################################
57
58import os
59from collections import Counter
60from itertools import groupby
61
62import numpy as np
63
64import networkx as nx
65
66from sklearn.manifold import TSNE
67from sklearn import metrics
68from sklearn.preprocessing import MinMaxScaler
69from sklearn.decomposition import PCA
70
71from scipy.ndimage import zoom
72from scipy.spatial import distance_matrix
73from scipy.sparse.csgraph import minimum_spanning_tree
74from scipy.stats import zscore
75
76import matplotlib.pyplot as plt
77from PIL import Image
78
79################################################################################
80# Constants
81################################################################################
82
83ALL_CHORDS_FULL = [[0], [0, 3], [0, 3, 5], [0, 3, 5, 8], [0, 3, 5, 9], [0, 3, 5, 10], [0, 3, 6],
84 [0, 3, 6, 9], [0, 3, 6, 10], [0, 3, 7], [0, 3, 7, 10], [0, 3, 8], [0, 3, 9],
85 [0, 3, 10], [0, 4], [0, 4, 6], [0, 4, 6, 9], [0, 4, 6, 10], [0, 4, 7],
86 [0, 4, 7, 10], [0, 4, 8], [0, 4, 9], [0, 4, 10], [0, 5], [0, 5, 8], [0, 5, 9],
87 [0, 5, 10], [0, 6], [0, 6, 9], [0, 6, 10], [0, 7], [0, 7, 10], [0, 8], [0, 9],
88 [0, 10], [1], [1, 4], [1, 4, 6], [1, 4, 6, 9], [1, 4, 6, 10], [1, 4, 6, 11],
89 [1, 4, 7], [1, 4, 7, 10], [1, 4, 7, 11], [1, 4, 8], [1, 4, 8, 11], [1, 4, 9],
90 [1, 4, 10], [1, 4, 11], [1, 5], [1, 5, 8], [1, 5, 8, 11], [1, 5, 9],
91 [1, 5, 10], [1, 5, 11], [1, 6], [1, 6, 9], [1, 6, 10], [1, 6, 11], [1, 7],
92 [1, 7, 10], [1, 7, 11], [1, 8], [1, 8, 11], [1, 9], [1, 10], [1, 11], [2],
93 [2, 5], [2, 5, 8], [2, 5, 8, 11], [2, 5, 9], [2, 5, 10], [2, 5, 11], [2, 6],
94 [2, 6, 9], [2, 6, 10], [2, 6, 11], [2, 7], [2, 7, 10], [2, 7, 11], [2, 8],
95 [2, 8, 11], [2, 9], [2, 10], [2, 11], [3], [3, 5], [3, 5, 8], [3, 5, 8, 11],
96 [3, 5, 9], [3, 5, 10], [3, 5, 11], [3, 6], [3, 6, 9], [3, 6, 10], [3, 6, 11],
97 [3, 7], [3, 7, 10], [3, 7, 11], [3, 8], [3, 8, 11], [3, 9], [3, 10], [3, 11],
98 [4], [4, 6], [4, 6, 9], [4, 6, 10], [4, 6, 11], [4, 7], [4, 7, 10], [4, 7, 11],
99 [4, 8], [4, 8, 11], [4, 9], [4, 10], [4, 11], [5], [5, 8], [5, 8, 11], [5, 9],
100 [5, 10], [5, 11], [6], [6, 9], [6, 10], [6, 11], [7], [7, 10], [7, 11], [8],
101 [8, 11], [9], [10], [11]]
102
103################################################################################
104
105CHORDS_TYPES = ['WHITE', 'BLACK', 'UNKNOWN', 'MIXED WHITE', 'MIXED BLACK', 'MIXED GRAY']
106
107################################################################################
108
109WHITE_NOTES = [0, 2, 4, 5, 7, 9, 11]
110
111################################################################################
112
113BLACK_NOTES = [1, 3, 6, 8, 10]
114
115################################################################################
116# Helper functions
117################################################################################
118
119def tones_chord_type(tones_chord,
120 return_chord_type_index=True,
121 ):
122
123 """
124 Returns tones chord type
125 """
126
127 WN = WHITE_NOTES
128 BN = BLACK_NOTES
129 MX = WHITE_NOTES + BLACK_NOTES
130
131
132 CHORDS = ALL_CHORDS_FULL
133
134 tones_chord = sorted(tones_chord)
135
136 ctype = 'UNKNOWN'
137
138 if tones_chord in CHORDS:
139
140 if sorted(set(tones_chord) & set(WN)) == tones_chord:
141 ctype = 'WHITE'
142
143 elif sorted(set(tones_chord) & set(BN)) == tones_chord:
144 ctype = 'BLACK'
145
146 if len(tones_chord) > 1 and sorted(set(tones_chord) & set(MX)) == tones_chord:
147
148 if len(sorted(set(tones_chord) & set(WN))) == len(sorted(set(tones_chord) & set(BN))):
149 ctype = 'MIXED GRAY'
150
151 elif len(sorted(set(tones_chord) & set(WN))) > len(sorted(set(tones_chord) & set(BN))):
152 ctype = 'MIXED WHITE'
153
154 elif len(sorted(set(tones_chord) & set(WN))) < len(sorted(set(tones_chord) & set(BN))):
155 ctype = 'MIXED BLACK'
156
157 if return_chord_type_index:
158 return CHORDS_TYPES.index(ctype)
159
160 else:
161 return ctype
162
163###################################################################################
164
165def tone_type(tone,
166 return_tone_type_index=True
167 ):
168
169 """
170 Returns tone type
171 """
172
173 tone = tone % 12
174
175 if tone in BLACK_NOTES:
176 if return_tone_type_index:
177 return CHORDS_TYPES.index('BLACK')
178 else:
179 return "BLACK"
180
181 else:
182 if return_tone_type_index:
183 return CHORDS_TYPES.index('WHITE')
184 else:
185 return "WHITE"
186
187###################################################################################
188
189def find_closest_points(points, return_points=True):
190
191 """
192 Find closest 2D points
193 """
194
195 coords = np.array(points)
196
197 num_points = coords.shape[0]
198 closest_matches = np.zeros(num_points, dtype=int)
199 distances = np.zeros((num_points, num_points))
200
201 for i in range(num_points):
202 for j in range(num_points):
203 if i != j:
204 distances[i, j] = np.linalg.norm(coords[i] - coords[j])
205 else:
206 distances[i, j] = np.inf
207
208 closest_matches = np.argmin(distances, axis=1)
209
210 if return_points:
211 points_matches = coords[closest_matches].tolist()
212 return points_matches
213
214 else:
215 return closest_matches.tolist()
216
217################################################################################
218
219def reduce_dimensionality_tsne(list_of_valies,
220 n_comp=2,
221 n_iter=5000,
222 verbose=True
223 ):
224
225 """
226 Reduces the dimensionality of the values using t-SNE.
227 """
228
229 vals = np.array(list_of_valies)
230
231 tsne = TSNE(n_components=n_comp,
232 n_iter=n_iter,
233 verbose=verbose)
234
235 reduced_vals = tsne.fit_transform(vals)
236
237 return reduced_vals.tolist()
238
239################################################################################
240
241def compute_mst_edges(similarity_scores_list):
242
243 """
244 Computes the Minimum Spanning Tree (MST) edges based on the similarity scores.
245 """
246
247 num_tokens = len(similarity_scores_list[0])
248
249 graph = nx.Graph()
250
251 for i in range(num_tokens):
252 for j in range(i + 1, num_tokens):
253 weight = 1 - similarity_scores_list[i][j]
254 graph.add_edge(i, j, weight=weight)
255
256 mst = nx.minimum_spanning_tree(graph)
257
258 mst_edges = list(mst.edges(data=False))
259
260 return mst_edges
261
262################################################################################
263
264def square_binary_matrix(binary_matrix,
265 matrix_size=128,
266 interpolation_order=5,
267 return_square_matrix_points=False
268 ):
269
270 """
271 Reduces an arbitrary binary matrix to a square binary matrix
272 """
273
274 zoom_factors = (matrix_size / len(binary_matrix), 1)
275
276 resized_matrix = zoom(binary_matrix, zoom_factors, order=interpolation_order)
277
278 resized_matrix = (resized_matrix > 0.5).astype(int)
279
280 final_matrix = np.zeros((matrix_size, matrix_size), dtype=int)
281 final_matrix[:, :resized_matrix.shape[1]] = resized_matrix
282
283 points = np.column_stack(np.where(final_matrix == 1)).tolist()
284
285 if return_square_matrix_points:
286 return points
287
288 else:
289 return resized_matrix
290
291################################################################################
292
293def square_matrix_points_colors(square_matrix_points):
294
295 """
296 Returns colors for square matrix points
297 """
298
299 cmap = generate_colors(12)
300
301 chords = []
302 chords_dict = set()
303 counts = []
304
305 for k, v in groupby(square_matrix_points, key=lambda x: x[0]):
306 pgroup = [vv[1] for vv in v]
307 chord = sorted(set(pgroup))
308 tchord = sorted(set([p % 12 for p in chord]))
309 chords_dict.add(tuple(tchord))
310 chords.append(tuple(tchord))
311 counts.append(len(pgroup))
312
313 chords_dict = sorted(chords_dict)
314
315 colors = []
316
317 for i, c in enumerate(chords):
318 colors.extend([cmap[round(sum(c) / len(c))]] * counts[i])
319
320 return colors
321
322################################################################################
323
324def hsv_to_rgb(h, s, v):
325
326 if s == 0.0:
327 return v, v, v
328
329 i = int(h*6.0)
330 f = (h*6.0) - i
331 p = v*(1.0 - s)
332 q = v*(1.0 - s*f)
333 t = v*(1.0 - s*(1.0-f))
334 i = i%6
335
336 return [(v, t, p), (q, v, p), (p, v, t), (p, q, v), (t, p, v), (v, p, q)][i]
337
338################################################################################
339
340def generate_colors(n):
341 return [hsv_to_rgb(i/n, 1, 1) for i in range(n)]
342
343################################################################################
344
345def add_arrays(a, b):
346 return [sum(pair) for pair in zip(a, b)]
347
348################################################################################
349
350def calculate_similarities(lists_of_values, metric='cosine'):
351 return metrics.pairwise_distances(lists_of_values, metric=metric).tolist()
352
353################################################################################
354
355def get_tokens_embeddings(x_transformer_model):
356 return x_transformer_model.net.token_emb.emb.weight.detach().cpu().tolist()
357
358################################################################################
359
360def minkowski_distance_matrix(X, p=3):
361
362 X = np.array(X)
363
364 n = X.shape[0]
365 dist_matrix = np.zeros((n, n))
366
367 for i in range(n):
368 for j in range(n):
369 dist_matrix[i, j] = np.sum(np.abs(X[i] - X[j])**p)**(1/p)
370
371 return dist_matrix.tolist()
372
373################################################################################
374
375def robust_normalize(values):
376
377 values = np.array(values)
378 q1 = np.percentile(values, 25)
379 q3 = np.percentile(values, 75)
380 iqr = q3 - q1
381
382 filtered_values = values[(values >= q1 - 1.5 * iqr) & (values <= q3 + 1.5 * iqr)]
383
384 min_val = np.min(filtered_values)
385 max_val = np.max(filtered_values)
386 normalized_values = (values - min_val) / (max_val - min_val)
387
388 normalized_values = np.clip(normalized_values, 0, 1)
389
390 return normalized_values.tolist()
391
392################################################################################
393
394def min_max_normalize(values):
395
396 scaler = MinMaxScaler()
397
398 return scaler.fit_transform(values).tolist()
399
400################################################################################
401
402def remove_points_outliers(points, z_score_threshold=3):
403
404 points = np.array(points)
405
406 z_scores = np.abs(zscore(points, axis=0))
407
408 return points[(z_scores < z_score_threshold).all(axis=1)].tolist()
409
410################################################################################
411
412def generate_labels(lists_of_values,
413 return_indices_labels=False
414 ):
415
416 ordered_indices = list(range(len(lists_of_values)))
417 ordered_indices_labels = [str(i) for i in ordered_indices]
418 ordered_values_labels = [str(lists_of_values[i]) for i in ordered_indices]
419
420 if return_indices_labels:
421 return ordered_indices_labels
422
423 else:
424 return ordered_values_labels
425
426################################################################################
427
428def reduce_dimensionality_pca(list_of_values, n_components=2):
429
430 """
431 Reduces the dimensionality of the values using PCA.
432 """
433
434 pca = PCA(n_components=n_components)
435 pca_data = pca.fit_transform(list_of_values)
436
437 return pca_data.tolist()
438
439def reduce_dimensionality_simple(list_of_values,
440 return_means=True,
441 return_std_devs=True,
442 return_medians=False,
443 return_vars=False
444 ):
445
446 '''
447 Reduces dimensionality of the values in a simple way
448 '''
449
450 array = np.array(list_of_values)
451 results = []
452
453 if return_means:
454 means = np.mean(array, axis=1)
455 results.append(means)
456
457 if return_std_devs:
458 std_devs = np.std(array, axis=1)
459 results.append(std_devs)
460
461 if return_medians:
462 medians = np.median(array, axis=1)
463 results.append(medians)
464
465 if return_vars:
466 vars = np.var(array, axis=1)
467 results.append(vars)
468
469 merged_results = np.column_stack(results)
470
471 return merged_results.tolist()
472
473################################################################################
474
475def reduce_dimensionality_2d_distance(list_of_values, p=5):
476
477 '''
478 Reduces the dimensionality of the values using 2d distance
479 '''
480
481 values = np.array(list_of_values)
482
483 dist_matrix = distance_matrix(values, values, p=p)
484
485 mst = minimum_spanning_tree(dist_matrix).toarray()
486
487 points = []
488
489 for i in range(len(values)):
490 for j in range(len(values)):
491 if mst[i, j] > 0:
492 points.append([i, j])
493
494 return points
495
496################################################################################
497
498def normalize_to_range(values, n):
499
500 min_val = min(values)
501 max_val = max(values)
502
503 range_val = max_val - min_val
504
505 normalized_values = [((value - min_val) / range_val * 2 * n) - n for value in values]
506
507 return normalized_values
508
509################################################################################
510
511def reduce_dimensionality_simple_pca(list_of_values, n_components=2):
512
513 '''
514 Reduces the dimensionality of the values using simple PCA
515 '''
516
517 reduced_values = []
518
519 for l in list_of_values:
520
521 norm_values = [round(v * len(l)) for v in normalize_to_range(l, (n_components+1) // 2)]
522
523 pca_values = Counter(norm_values).most_common()
524 pca_values = [vv[0] / len(l) for vv in pca_values]
525 pca_values = pca_values[:n_components]
526 pca_values = pca_values + [0] * (n_components - len(pca_values))
527
528 reduced_values.append(pca_values)
529
530 return reduced_values
531
532################################################################################
533
534def filter_and_replace_values(list_of_values,
535 threshold,
536 replace_value,
537 replace_above_threshold=False
538 ):
539
540 array = np.array(list_of_values)
541
542 modified_array = np.copy(array)
543
544 if replace_above_threshold:
545 modified_array[modified_array > threshold] = replace_value
546
547 else:
548 modified_array[modified_array < threshold] = replace_value
549
550 return modified_array.tolist()
551
552################################################################################
553
554def find_shortest_constellation_path(points,
555 start_point_idx,
556 end_point_idx,
557 p=5,
558 return_path_length=False,
559 return_path_points=False,
560 ):
561
562 """
563 Finds the shortest path between two points of the points constellation
564 """
565
566 points = np.array(points)
567
568 dist_matrix = distance_matrix(points, points, p=p)
569
570 mst = minimum_spanning_tree(dist_matrix).toarray()
571
572 G = nx.Graph()
573
574 for i in range(len(points)):
575 for j in range(len(points)):
576 if mst[i, j] > 0:
577 G.add_edge(i, j, weight=mst[i, j])
578
579 path = nx.shortest_path(G,
580 source=start_point_idx,
581 target=end_point_idx,
582 weight='weight'
583 )
584
585 path_length = nx.shortest_path_length(G,
586 source=start_point_idx,
587 target=end_point_idx,
588 weight='weight')
589
590 path_points = points[np.array(path)].tolist()
591
592
593 if return_path_points:
594 return path_points
595
596 if return_path_length:
597 return path_length
598
599 return path
600
601################################################################################
602# Core functions
603################################################################################
604
605def plot_ms_SONG(ms_song,
606 preview_length_in_notes=0,
607 block_lines_times_list = None,
608 plot_title='ms Song',
609 max_num_colors=129,
610 drums_color_num=128,
611 plot_size=(11,4),
612 note_height = 0.75,
613 show_grid_lines=False,
614 return_plt = False,
615 timings_multiplier=1,
616 save_plt='',
617 save_only_plt_image=True,
618 save_transparent=False
619 ):
620
621 '''ms SONG plot'''
622
623 notes = [s for s in ms_song if s[0] == 'note']
624
625 if (len(max(notes, key=len)) != 7) and (len(min(notes, key=len)) != 7):
626 print('The song notes do not have patches information')
627 print('Ploease add patches to the notes in the song')
628
629 else:
630
631 start_times = [(s[1] * timings_multiplier) / 1000 for s in notes]
632 durations = [(s[2] * timings_multiplier) / 1000 for s in notes]
633 pitches = [s[4] for s in notes]
634 patches = [s[6] for s in notes]
635
636 colors = generate_colors(max_num_colors)
637 colors[drums_color_num] = (1, 1, 1)
638
639 pbl = (notes[preview_length_in_notes][1] * timings_multiplier) / 1000
640
641 fig, ax = plt.subplots(figsize=plot_size)
642
643 for start, duration, pitch, patch in zip(start_times, durations, pitches, patches):
644 rect = plt.Rectangle((start, pitch), duration, note_height, facecolor=colors[patch])
645 ax.add_patch(rect)
646
647 ax.set_xlim([min(start_times), max(add_arrays(start_times, durations))])
648 ax.set_ylim([min(pitches)-1, max(pitches)+1])
649
650 ax.set_facecolor('black')
651 fig.patch.set_facecolor('white')
652
653 if preview_length_in_notes > 0:
654 ax.axvline(x=pbl, c='white')
655
656 if block_lines_times_list:
657 for bl in block_lines_times_list:
658 ax.axvline(x=bl, c='white')
659
660 if show_grid_lines:
661 ax.grid(color='white')
662
663 plt.xlabel('Time (s)', c='black')
664 plt.ylabel('MIDI Pitch', c='black')
665
666 plt.title(plot_title)
667
668 if save_plt != '':
669 if save_only_plt_image:
670 plt.axis('off')
671 plt.title('')
672 plt.savefig(save_plt,
673 transparent=save_transparent,
674 bbox_inches='tight',
675 pad_inches=0,
676 facecolor='black'
677 )
678 plt.close()
679
680 else:
681 plt.savefig(save_plt)
682 plt.close()
683
684 if return_plt:
685 return fig
686
687 plt.show()
688 plt.close()
689
690################################################################################
691
692def plot_square_matrix_points(list_of_points,
693 list_of_points_colors,
694 plot_size=(7, 7),
695 point_size = 10,
696 show_grid_lines=False,
697 plot_title = 'Square Matrix Points Plot',
698 return_plt=False,
699 save_plt='',
700 save_only_plt_image=True,
701 save_transparent=False
702 ):
703
704 '''Square matrix points plot'''
705
706 fig, ax = plt.subplots(figsize=plot_size)
707
708 ax.set_facecolor('black')
709
710 if show_grid_lines:
711 ax.grid(color='white')
712
713 plt.xlabel('Time Step', c='black')
714 plt.ylabel('MIDI Pitch', c='black')
715
716 plt.title(plot_title)
717
718 plt.scatter([p[0] for p in list_of_points],
719 [p[1] for p in list_of_points],
720 c=list_of_points_colors,
721 s=point_size
722 )
723
724 if save_plt != '':
725 if save_only_plt_image:
726 plt.axis('off')
727 plt.title('')
728 plt.savefig(save_plt,
729 transparent=save_transparent,
730 bbox_inches='tight',
731 pad_inches=0,
732 facecolor='black'
733 )
734 plt.close()
735
736 else:
737 plt.savefig(save_plt)
738 plt.close()
739
740 if return_plt:
741 return fig
742
743 plt.show()
744 plt.close()
745
746################################################################################
747
748def plot_cosine_similarities(lists_of_values,
749 plot_size=(7, 7),
750 save_plot=''
751 ):
752
753 """
754 Cosine similarities plot
755 """
756
757 cos_sim = metrics.pairwise_distances(lists_of_values, metric='cosine')
758
759 plt.figure(figsize=plot_size)
760
761 plt.imshow(cos_sim, cmap="inferno", interpolation="nearest")
762
763 im_ratio = cos_sim.shape[0] / cos_sim.shape[1]
764
765 plt.colorbar(fraction=0.046 * im_ratio, pad=0.04)
766
767 plt.xlabel("Index")
768 plt.ylabel("Index")
769
770 plt.tight_layout()
771
772 if save_plot != '':
773 plt.savefig(save_plot, bbox_inches="tight")
774 plt.close()
775
776 plt.show()
777 plt.close()
778
779################################################################################
780
781def plot_points_with_mst_lines(points,
782 points_labels,
783 points_mst_edges,
784 plot_size=(20, 20),
785 labels_size=24,
786 save_plot=''
787 ):
788
789 """
790 Plots 2D points with labels and MST lines.
791 """
792
793 plt.figure(figsize=plot_size)
794
795 for i, label in enumerate(points_labels):
796 plt.scatter(points[i][0], points[i][1])
797 plt.annotate(label, (points[i][0], points[i][1]), fontsize=labels_size)
798
799 for edge in points_mst_edges:
800 i, j = edge
801 plt.plot([points[i][0], points[j][0]], [points[i][1], points[j][1]], 'k-', alpha=0.5)
802
803 plt.title('Points Map with MST Lines', fontsize=labels_size)
804 plt.xlabel('X-axis', fontsize=labels_size)
805 plt.ylabel('Y-axis', fontsize=labels_size)
806
807 if save_plot != '':
808 plt.savefig(save_plot, bbox_inches="tight")
809 plt.close()
810
811 plt.show()
812
813 plt.close()
814
815################################################################################
816
817def plot_points_constellation(points,
818 points_labels,
819 p=5,
820 plot_size=(15, 15),
821 labels_size=12,
822 show_grid=False,
823 save_plot=''
824 ):
825
826 """
827 Plots 2D points constellation
828 """
829
830 points = np.array(points)
831
832 dist_matrix = distance_matrix(points, points, p=p)
833
834 mst = minimum_spanning_tree(dist_matrix).toarray()
835
836 plt.figure(figsize=plot_size)
837
838 plt.scatter(points[:, 0], points[:, 1], color='blue')
839
840 for i, label in enumerate(points_labels):
841 plt.annotate(label, (points[i, 0], points[i, 1]),
842 textcoords="offset points",
843 xytext=(0, 10),
844 ha='center',
845 fontsize=labels_size
846 )
847
848 for i in range(len(points)):
849 for j in range(len(points)):
850 if mst[i, j] > 0:
851 plt.plot([points[i, 0], points[j, 0]], [points[i, 1], points[j, 1]], 'k--')
852
853 plt.xlabel('X-axis', fontsize=labels_size)
854 plt.ylabel('Y-axis', fontsize=labels_size)
855 plt.title('2D Coordinates with Minimum Spanning Tree', fontsize=labels_size)
856
857 plt.grid(show_grid)
858
859 if save_plot != '':
860 plt.savefig(save_plot, bbox_inches="tight")
861 plt.close()
862
863 plt.show()
864
865 plt.close()
866
867################################################################################
868
869def binary_matrix_to_images(matrix,
870 step,
871 overlap,
872 output_folder='./Dataset/',
873 output_img_prefix='image',
874 output_img_ext='.png',
875 save_to_array=False,
876 verbose=True
877 ):
878
879 if not save_to_array:
880
881 if verbose:
882 print('=' * 70)
883 print('Checking output folder dir...')
884
885 os.makedirs(os.path.dirname(output_folder), exist_ok=True)
886
887 if verbose:
888 print('Done!')
889
890 if verbose:
891 print('=' * 70)
892 print('Writing images...')
893
894 matrix = np.array(matrix, dtype=np.uint8)
895
896 image_array = []
897
898 for i in range(0, max(1, matrix.shape[0]), overlap):
899
900 submatrix = matrix[i:i+step, :]
901
902 if submatrix.shape[0] < 128:
903 zeros_array = np.zeros((128-submatrix.shape[0], 128))
904 submatrix = np.vstack((submatrix, zeros_array))
905
906 img = Image.fromarray(submatrix * 255).convert('1')
907
908 if save_to_array:
909 image_array.append(np.array(img))
910
911 else:
912 img.save(output_folder + output_img_prefix + '_' + str(matrix.shape[1]) + '_' + str(i).zfill(7) + output_img_ext)
913
914 if verbose:
915 print('Done!')
916 print('=' * 70)
917 print('Saved', (matrix.shape[0] // min(step, overlap))+1, 'imges!')
918 print('=' * 70)
919
920 if save_to_array:
921 return np.array(image_array).tolist()
922
923################################################################################
924
925def images_to_binary_matrix(list_of_images):
926
927 image_array = np.array(list_of_images)
928
929 original_matrix = []
930
931 for img in image_array:
932
933 submatrix = np.array(img)
934 original_matrix.extend(submatrix.tolist())
935
936 return original_matrix
937
938################################################################################
939
940def square_image_matrix(image_matrix,
941 matrix_size=128,
942 num_pca_components=5,
943 filter_out_zero_rows=False,
944 return_square_matrix_points=False
945 ):
946
947 """
948 Reduces an arbitrary image matrix to a square image matrix
949 """
950
951 matrix = np.array(image_matrix)
952
953 if filter_out_zero_rows:
954 matrix = matrix[~np.all(matrix == 0, axis=1)]
955
956 target_rows = matrix_size
957
958 rows_per_group = matrix.shape[0] // target_rows
959
960 compressed_matrix = np.zeros((target_rows, matrix.shape[1]), dtype=np.int32)
961
962 for i in range(target_rows):
963 start_row = i * rows_per_group
964 end_row = (i + 1) * rows_per_group
965 group = matrix[start_row:end_row, :]
966
967 pca = PCA(n_components=num_pca_components)
968 pca.fit(group)
969
970 principal_component = np.mean(pca.components_, axis=0)
971 contributions = np.dot(group, principal_component)
972 selected_row_index = np.argmax(contributions)
973
974 compressed_matrix[i, :] = group[selected_row_index, :]
975
976 if return_square_matrix_points:
977 filtered_matrix = compressed_matrix[~np.all(compressed_matrix == 0, axis=1)]
978
979 row_indexes, col_indexes = np.where(filtered_matrix != 0)
980 points = np.column_stack((row_indexes, filtered_matrix[row_indexes, col_indexes])).tolist()
981
982 return points
983
984 else:
985 return compressed_matrix.tolist()
986
987################################################################################
988
989def image_matrix_to_images(image_matrix,
990 step,
991 overlap,
992 num_img_channels=3,
993 output_folder='./Dataset/',
994 output_img_prefix='image',
995 output_img_ext='.png',
996 save_to_array=False,
997 verbose=True
998 ):
999
1000 if num_img_channels > 1:
1001 n_mat_channels = 3
1002
1003 else:
1004 n_mat_channels = 1
1005
1006 if not save_to_array:
1007
1008 if verbose:
1009 print('=' * 70)
1010 print('Checking output folder dir...')
1011
1012 os.makedirs(os.path.dirname(output_folder), exist_ok=True)
1013
1014 if verbose:
1015 print('Done!')
1016
1017 if verbose:
1018 print('=' * 70)
1019 print('Writing images...')
1020
1021 matrix = np.array(image_matrix)
1022
1023 image_array = []
1024
1025 for i in range(0, max(1, matrix.shape[0]), overlap):
1026
1027 submatrix = matrix[i:i+step, :]
1028
1029 if submatrix.shape[0] < 128:
1030 zeros_array = np.zeros((128-submatrix.shape[0], 128))
1031 submatrix = np.vstack((submatrix, zeros_array))
1032
1033 if n_mat_channels == 3:
1034
1035 r = (submatrix // (256*256)) % 256
1036 g = (submatrix // 256) % 256
1037 b = submatrix % 256
1038
1039 rgb_image = np.stack((r, g, b), axis=-1).astype(np.uint8)
1040 img = Image.fromarray(rgb_image, 'RGB')
1041
1042 else:
1043 grayscale_image = submatrix.astype(np.uint8)
1044 img = Image.fromarray(grayscale_image, 'L')
1045
1046 if save_to_array:
1047 image_array.append(np.array(img))
1048
1049 else:
1050 img.save(output_folder + output_img_prefix + '_' + str(matrix.shape[1]) + '_' + str(i).zfill(7) + output_img_ext)
1051
1052 if verbose:
1053 print('Done!')
1054 print('=' * 70)
1055 print('Saved', (matrix.shape[0] // min(step, overlap))+1, 'imges!')
1056 print('=' * 70)
1057
1058 if save_to_array:
1059 return np.array(image_array).tolist()
1060
1061################################################################################
1062
1063def images_to_image_matrix(list_of_images,
1064 num_img_channels=3
1065 ):
1066
1067 if num_img_channels > 1:
1068 n_mat_channels = 3
1069
1070 else:
1071 n_mat_channels = 1
1072
1073 image_array = np.array(list_of_images)
1074
1075 original_matrix = []
1076
1077 for img in image_array:
1078
1079 if num_img_channels == 3:
1080
1081 rgb_array = np.array(img)
1082
1083 matrix = (rgb_array[..., 0].astype(np.int64) * 256*256 +
1084 rgb_array[..., 1].astype(np.int64) * 256 +
1085 rgb_array[..., 2].astype(np.int64))
1086
1087 else:
1088 matrix = np.array(img)
1089
1090 original_matrix.extend(matrix)
1091
1092 return original_matrix
1093
1094################################################################################
1095
1096def square_matrix_to_RGB_matrix(square_matrix):
1097
1098 smatrix = np.array(square_matrix)
1099 sq_matrix = smatrix[:smatrix.shape[1]]
1100
1101 r = (sq_matrix // (256 ** 2)) % 256
1102 g = (sq_matrix // 256) % 256
1103 b = sq_matrix % 256
1104
1105 rgb_array = np.stack((r, g, b), axis=-1)
1106
1107 return rgb_array.tolist()
1108
1109################################################################################
1110
1111def upsample_square_matrix(square_matrix, upsampling_factor=4):
1112
1113 smatrix = np.array(square_matrix)
1114 sq_matrix = smatrix[:smatrix.shape[1]]
1115
1116 scaling_array = np.ones((upsampling_factor, upsampling_factor))
1117 scaled_array = np.kron(sq_matrix, scaling_array)
1118 scaled_array = scaled_array.astype('int')
1119
1120 return scaled_array.tolist()
1121
1122################################################################################
1123
1124def downsample_square_matrix(square_matrix, downsampling_factor=4):
1125
1126 smatrix = np.array(square_matrix)
1127 sq_matrix = smatrix[:smatrix.shape[1]]
1128
1129 dmatrix = sq_matrix[::downsampling_factor, ::downsampling_factor]
1130 dmatrix = dmatrix.astype('int')
1131
1132 return dmatrix.tolist()
1133
1134################################################################################
1135
1136def plot_parsons_code(parsons_code,
1137 start_pitch=60,
1138 return_plot_dict=False,
1139 return_plot_string=False,
1140 plot_size=(10, 10),
1141 labels_size=16,
1142 save_plot=''
1143 ):
1144
1145 '''
1146 Plot parsons code string
1147 '''
1148
1149 if parsons_code[0] != "*":
1150 return None
1151
1152 contour_dict = {}
1153 pitch = 0
1154 index = 0
1155
1156 maxp = 0
1157 minp = 0
1158
1159 contour_dict[(pitch, index)] = "*"
1160
1161 for point in parsons_code:
1162 if point == "R":
1163 index += 1
1164 contour_dict[(pitch, index)] = "-"
1165
1166 index += 1
1167 contour_dict[(pitch, index)] = "*"
1168
1169 elif point == "U":
1170 index += 1
1171 pitch -= 1
1172 contour_dict[(pitch, index)] = "/"
1173
1174 index += 1
1175 pitch -= 1
1176 contour_dict[(pitch, index)] = "*"
1177
1178 if pitch < maxp:
1179 maxp = pitch
1180
1181 elif point == "D":
1182 index += 1
1183 pitch += 1
1184 contour_dict[(pitch, index)] = "\\"
1185
1186 index += 1
1187 pitch += 1
1188 contour_dict[(pitch, index)] = "*"
1189
1190 if pitch > minp:
1191 minp = pitch
1192
1193 if return_plot_dict:
1194 return contour_dict
1195
1196 if return_plot_string:
1197
1198 plot_string = ''
1199
1200 for pitch in range(maxp, minp+1):
