
    ^jH                    h   U d Z ddlmZ ddlZddlZddlmZ ddlmZm	Z	m
Z
 erddlmZ  ej                  d      ZdZ	 ddlZd	Zd
ed<   edddZddZddZddZddZddZd dZ	 	 	 	 	 	 	 	 	 	 	 	 	 	 d!dZ	 	 d"	 	 	 	 	 	 	 d#dZ	 d$	 	 	 	 	 d%dZd&dZ	 	 d"	 	 	 	 	 	 	 d#dZy# e$ r dZY fw xY w)'u\  Post-training metrics plotting utilities.

Reads the ``metrics.csv`` written by PTL's ``CSVLogger`` (always present after a ``build_trainer``-based run) and builds
seaborn figures grouped by metric type (Loss, AP@0.50, AP@0.50:0.95, AR).

Loss panel shows aggregate and component loss scalars. AP/AR panels show all ``train/``, ``val/``, and ``test/``
columns for each group — both the base and EMA series when EMA is enabled, so both are visible in the legend.

Usage::

    from rfdetr.visualize.training import plot_metrics
    fig = plot_metrics("output/rfdetr_base/metrics.csv")
    plt.show(fig)
    )annotationsN)Path)TYPE_CHECKINGAnyOptional)Figurez_\d+$   Tbool_IS_SEABORN_AVAILABLEF)	n_columnsc                   | j                         \  }}|sy| j                  ||ddt        |t        dt	        |                  dd       y)z<Place a compact multi-column legend below a matplotlib axes.Nzupper center)g      ?g
ףp=
ǿ   	   T)locbbox_to_anchorncolfontsizeframeon)get_legend_handles_labelslegendminmaxlen)axr   handleslabelss       d/var/www/ramen.bs-engineer-server.com/venv/lib/python3.12/site-packages/rfdetr/visualize/training.py_place_legend_below_axesr   *   sR    224OGVII#C3v;/0      c                H    | j                  d      \  }}}|r|dv r||fS d| fS )zBSplit a CSVLogger metric column into split prefix and metric name./>   valtesttrain )	partition)columnsplit	separatormetric_names       r   _split_metric_columnr+   :   s9    $*$4$4S$9!E9kU66k!!v:r   c                    | dk(  ry| dk(  ryy)z2Return the plotting line style for a metric split.r$   :r#   z-.- )r(   s    r   _line_style_for_splitr0   B   s    r   c                `   	 ddl m} |j                  d   j	                         j                  dg       }i }|D ]  }t        |      \  }	}
|
|vr!|r|t        |      t        |      z     nd||
<   ||
   }t        |	      }t        rEddl
}|d|g   j                  |g      }|j                  ru	 |j                  |d|| d	||d
|	       | j                  |d   ||   d
|||        y# t        $ r}t        d      |d}~ww xY w# t        $ r |j                  |d|| d||d
|	       Y w xY w)u  Plot columns with color by metric name and line style by split.

    When seaborn is installed, draws mean ± 1 std-dev bands computed from within-epoch step-level rows in ``raw_df``.
    Falls back to epoch-averaged lines from ``epoch_df`` when seaborn is absent.
    r   NYmatplotlib is required for training metric plots. Install it with: pip install matplotlibzaxes.prop_cyclecolorC0epoch)subset)sdr   g333333?)	dataxyr   errorbarr3   	linestyle	linewidthlabelr7   )	r8   r9   r:   r   cir3   r<   r=   r>   )r=   r<   r3   r>   )matplotlib.pyplotpyplotImportErrorrcParamsby_keygetr+   r   r0   r   seaborndropnaemptylineplot	TypeErrorplot)r   raw_dfepoch_dfmetric_columnspltexccolor_cyclemetric_colorsr'   r(   r*   r3   r<   snscol_datas                  r   _plot_columns_on_axesrU   K   s   ' ,,0188:>>wKK$&M  -1&9{m+_jS5G#kJZ5Z)[ptM+&k*)%0	 !w/077x7HH>>LL%! !*#"+"%$ ! 
2 GG! #  M-  g
	@ ! LL%! #"+"%$ ! 
s)   C* /D*	D3C??D#D-,D-c                    d fd}dd} j                   D cg c]8  } |t        |            s |   j                         j                         s7|: }} |dd      D cg c]  }d|vsd|vs| }} |dd      } |ddd	      D cg c]  }d|vsd|v s| }} |d
d      D cg c]	  }d|vs| }	} |dd      D cg c]  }d|vsd|vs| }
} |dd      } |dd      } |dd      } |ddd      }|||||	|
||||d
}|j	                         D ci c]  \  }}|s	|| c}}S c c}w c c}w c c}w c c}w c c}w c c}}w )a  Build plot groups from numeric PTL CSVLogger metrics.

    Args:
        df: DataFrame-like object with metric columns.

    Returns:
        Non-empty metric groups keyed by subplot title.

    Raises:
        AttributeError: If ``df`` does not provide DataFrame-like columns.
    c                     d}j                   D cg c]M  j                  |      st        fd| D              s*   j                         j                         sLO c}S c c}w )zLReturn split-prefixed columns whose name contains any of the given patterns.)train/val/test/c              3  &   K   | ]  }|v  
 y wNr/   ).0pcs     r   	<genexpr>z<_build_metric_groups.<locals>._split_cols.<locals>.<genexpr>   s     C]qAFC]s   )columns
startswithanynotna)patternsprefixesr_   dfs     `r   _split_colsz)_build_metric_groups.<locals>._split_cols   s]    .zz
Q\\(%;C]T\C]@]bdefbgbmbmbobsbsbuA
 	
 
s   A)A) !A)"A)c                    | dv ry| j                  dd      d   j                         }t        j                  |      ryd|v xs |j	                  d      S )	z0Return whether a metric column is a loss scalar.>   stepr5   Fr!   r   )maxsplitlosskp_)rsplitlower_AUXILIARY_LOSS_SUFFIX_REsearchrb   )nameleafs     r   _is_loss_colz*_build_metric_groups.<locals>._is_loss_col   sW    $${{3{+B/557$++D1~7!77r   mAP_50
ema_mAP_50	mAP_50_95mAP_75
ema_mAP_75ema_mAP_50_95z/AP/mARema_mAR	keypoint_keypoint_map_50ema_keypoint_map_50	map_50_95map_75keypoint_map_75ema_keypoint_map_75keypoint_map_50_95ema_keypoint_map_50_95keypoint_mARema_keypoint_mARF1	precisionrecall)
LossDetection AP@0.50Detection AP@0.50:0.95Detection AP@0.75zDetection ARKeypoint AP@0.50Keypoint AP@0.50:0.95Keypoint AP@0.75zKeypoint ARzF1 / Precision / Recall)re   strreturn	list[str])rs   r   r   r
   )ra   r   rd   rc   items)rg   rh   ru   rs   	loss_colsr_   detection_map_50detection_map_75detection_map_50_95detection_marr   r   r   keypoint_marf1_precision_recallmetric_groupsra   s   `                r   _build_metric_groupsr      s   
8 #%**c$SY0GBtHNNL\L`L`LbcIc#.x#Fua+]^J^ckstctuu"8\:{OVDXYHY]hlm]m  !,E9 =V1TUAUQVMV02GHK_`L`emuvevO  ""35JKO$%9;ST~/ABL%dKB -"5-%+!3+##6+M 0=/B/B/DPmdGD'MPP7 du W( QsX   D6!D6D6$	D;.D;3D;E E -	E7E	E
E
E
"
E-Ec                h   	 ddl }t        |       }|j                         st	        d|       |j                  |      }d|j                  vrt        d      t        |      }|j                  d      j                  d      j                         }||fS # t        $ r}t        d      |d}~ww xY w)	aR  Read a PTL CSVLogger metrics file and return both step-level and epoch-averaged DataFrames.

    Returns:
        A ``(raw_df, epoch_df)`` pair where ``raw_df`` contains every logged row
        (one per training step or validation epoch) and ``epoch_df`` is the per-epoch
        mean used for column detection and log-scale checks.
    r   NzQpandas is required for training metric plots. Install it with: pip install pandaszmetrics.csv not found: r5   z/metrics.csv does not contain an 'epoch' column.T)numeric_only)pandasrB   r   existsFileNotFoundErrorread_csvra   
ValueError%_drop_trailing_validation_only_epochsgroupbymeanreset_index)metrics_csvpdrP   csv_pathrg   rL   rM   s          r   _read_metrics_csvr      s    x K H??"9( DEE	X	Bbjj JKK226F~~g&+++>JJLH8  xmntwwxs   B 	B1 B,,B1c                   | j                   D cg c]  }t        |      j                  d      s|! }}| j                   D cg c]  }t        |      j                  d      s|! }}|r|s| S | |   j                         j	                  d      s| S | }t        |      dkD  r|d   j                  d   }|d   |k(  }|j                  |   }|j                  |    j                  r|S ||   j                         j	                  d      }||   j                         j	                  d      }	|s|	s|S |j                  |    }t        |      dkD  r|S c c}w c c}w )a  Remove post-fit validation rows that Lightning logs as a synthetic final epoch.

    The Roboflow finetune demos run ``trainer.validate(...)`` after ``trainer.fit(...)`` to write a final metrics JSON.
    PTL appends that validation pass to the same ``CSVLogger`` file using ``epoch == max_epochs``. That row is useful as
    a standalone final validation result, but it is not part of the training curve and can create a misleading last-
    epoch jump in plots. Only trailing epochs with validation/test metrics and no training metrics are removed, and only
    when the CSV also contains real training rows. Pure validation CSV files are preserved.
    rX   )rY   rZ   Naxisr   r5   rl   )	ra   r   rb   rd   rc   r   ilocr   rH   )
rg   r'   train_columnseval_columnscleaned
last_epoch
epoch_mask
epoch_rowshas_train_metricshas_eval_metricss
             r   r   r      s\    +-**YF8N8Nx8XVYMY)+avs6{7M7MN_7`FaLa	m""$((d(3	G
g,
W%**2.
W%3
[[,
;;
{#))N&}5;;=AAtAL%l399;??T?J$4N++zk* g,
 N' Zas   EEE#Ec                  	 ddl }|j                  d       ddlm} |st        d      t        |      }	|	dk(  rdnd}
|	|
z   dz
  |
z  }|j                  ||
dd	|z  fd
      \  }}|j                         }t        |j                               D ]  \  }\  }}||   }t        || ||       |j                  |dd       |j                  dd       |j                  |d       |j                  dd       |dk(  rJ|rH||   }|dk  j!                  d      rt#        j$                  dt&        d       n|j)                  d       |dk(  st+        |        t-        |	t        |            D ]  }||   j/                  d
        |j1                  |d       |j3                          d|v r|j5                  |	dk(  rdnd       ||j7                  |dd        |S # t        $ r}t	        d      |d}~ww xY w)!z#Build a figure for grouped metrics.r   NAggr2   zDmetrics.csv does not contain any supported non-empty metric columns.r            F)figsizesqueeze   boldr   
fontweightEpoch   r   T333333?alphar   r   zvloss_log_scale=True was requested, but at least one loss value is non-positive; using linear scale for the Loss panel.)
stacklevellog   Q?gQ?bottom   tightdpibbox_inches)
matplotlibuser@   rA   rB   r   r   subplotsflatten	enumerater   rU   	set_title
set_xlabel
set_ylabelgridrc   warningswarnUserWarning
set_yscaler   rangeset_visiblesuptitletight_layoutsubplots_adjustsavefig)rL   rM   r   titleoutput_pathloss_log_scaler   rO   rP   n_groupsn_colsn_rowsfigaxes	axes_flatidxsubplot_titlemetric_listr   
group_datas                       r   _plot_metric_groupsr      s   u' _``=!Ha-QQF!#.FVVb!f*5EuUICI-6}7J7J7L-M )))m[s^b&(K@
]RFC
g+
mb1
C F"~!+.Ja$$$$/= 	 e$F"$R(')* Xs9~. *#""5)* LLL$8q=4dCKSg>J[  g
	s   G   	G:)G55G:c                l    t        |       \  }}t        |      }t        ||d|v rd|d   ini d||      S )uE  Plot aggregate and component training losses from a PTL ``metrics.csv`` file.

    Reads the CSV written by PyTorch Lightning's ``CSVLogger``, groups loss columns
    (aggregate loss, per-component scalars, and keypoint NLL terms) into a single panel,
    and renders an optional seaborn error-band overlay when seaborn is installed.

    Args:
        metrics_csv: Path to the ``metrics.csv`` written by PTL's ``CSVLogger``.
        output_path: Optional filesystem path to save the rendered figure (PNG, PDF, …).
            When ``None`` the figure is returned but not written to disk.
        loss_log_scale: When ``True``, the loss y-axis uses a logarithmic scale.
            Useful when loss components span several orders of magnitude.

    Returns:
        A ``matplotlib.figure.Figure`` containing the loss panel.

    Raises:
        FileNotFoundError: When ``metrics_csv`` does not exist.
        ImportError: When ``pandas`` or ``matplotlib`` is not installed.
        ValueError: When ``metrics_csv`` contains no loss columns.

    Examples:
        .. code-block:: python

            from rfdetr.visualize.training import plot_loss_metrics
            fig = plot_loss_metrics("output/rfdetr_small/metrics.csv")
            fig = plot_loss_metrics("output/rfdetr_small/metrics.csv", output_path="loss.png", loss_log_scale=True)
    r   zRF-DETR Loss Metricsr   r   r   r   r   r   )r   r   r   rL   rM   groupss         r   plot_loss_metricsr   =  sO    B )5FH!(+F$*f$4 "$% r   c                    t        |       \  }}t        |      }|j                         D cg c]  \  }}|dv r	|D ]  }|  }}}}|st        d      t	        ||||      S c c}}}w )u  Plot train/val/test detection and keypoint mAP metrics from a PTL ``metrics.csv`` file.

    Reads the CSV written by PyTorch Lightning's ``CSVLogger``, selects all AP-family
    columns (AP@0.50, AP@0.75, AP@0.50:0.95 for both detection and keypoints), and renders
    them in a single combined panel.  EMA series are included when present so both live and
    EMA metric trajectories are visible in the legend.

    Args:
        metrics_csv: Path to the ``metrics.csv`` written by PTL's ``CSVLogger``.
        output_path: Optional filesystem path to save the rendered figure (PNG, PDF, …).
            When ``None`` the figure is returned but not written to disk.

    Returns:
        A ``matplotlib.figure.Figure`` containing the combined mAP panel.

    Raises:
        FileNotFoundError: When ``metrics_csv`` does not exist.
        ImportError: When ``pandas`` or ``matplotlib`` is not installed.
        ValueError: When ``metrics_csv`` contains no supported mAP metric columns.

    Examples:
        .. code-block:: python

            from rfdetr.visualize.training import plot_map_metrics
            fig = plot_map_metrics("output/rfdetr_small/metrics.csv")
            fig = plot_map_metrics("output/rfdetr_small/metrics.csv", output_path="map.png")
    >   r   r   r   r   r   r   zHmetrics.csv does not contain any supported non-empty mAP metric columns.)r   )r   r   r   r   _plot_map_columns)	r   r   rL   rM   r   
group_namera   r'   map_columnss	            r   plot_map_metricsr  j  s    > )5FH(2M $1#6#6#8 J

   	K  cddVX{TT!s   A%c                  	 ddl }|j                  d       ddlm} |j                  d      \  }}t        || ||       |j                  ddd	
       |j                  dd       |j                  dd       |j                  dd       t        |       |j                          |j                  d       ||j                  |dd       |S # t        $ r}t	        d      |d}~ww xY w)z;Plot mAP metrics on a single axes with line style by split.r   Nr   zTmatplotlib is required for mAP metric plots. Install it with: pip install matplotlib)r      )r   zRF-DETR mAP Metricsr   r   r   r   r   r   mAPTr   r   r   r   r   r   r   )r   r   r@   rA   rB   r   rU   r   r   r   r   r   r   r   r   )	rL   rM   rN   r   r   rO   rP   r   r   s	            r   r   r     s    u' ll7l+GC"fh?LL&LGMM'BM'MM%"M%GGDGR t$KSg>J#  b
	s   C 	C,C''C,c                V    t        |       \  }}t        |      }t        |||d||      S )u  Read a PTL ``CSVLogger`` metrics file and build a training plot.

    The figure contains one subplot per metric group (loss, detection metrics,
    keypoint metrics, and F1/precision/recall), arranged in a 2-column grid.
    Only groups with at least one non-NaN column are shown.

    When seaborn is installed, each series is drawn as mean ± 1 std-dev band
    computed from the within-epoch step-level rows logged by PTL's
    ``CSVLogger``.  Metrics recorded only once per epoch (e.g. ``val/mAP_50``)
    show a plain line because their per-epoch std is zero.  When seaborn is
    absent the plot falls back to epoch-averaged lines.

    Args:
        metrics_csv: Path to the ``metrics.csv`` file produced by
            ``CSVLogger``.
        output_path: Optional destination for the PNG file. If omitted, the
            figure is returned without saving.
        loss_log_scale: If ``True``, use a logarithmic y-axis for the Loss
            panel when all loss values are positive.

    Returns:
        The matplotlib figure. The figure is left open so notebook cells can
        display it inline.

    Raises:
        ImportError: If ``matplotlib`` or ``pandas`` are not installed.
        FileNotFoundError: If ``metrics_csv`` does not exist.
    zRF-DETR Training Metricsr   r   )r   r   r   rL   rM   r   s         r   plot_metricsr    s=    B )5FH(2M(% r   )r   r   r   intr   None)r'   r   r   ztuple[str, str])r(   r   r   r   )
r   r   rL   r   rM   r   rN   r   r   r  )rg   r   r   dict[str, list[str]])r   r   r   ztuple[Any, Any])rg   r   r   r   )rL   r   rM   r   r   r	  r   r   r   Optional[str]r   r
   r   r   )NF)r   r   r   r
  r   r
   r   r   r\   )r   r   r   r
  r   r   )
rL   r   rM   r   rN   r   r   r
  r   r   ) __doc__
__future__r   rer   pathlibr   typingr   r   r   matplotlib.figurer   compilerq   _LEGEND_COLUMNSrF   r   __annotations__rB   r   r+   r0   rU   r   r   r   r   r   r  r   r  r/   r   r   <module>r     sj   # 	   / /(&BJJx0 ""&4&
 ;J  =@8Qv2><<< (<
 < < < <B "& *** * 	*^ "&1U1U1U 1Uh: "& *** * 	*g  "!"s   B' 'B10B1