PCAGuidedIdifFitter ========================================================= .. py:class:: petpal.input_function.pca_guided_idif.PCAGuidedIdifFitter(input_image_path: str, mask_image_path: str, output_tac_path: str, num_pca_components: int, pca_comp_filter_min_value: float = 0.0, pca_comp_threshold: float = 0.1, verbose: bool = False) Bases: :py:obj:`PCAGuidedIdifFitterBase` Class to calculate the PCA-guided Image-Derived Input Function (IDIF) by fitting to find the best voxels. This class extends :class:`~.PCAGuidedIdifFitterBase` and implements domain-specific functions to guide the optimization process, including terms for voxel count, noise reduction, smoothness enforcement, and peak emphasis. The optimization aims to refine the voxel selection for IDIF estimation using the provided fitting terms. .. rubric:: Example .. code-block:: python from petpal.input_function.pca_guided_idif import PCAGuidedIdifFitter ## Initializing the fitting object pca_idif_fit = PCAGuidedIdifFitter(input_image_path='/path/to/4d/pet/image.nii.gz', mask_image_path='/path/to/arterial/mask.nii.gz', output_tac_path='/path/to/save/tac.tsv', num_pca_components=3, pca_comp_filter_min_value=0.0, pca_comp_threshold=0.1, verbose=True) ## Running the fitting with the dual_annealing minimization method pca_idif_fit.run(alpha=2.5, beta=0.0, method='dual_annealing') ## Saving the IDIF TACs to disk pca_idif_fit.save() Initializes the PCA-guided IDIF fitter. :param input_image_path: Path to the input dynamic image used for IDIF estimation. :type input_image_path: str :param mask_image_path: Path to the mask image for selecting the region of interest. :type mask_image_path: str :param output_tac_path: Path where the calculated TAC data will be saved. :type output_tac_path: str :param num_pca_components: Number of PCA components to compute for voxel selection. :type num_pca_components: int :param pca_comp_filter_min_value: Minimum value for filtering PCA components (default: 0.0). :type pca_comp_filter_min_value: float, optional :param pca_comp_threshold: Threshold for filtering PCA components (default: 0.1). :type pca_comp_threshold: float, optional :param verbose: Enables detailed diagnostic output if set to `True` (default: `False`). :type verbose: bool, optional Side Effects: - Calls the base class constructor to initialize parameters and fit-related attributes. .. py:method:: _voxel_term_func(voxel_nums: float) -> float :staticmethod: A softplus function to encourage optimization toward selecting more voxels. This term penalizes scenarios where fewer voxels are selected by incrementally rewarding configurations with larger voxel numbers. .. rubric:: Notes Applies the softplus function defined as :math:`\ln\left(1+e^{v/6}\right)` where :math:`v` is the number of voxels. :param voxel_nums: The number of voxels contributing to the TAC. :type voxel_nums: float :returns: *float* -- The calculated voxel term value. .. py:method:: _noise_term_func(tac_stderrs: numpy.ndarray[float]) -> float :staticmethod: Root-mean-square (RMS) noise term to favor voxels with reduced per-frame variance. This term penalizes higher noise levels in the selected voxels' TAC data, rewarding smoother per-frame signal averages. .. rubric:: Notes The noise term is computed as :math:`\sqrt{\frac{1}{n}\sum_{i}^{n}{\sigma_i}^2}`, where :math:`\sigma_i` is the per-frame standard deviation, and :math:`n` is the number of frames. :param tac_stderrs: The array of standard errors of the mean (SEM) for the TAC. :type tac_stderrs: np.ndarray[float] :returns: *float* -- The calculated noise term value. .. py:method:: _smoothness_term_func(tac_values: numpy.ndarray[float]) -> float :staticmethod: Smoothness term enforcing total variation consistency on the TAC values. This term penalizes abrupt changes in frame-to-frame TAC values by calculating the L1-norm of differences in TAC values. It helps ensure smoother TACs from the selected voxels. .. rubric:: Notes Defined as the sum of absolute differences in normalized TAC values: :math:`\frac{\beta}{c_\mathrm{max}}\sum_{i}^{n}{\left|c_{i+1} - c_{i}\right|}` where :math:`\beta` is a tuning parameter, and :math:`n` is the number of frames. :param tac_values: The TAC values obtained from the selected voxels. :type tac_values: np.ndarray[float] :returns: *float* -- The calculated smoothness term value. .. py:method:: _peak_term_func(tac_peak_ratio: float) -> float :staticmethod: Peak term driving the TAC peak value to be sufficiently high. This term rewards configurations where the TAC has a pronounced peak relative to its baseline, as higher peaks often correlate with improved IDIF estimates. .. rubric:: Notes Applies the softplus function defined as :math:`\alpha \times\ln\left(1+e^{-\frac{3}{2}r}\right)` where :math:`\alpha` is the tuning parameter, and :math:`r` is the ratio of the value of the computed TAC to the value of the average TAC of the mask. :param tac_peak_ratio: The ratio of the TAC peak value to a reference value. :type tac_peak_ratio: float :returns: *float* -- The calculated peak term value. .. py:property:: pca_comp_filter_flag_threshold :type: float .. py:property:: pca_comp_filter_min_val :type: float .. py:method:: get_pca_component_filter_flags(pca_components: numpy.ndarray, comp_min_val: float = 0.0, threshold: float = 0.1) -> numpy.ndarray[bool] :staticmethod: Generates filtering flags for PCA components based on their contribution. Given the `comp_min_val`, we check how many of the time-points of the PCA components are above the threshold, then `threshold` determines if the component contributes with ``>`` (if the fraction of higher points is above the threshold) or ``<`` (if the fraction of higher points is below the threshold). :param pca_components: Array of PCA components for evaluation. :type pca_components: np.ndarray :param comp_min_val: Minimum value for PCA components to contribute positively. :type comp_min_val: float :param threshold: Threshold for rejecting components with insufficient contributions. :type threshold: float :returns: *np.ndarray[bool]* -- Boolean flags indicating whether a PCA filters less than, or greater than. .. py:method:: get_pca_filter_signs_from_flags(pca_component_filter_flags: numpy.ndarray[bool]) -> list[str] :staticmethod: Derives signs ('>' or '<') for PCA components based on their filtering flags. .. seealso:: :meth:`get_pca_component_filter_flags` :param pca_component_filter_flags: Filtering flags for PCA components. :type pca_component_filter_flags: np.ndarray[bool] :returns: *list[str]* -- List of signs corresponding to the filter flags. .. py:method:: _generate_quantile_params(num_components: int = 3, value: float = 0.5, lower: float = 0.0001, upper: float = 0.999) -> lmfit.Parameters :staticmethod: Generates initial fitting quantile parameters for determining voxel filters. :param num_components: The number of PCA components to generate parameters for. :type num_components: int :param value: Default value for each quantile parameter (e.g., median = 0.5). :type value: float :param lower: Lower bound for the quantile parameter (default is 1e-4). :type lower: float :param upper: Upper bound for the quantile parameter (default is 0.999). :type upper: float :returns: *lmfit.Parameters* -- A parameter set with quantile values for each PCA component. .. py:method:: calculate_voxel_mask_from_quantiles(params: lmfit.Parameters, pca_values_per_voxel: numpy.ndarray, quantile_flags: numpy.ndarray[bool]) -> numpy.ndarray[bool] :staticmethod: Calculates a mask to identify selected voxels based on quantile thresholds. Given the passed in quantile `params`, we check if the value of the principal component for a voxel is above the quantile threshold, then `quantile_flags` is used to flip the signs of the components which should be lower than. For each voxel, the boolean product is calculated to determine if the voxel should be a part of the mask or not. Only voxels where every PC is above (or below) the quantile thresholds in `params` are included in the mask. :param params: Quantile parameters for each PCA component. :type params: lmfit.Parameters :param pca_values_per_voxel: PCA values for every voxel. :type pca_values_per_voxel: np.ndarray :param quantile_flags: Filtering flags for quantile-based masking. :type quantile_flags: np.ndarray[bool] :returns: *np.ndarray[bool]* -- Mask identifying selected voxels passing the quantile criteria. .. py:method:: calculate_filter_flags_and_signs(comp_min_val: float, threshold: float) Updates PCA filter flags and their respective signs based on component values. :param comp_min_val: Minimum acceptable value for filtering PCA components. :type comp_min_val: float :param threshold: Threshold percentage for filtering components. :type threshold: float Side Effects: - Updates the `pca_filter_flags` attribute with new flags for indicating filtered components. - Updates the `filter_signs` attribute with the corresponding value comparison signs based on the flags. .. py:method:: residual(params: lmfit.Parameters, pca_values_per_voxel: numpy.ndarray[float], quantile_flags: numpy.ndarray[bool], voxel_tacs: numpy.ndarray, alpha: float, beta: float) -> float Calculates the residual (objective function) for optimization. The residual has the following terms: .. math:: \mathcal{L} = \mathrm{VoxelTerm} + \mathrm{NoiseTerm} + (\alpha\cdot\mathrm{PeakTerm}) + (\beta\cdot\mathrm{SmoothnessTerm}) :param params: Parameters for the optimization. :type params: lmfit.Parameters :param pca_values_per_voxel: PCA values for each voxel. :type pca_values_per_voxel: np.ndarray[float] :param quantile_flags: Flags indicating quantile filtering. :type quantile_flags: np.ndarray[bool] :param voxel_tacs: TACs for valid voxels. :type voxel_tacs: np.ndarray :param alpha: Weight for the peak term. :type alpha: float :param beta: Weight for the smoothness term. :type beta: float :returns: *float* -- Residual value. .. py:method:: run(alpha: float, beta: float, method: str = 'ampgo', **method_kwargs) Runs the PCA-guided IDIF fitting process using optimization. :param alpha: Weight for the peak term in the optimization. :type alpha: float :param beta: Weight for the smoothness term in the optimization. :type beta: float :param method: Optimization method to use (default is 'ampgo'). :type method: str :param \*\*method_kwargs: Additional keyword arguments for the optimizer. Side Effects: - Updates optimization-related attributes (e.g., `fit_result`, `fit_quantiles`). - Updates `selected_voxels_mask` with the best voxel subset. - Marks `analysis_has_run` as True. .. rubric:: Notes The `run` method uses the residual function as the target for minimization. .. py:method:: __call__(alpha: float, beta: float, method: str, **meth_kwargs) -> None Callable interface for running the fitting process and saving results. :param alpha: Weight for the peak term in the optimization. :type alpha: float :param beta: Weight for the smoothness term in the optimization. :type beta: float :param method: Optimization method to use. :type method: str :param \*\*method_kwargs: Additional keyword arguments for the optimizer. Side Effects: - Executes the analysis and saves the resulting TAC data to the output file. .. py:method:: perform_temporal_pca() Performs PCA decomposition on the dynamic image data within the specified mask. This method applies temporal PCA analysis on the input dynamic image constrained to the region defined by the mask. The resulting PCA object and fitted data are stored as attributes for subsequent processing. Uses :func:`~.extract_roi_voxel_tacs_from_image_using_mask` Attributes Updated: - `pca_obj` - `pca_fit` :raises ValueError: If PCA analysis fails during execution. .. py:method:: rescale_tacs(rescale_constant: float = CONVERT_kBq_to_mCi_) -> None Rescales the time-activity curves (TACs) and associated data by a constant factor. This method uniformly rescales voxel-level TACs, IDIF values, PCA outputs, and other TAC-associated metrics using the specified constant. The default value corresponds to a pre-defined scaling factor to convert units between kilobecquerels and millicuries. :param rescale_constant: The constant factor used for rescaling. Must be greater than zero. Default is 37000.0. :type rescale_constant: float Attributes Updated: - `mask_voxel_tacs` - `mask_avg` - `mask_std` - `mask_peak_val` - `idif_vals` - `idif_errs` - `prj_idif_vals` - `prj_idif_errs` - `selected_voxels_tacs` - `selected_voxels_prj_tacs` :raises AssertionError: If the `rescale_constant` is not greater than 0. .. py:method:: save() Saves the computed IDIF and related metrics to a file. This method exports the time-activity curves (TACs) and other analysis results into a tab-delimited text file. The file will include time points, IDIF values, errors, PCA-projected metrics, and mask-derived averages, along with their associated standard deviations. The output file is saved at the path specified in the `output_tac_path` attribute. :raises AssertionError: If the analysis has not been run prior to calling this method. Output Format: The saved text file will have the following tab-delimited columns: - time: Time in minutes. - idif: IDIF values. - d_idif: Errors (standard deviations) for IDIF. - prj_idif: PCA-projected IDIF values. - d_prj_idif: Errors (standard deviations) for PCA-projected IDIF values. - mask: Mean voxel activity in the mask. - d_mask: Standard deviation of voxel activity in the mask. .. py:method:: calculate_tacs_from_mask() -> None Calculates Time-Activity Curves (TACs) and IDIF-related metrics from the selected voxel mask. This method processes the data from selected voxels within a previously defined mask and computes key metrics such as mean IDIF values, standard deviations, PCA-projected values, and their respective errors. It utilizes PCA transformations to refine the IDIF and ensure consistency with the voxel data. Side Effects: idif_vals (np.ndarray): Mean IDIF values calculated from the selected voxel TACs. idif_errs (np.ndarray): Standard deviations of the IDIF values derived from selected voxels. prj_idif_vals (np.ndarray): Mean PCA-projected IDIF values, with negative values set to zero. prj_idif_errs (np.ndarray): Standard deviations for the PCA-projected IDIF values. :raises AssertionError: If the analysis has not been previously performed (i.e., `.run()` was not called). .. rubric:: Notes - Negative PCA-projected IDIF values are set to zero since negative values are non-physical. .. py:property:: idif_tac Get the calculated IDIF TAC :returns: *TimeActivityCurve* -- TAC object with (times, idif_vals) .. seealso:: :class:`~.TimeActivityCurve` .. py:property:: idif_tac_werr Get the calculated IDIF TAC with the standard deviations :returns: *TimeActivityCurve* -- TAC object with (times, idif_vals, idif_stderrs) .. seealso:: :class:`~.TimeActivityCurve` .. py:property:: prj_idif_tac Get the calculated PCA-projected IDIF TAC :returns: *TimeActivityCurve* -- TAC object with (times, projected_idif_vals) .. seealso:: :class:`~.TimeActivityCurve` .. py:property:: prj_idif_tac_werr Get the calculated PCA-projected IDIF TAC with the standard deviations :returns: *TimeActivityCurve* -- TAC object with (times, projected_idif_vals, projected_idif_stderrs) .. seealso:: :class:`~.TimeActivityCurve`