99from tqdm import tqdm
1010from typing import Optional , Tuple
1111from scipy .spatial .distance import pdist
12+ import random
13+ import re
1214
1315try :
1416 from detectron2 .engine import DefaultPredictor
@@ -122,7 +124,6 @@ def _create_error_return(self) -> Tuple:
122124 None ,
123125 gr .update (visible = False ),
124126 gr .update (visible = False ),
125- gr .update (visible = False ),
126127 None ,
127128 None ,
128129 None ,
@@ -152,8 +153,10 @@ def analyze_image(
152153 overlap_width_ratio : float ,
153154 sahi_mode : bool ,
154155 number_of_bins : int ,
155- segment_mode : bool ,
156156 show_Feret_diametr : bool ,
157+ outline_color ,
158+ show_fillPoly ,
159+ show_polylines ,
157160 api_key : bool ,
158161 request : gr .Request ,
159162 pr = gr .Progress (),
@@ -223,6 +226,9 @@ def analyze_image(
223226 "scale" : scale ,
224227 "scale_factor_glob" : scale_factor_glob ,
225228 "show_Feret_diametr" : show_Feret_diametr ,
229+ "outline_color" : outline_color ,
230+ "show_fillPoly" : show_fillPoly ,
231+ "show_polylines" : show_polylines ,
226232 }
227233
228234 # Выбор стратегии обработки
@@ -233,6 +239,8 @@ def analyze_image(
233239
234240 output_image = cv2 .cvtColor (output_image , cv2 .COLOR_BGR2RGB )
235241 df = pd .DataFrame (particle_data )
242+ points_df = pd .DataFrame (df ["points" ])
243+ df = df .drop (columns = ["points" ])
236244
237245 pbar .set_description (self ._get_translation ("Построение таблицы..." ))
238246 pr (0.75 , desc = self ._get_translation ("Построение таблицы..." ))
@@ -245,20 +253,42 @@ def analyze_image(
245253 lang = self .lang ,
246254 )
247255 stats_df = builder .build_stats_table ()
248- d_max_max = stats_df .data .iloc [1 , 3 ] + stats_df .data .iloc [0 , 3 ] * 0.01
249- d_max_min = stats_df .data .iloc [1 , 4 ]
250- d_min_max = stats_df .data .iloc [2 , 3 ] + stats_df .data .iloc [1 , 3 ] * 0.01
251- d_min_min = stats_df .data .iloc [2 , 4 ]
252- theta_max_max = stats_df .data .iloc [4 , 3 ] + stats_df .data .iloc [4 , 3 ] * 0.01
253- theta_max_min = stats_df .data .iloc [4 , 4 ]
254- theta_min_max = stats_df .data .iloc [5 , 3 ] + stats_df .data .iloc [5 , 3 ] * 0.01
255- theta_min_min = stats_df .data .iloc [5 , 4 ]
256- S_max = stats_df .data .iloc [6 , 3 ] + stats_df .data .iloc [6 , 3 ] * 0.01
257- S_min = stats_df .data .iloc [6 , 4 ]
258- P_max = stats_df .data .iloc [7 , 3 ] + stats_df .data .iloc [7 , 3 ] * 0.01
259- P_min = stats_df .data .iloc [7 , 4 ]
260- I_max = stats_df .data .iloc [9 , 3 ] + stats_df .data .iloc [9 , 3 ] * 0.01
261- I_min = stats_df .data .iloc [9 , 4 ]
256+
257+ d_max_max = round (
258+ stats_df .data .iloc [1 , 3 ] + stats_df .data .iloc [0 , 3 ] * 0.01 ,
259+ config ["round_value" ],
260+ )
261+ d_max_min = round (stats_df .data .iloc [1 , 4 ], config ["round_value" ])
262+ d_min_max = round (
263+ stats_df .data .iloc [2 , 3 ] + stats_df .data .iloc [1 , 3 ] * 0.01 ,
264+ config ["round_value" ],
265+ )
266+ d_min_min = round (stats_df .data .iloc [2 , 4 ], config ["round_value" ])
267+ theta_max_max = round (
268+ stats_df .data .iloc [4 , 3 ] + stats_df .data .iloc [4 , 3 ] * 0.01 ,
269+ config ["round_value" ],
270+ )
271+ theta_max_min = round (stats_df .data .iloc [4 , 4 ], config ["round_value" ])
272+ theta_min_max = round (
273+ stats_df .data .iloc [5 , 3 ] + stats_df .data .iloc [5 , 3 ] * 0.01 ,
274+ config ["round_value" ],
275+ )
276+ theta_min_min = round (stats_df .data .iloc [5 , 4 ], config ["round_value" ])
277+ S_max = round (
278+ stats_df .data .iloc [6 , 3 ] + stats_df .data .iloc [6 , 3 ] * 0.01 ,
279+ config ["round_value" ],
280+ )
281+ S_min = round (stats_df .data .iloc [6 , 4 ], config ["round_value" ])
282+ P_max = round (
283+ stats_df .data .iloc [7 , 3 ] + stats_df .data .iloc [7 , 3 ] * 0.01 ,
284+ config ["round_value" ],
285+ )
286+ P_min = round (stats_df .data .iloc [7 , 4 ], config ["round_value" ])
287+ I_max = round (
288+ stats_df .data .iloc [9 , 3 ] + stats_df .data .iloc [9 , 3 ] * 0.01 ,
289+ config ["round_value" ],
290+ )
291+ I_min = round (stats_df .data .iloc [9 , 4 ], config ["round_value" ])
262292
263293 pbar .update (1 )
264294
@@ -270,30 +300,51 @@ def analyze_image(
270300 return (
271301 output_image ,
272302 df ,
303+ points_df ,
273304 fig ,
274305 stats_df ,
275- (orig_image , annotations ) if segment_mode else None ,
276- gr .update (visible = segment_mode ),
277306 gr .update (visible = api_key ),
278307 gr .update (visible = True ),
279- gr .update (minimum = d_max_min , maximum = d_max_max , value = (d_max_min , d_max_max ), label = f"Dₘₐₓ [{ self ._get_translation (scale_selector ['unit' ])} ]" ),
280- gr .update (minimum = d_min_min , maximum = d_min_max , value = (d_min_min , d_min_max ), label = f"Dₘᵢₙ [{ self ._get_translation (scale_selector ['unit' ])} ]" ),
281308 gr .update (
282- minimum = theta_max_min , maximum = theta_max_max , value = (theta_max_min , theta_max_max )
309+ minimum = d_max_min ,
310+ maximum = d_max_max ,
311+ value = (d_max_min , d_max_max ),
312+ step = d_max_max * 0.01 ,
313+ label = f"Dₘₐₓ [{ self ._get_translation (scale_selector ['unit' ])} ]" ,
283314 ),
284315 gr .update (
285- minimum = theta_min_min , maximum = theta_min_max , value = (theta_min_min , theta_min_max )
316+ minimum = d_min_min ,
317+ maximum = d_min_max ,
318+ value = (d_min_min , d_min_max ),
319+ step = d_min_max * 0.01 ,
320+ label = f"Dₘᵢₙ [{ self ._get_translation (scale_selector ['unit' ])} ]" ,
286321 ),
287- gr .update (minimum = 0 , maximum = 1 , value = (0 , 1 )),
288322 gr .update (
289- minimum = S_min , maximum = S_max , value = (S_min , S_max ), label = f"S [{ self ._get_translation (scale_selector ['unit' ])} ²]"
323+ minimum = theta_max_min ,
324+ maximum = theta_max_max ,
325+ value = (theta_max_min , theta_max_max ),
290326 ),
291327 gr .update (
292- minimum = P_min , maximum = P_max , value = (P_min , P_max ), label = f"P [{ self ._get_translation (scale_selector ['unit' ])} ]"
328+ minimum = theta_min_min ,
329+ maximum = theta_min_max ,
330+ value = (theta_min_min , theta_min_max ),
293331 ),
332+ gr .update (minimum = 0 , maximum = 1 , value = (0 , 1 )),
294333 gr .update (
295- minimum = I_min , maximum = I_max , value = (I_min , I_max )
334+ minimum = S_min ,
335+ maximum = S_max ,
336+ value = (S_min , S_max ),
337+ step = S_max * 0.01 ,
338+ label = f"S [{ self ._get_translation (scale_selector ['unit' ])} ²]" ,
296339 ),
340+ gr .update (
341+ minimum = P_min ,
342+ maximum = P_max ,
343+ value = (P_min , P_max ),
344+ step = P_max * 0.01 ,
345+ label = f"P [{ self ._get_translation (scale_selector ['unit' ])} ]" ,
346+ ),
347+ gr .update (minimum = I_min , maximum = I_max , value = (I_min , I_max )),
297348 gr .update (visible = True ),
298349 )
299350 except Exception as e :
@@ -570,13 +621,27 @@ def get_feret(contour, angles=np.arange(0, 180, 1)):
570621 mean_intensity = cv2 .mean (config ["gray_image" ], mask = mask_img )[0 ]
571622
572623 # Отрисовка контура и Feret-линий (опционально)
573- cv2 .polylines (
574- config ["output_image" ],
575- [points ],
576- isClosed = True ,
577- color = (0 , 255 , 0 ),
578- thickness = config ["thickness" ],
579- )
624+ if config ["show_fillPoly" ]:
625+ overlay = config ["output_image" ].copy ()
626+ cv2 .fillPoly (overlay , [points ], self .get_random_color ())
627+ alpha = 0.3
628+ cv2 .addWeighted (
629+ overlay ,
630+ alpha ,
631+ config ["output_image" ],
632+ 1 - alpha ,
633+ 0 ,
634+ config ["output_image" ],
635+ )
636+ if config ["show_polylines" ]:
637+ cv2 .polylines (
638+ config ["output_image" ],
639+ [points ],
640+ isClosed = True ,
641+ color = self .rgba_to_bgr (config ["outline_color" ]),
642+ thickness = config ["thickness" ],
643+ )
644+
580645 if config ["show_Feret_diametr" ]:
581646 self ._draw_feret_lines (
582647 config ["output_image" ], points , angle_max , (0 , 255 , 255 )
@@ -610,8 +675,6 @@ def get_feret(contour, angles=np.arange(0, 180, 1)):
610675 config ["particle_data" ].append (
611676 {
612677 "№" : round (config ["particle_counter" ], config ["round_value" ]),
613- "centroid_x" : round (centroid_x , config ["round_value" ]),
614- "centroid_y" : round (centroid_y , config ["round_value" ]),
615678 "D [{}]" .format (unit ): round (
616679 diameter * scale_factor , config ["round_value" ]
617680 ),
@@ -634,6 +697,8 @@ def get_feret(contour, angles=np.arange(0, 180, 1)):
634697 f'I [{ self ._get_translation ("ед." )} ]' : round (
635698 mean_intensity , config ["round_value" ]
636699 ),
700+ "centroid_x" : round (centroid_x , config ["round_value" ]),
701+ "centroid_y" : round (centroid_y , config ["round_value" ]),
637702 "points" : points .tolist (),
638703 }
639704 )
@@ -700,6 +765,23 @@ def _handle_gpu_error(self, error: Exception):
700765 )
701766 )
702767
768+ @staticmethod
769+ def get_random_color ():
770+ """Генерирует случайный цвет в BGR формате"""
771+ return (random .randint (0 , 255 ), random .randint (0 , 255 ), random .randint (0 , 255 ))
772+
773+ @staticmethod
774+ def rgba_to_bgr (rgba_str ):
775+ numbers = re .findall (r"[\d.]+" , rgba_str )
776+ if len (numbers ) < 3 :
777+ raise ValueError ("Неправильный формат rgba" )
778+
779+ r = int (float (numbers [0 ]))
780+ g = int (float (numbers [1 ]))
781+ b = int (float (numbers [2 ]))
782+
783+ return (b , g , r )
784+
703785 def _cleanup (self , pbar : Optional [tqdm ] = None ):
704786 """Очистка ресурсов"""
705787 if pbar :
0 commit comments