Retrieving multiple metrics information

Hello everybody,

I have a code like this:


#You can ignore this class, it is just a subclass of SizedDiskLocator to plot a number next to each ROI
class CustomSizedDiskLocator(SizedDiskLocator):  
    
    def set_numero(self,value):
        self.numero=value


    def plot(self, axis: plt.Axes, show_boundaries: bool = True, color: str = "red", markersize: float = 3, alpha: float = 0.25) -> None:
        """Plot the BB center"""
        for point in self.points:
            axis.text(point.x, point.y, f"{self.numero}", color=color, fontsize=10)
            axis.plot(point.x, point.y, color=color, marker="o", alpha=1, markersize=markersize)

img_path=r"OTP-EPID_qualiformed_path.dcm"

img= DicomImage(img_path)

liste_metrics=[CustomSizedDiskLocator.from_center_physical(
        expected_position_mm=Point(x=0, y=0, z=0.00),
        search_window_mm=(5,5),
        radius_mm=1.2,
        radius_tolerance_mm=0.3,
        invert=False,
    ),
    CustomSizedDiskLocator.from_center_physical(
        expected_position_mm=Point(x=24, y=0, z=0.00),
        search_window_mm=(5,5),
        radius_mm=1.2,
        radius_tolerance_mm=0.4,
        invert=False
    )
]

for i, metric in enumerate(liste_metrics):
    metric.set_numero(i)

results_metrics=img.compute(liste_metrics)

print(results_metrics)

img.plot()

This plots all the points I want, but it only prints this: {‘Disk Region’: [Point(x=860.14, y=38.11, z=0.00)]} (I expect it to print 2 points)

Is there a way to get all the points printed without messing too much with pylinac’s code ?

I found a way to do this with a customCompute:

class customBaseImage(BaseImage):
    def customCompute(self, metrics: list[MetricBase] | MetricBase) -> Any | dict[str, Any]:
        """Compute the given metrics on the image.

        This can be called multiple times to compute different metrics.
        Metrics are appended on each call. This allows for modification
        of the image between metric calls as well as the ability to compute
        different metrics on the same image that might depend on
        earlier metrics.

        Metrics are both returned and stored in the ``metrics`` attribute.
        The ``metrics`` attribute will store all metrics every calculated.
        The metrics returned are only those passed in the ``metrics`` argument.

        Parameters
        ----------
        metrics : list[MetricBase] | MetricBase
            The metric(s) to compute.
        """
        metric_data = {}
        if isinstance(metrics, MetricBase):
            metrics = [metrics]
        for i, metric in enumerate(metrics):
            metric.inject_image(self)
            value = metric.context_calculate()
            self.metrics.append(metric)
            metric_data[f"{metric.name}{i}"] = value
        # TODO: use |= when 3.9 is min supported version
        self.metric_values.update(metric_data)
        if len(metrics) == 1:
            return metric_data[f"{metrics[0].name}{0}"]
        return metric_data

This works fine but it is a bit fastidious because to use this function I need my image to be an object that derives from customBaseImage, so I have to change quite a lot of things in my script. My issue is solved but if anyone with a better level in python knows an elegant solution I would be happy to hear it :wink:

The names of the metrics have to be different. I didn’t realize this until digging in. The name is some default value. If it’s the same, it will be overwritten. I’ll have to figure out how to avoid doing this.

To work around this, you should be able to do:

liste_metrics=[CustomSizedDiskLocator.from_center_physical(
        expected_position_mm=Point(x=0, y=0, z=0.00),
        search_window_mm=(5,5),
        radius_mm=1.2,
        radius_tolerance_mm=0.3,
        invert=False,
        name="metric 1"  # note the name
    ),
    CustomSizedDiskLocator.from_center_physical(
        expected_position_mm=Point(x=24, y=0, z=0.00),
        search_window_mm=(5,5),
        radius_mm=1.2,
        radius_tolerance_mm=0.4,
        invert=False,
        name="metric 2"   # different name
    )
]

It worked ! Thank you James