syncsegm.py
1 """!
2 
3 @brief Double-layer oscillatory network with phase oscillator for image segmentation.
4 @details Implementation based on paper @cite inproceedings::nnet::syncsegm::1.
5 
6 @authors Andrei Novikov (pyclustering@yandex.ru)
7 @date 2014-2020
8 @copyright GNU Public License
9 
10 @cond GNU_PUBLIC_LICENSE
11  PyClustering is free software: you can redistribute it and/or modify
12  it under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  PyClustering is distributed in the hope that it will be useful,
17  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  GNU General Public License for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with this program. If not, see <http://www.gnu.org/licenses/>.
23 @endcond
24 
25 """
26 
27 import warnings
28 
29 from math import floor
30 
31 try:
32  from PIL import Image
33 except Exception as error_instance:
34  warnings.warn("Impossible to import PIL (please, install 'PIL'), pyclustering's visualization "
35  "functionality is partially not available (details: '%s')." % str(error_instance))
36 
37 from pyclustering.cluster.syncnet import syncnet
38 
39 from pyclustering.nnet import solve_type, initial_type
40 from pyclustering.nnet.sync import sync_visualizer
41 
42 from pyclustering.utils import read_image
43 
44 
46  """!
47  @brief Result visualizer of double-layer oscillatory network 'syncsegm'.
48 
49  """
50 
51  @staticmethod
52  def show_first_layer_dynamic(analyser):
53  """!
54  @brief Shows output dynamic of the first layer.
55 
56  @param[in] analyser (syncsegm_analyser): Analyser of output dynamic of the 'syncsegm' oscillatory network.
57 
58  """
59 
60  sync_visualizer.show_output_dynamic(analyser.get_first_layer_analyser());
61 
62 
63  @staticmethod
65  """!
66  @brief Shows output dynamic of the second layer.
67 
68  @param[in] analyser (syncsegm_analyser): Analyser of output dynamic of the 'syncsegm' oscillatory network.
69 
70  """
71 
72  second_layer_analysers = analyser.get_second_layer_analysers();
73  analysers_sequence = [ object_segment_analyser['analyser'] for object_segment_analyser in second_layer_analysers ]
74 
75  sync_visualizer.show_output_dynamics(analysers_sequence);
76 
77 
79  """!
80  @brief Performs analysis of output dynamic of the double-layer oscillatory network 'syncsegm' to extract information about segmentation results.
81 
82  """
83 
84  def __init__(self, color_analyser, object_segment_analysers = None):
85  """!
86  @brief Constructor of the analyser.
87 
88  @param[in] color_analyser (list): Analyser of coloring segmentation results of the first layer.
89  @param[in] object_segment_analysers (list): Analysers of objects on image segments - results of the second layer.
90 
91  """
92 
93  self.__color_analyser = color_analyser;
94  self.__object_segment_analysers = object_segment_analysers;
95 
96 
98  """!
99  @brief Returns analyser of coloring segmentation of the first layer.
100 
101  """
102 
103  return self.__color_analyser;
104 
105 
107  """!
108  @brief Returns analysers of object segmentation of the second layer.
109 
110  """
111 
112  return self.__object_segment_analysers;
113 
114 
115  def allocate_colors(self, eps = 0.01, noise_size = 1):
116  """!
117  @brief Allocates color segments.
118 
119  @param[in] eps (double): Tolerance level that define maximal difference between phases of oscillators in one segment.
120  @param[in] noise_size (uint): Threshold that defines noise - segments size (in pixels) that is less then the threshold is considered as a noise.
121 
122  @return (list) Color segments where each color segment consists of indexes of pixels that forms color segment.
123 
124  """
125 
126  segments = self.__color_analyser.allocate_clusters(eps);
127  real_segments = [cluster for cluster in segments if len(cluster) > noise_size];
128  return real_segments;
129 
130 
131  def allocate_objects(self, eps = 0.01, noise_size = 1):
132  """!
133  @brief Allocates object segments.
134 
135  @param[in] eps (double): Tolerance level that define maximal difference between phases of oscillators in one segment.
136  @param[in] noise_size (uint): Threshold that defines noise - segments size (in pixels) that is less then the threshold is considered as a noise.
137 
138  @return (list) Object segments where each object segment consists of indexes of pixels that forms object segment.
139 
140  """
141 
142  if (self.__object_segment_analysers is None):
143  return [];
144 
145  segments = [];
146  for object_segment_analyser in self.__object_segment_analysers:
147  indexes = object_segment_analyser['color_segment'];
148  analyser = object_segment_analyser['analyser'];
149 
150  segments += analyser.allocate_clusters(eps, indexes);
151 
152  real_segments = [segment for segment in segments if len(segment) > noise_size];
153  return real_segments;
154 
155 
156 class syncsegm:
157  """!
158  @brief Class represents segmentation algorithm syncsegm.
159  @details syncsegm is a bio-inspired algorithm that is based on double-layer oscillatory network that uses modified Kuramoto model.
160  Algorithm extracts colors and colored objects. It uses only CCORE (C++ implementation of pyclustering) parts to implement the algorithm.
161 
162  CCORE option is True by default to use sync network in the pyclustering core - C/C++ shared library for processing that significantly increases performance.
163 
164  Example:
165  @code
166  # create oscillatory for image segmentaion - extract colors (radius 128) and objects (radius 4),
167  # and ignore noise (segments with size that is less than 10 pixels)
168  algorithm = syncsegm(128, 4, 10);
169 
170  # extract segments (colors and objects)
171  analyser = algorithm(path_to_file);
172 
173  # obtain segmentation results (only colors - from the first layer)
174  color_segments = analyser.allocate_colors(0.01, 10);
175  draw_image_mask_segments(path_to_file, color_segments);
176 
177  # obtain segmentation results (objects - from the second layer)
178  object_segments = analyser.allocate_objects(0.01, 10);
179  draw_image_mask_segments(path_to_file, object_segments);
180  @endcode
181 
182  """
183 
184  def __init__(self, color_radius, object_radius, noise_size = 0, ccore = True):
185  """!
186  @brief Contructor of the oscillatory network SYNC for cluster analysis.
187 
188  @param[in] color_radius (double): Radius of color connectivity (color similarity) for the first layer.
189  @param[in] object_radius (double): Radius of object connectivity (object similarity) for the second layer,
190  if 'None' then object segmentation is not performed (only color segmentation).
191  @param[in] noise_size (double): Size of segment that should be considered as a noise and ignored by the second layer.
192  @param[in] ccore (bool): If 'True' then C/C++ implementation is used to increase performance.
193 
194  """
195 
196  self.__color_radius = color_radius;
197  self.__object_radius = object_radius;
198  self.__noise_size = noise_size;
199 
200  self.__order_color = 0.9995;
201  self.__order_object = 0.999;
202 
203  self.__network = None;
204  self.__ccore = ccore;
205 
206 
207  def process(self, image_source, collect_dynamic = False, order_color = 0.9995, order_object = 0.999):
208  """!
209  @brief Performs image segmentation.
210 
211  @param[in] image_source (string): Path to image file that should be processed.
212  @param[in] collect_dynamic (bool): If 'True' then whole dynamic of each layer of the network is collected.
213  @param[in] order_color (double): Local synchronization order for the first layer - coloring segmentation.
214  @param[in] order_object (double): Local synchronization order for the second layer - object segmentation.
215 
216  @return (syncsegm_analyser) Analyser of segmentation results by the network.
217 
218  """
219 
220  self.__order_color = order_color
221  self.__order_object = order_object
222 
223  data = read_image(image_source)
224  color_analyser = self.__analyse_colors(data, collect_dynamic)
225 
226  if self.__object_radius is None:
227  return syncsegm_analyser(color_analyser, None)
228 
229  object_segment_analysers = self.__analyse_objects(image_source, color_analyser, collect_dynamic)
230  return syncsegm_analyser(color_analyser, object_segment_analysers)
231 
232 
233  def __analyse_colors(self, image_data, collect_dynamic):
234  """!
235  @brief Performs color segmentation by the first layer.
236 
237  @param[in] image_data (array_like): Image sample as a array-like structure.
238  @param[in] collect_dynamic (bool): If 'True' then whole dynamic of the first layer of the network is collected.
239 
240  @return (syncnet_analyser) Analyser of color segmentation results of the first layer.
241 
242  """
243 
244  network = syncnet(image_data, self.__color_radius, initial_phases = initial_type.RANDOM_GAUSSIAN, ccore = self.__ccore);
245  analyser = network.process(self.__order_color, solve_type.FAST, collect_dynamic);
246 
247  return analyser;
248 
249 
250  def __analyse_objects(self, image_source, color_analyser, collect_dynamic):
251  """!
252  @brief Performs object segmentation by the second layer.
253 
254  @param[in] image_source (string): Path to image file that should be processed.
255  @param[in] color_analyser (syncnet_analyser): Analyser of color segmentation results.
256  @param[in] collect_dynamic (bool): If 'True' then whole dynamic of the first layer of the network is collected.
257 
258  @return (map) Analysers of object segments.
259 
260  """
261 
262  # continue analysis
263  pointer_image = Image.open(image_source);
264  image_size = pointer_image.size;
265 
266  object_analysers = [];
267 
268  color_segments = color_analyser.allocate_clusters();
269 
270  for segment in color_segments:
271  object_analyser = self.__analyse_color_segment(image_size, segment, collect_dynamic);
272  if (object_analyser is not None):
273  object_analysers.append( { 'color_segment': segment, 'analyser': object_analyser } );
274 
275  pointer_image.close();
276  return object_analysers;
277 
278 
279  def __analyse_color_segment(self, image_size, color_segment, collect_dynamic):
280  """!
281  @brief Performs object segmentation of separate segment.
282 
283  @param[in] image_size (list): Image size presented as a [width x height].
284  @param[in] color_segment (list): Image segment that should be processed.
285  @param[in] collect_dynamic (bool): If 'True' then whole dynamic of the second layer of the network is collected.
286 
287  @return (syncnet_analyser) Analyser of object segmentation results of the second layer.
288 
289  """
290  coordinates = self.__extract_location_coordinates(image_size, color_segment);
291 
292  if (len(coordinates) < self.__noise_size):
293  return None;
294 
295  network = syncnet(coordinates, self.__object_radius, initial_phases = initial_type.EQUIPARTITION, ccore = True);
296  analyser = network.process(self.__order_object, solve_type.FAST, collect_dynamic);
297 
298  return analyser;
299 
300 
301  def __extract_location_coordinates(self, image_size, color_segment):
302  """!
303  @brief Extracts coordinates of specified image segment.
304 
305  @param[in] image_size (list): Image size presented as a [width x height].
306  @param[in] color_segment (list): Image segment whose coordinates should be extracted.
307 
308  @return (list) Coordinates of each pixel.
309 
310  """
311  coordinates = [];
312  for index in color_segment:
313  y = floor(index / image_size[0]);
314  x = index - y * image_size[0];
315 
316  coordinates.append([x, y]);
317 
318  return coordinates;
319 
def __init__(self, color_analyser, object_segment_analysers=None)
Constructor of the analyser.
Definition: syncsegm.py:84
def __analyse_objects(self, image_source, color_analyser, collect_dynamic)
Performs object segmentation by the second layer.
Definition: syncsegm.py:250
def get_second_layer_analysers(self)
Returns analysers of object segmentation of the second layer.
Definition: syncsegm.py:106
def get_first_layer_analyser(self)
Returns analyser of coloring segmentation of the first layer.
Definition: syncsegm.py:97
def __init__(self, color_radius, object_radius, noise_size=0, ccore=True)
Contructor of the oscillatory network SYNC for cluster analysis.
Definition: syncsegm.py:184
Utils that are used by modules of pyclustering.
Definition: __init__.py:1
def show_first_layer_dynamic(analyser)
Shows output dynamic of the first layer.
Definition: syncsegm.py:52
Class represents clustering algorithm SyncNet.
Definition: syncnet.py:166
def __analyse_color_segment(self, image_size, color_segment, collect_dynamic)
Performs object segmentation of separate segment.
Definition: syncsegm.py:279
def __extract_location_coordinates(self, image_size, color_segment)
Extracts coordinates of specified image segment.
Definition: syncsegm.py:301
Result visualizer of double-layer oscillatory network &#39;syncsegm&#39;.
Definition: syncsegm.py:45
Neural Network: Oscillatory Neural Network based on Kuramoto model.
Definition: sync.py:1
def show_second_layer_dynamic(analyser)
Shows output dynamic of the second layer.
Definition: syncsegm.py:64
def allocate_objects(self, eps=0.01, noise_size=1)
Allocates object segments.
Definition: syncsegm.py:131
def allocate_colors(self, eps=0.01, noise_size=1)
Allocates color segments.
Definition: syncsegm.py:115
Cluster analysis algorithm: Sync.
Definition: syncnet.py:1
def __analyse_colors(self, image_data, collect_dynamic)
Performs color segmentation by the first layer.
Definition: syncsegm.py:233
def process(self, image_source, collect_dynamic=False, order_color=0.9995, order_object=0.999)
Performs image segmentation.
Definition: syncsegm.py:207
Class represents segmentation algorithm syncsegm.
Definition: syncsegm.py:156
Performs analysis of output dynamic of the double-layer oscillatory network &#39;syncsegm&#39; to extract inf...
Definition: syncsegm.py:78
Neural and oscillatory network module.
Definition: __init__.py:1