Repository: Project-MONAI/MONAI
File: monai/apps/auto3dseg/data_analyzer.py (Python)

Code before:
    def _check_data_uniformity(keys: list[str], result: dict) -> bool:
        """
        Check data uniformity since DataAnalyzer provides no support to multi-modal images with different
        affine matrices/spacings due to monai transforms.

        Args:
            keys: a list of string-type keys under image_stats dictionary.

        Returns:
            False if one of the selected key values is not constant across the dataset images.

        """

        constant_props = [result[DataStatsKeys.SUMMARY][DataStatsKeys.IMAGE_STATS][key] for key in keys]
        for prop in constant_props:
            if "stdev" in prop and np.any(prop["stdev"]):
                return False

        return True

    def get_all_case_stats(self, key="training", transform_list=None):
        """ Wrapper for the internal _get_all_case_stats to perform multi-gpu processing
        """
        if self.device.type == 'cpu':
            nprocs = 1
            print(f'Using CPU for data analyzing!')
        else:
            nprocs = torch.cuda.device_count()
            print(f'Found {nprocs} GPUs for data analyzing!')
        set_start_method('forkserver', force=True)
        with Manager() as manager:
            manager_list = manager.list()
            processes = []
            for rank in range(nprocs):
                p = Process(target=self._get_all_case_stats, args=(rank, nprocs, manager_list, key, transform_list))
                processes.append(p)
            for p in processes:
                p.start()
            for p in processes:
                p.join()
            # merge DataStatsKeys.BY_CASE
            result: dict[DataStatsKeys, Any] = {DataStatsKeys.SUMMARY: {}, DataStatsKeys.BY_CASE: []}
            for _ in manager_list:
                result[DataStatsKeys.BY_CASE].extend(_[DataStatsKeys.BY_CASE])
            summarizer = SegSummarizer(
                self.image_key,
                self.label_key,
                average=self.average,
                do_ccp=self.do_ccp,
                hist_bins=self.hist_bins,
                hist_range=self.hist_range,

Review comment (suggestion):
Thank you @heyufan1995 for preparing the PR.

Here we should use the logging instead of `print()`.

Code after:

        Args:
            keys: a list of string-type keys under image_stats dictionary.

        Returns:
            False if one of the selected key values is not constant across the dataset images.

        """

        constant_props = [result[DataStatsKeys.SUMMARY][DataStatsKeys.IMAGE_STATS][key] for key in keys]
        for prop in constant_props:
            if "stdev" in prop and np.any(prop["stdev"]):
                return False

        return True

    def get_all_case_stats(self, key="training", transform_list=None):
        """
        Get all case stats. Caller of the DataAnalyser class. The function initiates multiple GPU or CPU processes of the internal
        _get_all_case_stats functions, which iterates datalist and call SegSummarizer to generate stats for each case.
        After all case stats are generated, SegSummarizer is called to combine results.

        Args:
            key: dataset key
            transform_list: option list of transforms before SegSummarizer

        Returns:
            A data statistics dictionary containing
                "stats_summary" (summary statistics of the entire datasets). Within stats_summary
                there are "image_stats"  (summarizing info of shape, channel, spacing, and etc
                using operations_summary), "image_foreground_stats" (info of the intensity for the
                non-zero labeled voxels), and "label_stats" (info of the labels, pixel percentage,
                image_intensity, and each individual label in a list)
                "stats_by_cases" (List type value. Each element of the list is statistics of
                an image-label info. Within each element, there are: "image" (value is the
                path to an image), "label" (value is the path to the corresponding label), "image_stats"
                (summarizing info of shape, channel, spacing, and etc using operations),
                "image_foreground_stats" (similar to the previous one but one foreground image), and
                "label_stats" (stats of the individual labels )

        Notes:
            Since the backend of the statistics computation are torch/numpy, nan/inf value
            may be generated and carried over in the computation. In such cases, the output
            dictionary will include .nan/.inf in the statistics.

        """
        result: dict[DataStatsKeys, Any] = {DataStatsKeys.SUMMARY: {}, DataStatsKeys.BY_CASE: []}
        result_bycase: dict[DataStatsKeys, Any] = {DataStatsKeys.SUMMARY: {}, DataStatsKeys.BY_CASE: []}
        if self.device.type == "cpu":
            nprocs = 1
            logger.info("Using CPU for data analyzing!")