diff --git a/CHANGELOG.md b/CHANGELOG.md index 24f95c932c..a18ebac732 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,62 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ### New Contributors +## [v1.2.0] + +### Added + +- 🚀 Add ensembling methods for tiling to Anomalib by @blaz-r in https://github.com/openvinotoolkit/anomalib/pull/1226 +- 📚 optimization/quantization added into 500 series by @paularamo in https://github.com/openvinotoolkit/anomalib/pull/2197 +- 🚀 Add PIMO by @ashwinvaidya17 in https://github.com/openvinotoolkit/anomalib/pull/2329 +- 📚 Add PIMO tutorial advanced i (fixed) by @jpcbertoldo in https://github.com/openvinotoolkit/anomalib/pull/2336 +- 🚀 Add VLM based Anomaly Model by @ashwinvaidya17 in https://github.com/openvinotoolkit/anomalib/pull/2344 +- 📚 Add PIMO tutorials/02 advanced ii by @jpcbertoldo in https://github.com/openvinotoolkit/anomalib/pull/2347 +- 📚 Add PIMO tutorials/03 advanced iii by @jpcbertoldo in https://github.com/openvinotoolkit/anomalib/pull/2348 +- 📚 Add PIMO tutorials/04 advanced iv by @jpcbertoldo in https://github.com/openvinotoolkit/anomalib/pull/2352 +- 🚀 Add datumaro annotation dataloader by @ashwinvaidya17 in https://github.com/openvinotoolkit/anomalib/pull/2377 +- 📚 Add training from a checkpoint example by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2389 + +### Changed + +- 🔨 Refactor folder3d to avoid complex-structure (C901) issue by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2185 +- Update open-clip-torch requirement from <2.26.1,>=2.23.0 to >=2.23.0,<2.26.2 by @dependabot in https://github.com/openvinotoolkit/anomalib/pull/2189 +- Update sphinx requirement by @dependabot in https://github.com/openvinotoolkit/anomalib/pull/2235 +- Refactor Lightning's `trainer.model` to `trainer.lightning_module` by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2255 +- Revert "Update open-clip-torch requirement from <2.26.1,>=2.23.0 to >=2.23.0,<2.26.2" by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2270 +- Update ruff configuration by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2269 +- Update timm requirement by @dependabot in https://github.com/openvinotoolkit/anomalib/pull/2274 +- Refactor BaseThreshold to Threshold by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2278 +- 🔨 Lint: Update Ruff Config - Add Missing Copyright Headers by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2281 +- Reduce rich methods by @ashwinvaidya17 in https://github.com/openvinotoolkit/anomalib/pull/2283 +- Enable Ruff Rules: PLW1514 and PLR6201 by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2284 +- Update nncf export by @ashwinvaidya17 in https://github.com/openvinotoolkit/anomalib/pull/2286 +- Linting: Enable `PLR6301`, # could be a function, class method or static method by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2288 +- 🐞 Update `setuptools` requirement for PEP 660 support by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2320 +- 🔨 Update the issue templates by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2363 +- 🐞 Defer OpenVINO import to avoid unnecessary warnings by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2385 +- 🔨 Make single GPU benchmarking 5x more efficient by @mzweilin in https://github.com/openvinotoolkit/anomalib/pull/2390 +- 🐞 Export the flattened config in benchmark CSV. by @mzweilin in https://github.com/openvinotoolkit/anomalib/pull/2391 +- 🔨 Export experiment duration in seconds in CSV. by @mzweilin in https://github.com/openvinotoolkit/anomalib/pull/2392 +- 🐞 Fix installation package issues by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2395 + +### Deprecated + +- 🔨 Deprecate try import and replace it with Lightning's package_available by @samet-akcay in https://github.com/openvinotoolkit/anomalib/pull/2373 + +### Fixed + +- Add check before loading metrics data from checkpoint by @blaz-r in https://github.com/openvinotoolkit/anomalib/pull/2323 +- Fix transforms for draem, dsr and rkde by @blaz-r in https://github.com/openvinotoolkit/anomalib/pull/2324 +- Makes batch size dynamic by @Marcus1506 in https://github.com/openvinotoolkit/anomalib/pull/2339 + +## New Contributors + +- @Marcus1506 made their first contribution in https://github.com/openvinotoolkit/anomalib/pull/2339 + +**Full Changelog**: https://github.com/openvinotoolkit/anomalib/compare/v1.1.1...v1.2.0 + +### New Contributors + **Full Changelog**: ## [v1.1.1] diff --git a/configs/data/datumaro.yaml b/configs/data/datumaro.yaml new file mode 100644 index 0000000000..c6b7c863e1 --- /dev/null +++ b/configs/data/datumaro.yaml @@ -0,0 +1,11 @@ +class_path: anomalib.data.Datumaro +init_args: + root: "datasets/datumaro" + train_batch_size: 32 + eval_batch_size: 32 + num_workers: 8 + test_split_mode: FROM_DIR + test_split_ratio: 0.2 + val_split_mode: FROM_TEST + val_split_ratio: 0.5 + seed: null diff --git a/docs/source/images/tiled_ensemble/ensemble_flow.png b/docs/source/images/tiled_ensemble/ensemble_flow.png new file mode 100644 index 0000000000..7a5a81fa79 Binary files /dev/null and b/docs/source/images/tiled_ensemble/ensemble_flow.png differ diff --git a/docs/source/markdown/get_started/anomalib.md b/docs/source/markdown/get_started/anomalib.md index 37af563b3e..4580c7fae5 100644 --- a/docs/source/markdown/get_started/anomalib.md +++ b/docs/source/markdown/get_started/anomalib.md @@ -17,7 +17,7 @@ The installer can be installed using the following commands: :::{tab-item} API :sync: label-1 -```{literalinclude} ../../snippets/install/pypi.txt +```{literalinclude} /snippets/install/pypi.txt :language: bash ``` @@ -26,7 +26,7 @@ The installer can be installed using the following commands: :::{tab-item} Source :sync: label-2 -```{literalinclude} ../../snippets/install/source.txt +```{literalinclude} /snippets/install/source.txt :language: bash ``` @@ -42,7 +42,7 @@ The next section demonstrates how to install the full package using the CLI inst :::::{dropdown} Installing the Full Package After installing anomalib, you can install the full package using the following commands: -```{literalinclude} ../../snippets/install/anomalib_help.txt +```{literalinclude} /snippets/install/anomalib_help.txt :language: bash ``` @@ -50,14 +50,14 @@ As can be seen above, the only available sub-command is `install` at the moment. The `install` sub-command has options to install either the full package or the specific components of the package. -```{literalinclude} ../../snippets/install/anomalib_install_help.txt +```{literalinclude} /snippets/install/anomalib_install_help.txt :language: bash ``` By default the `install` sub-command installs the full package. If you want to install only the specific components of the package, you can use the `--option` flag. -```{literalinclude} ../../snippets/install/anomalib_install.txt +```{literalinclude} /snippets/install/anomalib_install.txt :language: bash ``` @@ -66,13 +66,15 @@ After following these steps, your environment will be ready to use anomalib! ## {octicon}`mortar-board` Training -Anomalib supports both API and CLI-based training. The API is more flexible and allows for more customization, while the CLI training utilizes command line interfaces, and might be easier for those who would like to use anomalib off-the-shelf. +Anomalib supports both API and CLI-based training. The API is more flexible +and allows for more customization, while the CLI training utilizes command line +interfaces, and might be easier for those who would like to use anomalib off-the-shelf. ::::{tab-set} :::{tab-item} API -```{literalinclude} ../../snippets/train/api/default.txt +```{literalinclude} /snippets/train/api/default.txt :language: python ``` @@ -80,7 +82,7 @@ Anomalib supports both API and CLI-based training. The API is more flexible and :::{tab-item} CLI -```{literalinclude} ../../snippets/train/cli/default.txt +```{literalinclude} /snippets/train/cli/default.txt :language: bash ``` @@ -100,7 +102,7 @@ Anomalib includes multiple inferencing scripts, including Torch, Lightning, Grad :::{tab-item} API :sync: label-1 -```{literalinclude} ../../snippets/inference/api/lightning.txt +```{literalinclude} /snippets/inference/api/lightning.txt :language: python ``` @@ -109,7 +111,7 @@ Anomalib includes multiple inferencing scripts, including Torch, Lightning, Grad :::{tab-item} CLI :sync: label-2 -```{literalinclude} ../../snippets/inference/cli/lightning.txt +```{literalinclude} /snippets/inference/cli/lightning.txt :language: bash ``` @@ -201,7 +203,7 @@ Anomalib supports hyper-parameter optimization using [wandb](https://wandb.ai/) :::{tab-item} CLI -```{literalinclude} ../../snippets/pipelines/hpo/cli.txt +```{literalinclude} /snippets/pipelines/hpo/cli.txt :language: bash ``` @@ -209,7 +211,7 @@ Anomalib supports hyper-parameter optimization using [wandb](https://wandb.ai/) :::{tab-item} API -```{literalinclude} ../../snippets/pipelines/hpo/api.txt +```{literalinclude} /snippets/pipelines/hpo/api.txt :language: bash ``` @@ -233,7 +235,7 @@ To run a training experiment with experiment tracking, you will need the followi By using the configuration file above, you can run the experiment with the following command: -```{literalinclude} ../../snippets/logging/cli.txt +```{literalinclude} /snippets/logging/cli.txt :language: bash ``` @@ -241,7 +243,7 @@ By using the configuration file above, you can run the experiment with the follo :::{tab-item} API -```{literalinclude} ../../snippets/logging/api.txt +```{literalinclude} /snippets/logging/api.txt :language: bash ``` diff --git a/docs/source/markdown/guides/how_to/pipelines/custom_pipeline.md b/docs/source/markdown/guides/how_to/pipelines/custom_pipeline.md new file mode 100644 index 0000000000..ed3d66f81d --- /dev/null +++ b/docs/source/markdown/guides/how_to/pipelines/custom_pipeline.md @@ -0,0 +1,254 @@ +# Pipelines + +This guide demonstrates how to create a [Pipeline](../../reference/pipelines/index.md) for your custom task. + +A pipeline is made up of runners. These runners are responsible for running a single type of job. A job is the smallest unit of work that is independent, such as, training a model or statistical comparison of the outputs of two models. Each job should be designed to be independent of other jobs so that they are agnostic to the runner that is running them. This ensures that the job can be run in parallel or serially without any changes to the job itself. The runner does not directly instantiate a job but rather has a job generator that generates the job based on the configuration. This generator is responsible for parsing the config and generating the job. + +## Birds Eye View + +In this guide we are going to create a dummy significant parameter search pipeline. The pipeline will have two jobs. The first job trains a model and computes the metric. The second job computes the significance of the parameters to the final score using shapely values. The final output of the pipeline is a plot that shows the contribution of each parameter to the final score. This will help teach you how to create a pipeline, a job, a job generator, and how to expose it to the `anomalib` CLI. The pipeline is going to be named `experiment`. So by the end of this you will be able to generate significance plot using + +```{literalinclude} ../../../../snippets/pipelines/dummy/anomalib_cli.txt +:language: bash +``` + +The final directory structure will look as follows: + +```{literalinclude} ../../../../snippets/pipelines/dummy/src_dir_structure.txt + +``` + +```{literalinclude} ../../../../snippets/pipelines/dummy/tools_dir_structure.txt +:language: bash +``` + +## Creating the Jobs + +Let's first look at the base class for the [jobs](../../reference/pipelines/base/job.md). It has a few methods defined. + +- The `run` method is the main method that is called by the runner. This is where we will train the model and return the model metrics. +- The `collect` method is used to gather the results from all the runs and collate them. This is handy as we want to pass a single object to the next job that contains details of all the runs including the final score. +- The `save` method is used to write any artifacts to the disk. It accepts the gathered results as a parameter. This is useful in a variety of situations. Say, when we want to write the results in a csv file or write the raw anomaly maps for further processing. + +Let's create the first job that trains the model and computes the metric. Since it is a dummy example, we will just return a random number as the metric. + +```python +class TrainJob(Job): + name = "train" + + def __init__(self, lr: float, backbone: str, stride: int): + self.lr = lr + self.backbone = backbone + self.stride = stride + + def run(self, task_id: int | None = None) -> dict: + print(f"Training with lr: {self.lr}, backbone: {self.backbone}, stride: {self.stride}") + time.sleep(2) + score = np.random.uniform(0.7, 0.1) + return {"lr": self.lr, "backbone": self.backbone, "stride": self.stride, "score": score} +``` + +Ignore the `task_id` for now. It is used for parallel jobs. We will come back to it later. + +````{note} +The `name` attribute is important and is used to identify the arguments in the job config file. +So, in our case the config `yaml` file will contain an entry like this: + +```yaml +... +train: + lr: + backbone: + stride: +... +```` + +Of course, it is up to us to choose what parameters should be shown under the `train` key. + +Let's also add the `collect` method so that we return a nice dict object that can be used by the next job. + +```python +def collect(results: list[dict]) -> dict: + output: dict = {} + for key in results[0]: + output[key] = [] + for result in results: + for key, value in result.items(): + output[key].append(value) + return output +``` + +We can also define a `save` method that writes the dictionary as a csv file. + +```python +@staticmethod +def save(results: dict) -> None: + """Save results in a csv file.""" + results_df = pd.DataFrame(results) + file_path = Path("runs") / TrainJob.name + file_path.mkdir(parents=True, exist_ok=True) + results_df.to_csv(file_path / "results.csv", index=False) +``` + +The entire job class is shown below. + +```{literalinclude} ../../../../snippets/pipelines/dummy/train_job.txt +:language: python +``` + +Now we need a way to generate this job when the pipeline is run. To do this we need to subclass the [JobGenerator](../../reference/pipelines/base/generator.md) class. + +The job generator is the actual object that is attached to a runner and is responsible for parsing the configuration and generating jobs. It has two methods that need to be implemented. + +- `generate_job`: This method accepts the configuration as a dictionary and, optionally, the results of the previous job. For the train job, we don't need results for previous jobs, so we will ignore it. +- `job_class`: This holds the reference to the class of the job that the generator will yield. It is used to inform the runner about the job that is being run, and is used to access the static attributes of the job such as its name, collect method, etc. + +Let's first start by defining the configuration that the generator will accept. The train job requires three parameters: `lr`, `backbone`, and `stride`. We will also add another parameter that defines the number of experiments we want to run. One way to define it would be as follows: + +```yaml +train: + experiments: 10 + lr: [0.1, 0.99] + backbone: + - resnet18 + - wide_resnet50 + stride: + - 3 + - 5 +``` + +For this example the specification is defined as follows. + +1. The number of experiments is set to 10. +2. Learning rate is sampled from a uniform distribution in the range `[0.1, 0.99]`. +3. The backbone is chosen from the list `["resnet18", "wide_resnet50"]`. +4. The stride is chosen from the list `[3, 5]`. + +```{note} +While the `[ ]` and `-` syntax in `yaml` both signify a list, for visual disambiguation this example uses `[ ]` to denote closed interval and `-` for a list of options. +``` + +With this defined, we can define the generator class as follows. + +```{literalinclude} ../../../../snippets/pipelines/dummy/train_generator.txt +:language: python +``` + +Since this is a dummy example, we generate the next experiment randomly. In practice, you would use a more sophisticated method that relies on your validation metrics to generate the next experiment. + +```{admonition} Challenge +:class: tip +For a challenge define your own configuration and a generator to parse that configuration. +``` + +Okay, so now we can train the model. We still need a way to find out which parameters contribute the most to the final score. We will do this by computing the shapely values to find out the contribution of each parameter to the final score. + +Let's first start by adding the library to our environment + +```bash +pip install shap +``` + +The following listing shows the job that computes the shapely values and saves a plot that shows the contribution of each parameter to the final score. A quick rundown without going into the details of the job (as it is irrelevant to the pipeline) is as follows. We create a `RandomForestRegressor` that is trained on the parameters to predict the final score. We then compute the shapely values to identify the parameters that have the most significant impact on the model performance. Finally, the `save` method saves the plot so we can visually inspect the results. + +```{literalinclude} ../../../../snippets/pipelines/dummy/significance_job.txt + +``` + +Great! Now we have the job, as before, we need the generator. Since we only need the results from the previous stage, we don't need to define the config. Let's quickly write that as well. + +```{literalinclude} ../../../../snippets/pipelines/dummy/significance_job_generator.txt + +``` + +## Experiment Pipeline + +So now we have the jobs, and a way to generate them. Let's look at how we can chain them together to achieve what we want. We will use the [Pipeline](../../reference/pipelines/base/pipeline.md) class to define the pipeline. + +When creating a custom pipeline, there is only one important method that we need to implement. That is the `_setup_runners` method. This is where we chain the runners together. + +```{literalinclude} ../../../../snippets/pipelines/dummy/pipeline_serial.txt +:language: python +``` + +In this example we use `SerialRunner` for running each job. It is a simple runner that runs the jobs in a serial manner. For more information on `SerialRunner` look [here](../../reference/pipelines/runners/serial.md). + +Okay, so we have the pipeline. How do we run it? To do this let's create a simple entrypoint in `tools` folder of Anomalib. + +Here is how the directory looks. + +```{literalinclude} ../../../../snippets/pipelines/dummy/tools_dir_structure.txt +:language: bash +``` + +As you can see, we have the `config.yaml` file in the same directory. Let's quickly populate `experiment.py`. + +```python +from anomalib.pipelines.experiment_pipeline import ExperimentPipeline + +if __name__ == "__main__": + ExperimentPipeline().run() +``` + +Alright! Time to take it on the road. + +```bash +python tools/experimental/experiment/experiment.py --config tools/experimental/experiment/config.yaml +``` + +If all goes well you should see the summary plot in `runs/significant_feature/summary_plot.png`. + +## Exposing to the CLI + +Now that you have your shiny new pipeline, you can expose it as a subcommand to `anomalib` by adding an entry to the pipeline registry in `anomalib/cli/pipelines.py`. + +```python +if try_import("anomalib.pipelines"): + ... + from anomalib.pipelines import ExperimentPipeline + +PIPELINE_REGISTRY: dict[str, type[Pipeline]] | None = { + "experiment": ExperimentPipeline, + ... +} +``` + +With this you can now call + +```{literalinclude} ../../../../snippets/pipelines/dummy/anomalib_cli.txt +:language: bash +``` + +Congratulations! You have successfully created a pipeline that trains a model and computes the significance of the parameters to the final score 🎉 + +```{admonition} Challenge +:class: tip +This example used a random model hence the scores were meaningless. Try to implement a real model and compute the scores. Look into which parameters lead to the most significant contribution to your score. +``` + +## Final Tweaks + +Before we end, let's look at a few final tweaks that you can make to the pipeline. + +First, let's run the initial model training in parallel. Since all jobs are independent, we can use the [ParallelRunner](../../reference/pipelines/runners/parallel.md). Since the `TrainJob` is a dummy job in this example, the pool of parallel jobs is set to the number of experiments. + +```{literalinclude} ../../../../snippets/pipelines/dummy/pipeline_parallel.txt + +``` + +You now notice that the entire pipeline takes lesser time to run. This is handy when you have large number of experiments, and when each job takes substantial time to run. + +Now on to the second one. When running the pipeline we don't want our terminal cluttered with the outputs from each run. Anomalib provides a handy decorator that temporarily hides the output of a function. It suppresses all outputs to the standard out and the standard error unless an exception is raised. Let's add this to the `TrainJob` + +```python +from anomalib.utils.logging import hide_output + +class TrainJob(Job): + ... + + @hide_output + def run(self, task_id: int | None = None) -> dict: + ... +``` + +You will no longer see the output of the `print` statement in the `TrainJob` method in the terminal. diff --git a/docs/source/markdown/guides/how_to/pipelines/index.md b/docs/source/markdown/guides/how_to/pipelines/index.md index ed3d66f81d..c7f2c44706 100644 --- a/docs/source/markdown/guides/how_to/pipelines/index.md +++ b/docs/source/markdown/guides/how_to/pipelines/index.md @@ -1,254 +1,30 @@ -# Pipelines +# Pipeline Tutorials -This guide demonstrates how to create a [Pipeline](../../reference/pipelines/index.md) for your custom task. +This section contains tutorials on how to use different pipelines of Anomalib and how to creat your own. -A pipeline is made up of runners. These runners are responsible for running a single type of job. A job is the smallest unit of work that is independent, such as, training a model or statistical comparison of the outputs of two models. Each job should be designed to be independent of other jobs so that they are agnostic to the runner that is running them. This ensures that the job can be run in parallel or serially without any changes to the job itself. The runner does not directly instantiate a job but rather has a job generator that generates the job based on the configuration. This generator is responsible for parsing the config and generating the job. +::::{grid} +:margin: 1 1 0 0 +:gutter: 1 -## Birds Eye View +:::{grid-item-card} {octicon}`stack` Tiled Ensemble +:link: ./tiled_ensemble +:link-type: doc -In this guide we are going to create a dummy significant parameter search pipeline. The pipeline will have two jobs. The first job trains a model and computes the metric. The second job computes the significance of the parameters to the final score using shapely values. The final output of the pipeline is a plot that shows the contribution of each parameter to the final score. This will help teach you how to create a pipeline, a job, a job generator, and how to expose it to the `anomalib` CLI. The pipeline is going to be named `experiment`. So by the end of this you will be able to generate significance plot using +Learn more about how to use the tiled ensemble pipelines. +::: -```{literalinclude} ../../../../snippets/pipelines/dummy/anomalib_cli.txt -:language: bash -``` - -The final directory structure will look as follows: - -```{literalinclude} ../../../../snippets/pipelines/dummy/src_dir_structure.txt - -``` - -```{literalinclude} ../../../../snippets/pipelines/dummy/tools_dir_structure.txt -:language: bash -``` - -## Creating the Jobs - -Let's first look at the base class for the [jobs](../../reference/pipelines/base/job.md). It has a few methods defined. - -- The `run` method is the main method that is called by the runner. This is where we will train the model and return the model metrics. -- The `collect` method is used to gather the results from all the runs and collate them. This is handy as we want to pass a single object to the next job that contains details of all the runs including the final score. -- The `save` method is used to write any artifacts to the disk. It accepts the gathered results as a parameter. This is useful in a variety of situations. Say, when we want to write the results in a csv file or write the raw anomaly maps for further processing. - -Let's create the first job that trains the model and computes the metric. Since it is a dummy example, we will just return a random number as the metric. - -```python -class TrainJob(Job): - name = "train" +:::{grid-item-card} {octicon}`gear` Custom Pipeline +:link: ./custom_pipeline +:link-type: doc - def __init__(self, lr: float, backbone: str, stride: int): - self.lr = lr - self.backbone = backbone - self.stride = stride - - def run(self, task_id: int | None = None) -> dict: - print(f"Training with lr: {self.lr}, backbone: {self.backbone}, stride: {self.stride}") - time.sleep(2) - score = np.random.uniform(0.7, 0.1) - return {"lr": self.lr, "backbone": self.backbone, "stride": self.stride, "score": score} -``` - -Ignore the `task_id` for now. It is used for parallel jobs. We will come back to it later. - -````{note} -The `name` attribute is important and is used to identify the arguments in the job config file. -So, in our case the config `yaml` file will contain an entry like this: - -```yaml -... -train: - lr: - backbone: - stride: -... -```` - -Of course, it is up to us to choose what parameters should be shown under the `train` key. - -Let's also add the `collect` method so that we return a nice dict object that can be used by the next job. - -```python -def collect(results: list[dict]) -> dict: - output: dict = {} - for key in results[0]: - output[key] = [] - for result in results: - for key, value in result.items(): - output[key].append(value) - return output -``` - -We can also define a `save` method that writes the dictionary as a csv file. - -```python -@staticmethod -def save(results: dict) -> None: - """Save results in a csv file.""" - results_df = pd.DataFrame(results) - file_path = Path("runs") / TrainJob.name - file_path.mkdir(parents=True, exist_ok=True) - results_df.to_csv(file_path / "results.csv", index=False) -``` - -The entire job class is shown below. - -```{literalinclude} ../../../../snippets/pipelines/dummy/train_job.txt -:language: python -``` - -Now we need a way to generate this job when the pipeline is run. To do this we need to subclass the [JobGenerator](../../reference/pipelines/base/generator.md) class. - -The job generator is the actual object that is attached to a runner and is responsible for parsing the configuration and generating jobs. It has two methods that need to be implemented. - -- `generate_job`: This method accepts the configuration as a dictionary and, optionally, the results of the previous job. For the train job, we don't need results for previous jobs, so we will ignore it. -- `job_class`: This holds the reference to the class of the job that the generator will yield. It is used to inform the runner about the job that is being run, and is used to access the static attributes of the job such as its name, collect method, etc. - -Let's first start by defining the configuration that the generator will accept. The train job requires three parameters: `lr`, `backbone`, and `stride`. We will also add another parameter that defines the number of experiments we want to run. One way to define it would be as follows: - -```yaml -train: - experiments: 10 - lr: [0.1, 0.99] - backbone: - - resnet18 - - wide_resnet50 - stride: - - 3 - - 5 -``` - -For this example the specification is defined as follows. - -1. The number of experiments is set to 10. -2. Learning rate is sampled from a uniform distribution in the range `[0.1, 0.99]`. -3. The backbone is chosen from the list `["resnet18", "wide_resnet50"]`. -4. The stride is chosen from the list `[3, 5]`. - -```{note} -While the `[ ]` and `-` syntax in `yaml` both signify a list, for visual disambiguation this example uses `[ ]` to denote closed interval and `-` for a list of options. -``` - -With this defined, we can define the generator class as follows. - -```{literalinclude} ../../../../snippets/pipelines/dummy/train_generator.txt -:language: python -``` - -Since this is a dummy example, we generate the next experiment randomly. In practice, you would use a more sophisticated method that relies on your validation metrics to generate the next experiment. - -```{admonition} Challenge -:class: tip -For a challenge define your own configuration and a generator to parse that configuration. -``` - -Okay, so now we can train the model. We still need a way to find out which parameters contribute the most to the final score. We will do this by computing the shapely values to find out the contribution of each parameter to the final score. - -Let's first start by adding the library to our environment - -```bash -pip install shap -``` +Learn more about how to create a new custom pipeline. +::: -The following listing shows the job that computes the shapely values and saves a plot that shows the contribution of each parameter to the final score. A quick rundown without going into the details of the job (as it is irrelevant to the pipeline) is as follows. We create a `RandomForestRegressor` that is trained on the parameters to predict the final score. We then compute the shapely values to identify the parameters that have the most significant impact on the model performance. Finally, the `save` method saves the plot so we can visually inspect the results. +:::: -```{literalinclude} ../../../../snippets/pipelines/dummy/significance_job.txt +```{toctree} +:caption: Model Tutorials +:hidden: +./feature_extractors ``` - -Great! Now we have the job, as before, we need the generator. Since we only need the results from the previous stage, we don't need to define the config. Let's quickly write that as well. - -```{literalinclude} ../../../../snippets/pipelines/dummy/significance_job_generator.txt - -``` - -## Experiment Pipeline - -So now we have the jobs, and a way to generate them. Let's look at how we can chain them together to achieve what we want. We will use the [Pipeline](../../reference/pipelines/base/pipeline.md) class to define the pipeline. - -When creating a custom pipeline, there is only one important method that we need to implement. That is the `_setup_runners` method. This is where we chain the runners together. - -```{literalinclude} ../../../../snippets/pipelines/dummy/pipeline_serial.txt -:language: python -``` - -In this example we use `SerialRunner` for running each job. It is a simple runner that runs the jobs in a serial manner. For more information on `SerialRunner` look [here](../../reference/pipelines/runners/serial.md). - -Okay, so we have the pipeline. How do we run it? To do this let's create a simple entrypoint in `tools` folder of Anomalib. - -Here is how the directory looks. - -```{literalinclude} ../../../../snippets/pipelines/dummy/tools_dir_structure.txt -:language: bash -``` - -As you can see, we have the `config.yaml` file in the same directory. Let's quickly populate `experiment.py`. - -```python -from anomalib.pipelines.experiment_pipeline import ExperimentPipeline - -if __name__ == "__main__": - ExperimentPipeline().run() -``` - -Alright! Time to take it on the road. - -```bash -python tools/experimental/experiment/experiment.py --config tools/experimental/experiment/config.yaml -``` - -If all goes well you should see the summary plot in `runs/significant_feature/summary_plot.png`. - -## Exposing to the CLI - -Now that you have your shiny new pipeline, you can expose it as a subcommand to `anomalib` by adding an entry to the pipeline registry in `anomalib/cli/pipelines.py`. - -```python -if try_import("anomalib.pipelines"): - ... - from anomalib.pipelines import ExperimentPipeline - -PIPELINE_REGISTRY: dict[str, type[Pipeline]] | None = { - "experiment": ExperimentPipeline, - ... -} -``` - -With this you can now call - -```{literalinclude} ../../../../snippets/pipelines/dummy/anomalib_cli.txt -:language: bash -``` - -Congratulations! You have successfully created a pipeline that trains a model and computes the significance of the parameters to the final score 🎉 - -```{admonition} Challenge -:class: tip -This example used a random model hence the scores were meaningless. Try to implement a real model and compute the scores. Look into which parameters lead to the most significant contribution to your score. -``` - -## Final Tweaks - -Before we end, let's look at a few final tweaks that you can make to the pipeline. - -First, let's run the initial model training in parallel. Since all jobs are independent, we can use the [ParallelRunner](../../reference/pipelines/runners/parallel.md). Since the `TrainJob` is a dummy job in this example, the pool of parallel jobs is set to the number of experiments. - -```{literalinclude} ../../../../snippets/pipelines/dummy/pipeline_parallel.txt - -``` - -You now notice that the entire pipeline takes lesser time to run. This is handy when you have large number of experiments, and when each job takes substantial time to run. - -Now on to the second one. When running the pipeline we don't want our terminal cluttered with the outputs from each run. Anomalib provides a handy decorator that temporarily hides the output of a function. It suppresses all outputs to the standard out and the standard error unless an exception is raised. Let's add this to the `TrainJob` - -```python -from anomalib.utils.logging import hide_output - -class TrainJob(Job): - ... - - @hide_output - def run(self, task_id: int | None = None) -> dict: - ... -``` - -You will no longer see the output of the `print` statement in the `TrainJob` method in the terminal. diff --git a/docs/source/markdown/guides/how_to/pipelines/tiled_ensemble.md b/docs/source/markdown/guides/how_to/pipelines/tiled_ensemble.md new file mode 100644 index 0000000000..3550efb5fd --- /dev/null +++ b/docs/source/markdown/guides/how_to/pipelines/tiled_ensemble.md @@ -0,0 +1,157 @@ +# Tiled ensemble + +This guide will show you how to use **The Tiled Ensemble** method for anomaly detection. For more details, refer to the official [Paper](https://openaccess.thecvf.com/content/CVPR2024W/VAND/html/Rolih_Divide_and_Conquer_High-Resolution_Industrial_Anomaly_Detection_via_Memory_Efficient_CVPRW_2024_paper.html). + +The tiled ensemble approach reduces memory consumption by dividing input images into a grid of tiles and training a dedicated model for each tile location. +It is compatible with any existing image anomaly detection model without the need for any modification of the underlying architecture. + +![Tiled ensemble flow](../../../../images/tiled_ensemble/ensemble_flow.png) + +```{note} +This feature is experimental and may not work as expected. +For any problems refer to [Issues](https://github.com/openvinotoolkit/anomalib/issues) and feel free to ask any question in [Discussions](https://github.com/openvinotoolkit/anomalib/discussions). +``` + +## Training + +You can train a tiled ensemble using the training script located inside `tools/tiled_ensemble` directory: + +```{code-block} bash + +python tools/tiled_ensemble/train_ensemble.py \ + --config tools/tiled_ensemble/ens_config.yaml +``` + +By default, the Padim model is trained on **MVTec AD bottle** category using image size of 256x256, divided into non-overlapping 128x128 tiles. +You can modify these parameters in the [config file](#ensemble-configuration). + +## Evaluation + +After training, you can evaluate the tiled ensemble on test data using: + +```{code-block} bash + +python tools/tiled_ensemble/eval.py \ + --config tools/tiled_ensemble/ens_config.yaml \ + --root path_to_results_dir + +``` + +Ensure that `root` points to the directory containing the training results, typically `results/padim/mvtec/bottle/runX`. + +## Ensemble configuration + +Tiled ensemble is configured using `ens_config.yaml` file in the `tools/tiled_ensemble` directory. +It contains general settings and tiled ensemble specific settings. + +### General + +General settings at the top of the config file are used to set up the random `seed`, `accelerator` (device) and the path to where results will be saved `default_root_dir`. + +```{code-block} yaml +seed: 42 +accelerator: "gpu" +default_root_dir: "results" +``` + +### Tiling + +This section contains the following settings, used for image tiling: + +```{code-block} yaml + +tiling: + tile_size: 256 + stride: 256 +``` + +These settings determine the tile size and stride. Another important parameter is image_size from `data` section later in the config. It determines the original size of the image. + +Input image is split into tiles, where each tile is of shape set by `tile_size` and tiles are taken with step set by `stride`. +For example: having image_size: 512, tile_size: 256, and stride: 256, results in 4 non-overlapping tile locations. + +### Normalization and thresholding + +Next up are the normalization and thresholding settings: + +```{code-block} yaml +normalization_stage: image +thresholding: + method: F1AdaptiveThreshold + stage: image +``` + +- **Normalization**: Can be applied per each tile location separately (`tile` option), after combining prediction (`image` option), or skipped (`none` option). + +- **Thresholding**: Can also be applied at different stages, but it is limited to `tile` and `image`. Another setting for thresholding is the method used. It can be specified as a string or by the class path. + +### Data + +The `data` section is used to configure the input `image_size` and other parameters for the dataset used. + +```{code-block} yaml +data: + class_path: anomalib.data.MVTec + init_args: + root: ./datasets/MVTec + category: bottle + train_batch_size: 32 + eval_batch_size: 32 + num_workers: 8 + task: segmentation + transform: null + train_transform: null + eval_transform: null + test_split_mode: from_dir + test_split_ratio: 0.2 + val_split_mode: same_as_test + val_split_ratio: 0.5 + image_size: [256, 256] +``` + +Refer to [Data](../../reference/data/image/index.md) for more details on parameters. + +### SeamSmoothing + +This section contains settings for `SeamSmoothing` block of pipeline: + +```{code-block} yaml +SeamSmoothing: + apply: True + sigma: 2 + width: 0.1 + +``` + +SeamSmoothing job is responsible for smoothing of regions where tiles meet - called tile seams. + +- **apply**: If True, smoothing will be applied. +- **sigma**: Controls the sigma of Gaussian filter used for smoothing. +- **width**: Sets the percentage of the region around the seam to be smoothed. + +### TrainModels + +The last section `TrainModels` contains the setup for model training: + +```{code-block} yaml +TrainModels: + model: + class_path: Fastflow + + metrics: + pixel: AUROC + image: AUROC + + trainer: + max_epochs: 500 + callbacks: + - class_path: lightning.pytorch.callbacks.EarlyStopping + init_args: + patience: 42 + monitor: pixel_AUROC + mode: max +``` + +- **Model**: Specifies the model used. Refer to [Models](../../reference/models/image/index.md) for more details on the model parameters. +- **Metrics**: Defines evaluation metrics for pixel and image level. +- **Trainer**: _optional_ parameters, used to control the training process. Refer to [Engine](../../reference/engine/index.md) for more details. diff --git a/docs/source/snippets/train/api/default.txt b/docs/source/snippets/train/api/default.txt index 30293cf501..1fe6cb895c 100644 --- a/docs/source/snippets/train/api/default.txt +++ b/docs/source/snippets/train/api/default.txt @@ -1,12 +1,15 @@ # Import the required modules from anomalib.data import MVTec -from anomalib.models import Patchcore from anomalib.engine import Engine +from anomalib.models import EfficientAd # Initialize the datamodule, model and engine -datamodule = MVTec() -model = Patchcore() -engine = Engine() +datamodule = MVTec(train_batch_size=1) +model = EfficientAd() +engine = Engine(max_epochs=5) # Train the model engine.fit(datamodule=datamodule, model=model) + +# Continue from a checkpoint +engine.fit(datamodule=datamodule, model=model, ckpt_path="path/to/checkpoint.ckpt") diff --git a/docs/source/snippets/train/cli/default.txt b/docs/source/snippets/train/cli/default.txt index 3f64f687ad..1990dbf97e 100644 --- a/docs/source/snippets/train/cli/default.txt +++ b/docs/source/snippets/train/cli/default.txt @@ -2,10 +2,13 @@ anomalib train -h # Train by using the default values. -anomalib train --model Patchcore --data anomalib.data.MVTec +anomalib train --model EfficientAd --data anomalib.data.MVTec --data.train_batch_size 1 # Train by overriding arguments. -anomalib train --model Patchcore --data anomalib.data.MVTec --data.category transistor +anomalib train --model EfficientAd --data anomalib.data.MVTec --data.train_batch_size 1 --data.category transistor # Train by using a config file. anomalib train --config + +# Continue training from a checkpoint +anomalib train --config --ckpt_path diff --git a/notebooks/700_metrics/701a_aupimo.ipynb b/notebooks/700_metrics/701a_aupimo.ipynb index c3be846a77..10e198fef9 100644 --- a/notebooks/700_metrics/701a_aupimo.ipynb +++ b/notebooks/700_metrics/701a_aupimo.ipynb @@ -404,7 +404,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.8" + "version": "3.10.14" }, "orig_nbformat": 4 }, diff --git a/notebooks/700_metrics/701e_aupimo_advanced_iv.ipynb b/notebooks/700_metrics/701e_aupimo_advanced_iv.ipynb new file mode 100644 index 0000000000..e117006951 --- /dev/null +++ b/notebooks/700_metrics/701e_aupimo_advanced_iv.ipynb @@ -0,0 +1,1507 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# AUPIMO statistical comparison between two models\n", + "\n", + "Model A has a higher average AUPIMO than model B. Can you be _sure_ that A is better than B? \n", + "\n", + "We'll use statistical tests here to make informed decisions about this.\n", + "\n", + "This notebook covers:\n", + "- load/save functions to import/export AUPIMO scores;\n", + "- statistical tests between two models, in particular:\n", + " - parametrical test with Student's t-test;\n", + " - non-parametrical test with Wilcoxon signed-rank test;\n", + "\n", + "> AUPIMO is pronounced \"a-u-pee-mo\".\n", + "\n", + "> For basic usage, please check the notebook [701a_aupimo.ipynb](./701a_aupimo.ipynb)." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "\n", + "# What is AUPIMO?\n", + "\n", + "The `Area Under the Per-Image Overlap [curve]` (AUPIMO) is a metric of recall (higher is better) designed for visual anomaly detection.\n", + "\n", + "Inspired by the [ROC](https://en.wikipedia.org/wiki/Receiver_operating_characteristic) and [PRO](https://link.springer.com/article/10.1007/s11263-020-01400-4) curves, \n", + "\n", + "> AUPIMO is the area under a curve of True Positive Rate (TPR or _recall_) as a function of False Positive Rate (FPR) restricted to a fixed range. \n", + "\n", + "But:\n", + "- the TPR (Y-axis) is *per-image* (1 image = 1 curve/score);\n", + "- the FPR (X-axis) considers the (average of) **normal** images only; \n", + "- the FPR (X-axis) is in log scale and its range is [1e-5, 1e-4]\\* (harder detection task!).\n", + "\n", + "\\* The score (the area under the curve) is normalized to be in [0, 1].\n", + "\n", + "AUPIMO can be interpreted as\n", + "\n", + "> average segmentation recall in an image given that the model (nearly) does not yield false positives in normal images.\n", + "\n", + "References in the last cell.\n", + "\n", + "![AUROC vs. AUPRO vs. AUPIMO](./roc_pro_pimo.svg)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Setup" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Install `anomalib` using `pip`." + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "# TODO(jpcbertoldo): replace by `pip install anomalib` when AUPIMO is released # noqa: TD003\n", + "%pip install ../.." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Imports" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import json\n", + "import urllib.request\n", + "from pathlib import Path\n", + "\n", + "import numpy as np\n", + "import pandas as pd\n", + "import torch\n", + "from matplotlib import pyplot as plt\n", + "from matplotlib.ticker import FixedLocator, IndexLocator, MaxNLocator, PercentFormatter\n", + "from scipy import stats\n", + "\n", + "from anomalib.metrics.pimo import AUPIMOResult" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "pd.options.display.float_format = \"{:.3f}\".format" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "%matplotlib inline" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Load AUPIMO scores\n", + "\n", + "Unlike previous notebook, we will not train and evaluate the models here.\n", + "\n", + "We'll load the AUPIMO scores from the benchmark presented in our paper (check the reference in the last cell).\n", + "\n", + "These scores can be found in AUPIMO's official repository in [`jpcbertoldo:aupimo/data/experiments/benchmark`](https://github.com/jpcbertoldo/aupimo/tree/main/data/experiments/benchmark). " + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loading benchmark results for model 'patchcore_wr101' and dataset 'mvtec/capsule'\n", + "Dowloading JSON file from https://raw.githubusercontent.com/jpcbertoldo/aupimo/refs/heads/main/data/experiments/benchmark/patchcore_wr101/mvtec/capsule/aupimo/aupimos.json\n", + "Converting payload to dataclass\n", + "Done!\n", + "Loading benchmark results for model 'patchcore_wr50' and dataset 'mvtec/capsule'\n", + "Dowloading JSON file from https://raw.githubusercontent.com/jpcbertoldo/aupimo/refs/heads/main/data/experiments/benchmark/patchcore_wr50/mvtec/capsule/aupimo/aupimos.json\n", + "Converting payload to dataclass\n", + "Done!\n" + ] + } + ], + "source": [ + "def get_benchmark_scores_url(model: str, dataset: str) -> str:\n", + " \"\"\"Generate the URL for the JSON file of a specific model and dataset.\"\"\"\n", + " root_url = \"https://raw.githubusercontent.com/jpcbertoldo/aupimo/refs/heads/main/data/experiments/benchmark\"\n", + " models = {\n", + " \"efficientad_wr101_m_ext\",\n", + " \"efficientad_wr101_s_ext\",\n", + " \"fastflow_cait_m48_448\",\n", + " \"fastflow_wr50\",\n", + " \"padim_r18\",\n", + " \"padim_wr50\",\n", + " \"patchcore_wr101\",\n", + " \"patchcore_wr50\",\n", + " \"pyramidflow_fnf_ext\",\n", + " \"pyramidflow_r18_ext\",\n", + " \"rd++_wr50_ext\",\n", + " \"simplenet_wr50_ext\",\n", + " \"uflow_ext\",\n", + " }\n", + " if model not in models:\n", + " msg = f\"Model '{model}' not available. Choose one of {sorted(models)}.\"\n", + " raise ValueError(msg)\n", + " datasets = {\n", + " \"mvtec/bottle\",\n", + " \"mvtec/cable\",\n", + " \"mvtec/capsule\",\n", + " \"mvtec/carpet\",\n", + " \"mvtec/grid\",\n", + " \"mvtec/hazelnut\",\n", + " \"mvtec/leather\",\n", + " \"mvtec/metal_nut\",\n", + " \"mvtec/pill\",\n", + " \"mvtec/screw\",\n", + " \"mvtec/tile\",\n", + " \"mvtec/toothbrush\",\n", + " \"mvtec/transistor\",\n", + " \"mvtec/wood\",\n", + " \"mvtec/zipper\",\n", + " \"visa/candle\",\n", + " \"visa/capsules\",\n", + " \"visa/cashew\",\n", + " \"visa/chewinggum\",\n", + " \"visa/fryum\",\n", + " \"visa/macaroni1\",\n", + " \"visa/macaroni2\",\n", + " \"visa/pcb1\",\n", + " \"visa/pcb2\",\n", + " \"visa/pcb3\",\n", + " \"visa/pcb4\",\n", + " \"visa/pipe_fryum\",\n", + " }\n", + " if dataset not in datasets:\n", + " msg = f\"Dataset '{dataset}' not available. Choose one of {sorted(datasets)}.\"\n", + " raise ValueError(msg)\n", + " return f\"{root_url}/{model}/{dataset}/aupimo/aupimos.json\"\n", + "\n", + "\n", + "def download_json(url_str: str) -> dict[str, str | float | int | list[str]]:\n", + " \"\"\"Download the JSON content from an URL.\"\"\"\n", + " with urllib.request.urlopen(url_str) as url: # noqa: S310\n", + " return json.load(url)\n", + "\n", + "\n", + "def load_aupimo_result_from_json_dict(payload: dict[str, str | float | int | list[str]]) -> AUPIMOResult:\n", + " \"\"\"Convert the JSON payload to an AUPIMOResult dataclass.\"\"\"\n", + " if not isinstance(payload, dict):\n", + " msg = f\"Invalid payload. Must be a dictionary. Got {type(payload)}.\"\n", + " raise TypeError(msg)\n", + " try:\n", + " return AUPIMOResult(\n", + " fpr_lower_bound=payload[\"fpr_lower_bound\"],\n", + " fpr_upper_bound=payload[\"fpr_upper_bound\"],\n", + " # `num_threshs` vs `num_thresholds` is an inconsistency with an older version of the JSON file\n", + " num_thresholds=payload[\"num_threshs\"] if \"num_threshs\" in payload else payload[\"num_thresholds\"],\n", + " thresh_lower_bound=payload[\"thresh_lower_bound\"],\n", + " thresh_upper_bound=payload[\"thresh_upper_bound\"],\n", + " aupimos=torch.tensor(payload[\"aupimos\"], dtype=torch.float64),\n", + " )\n", + "\n", + " except KeyError as ex:\n", + " msg = f\"Invalid payload. Missing key {ex}.\"\n", + " raise ValueError(msg) from ex\n", + "\n", + " except (TypeError, ValueError) as ex:\n", + " msg = f\"Invalid payload. Cause: {ex}.\"\n", + " raise ValueError(msg) from ex\n", + "\n", + "\n", + "def get_benchmark_aupimo_scores(model: str, dataset: str, verbose: bool = True) -> AUPIMOResult:\n", + " \"\"\"Get the benchmark AUPIMO scores for a specific model and dataset.\n", + "\n", + " Args:\n", + " model: The model name. See `_get_json_url` for the available models.\n", + " dataset: The \"collection/dataset\", where 'collection' is either 'mvtec' or 'visa', and 'dataset' is\n", + " the name of the dataset within the collection. See `_get_json_url` for the available datasets.\n", + " verbose: Whether to print the progress.\n", + "\n", + " Returns:\n", + " A `AUPIMOResult` dataclass with the AUPIMO scores from the benchmark results.\n", + "\n", + " More details in our paper: https://arxiv.org/abs/2401.01984\n", + " \"\"\"\n", + " if verbose:\n", + " print(f\"Loading benchmark results for model '{model}' and dataset '{dataset}'\")\n", + " url = get_benchmark_scores_url(model, dataset)\n", + " if verbose:\n", + " print(f\"Dowloading JSON file from {url}\")\n", + " payload = download_json(url)\n", + " if verbose:\n", + " print(\"Converting payload to dataclass\")\n", + " aupimo_result = load_aupimo_result_from_json_dict(payload)\n", + " if verbose:\n", + " print(\"Done!\")\n", + " return payload, aupimo_result\n", + "\n", + "\n", + "json_model_a, aupimo_result_model_a = get_benchmark_aupimo_scores(\"patchcore_wr101\", \"mvtec/capsule\")\n", + "_, aupimo_result_model_b = get_benchmark_aupimo_scores(\"patchcore_wr50\", \"mvtec/capsule\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's remove the `nan` values from the normal images." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "modela.shape=(109,) modelb.shape=(109,) labels.shape=(109,)\n" + ] + } + ], + "source": [ + "# corresponding paths to the images\n", + "# where the AUPIMO scores were computed from\n", + "paths = json_model_a[\"paths\"]\n", + "\n", + "# extract the labels (i.e. anomaly type or 'good')\n", + "labels = np.array([p.split(\"/\")[-2] for p in paths])\n", + "\n", + "# let's extract only the AUPIMO scores from anomalies\n", + "modela = aupimo_result_model_a.aupimos[labels != \"good\"].numpy()\n", + "modelb = aupimo_result_model_b.aupimos[labels != \"good\"].numpy()\n", + "labels = labels[labels != \"good\"]\n", + "print(f\"{modela.shape=} {modelb.shape=} {labels.shape=}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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", + "text/plain": [ + "
" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(6, 3))\n", + "ax.boxplot(\n", + " [modela, modelb],\n", + " tick_labels=[f\"A mean: {modela.mean():.0%}\", f\"B mean: {modelb.mean():.0%}\"],\n", + " vert=False,\n", + " showmeans=True,\n", + " meanline=True,\n", + " widths=0.5,\n", + ")\n", + "ax.invert_yaxis()\n", + "ax.set_title(\"AUPIMO scores distributions from two models\")\n", + "fig # noqa: B018, RUF100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Is this difference significant?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Image by image comparison\n", + "\n", + "Since we have the scores of each model for each image, we can compare them image by image." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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1bhPkyZNEBoaigkTJqjXhYSEGHVWjr17AVVfo8WLwSRJpKusY8VaWemniZTjz5KByN70OnHiRAwePBgNGzZEo0aNsGTJEiQnJ2Po0KEAgEGDBsHd3R3z5s0DAIwfPx4tWrTAt99+i06dOiE4OBj//PMPVqxYYdS4Bw6UEiabW4kKyNlZ+qfPjiqqYxLpkeyJMjAwEHFxcZg+fToePXqEunXrYv/+/eoOO3fu3IGZ2aunWJo2bYpNmzbh888/x6effoo333wTv//+O2rVqiXXRyAiomJM9kQJAGPHjs21qTUsLCzHut69e6N3794GjoqIiMgEBhwgIiIyZUyUREREWjBREhERacFESUREpAUTJRERkRZMlERERFowURIREWnBRElERKQFEyUREZEWTJRERERamMQQdsYkhACQ/3nIcqNUKpGSkoLExEROf5MFyyV3LBvNWC65Y9lopq9yUeUBVV7ITYlLlM+fPwcAeHp6yhwJERGZgufPn8PR0THX9xUir1RazGRmZuLBgwcoXbo0FApFgY+TmJgIT09P3L17Fw4ODnqMsGhjueSOZaMZyyV3LBvN9FUuQgg8f/4cFSpUyDZL1etKXI3SzMwMHh4eejueg4MDL2ANWC65Y9loxnLJHctGM32Ui7aapAo78xAREWnBRElERKQFE2UBWVlZYcaMGbCyspI7FJPCcskdy0YzlkvuWDaaGbtcSlxnHiIiIl2wRklERKQFEyUREZEWTJRERERaMFESERFpwUSpxbJly+Dl5QVra2s0btwYp0+f1rr91q1bUb16dVhbW6N27drYu3evkSI1Ll3KZeXKlWjevDmcnZ3h7OwMPz+/PMuxKNP1mlEJDg6GQqFAt27dDBugTHQtl4SEBIwZMwbly5eHlZUV3nrrLf4+/c+SJUtQrVo12NjYwNPTEx999BFevnxppGiN49ixYwgICECFChWgUCjw+++/57lPWFgY6tevDysrK1StWhXr1q3TX0CCNAoODhaWlpZizZo14r///hMjRowQTk5OIiYmRuP2x48fF+bm5mLBggXi8uXL4vPPPxcWFhbi0qVLRo7csHQtl379+olly5aJ8+fPi8jISDFkyBDh6Ogo7t27Z+TIDU/XslGJiooS7u7uonnz5qJr167GCdaIdC2X1NRU0bBhQ9GxY0cRHh4uoqKiRFhYmIiIiDBy5Ian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iejoaBEdHS2sra1FXFyc6Nq1q3qqoaSkJGFhYSE2btyo3j8tLU1UqFBBLFiwQAghxLRp00SNGjWynWPKlCkCgHpqpqCgIDFy5Mhs2/z111/CzMxMvHjxQgghTZe2ePFirbH369dPTJgwQb1cp04dvU51RCSXUnInaiLKycXFBZ06dcK6desghECnTp1QtmzZbNvcvHkTSqUSzZo1U6+zsLBAo0aNEBkZCQCIjIxE48aNs+33+qzvFy5cwMWLF7Fx40b1OiEEMjMzERUVBR8fnzzjTUhIwI4dOxAeHq5eN2DAAKxevdqkOx8R5QcTJZGJGjZsmLr5c9myZQY7T1JSEkaNGoVx48bleC+/nYc2bdqEly9fZkvKqmR77do1vPXWW3qLl8jYeI+SyES1b98eaWlpUCqV8Pf3z/F+lSpVYGlpiePHj6vXKZVKnDlzBjVq1AAA+Pj44PTp09n2+/vvv7Mt169fH5cvX0bVqlVz/LO0tMxXrKtXr8bHH3+MiIgI9b8LFy6gefPmWLNmja4fncikMFESmShzc3NERkbi8uXLMDc3z/G+nZ0dPvzwQ0yePBn79+/H5cuXMWLECKSkpCAoKAgA8MEHH+D69euYPHkyrl69ik2bNmHdunXZjjNlyhScOHECY8eORUREBK5fv45du3bluzNPREQEzp07h+HDh6NWrVrZ/vXt2xfr169Henp6ocuDSC5MlEQmzMHBAQ4ODrm+P3/+fPTs2RMDBw5E/fr1cePGDRw4cADOzs4ApKbT7du34/fff0edOnWwfPlyzJ07N9sx3n77bRw9ehTXrl1D8+bNUa9ePUyfPh0VKlTIV4yrV69GjRo1UL169Rzvde/eHbGxsdi7d68On5rItCiEEELuIIiIiEwVa5RERERaMFESERFpwURJRESkBRMlERGRFkyUREREWjBREhERacFESUREpAUTJRERkRZMlERERFowURIREWnBRElERKTF/wN1z1wlOOSlmQAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "fig, ax = plt.subplots(figsize=(5, 5))\n", + "modela_is_better = modela > modelb\n", + "ax.scatter(modela[modela_is_better], modelb[modela_is_better], alpha=0.3, s=10, color=\"red\", marker=\"o\")\n", + "ax.scatter(modela[~modela_is_better], modelb[~modela_is_better], alpha=0.3, s=10, color=\"blue\", marker=\"o\")\n", + "ax.plot([0, 1], [0, 1], color=\"black\", linestyle=\"--\")\n", + "ax.set_xlabel(\"Model A\")\n", + "ax.set_ylabel(\"Model B\")\n", + "ax.set_title(\"AUPIMO scores direct comparison\")\n", + "ax.grid()\n", + "fig # noqa: B018, RUF100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The dashed line is where both models have the same AUPIMO score.\n", + "\n", + "Notice that there are images where one performs better than the other and vice-versa." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Parametric Comparison\n", + "\n", + "Before using the statistical test, let's first visualize the data seen by the test.\n", + "\n", + "We'll use a _paired_ t-test, which means we'll compare the AUPIMO scores of the same image one by one." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABcsAAAGXCAYAAABslwhJAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOydd3gc1dX/vzPbd9WsLndZlixjWuhYlm0M2NhgIIRQE0oMpuVN8ibhDSQQSAK8tCQEEnoJyc8EQglvCKaYYrBlTDdusootd3VZbVfaNvf3x2pXs6stM7Pa8dXqfJ6HB2v3nr33e++ZMzN37pwrMMYYCIIgCIIgCIIgCIIgCIIgCGIcIx7uBhAEQRAEQRAEQRAEQRAEQRDE4YYmywmCIAiCIAiCIAiCIAiCIIhxD02WEwRBEARBEARBEARBEARBEOMemiwnCIIgCIIgCIIgCIIgCIIgxj00WU4QBEEQBEEQBEEQBEEQBEGMe2iynCAIgiAIgiAIgiAIgiAIghj30GQ5QRAEQRAEQRAEQRAEQRAEMe6hyXKCIAiCIAiCIAiCIAiCIAhi3EOT5QRBEARBEARBEARBEARBEMS4hybLCYIgCIIgCGKcc+edd0IQhLDPpk+fjquuuurwNIhQxFVXXYXp06cf7mYQBEEQBEGkDTRZThAEQRAEMc549NFHIQgCTj755Kjf7969G4Ig4MEHH4z6/YMPPghBELB79+7QZwsXLoQgCKH/cnNzceKJJ+LZZ5+FJEmhcldddRUyMjLCfi9oW15eHrW+NWvWhH73lVdeGfH9tm3b8L3vfQ+TJk2CxWLBxIkTcfnll2Pbtm2JuoLgkA0bNuDOO+9Ed3f34W4KQRAEQRAEMc6gyXKCIAiCIIhxxqpVqzB9+nR89tlnaGxsHLXfnTx5Mv7+97/j73//O26//Xb4fD6sWLECv/zlLxPaWq1WNDY24rPPPovaXqvVGtXutddew3HHHYf3338fV199NR599FGsWLECH374IY477jj861//SlrXeKWurg5PPfWU7vVu2LABv/nNb2iyXAFPPfUU6urqDnczCIIgCIIg0gaaLCcIgiAIghhHNDU1YcOGDfjDH/6AgoICrFq1atR+Ozs7G9/73vfwve99D//93/+NmpoaTJ48GX/+85/h9Xrj2paVlWHWrFn4xz/+Efb54OAg/vWvf+Hss88eYbNz5058//vfx4wZM7B582bcddddWLFiBX73u99h8+bNmDFjBr7//e9j165do6YxVQwODoatwOcBi8UCk8kUt4zT6dSpNYScYL+bTCZYLJbD3BqCIAiCIIj0gSbLCYIgCIIgxhGrVq3ChAkTcPbZZ+PCCy8c1cnySOx2O0455RQ4nU60t7cnLH/ppZfipZdeCps0fuONN+ByuXDRRReNKP/AAw/A5XLhySefREFBQdh3+fn5eOKJJ+B0OnH//fcnrPuRRx7BnDlzYLfbMWHCBJxwwgl44YUXwsocOHAAK1aswMSJE2GxWFBaWoobbrgBHo8nVGbXrl347ne/i9zc3JD+N998M+x31q5dC0EQ8OKLL+K2227DpEmTYLfb0dvbCwD49NNPcdZZZyE7Oxt2ux0LFixATU1N2G/09fXhJz/5CaZPnw6LxYLCwkKceeaZ+OqrrxJqXb9+PU488URYrVaUlZXhiSeeiFouMmf5X//6VwiCgI8++gg33ngjCgsLMXny5ND3b731Fqqrq+FwOJCZmYmzzz47aiqcHTt24KKLLkJBQQFsNhtmzZqFX/3qVwACudNvvvlmAEBpaWko/Y485U80Pv30UyxbtgwTJkyAw+HA0UcfjT/96U9hZT744INQ+3JycnDeeeehtrY2rEwwd3t9fT2+973vITs7GwUFBbj99tvBGMO+fftw3nnnISsrC8XFxfj9738fZh8c25deegm//OUvUVxcDIfDgXPPPRf79u0LK7tu3Tp897vfxdSpU2GxWDBlyhT893//NwYGBsLKBVMX7dy5E8uWLUNmZiYuv/zy0HeROctffPFFHH/88cjMzERWVhaOOuqoEX2hxk//+c9/4u6778bkyZNhtVpx+umnj+obKQRBEARBEDxhPNwNIAiCIAiCIPRj1apVuOCCC2A2m3HppZfisccew+eff44TTzwxJfXt2rULBoMBOTk5CctedtlluPPOO7F27VosWrQIAPDCCy/g9NNPR2Fh4Yjyb7zxBqZPn47q6uqovzd//nxMnz59xCRgJE899RR+9KMf4cILL8SPf/xjDA4OYvPmzfj0009x2WWXAQAOHjyIk046Cd3d3Vi5ciUqKytx4MABvPLKK3C5XDCbzWhtbcXcuXPhcrnwox/9CHl5eXj++edx7rnn4pVXXsG3v/3tsHp/97vfwWw24+c//znc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ERUVFVi6dCkmT56MH/zgB3j77bfDyqj108i+mDBhAgBE1U0QBEEQBDHWoZzlBEEQBEEQ44APPvgAzc3NePHFF/Hiiy+O+H7VqlVYvHgxAMBqtQLAiI0Gg7hcrrByQRwOB84444yk2llSUoKFCxfi97//PWpqavDqq69GLZednY2SkhJs3rw57u9t3rwZkyZNQlZWVswys2fPRl1dHf7zn//g7bffxquvvopHH30Uv/71r/Gb3/wmKT3xkK8sBhBaNf7AAw/g2GOPjWqTkZEBALjoootQXV2Nf/3rX3j33XfxwAMP4L777sNrr72GpUuXpqzN8dr997//HcXFxSPKG41j75ZDvnI+3mcAwBhT/ft+vx9nnnkmurq68Itf/AKVlZVwOBw4cOAArrrqqrBNbgHAYrFAFBOvcyosLMSmTZvwzjvv4K233sJbb72F5557DldccQWef/551e0ERlc3QRAEQRAE74y9K1eCIAiCIAhCNatWrUJhYSH+8pe/jPjutddew7/+9S88/vjjsNlsKCgogN1uR11dXdTfqqurg91uV7ViXA2XXXYZrrnmGuTk5GDZsmUxy51zzjl46qmnsH79+rCUEkHWrVuH3bt347rrrktYp8PhwMUXX4yLL74YHo8HF1xwAe6++27ceuutKCgoQFZWFrZu3Rr3N6ZNmxa1z3bs2BH6Ph5lZWUAgKysLEUPHUpKSnDjjTfixhtvRFtbG4477jjcfffdMSfLCwoKYLPZQivB5cQaayUE211YWBi33TNmzACAhP2oJiVLsO6tW7fGrDvY77HGJj8/Hw6HQ3GdSojsY8YYGhsbcfTRRwMIbLRaX1+P559/HldccUWoXLz0KUoxm81Yvnw5li9fDkmScOONN+KJJ57A7bffjpkzZybtpwRBEARBEOkMpWEhCIIgCIJIcwYGBvDaa6/hnHPOwYUXXjjivx/+8Ifo6+vDv//9bwCBlaSLFy/GG2+8gb1794b91t69e/HGG29g8eLFMVecJsuFF16IO+64A48++mhYruRIbr75ZthsNlx33XXo7OwM+66rqwvXX3897HY7br755rj1RdqazWYcccQRYIzB6/VCFEWcf/75eOONN/DFF1+MsA+usF22bBk+++wzfPLJJ6HvnE4nnnzySUyfPh1HHHFE3HYcf/zxKCsrw4MPPoj+/v4R37e3twMIrEqO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6VwurSznHEEQkJGRkVIbPerQAo86eNStl026aCd/J+2pqkMtvI5humgnfyftqapDLbzqIO18aee1r7QwXrXzqoO086Wd177SwnjVzqsO0s6Xdl77Si3pdC+ihtjZzAku8Pv9qK+vh9/vT5mNHnVogUcdPOrWyyZdtJO/k/ZU1aEWXscwXbSTv5P2VNWhFl51kHa+tPPaV1oYr9p51UHa+dLOa19pYbxq51UHaedLO699pZZ0uhdRA02WjwEkSUq5jR51aIFHHTzq1ssmXbSTv6feJtV18OonauF1DNNFO/l76m1SXQevfqIWXnWQ9tTCow49dGupJ12086qDtKcWHnXQsZ5aeNVB2lMLjzp41K2nTaqgyXKCIAiCIAiCIAiCIAiCIAhi3EOT5QRBEARBEARBEARBEARBEMS4hybLOUcURZSWlkIUlQ+VWhs96tACjzp41K2XTbpoJ38n7amqQy28jmG6aCd/J+2pqkMtvOog7Xxp57WvtDBetfOqg7TzpZ3XvtLCeNXOqw7Szpd2XvtKLel0L6IGPlpBxMVoNKbcRo86tMCjDh5162WTLtrJ31Nvk+o6ePUTtfA6huminfw99TaproNXP1ELrzpIe2rhUYceurXUky7aedVB2lMLjzroWE8tvOog7amFRx086tbTJlUc1snyjz/+GMuXL8fEiRMhCAJef/31sO8ZY/j1r3+NkpIS2Gw2nHHGGWhoaAgr09XVhcsvvxxZWVnIycnBihUr0N/fH/p+9+7dmD9/PhwOB+bPn4/du3eH2Z9zzjl49dVXUyVRM5LHg761a9Fy992oX7kSLXffjb61ayF5PIltJQkNDQ2Kk+OrKZ9Mu9SSSh161BHsq/Z770XxqlVov/feuH2lpW/1sEmmDqXaQ3Yp9EU9jimturXUkWo/UduuZBjrx7pa9BoPrfXwOOZabPSoQwu8naO1xlI1cU5PX+TpvK61TWpseI3vep5z00U7b/6eDDzGax7juxabdDm3abHRI5am8hpea3m96tDCeNXO6/U4b9q12tD81Nj1d71tUslhnbZ3Op045phj8IMf/AAXXHDBiO/vv/9+PPzww3j++edRWlqK22+/HUuWLMH27dthtVoBAJdffjmam5uxZs0aeL1eXH311Vi5ciVeeOEFAMDPfvYzTJo0Cc888wxuu+02/PznP8crr7wCAHjppZcg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sUpqDmaXlV3qWJkl9TOr3VyxWQ4As2fPPuSyK61lZWVh9+7dhzx+99134+6777axZ0REREREREREREQknSsuw0JEREREREREREREFA5ulgunaRpSUlIO3vnaphon2rBCYg6JuZ2qcUt2rndmt6sNs6TOoVuyc70zu11tmCU1B7PLyi51rKyI1uxSczC7rOxSx8qKaM0uNQezy8oudazMctNnETNccxkWt9J1HT179rS1xok2rJCYQ2Jup2rckp3rndntasMsqXPoluxc78xuVxtmSc3B7LKySx0rK6I1u9QczC4ru9SxsiJas0vNweyysksdK7Pc9FnEDJ5ZLpxhGNizZ4/pu8iaqXGiDSsk5pCY26kat2Tnemd2u9owS+ocuiU71zuz29WGWVJzMLus7FLHyopozS41B7PLyi51rKyI1uxSczC7rOxSx8osN30WMYOb5cIppVBVVQWllG01TrRhhcQcEnM7VeOW7FzvzG5XG2ZJnUO3ZOd6Z3a72jBLag5ml5Vd6lhZEa3ZpeZgdlnZpY6VFdGaXWoOZpeVXepYmeWmzyJmcLOciIiIiIiIiIiIiKIer1luUfNvO6qrq0OuaWxsRF1dHaqrqxEXFxdSTTAYRG1tLaqrqxETE2NLjRNtOJFd6liZze5EDis1bsnO9c7s0bTerdS4JTvXO7NzvUe+Jlqzc70zO9d75/fLLdmljhWz81i3ow0rNW7JLnWsJK53qzVWNO/hdnQGOzfLLaqpqQEA9OrVq5N7QkREREREREREREQdqampQUpKymGf15SUC8J0MYZh4Pvvv0dycjI0TQupprq6Gr169cI333yDbt26hdzWiSeeiHXr1pnqn9kau9twKrvEsbKS3YkcVmrckp3rndnteL3U9W6lxi3Zud6Z3Y7XR+t6B6I3O9c7s3O9d36/nGiD653Zeax3fr+caIPrXd56t1pjllIKNTU1OOqoo6Drh78yOc8st0jXdRxzzDGWart162bqgIyJiTH1eis1TrQB2J9d6lgB5rI7NR/Rmp3rndntagOQt96t1LglO9c7s9vVBhC96x2I3uxc78xuRxsSxwrgZ1audzn9kphdag6J691KjVuySx0rQN56t1pjxZHOKG/GG3x2AbNmzbK9xok2rJCYQ2Jup2rckp3r3f4au9uQuk7MkjqHbsnO9W5/jd1tSF0nZknNwez2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/fv3t3m9W+b9cLnbM3bsWABwxbx7PB4MGDAAJ5xwAh5++GGMHDkS//u//+v6+QYOn709bpnz9evXY+/evRg9ejRiY2MRGxuLlStX4qmnnkJsbCyysrJcO+8dZQ8Gg4fUuGXef6r1+5doONZb6+i9m1vmvGfPni1/KdNs6NChLZegcfN7uY6yt8dt7+e++uorfPDBB7jmmmtaHouWY7297O1xy7F+2223Ye7cubj44otx7LHH4rLLLsPNN9+Mhx9+GIC7j/WOsrfHTcd6//79sXLlStTW1uKbb77BP//5TzQ2NqJfv36unnfgyNnb46Z5bxbKHGdnZ2Pv3r1tnm9qasIPP/zQZdeB1bU9duxYNDU1Yffu3XZ2TyxuljvA4/HghBNOwIcfftjymGEY+PDDD9tc6zUa1NbWYufOnejZs2dnd8VROTk5yM7ObrMGqqursXbt2qhbA99++y0qKytdsQaUUpg9ezbefvttfPTRR8jJyWnz/AknnIC4uLg2875161Z8/fXXXXreO8rdns8//xwAXDHvP2UYBhoaGlw730fSnL09bpnz0047DRs2bMDnn3/e8t+YMWMwbdq0lv/v1nnvKHt7f5brlnn/qdbvX6LtWO/ovZtb5jwvLw9bt25t89i2bdvQp08fAO5+L9dR9va46f0cACxZsgSZmZmYMmVKy2PRcqy3l709bjnW6+rqoOttt0FiYmJgGAYAdx/rHWVvj9uOdQDw+Xzo2bMn9u3bh+XLl+Pcc8919by31l729rhx3kOZ4/Hjx2P//v1t/trgo48+gmEYLb8w7Gqsru3PP/8cuq4fclmaqNHZdxiNFq+//rryer1q6dKlatOmTeraa69VqampqrS0tLO7Zqtbb71VFRQUqJKSElVYWKhOP/10lZ6ervbu3dvZXYu4mpoa9a9//Uv961//UgDUE088of71r3+pr776Siml1Pz581Vqaqr685//rL744gt17rnnqpycHHXgwIFO7nl4jpS7pqZG/frXv1Zr1qxRJSUl6oMPPlCjR49WAwcOVPX19Z3d9bBdf/31KiUlRRUUFKg9e/a0/FdXV9fympkzZ6revXurjz76SH366adq/Pjxavz48Z3Y6/B1lHvHjh1q3rx56tNPP1UlJSXqz3/+s+rXr5865ZRTOrnn4Zs7d65auXKlKikpUV988YWaO3eu0jRN/eMf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3HPo378/AOC///u/8fLLL6OsrAxJSUkYNmwYTj31VHz88cdtNsvz8vIwd+5cAMCgQYNQWFiIBQsWtLtZ/sYbb8AwDLzwwgstl11asmQJUlNTUVBQgDPOOCOk3I2NjVi4cGFLX2fPnt3mbO8nn3wSd955J6ZOnQoAWLhwIZYvX97yfENDAx566CF88MEHGD9+PACgX79+WL16NZ5//nlMnDgRRx99NJ5//nlcfvnlKC0txXvvvYd//etfiI3lRxsiIiIiCg+vD2IjXt+ViIi6Ov4sI7LuggsuwPfff493330XZ511FgoKCjB69GgsXbq05TUffPABTjvtNBx99NFITk7GZZddhsrKStTV1bW8JjExsWXzGQCysrLQt2/flku1ND/WfNmkZs2bza3/vXnz5nb7+u9//xs7duxAcnJyy1nxPXr0QH19PXbu3Bly5p/2tWfPni39qqqqwp49ezB27NiW52NjYzFmzJiWf+/YsQN1dXWYPHlySz+SkpLwxz/+sU0/LrzwQpx//vmYP38+fve732HgwIEh95GIiIiI6HC4WU6OKigogKZp2L9/f8g1ffv2xZNPPmlbn4iiEY9FIiJnxMfHY/LkybjrrrtQVFSEK664Avfccw8AYPfu3fjZz36G4447Dv/3f/+H9evX49lnnwVw8GbxzeLi4tp8TU3T2n0snBvL19bW4oQTTsDnn3/e5r9t27bhkksuCfnrtNevn16TvKN+AMDf/va3Nv3YtGkT/t//+38tr6urq8P69esRExOD7du3h/z1iYiIiIiOhJvl1OKKK66ApmmYOXPmIc/NmjULmqbhiiuucL5jIfr222/h8XgwYsSIzu6KeF19rt2uq87PvffeC03TWv5LSUnBhAkTsHLlys7umlhdda6JyLphw4bB7/cDANavXw/DMPD4449j3LhxGDRoEL7//vuItVVcXHzIv4cOHdrua0ePHo3t27cjMzMTAwYMaPNfSkpKRPqTkpKCnj17Yu3atS2PNTU1Yf369S3/HjZsGLxeL77++utD+tGrV6+W1916663QdR3vv/8+nnrqKXz00UcR6SMRERERRTdullMbvXr1wuuvv44DBw60PFZfX49ly5ahd+/endizji1duhQXXXQRqqur23wIo/Z15bmOBl11foYPH449e/Zgz549WLNmDQYOHIif/exnqKqq6uyuidVV55qIjqyyshKTJk3CK6+8gi+++AIlJSV488038eijj+Lcc88FAAwYMACNjY14+umnsWvXLrz88stYuHBhxPpQWFiIRx99FNu2bcOzzz6LN998EzfddFO7r502bRrS09Nx7rnnYtWqVSgpKUFBQQFuvPFGfPvttxHr00033YT58+fjnXfewZYtW3DDDTe0+Sun5ORk/PrXv8bNN9+MP/zhD9i5cyc+++wzPP300/jDH/4A4OBZ5y+99BJeffVVTJ48GbfddhumT5+Offv2RayfRERERBSduFlObYwePRq9evXCW2+91fLYW2+9hd69e2PUqFFtXtvQ0IAbb7wRmZmZiI+Px8knn4x169a1ec17772HQYMGISEhAaeeeip27959SJurV6/GhAkTkJCQgF69euHGG29sOeMqVEopLFmyBJdddhkuueQSvPjii6bqo1Goc20YBh5++GHk5OQgISEBI0eObPNn0MFgEFdffXXL84MHD8b//u//tmnriiuuwHnnnYff/e536NmzJ9LS0jBr1iw0NjbaH7SL6qrHYmxsLLKzs5GdnY1hw4Zh3rx5qK2txbZt20x9nWjCY5HInZKSkjB27FgsWLAAp5xyCkaMGIG77roLM2bMwDPPPAMAGDlyJJ544gk88sgjGDFiBF599VU8/PDDEevDrbfeik8//RSjRo3CAw88gCeeeAJnnnlmu69NTEzEJ598gt69e2Pq1KkYOnQorr76atTX16Nbt24R7dNll12G6dOnY/z48UhOTsb555/f5jX3338/7rrrLjz88MMYOnQozjrrLPztb39DTk4OysvLcfXVV+Pee+/F6NGjAQD33XcfsrKy2v0rHSIiIiIiUxRF3IEDB9SmTZvUgQMHLH+N+sYm9eHmUnXvnzeqG15Zr+7980b14eZSVd/YFMGetjV9+nR17rnnqieeeEKddtppLY+fdtppasGCBercc89V06dPb3n8xhtvVEcddZR677331JdffqmmT5+uunfvriorK5VSSn399dfK6/WqW265RW3ZskW98sorKisrSwFQ+/btU0optWPHDuXz+dSCBQvUtm3bVGFhoRo1apS64oorWtrp06ePWrBgwRH7/uGHH6rs7GzV1NSkNmzYoJKTk1VtbW3ExsZtzMz1Aw88oIYMGaL+/ve/q507d6olS5Yor9erCgoKlFJKBQIBdffd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bdu2YcKECRg1ahTuvvtuHHXUUSH18cUXX8SwYcMO2SgHgPPPPx979+7Fe++9ZyJ1dDrSXN9///2466678PDDD2Po0KE466yz8Le//a1lA+66667D1KlT8Ytf/AJjx45FZWVlmzNbuzJfXi6UYcDw+9s8bvj9B2/ymZfrSD+6wrHY7Msvv0TPnj3Rs2dPHH/88fjTn/6E5557Dpdffrn54FGIxyIRERERERFR59GUUqqzO+E29fX1KCkpQU5ODuLj403XNzQFsbBgJwp3VkLXDl56xR84uFGe1z8NM/P7wxvb/mVaiChyjEAAlYsWw19cfPDa5YmJMOrqDm6UjxuHtGtnQPd4OrubRLYK92caERERERERUVfBy7AI5I2Nwcz8/jj2mBSs2laBvTUN6N0jERMGpSNvQDo3yokcons8SLt2BuJHDIe/sAhN5eXw9uoFX14ufLm53CgnIiIiIiIiInIRnlluA56FR0REbsGfaURERERERBQteM1yIiIiIiIiIiIiIop63CwnIiIiIiIiIiIioqjHzXIiIiIiIiIiIiIiinrcLLcRLwdPRERdHX+WERERERERUbTgZrkNYmJiAACBQKCTe0JERBSeuro6AEBcXFwn94SIiIiIiIjIXrGd3QE3io2NRWJiIsrLyxEXFwdd5+8kiIioa1FKoa6uDnv37kVqamrLL4KJiIiIiIiI3EpT/PtqWwQCAZSUlMAwjM7uChERkWWpqanIzs6Gpmmd3RUiIiIiIiIiW3Gz3EaGYfBSLERE1GXFxcXxjHIiIiIiIiKKGtwsJyIiIiIiIiIiIqKox4tpExEREREREREREVHU42Y5EREREREREREREUU9bpYTERERERERERERUdTjZjkRERERERERERERRT1ulhMRERERERERERFR1ONmORERERERERERERFFPW6WExEREREREREREVHU+/8BXMx9HHGlqgAAAABJRU5ErkJggg==", + "text/plain": [ + "
" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "num_samples = modela.shape[0]\n", + "indexes = np.arange(num_samples)\n", + "\n", + "fig, ax = plt.subplots(figsize=(18, 4))\n", + "\n", + "# plot sample index vs score and their mean\n", + "ax.scatter(indexes, modela, s=30, color=\"tab:blue\", marker=\"o\", label=\"Model A\", zorder=3, alpha=0.6)\n", + "ax.axhline(modela.mean(), color=\"tab:blue\", linestyle=\"--\", label=\"Mean\", zorder=3)\n", + "ax.scatter(indexes, modelb, s=30, color=\"tab:red\", marker=\"o\", label=\"Model B\", zorder=3, alpha=0.6)\n", + "ax.axhline(modelb.mean(), color=\"tab:red\", linestyle=\"--\", label=\"Mean\", zorder=3)\n", + "\n", + "# configure the x-axis\n", + "ax.set_xlabel(\"Sample index\")\n", + "ax.set_xlim(0 - (eps := 0.01 * num_samples), num_samples + eps)\n", + "ax.xaxis.set_major_locator(IndexLocator(5, 0))\n", + "ax.xaxis.set_minor_locator(IndexLocator(1, 0))\n", + "\n", + "# configure the y-axis\n", + "ax.set_ylabel(\"AUPIMO [%]\")\n", + "ax.set_ylim(0 - 0.05, 1 + 0.05)\n", + "ax.yaxis.set_major_locator(MaxNLocator(6))\n", + "ax.yaxis.set_major_formatter(PercentFormatter(1))\n", + "\n", + "# configure the grid, legend, etc\n", + "ax.grid(axis=\"both\", which=\"major\", linestyle=\"-\")\n", + "ax.grid(axis=\"x\", which=\"minor\", linestyle=\"--\", alpha=0.5)\n", + "ax.legend(ncol=4, loc=\"upper left\", bbox_to_anchor=(0, -0.08))\n", + "ax.set_title(\"AUPIMO scores direct comparison\")\n", + "\n", + "fig # noqa: B018, RUF100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Notice that several images actually have the same AUPIMO score for both models (e.g. from 10 to 15).\n", + "\n", + "Others like 21 show a big difference -- model B didn't detect the anomaly at all, but model A did a good job (60% AUPIMO).\n", + "\n", + "Let's simplify this and only show the differences." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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FR5wFY1jSbkUc3BwMLpvTZWLpcQwZRyg+vStpeUCsG8NjaFQKBATE4OcdDWUtrIy47ik5wIA4rMKkFhUevv8dDXiY2KgUCikbfH4DH/Ozs4V/sBhCdvIlOMWqNjYfRrGnLniAOzbyooDcJ9bGTGKT9M3h3IVEAkJCXqnb5WC2dggKSmpwkmV5O7du0hJSUGtWrUq7TWIiMh8bGxsMGrUKOTl5cHGxqjL9IiIqJKVa+9cu3ZtXL58GUFBQXrXX7p0qVwf7rOysnDjxg3p8a1bt3DhwgW4uLjAxcUFCxcuxIABA+Dl5YWbN2/i7bffRlBQEEJCQsqTNhERPSWUSiUiIiKQmJio92gyERGZX7mOmfTu3RsffPAB8vLydNbl5uZiwYIF6NOnj8Hxzpw5g1atWqFVq1YAgFmzZqFVq1aYP38+rK2tcenSJfTr1w/169dHWFgYgoODceTIEf5RISIiIiIyk3IdgXj//fexdetW1K9fH9OmTUODBg0APLrIbuXKlSgqKsJ7771ncLyuXbuitGu49+zZU570iIjoKSeEQHZ2NnJyckr9+0BEROZTrgLC09MTx48fx+TJkzFv3jxp5y6TyRASEoKVK1fC09OzUhIlIqLqLycnR5pNJCMjo0IXURMRUeUo9xVqdevWxe+//47U1FTcuHEDQgjUq1cPNWvWrIz8iIiIiIjIghg9xUXNmjXRtm1bU+ZCREREREQWruITUhMRERER0TODBQQRERERERmMBQQRERERERmMBQQRERERERnM6IuoiYiITM3a2hoDBgxAfn4+rK2tzZ0OERHpwQKCiIgshkqlwk8//YTExESoVCpzp0NERHrwFCYiIiIiIjIYCwgiIiIiIjIYCwgiIrIY2dnZsLa2Rq1atZCdnW3udIiISA8WEEREREREZDAWEEREREREZDAWEEREREREZDAWEEREREREZDAWEEREREREZDAWEEREREREZDDeiZqIiCyGtbU1evXqBbVaDWtra3OnQ0REerCAICIii6FSqbBz504kJiZCpVKZOx0iItKDpzAREREREZHBWEAQEREREZHBWEAQEZHFyM7OhoODAwICApCdnW3udIiISA+zFhB//vkn+vbtC29vb8hkMmzfvl1rvRAC8+fPR61atWBra4sePXrg+vXr5kmWiIiqRE5ODnJzc82dBhERlcCsBUR2djZa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eustcfHiReHp6SkSExPF+vXrRevWrasi5afCpEmTRN26dcWdO3dKbbd//34BQNy4caPENt26dRPbt28XX3zxhXjppZeEWq0Wo0ePFrNmzTJ12k+N1NRU4ejoKP79738bVEDow37V1bNnT9GnT59S23DMGqayCogn5eXliSZNmogzZ86ImTNninHjxgm1Wi1efPFF8eWXX5YrZxvjj12YzuzZszFmzJhS2wQEBBgUy8vLS2tGEADS7ApeXl7Sv4/PuFDcxtHREba2tjox8/PzMWXKFHz//fe4ceMGCgsL0aVLFwBA/fr1cerUqadmZhFj+joiIgKurq7o169fmfHbt2+PvXv3lrh+4sSJWLZsGTQaDc6fP4+BAwfCzs4OXbp0weHDh6XDa0+riozl9u3bo7CwELGxsWjQoEGJ4xT431h+UnXu3/L27f3799GtWzd07NgRa9euLTP+sz52DeHm5gZra2u941LfmLx69So2btyI8+fPY/369ejcuTPc3d0xaNAgjBs3DpmZmXBwcKiq9C3StGnTsHPnTvz555+oU6dOqW3bt28PALhx4wYCAwN11kdERMDZ2RmvvPIKXnvtNfTv3x9yuRwDBw7E/PnzKyX/p4GzszPq16+PGzdu4KWXXoJarUZaWprWTI4ljWGA/arP7du3sW/fPmzdurXUdhyzxikeiwkJCahVq5a0PCEhAS1btpTaJCYmaj2vsLAQDx8+LHEsf/jhh+jZsyeCg4Mxfvx4LF68GHK5HK+99hoOHDiA6dOnG5yjRRQQ7u7ucHd3N0msDh06YMmSJUhMTISHhweAR9MxOjo6onHjxlKb33//Xet5e/fuRYcOHfTGXLx4MV5++WW0bt0a58+f1zpPvaCgAEVFRSbJvSqUt6+FEIiIiMCoUaMgl8vLbH/hwgWtwf64devWwcXFBf369UNqaiqAR/1X/O/T1I8lqchYvnDhAqysrKRx26FDB7z33nsoKCiQ+n7v3r1o0KABatasqfP86t6/5enbe/fuoVu3bggODkZERASsrMq+3OtZH7uGUCgUCA4Oxv79+9G/f38AgEajwf79+zFt2jSttkIITJw4EcuXL4e9vT2Kioq0+gzAM9Nv+gghMH36dGzbtg2HDh2Cv79/mc+5cOECAOgdp0lJSVi0aBGOHj0KADr9/Sz3dVZWFm7evImRI0ciODgYcrkc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", + "text/plain": [ + "
" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "differences = modela - modelb\n", + "\n", + "fig, ax = plt.subplots(figsize=(9, 3))\n", + "ax.hist(differences, bins=np.linspace(-1, 1, 61), edgecolor=\"black\")\n", + "ax.axvline(differences.mean(), color=\"black\", linestyle=\"--\", label=\"Mean\")\n", + "\n", + "# configure the x-axis\n", + "ax.set_xlabel(\"AUPIMO [%]\")\n", + "ax.set_xlim(-1, 1)\n", + "ax.xaxis.set_major_locator(MaxNLocator(9))\n", + "ax.xaxis.set_minor_locator(MaxNLocator(41))\n", + "ax.xaxis.set_major_formatter(PercentFormatter(1))\n", + "\n", + "# configure the y-axis\n", + "ax.set_ylabel(\"Count\")\n", + "\n", + "# configure the grid, legend, etc\n", + "ax.grid(axis=\"both\", which=\"major\", linestyle=\"-\", alpha=1, linewidth=1.0)\n", + "ax.grid(axis=\"x\", which=\"minor\", linestyle=\"-\", alpha=0.3)\n", + "ax.legend(loc=\"upper right\")\n", + "ax.set_title(\"AUPIMO scores differences distribution (Model A - Model B)\")\n", + "\n", + "fig # noqa: B018, RUF100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "It looks like there is a bias to the right indeed (so model A > model B). \n", + "\n", + "Is that statistically significant or just random?\n", + "\n", + "> **Dependent t-test for paired samples**\n", + "> \n", + "> - null hypothesis: `average(A) == average(B)` \n", + "> - alternative hypothesis: `average(A) != average(B)`\n", + "> \n", + "> See [`scipy.stats.ttest_rel`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.ttest_rel.html) and [\" Wikipedia's page on \"Student's t-test\"](https://en.wikipedia.org/wiki/Student's_t-test#Dependent_t-test_for_paired_samples).\n", + ">\n", + "> **Confidence Level**\n", + "> \n", + "> Instead of reporting the p-value, we'll report the \"confidence level\" [that the null hypothesis is false], which is `1 - pvalue`.\n", + "> \n", + "> *Higher* confidence level *more confident* that `average(A) > average(B)`." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "test_result=TtestResult(statistic=2.8715471705520033, pvalue=0.004917091449731462, df=108)\n", + "confidence=99.5%\n" + ] + } + ], + "source": [ + "test_result = stats.ttest_rel(modela, modelb)\n", + "confidence = 1.0 - float(test_result.pvalue)\n", + "print(f\"{test_result=}\")\n", + "print(f\"{confidence=:.1%}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "So, we're very confident that model A has a higher AUPIMO score than model B.\n", + "\n", + "Maybe is that due to some big differences in a few images?\n", + "\n", + "What if we don't count much for these big differences?" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Non-parametric (rank comparison)\n", + "\n", + "In non-parametric comparison, bigger differences don't matter more than smaller differences. \n", + "\n", + "It's all about their relative position.\n", + "\n", + "Let's look at the analogous plots for this type of comparison." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAABboAAAEsCAYAAAAFEQVZAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAC2n0lEQVR4nOydd3hUxfrHv7tJNqSQhBAgdAKEIggXRBBQwUa1XxuiF0SsIFbsKJYr2PVarw0sIDbEBgoqIE1/CnJRQAQEaQEFJIGEtD3z+yNm2STb5iTn5M3Z7+d58mh2Z3bmM/POe2aHzVmXUkqBEEIIIYQQQgghhBBCCKmjuGu7A4QQQgghhBBCCCGEEEJIdeBBNyGEEEIIIYQQQgghhJA6DQ+6CSGEEEIIIYQQQgghhNRpeNBNCCGEEEIIIYQQQgghpE7Dg25CCCGEEEIIIYQQQgghdRoedBNCCCGEEEIIIYQQQgip0/CgmxBCCCGEEEIIIYQQQkidhgfdhBBCCCGEEEIIIYQQQuo0POgmhBBCCCGEEEIIIYQQUqfhQTchhBBCCCHE8bhcLowfP762u0EIIYQQQiyCB92EEEIIIaTWeP755+FyudCnT5+Az2/duhUulwuPPfZYwOcfe+wxuFwubN261ffYwIED4XK5fD/p6ek49thj8dprr8EwDF+50aNHIzk5ucLrldfNzs4O2N6CBQt8r/v+++9XeX7t2rW45JJL0Lx5c8THx6NZs2YYOXIk1q5dG24oCCGEEEIIIdWAB92EEEIIIaTWmDFjBtq0aYP/+7//w6ZNm2rsdVu0aIE333wTb775JiZNmoTS0lJcfvnluPPOO8PWrVevHjZt2oT/+7//C9jfevXqBaw3e/Zs9OzZE1999RUuu+wyPP/887j88suxcOFC9OzZEx9++GG1vQghhBBCCCGB4UE3IYQQQgipFbZs2YLly5fjiSeeQKNGjTBjxowae+3U1FRccskluOSSS3DjjTdi2bJlaNGiBZ599lmUlJSErNuuXTt07NgRb7/9doXHCwsL8eGHH2L48OFV6mzevBmXXnop2rZtizVr1uDBBx/E5ZdfjgceeABr1qxB27Ztcemll+K3336rMUerKCwsrPDJdzvJz8+vlXYJIYQQQkjdhwfdhBBCCCGkVpgxYwYaNGiA4cOH47zzzqvRg+7KJCYm4rjjjkN+fj7+/PPPsOVHjBiBd955p8KB7yeffIKCggJccMEFVco/+uijKCgowEsvvYRGjRpVeC4jIwP//e9/kZ+fj0ceeSRs28888wy6dOmCxMRENGjQAL169cLMmTMrlNm5cycuv/xyNGvWDPHx8cjKysI111yD4uJiX5nffvsN559/PtLT033+n332WYXXWbRoEVwuF2bNmoW7774bzZs3R2JiIvLy8gAA3333HYYMGYLU1FQkJiZiwIABWLZsWYXXOHjwIG644Qa0adMG8fHxaNy4MU477TSsWrUqpOfkyZPhcrmwbt06XHzxxWjQoAGOP/54AMCaNWswevRotG3bFvXq1UNmZibGjBmDffv2BXyNTZs2YfTo0UhLS0Nqaiouu+wyFBQUhB3rBx98EG63G88884zW+BNCCCGEEHnE1nYHCCGEEEJIdDJjxgyce+658Hg8GDFiBF544QV8//33OPbYYy1p77fffkNMTAzS0tLClr344osxefJkLFq0CCeffDIAYObMmTjllFPQuHHjKuU/+eQTtGnTBieccELA1zvxxBPRpk2bKgfNlXn55ZcxYcIEnHfeebj++utRWFiINWvW4LvvvsPFF18MANi1axd69+6NAwcO4Morr0SnTp2wc+dOvP/++ygoKIDH48GePXvQr18/FBQUYMKECWjYsCFef/11nHnmmXj//fdxzjnnVGj3gQcegMfjwS233IKioiJ4PB58/fXXGDp0KI455hjce++9cLvdmDZtGk4++WQsWbIEvXv3BgBcffXVeP/99zF+/HgcddRR2LdvH5YuXYr169ejZ8+eYcf6/PPPR3Z2Nh566CEopQCU3Qv9t99+w2WXXYbMzEysXbsWL730EtauXYtvv/0WLperwmtccMEFyMrKwpQpU7Bq1Sq88soraNy4MR5++OGg7d5999146KGH8N///hdXXHFFxONPCCGEEEJkwoNuQgghhBBiOytXrsQvv/zi+yTt8ccfjxYtWmDGjBk1ctDt9Xqxd+9eAMDevXvxwgsvYNWqVTjjjDOQmJgYtn52drbvk7wnn3wyDhw4gLlz5+Lll1+uUjY3Nxe7du3CWWedFfI1u3Xrho8//hgHDx5E/fr1A5b57LPP0KVLF7z33ntBX+eOO+7A7t278d1336FXr16+x++//37fQfHUqVOxZ88eLFmyxPcp6SuuuALdunXDTTfd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3fo6VrchNU50kepBd2uR6hGt7hK9zdRxypybqeMUd6ljZYZodZfqQXdrkegh0duuOk5xd0q8m6kj6YsoAY2DbgDYunUrFixYgOLiYgwcOBBdunSxql+OICYmBh06dLCsvF1t6CLVg+6y3KWOlS52zUe0ujPe6W5VG7pI9aC7LHeudXlxootUD7rLcpc6VmaIVnepHnSX5S51rHSRuj9xirtT4t1MHbuuVTpEfOy+cOFCdOnSBVdddRWuu+469OjRA2+99ZaVfavzKKVw6NAhKKUsKW9XG7pI9aC7LHepY6WLXfMRre6Md7pb1YYuUj3oLsuda11enOgi1YPustyljpUZotVdqgfdZblLHStdpO5PnOLulHg3U8eua5UOER90T5o0Caeddhp27tyJffv24YorrsCtt95qZd/qPIZhYMeOHVrfVqpT3q42dJHqQXdZ7lLHShe75iNa3RnvdLeqDV2ketBdljvXurw40UWqB91luUsdKzNEq7tUD7rLcpc6VrpI3Z84xd0p8W6mjl3XKh0iPuj++eef8dBDD6Fp06Zo0KABHn30Ufzxxx/Yt2+flf0jhBBCCCGEEEIIIYQQQkIS8UF3Xl4eMjIyfL8nJiYiISEBubm5lnSMEEIIIYQQQgghhBBCCIkErS+j/OKLL5Camur73TAMfPXVV/j55599j5155pk117s6jsvlgsfjgcvlsqS8XW3oItWD7rLcpY6VLnbNR7S6M97pblUbukj1oLssd651eXGii1QPustylzpWZohWd6kedJflLnWsdJG6P3GKu1Pi3Uwdu65VOmgddI8aNarKY1dddZXv/10uF7xeb/V75RDcbjfatm1rWXm72tBFqgfdZblLHStd7JqPaHVnvNPdqjZ0kepBd1nuXOvy4kQXqR50l+UudazMEK3uUj3oLstd6ljpInV/4hR3p8S7mTp2Xat0iPjWJYZhhP3hIXdFlFI4cOCA1reV6pS3qw1dpHrQXZa71LHSxa75iFZ3xjvdrWpDF6kedJflzrUuL050kepBd1nuUsfKDNHqLtWD7rLcpY6VLlL3J05xd0q8m6lj17VKh4gPuok+hmFg9+7dWt9WqlPerjZ0kepBd1nuUsdKF7vmI1rdGe90t6oNXaR60F2WO9e6vDjRRaoH3WW5Sx0rM0Sru1QPustylzpWukjdnzjF3SnxbqaOXdcqHSK+dcl//vOfgI+npqaiQ4cO6Nu3b411ihBCCCGEEEIIIYQQQgiJlIgPup988smAjx84cAC5ubno168fPv74Y6Snp9dY5wghhBBCCCGEEEIIIYSQcER865ItW7YE/Pnrr7+wadMmGIaBu+++28q+1jlcLheSkpK0vq1Up7xdbegi1YPustyljpUuds1HtLoz3uluVRu6SPWguyx3rnV5caKLVA+6y3KXOlZmiFZ3qR50l+Uudax0kbo/cYq7U+LdTB27rlU6RPyJ7lC0bdsWU6dOxZgxY2ri5RyD2+1Gy5YtLStvVxu6SPWguyx3qWOli13zEa3ujHe6W9WGLlI96C7LnWtdXpzoItWD7rLcpY6VGaLVXaoH3WW5Sx0rXaTuT5zi7pR4N1PHrmuVDjX2ZZStWrXC7t27a+rlHIFhGNi7d6/WTdx1ytvVhi5SPeguy13qWOli13xEqzvjne5WtaGLVA+6y3LnWpcXJ7pI9aC7LHepY2WGaHWX6kF3We5Sx0oXqfsTp7g7Jd7N1LHrWqVDjR10//TTT2jdunVNvZwjUEph7969UEpZUt6uNnSR6kF3We5Sx0oXu+YjWt0Z73S3qg1dpHrQXZY717q8ONFFqgfdZblLHSszRKu7VA+6y3KXOla6SN2fOMXdKfFupo5d1yodIr51SV5eXsDHc3NzsXLlStx8880YNWpUjXWMEEIIIYQQQgghhBBCCImEiA+609LSgt5c3OVyYezYsbj99ttrrGOEEEIIIYQQQgghhBBCSCREfNC9cOHCgI+npKQgOzsbycnJNdYpp+ByuZCamqr1baU65e1qQxepHnSX5S51rHSxaz6i1Z3xTner2tBFqgfdZblzrcuLE12ketBdlrvUsTJDtLpL9aC7LHepY6WL1P2JU9ydEu9m6th1rdIh4oPuAQMGhC3z888/o2vXrtXqkJNwu91o2rSpZeXtakMXqR50l+Uudax0sWs+otWd8U53q9rQRaoH3WW5c63LixNdpHrQXZa71LEyQ7S6S/Wguyx3qWOli9T9iVPcnRLvZurYda3SodpfRnnw4EG89NJL6N27N7p3714TfXIMhmEgJydH69tKdcrb1YYuUj3oLstd6ljpYtd8RKs7453uVrWhi1QPusty51qXFye6SPWguyx3qWNlhmh1l+pBd1nuUsdKF6n7E6e4OyXezdSx61qlg+mD7m+++QajRo1C06ZN8dhjj+Hkk0/Gt99+W5N9q/MopZCbm6v1baU65e1qQxepHnSX5S51rHSxaz6i1Z3xTner2tBFqgfdZblzrcuLE12ketBdlrvUsTJDtLpL9aC7LHepY6WL1P2JU9ydEu9m6th1rdIh4luXAMDu3bsxffp0vPrqq8jLy8MFF1yAoqIizJkzB0cddZRVfSSEEEIIIYQQQgghhBBCghLxJ7rPOOMMdOzYEWvWrMFTTz2FXbt24ZlnnrGyb4QQQgghhBBCCCGEEEJIWCL+RPe8efMwYcIEXHPNNcjOzrayT47B5XIhIyND69tKdcrb1YYuUj3oLstd6ljpYtd8RKs7453uVrWhi1QPusty51qXFye6SPWguyx3qWNlhmh1l+pBd1nuUsdKF6n7E6e4OyXezdSx61qlQ8QH3UuXLsWrr76KY445Bp07d8all16Kiy66yMq+1XncbjcyMjIsK29XG7pI9aC7LHepY6WLXfMRre6Md7pb1YYuUj3oLsuda11enOgi1YPustyljpUZotVdqgfdZblLHStdpO5PnOLulHg3U8eua5UOEd+65LjjjsPLL7+MnJwcXHXVVZg1axaaNWsGwzCwYMECHDx40Mp+1kkMw8D27du1vq1Up7xdbegi1YPustyljpUuds1HtLoz3uluVRu6SPWguyx3rnV5caKLVA+6y3KXOlZmiFZ3qR50l+Uudax0kbo/cYq7U+LdTB27rlU6RHzQXU5SUhLGjBmDpUuX4qeffsLNN9+MqVOnonHjxjjzzDOt6GOdRSmF/Px8rW8r1SlvVxu6SPWguyx3qWOli13zEa3ujHe6W9WGLlI96C7LnWtdXpzoItWD7rLcpY6VGaLVXaoH3WW5Sx0rXaTuT5zi7pR4N1PHrmuVDtoH3f507NgRjzzyCHbs2IG33367pvpECCGEEEIIIYQQQgghhERMtQ66y4mJicHZZ5+Njz/+uCZejhBCCCGEEEIIIYQQQgiJmBo56CaBcbvdyMzMhNsd2TDrlrerDV2ketBdlrvUsdLFrvmIVnfGO92takMXqR50l+XOtS4vTnSR6kF3We5Sx8oM0eou1YPustyljpUuUvcnTnF3SrybqWPXtUqH2NrugJNxuVxIS0uzrLxdbegi1YPuenV0kegh0duuOk5xZ7xbX0cXie4Svc3Uccqcm6njFHeu9cjL29WGLlI96K5XRxeJHnZ4m2nHKe5SPeiuV0cXiR4Sve2q4xR3p8S7mTp2Xat0kHPk7kAMw8Bvv/2m9W2lOuXtakMXqR50l+Uudax0sWs+otWd8U53q9rQRaoH3WW5c63LixNdpHrQXZa71LEyQ7S6S/Wguyx3qWOli9T9iVPcnRLvZurYda3SgQfdFqKUQnFxsda3leqUt6sNXaR60F2Wu9Sx0sWu+YhWd8Y73a1qQxepHnSX5c61Li9OdJHqQXdZ7lLHygzR6i7Vg+6y3KWOlS5S9ydOcXdKvJupY9e1SgcedBNCCCGEEEIIIYQQQgip0/CgmxBCCCGEEEIIIYQQQkidhgfdFuJ2u9GiRQutbyvVKW9XG7pI9aC7LHepY6WLXfMRre6Md7pb1YYuUj3oLsuda11enOgi1YPustyljpUZotVdqgfdZblLHStdpO5PnOLulHg3U8eua5UOsbXdASfjcrmQnJxsWXm72tBFqgfdZblLHStd7JqPaHVnvNPdqjZ0kepBd1nuXOvy4kQXqR50l+UudazMEK3uUj3oLstd6ljpInV/4hR3p8S7mTp2Xat0kHPk7kC8Xi9+/fVXeL1eS8rb1YYuUj3oLstd6ljpYtd8RKs7453uVrWhi1QPusty51qXFye6SPWguyx3qWNlhmh1l+pBd1nuUsdKF6n7E6e4OyXezdSx61qlAw+6LcYwDEvL29WGLlI96G4tEj0kettVxynujHfr61jdhtQ40UWqB92tRapHtLpL9DZTxylzbqaOU9yljpUZotVdqgfdrUWih0Rvu+o4xd0p8W6mjl3XqkjhQTchhBBCCCGEEEIIIYSQOg0PugkhhBBCCCGEEEIIIYTUaXjQbSFutxtZWVla31aqU96uNnSR6kF3We5Sx0oXu+YjWt0Z73S3qg1dpHrQXZY717q8ONFFqgfdZblLHSszRKu7VA+6y3KXOla6SN2fOMXdKfFupo5d1yod5PTEocTGxlpa3q42dJHqQXdrkegh0duuOk5xZ7xbX8fqNqTGiS5SPehuLVI9otVdoreZOk6ZczN1nOIudazMEK3uUj3obi0SPSR621XHKe5OiXczdey6VkUKD7otxDAMbNy4MeIbs+uWt6sNXaR60F2Wu9Sx0sWu+YhWd8Y73a1qQxepHnSX5c61Li9OdJHqQXdZ7lLHygzR6i7Vg+6y3KWOlS5S9ydOcXdKvJupY9e1SgcedBNCCCGEEEIIIYQQQgip0/CgmxBCCCGEEEIIIYQQQkidhgfdhBBCCCGEEEIIIYQQQuo0POi2ELfbjezsbK1vK9Upb1cbukj1oLssd6ljpYtd8xGt7ox3ulvVhi5SPeguy51rXV6c6CLVg+6y3KWOlRmi1V2qB91luUsdK12k7k+c4u6UeDdTx65rlQ5yeuJQSktLLS1vVxu6SPWgu7VI9JDobVcdp7gz3q2vY3UbUuNEF6kedLcWqR7R6i7R20wdp8y5mTpOcZc6VmaIVnepHnS3FokeEr3tquMUd6fEu5k6dl2rIoUH3RZiGAa2bNmi9W2lOuXtakMXqR50l+Uudax0sWs+otWd8U53q9rQRaoH3WW5c63LixNdpHrQXZa71LEyQ7S6S/Wguyx3qWOli9T9iVPcnRLvZurYda3SgQfdhBBCCCGEEEIIIYQQQuo0POgmhBBCCCGEEEIIIYQQUqfhQbfF6N6Q3cwN3O1oQxepHnS3FokeEr3tquMUd8a79XWsbkNqnOgi1YPu1iLVI1rdJXqbqeOUOTdTxynuUsfKDNHqLtWD7tYi0UOit111nOLulHg3U0fSF1ECQGxtd8DJxMTEoEOHDpaVt6sNXaR60F2Wu9Sx0sWu+YhWd8Y73a1qQxepHnSX5c61Li9OdJHqQXdZ7lLHygzR6i7Vg+6y3KWOlS5S9ydOcXdKvJupY9e1SgdZx+4OQymFQ4cOQSllSXm72tBFqgfdZblLHStd7JqPaHVnvNPdqjZ0kepBd1nuXOvy4kQXqR50l+UudazMEK3uUj3oLstd6ljpInV/4hR3p8S7mTp2Xat0qNVPdE+ZMgWzZ8/GL7/8goSEBPTr1w8PP/wwOnbsGLLee++9h0mTJmHr1q3Izs7Gww8/jGHDhlnSR6O4GPnLl+PgkiXI/Okn/LlmDeqfcAKS+vWD2+MJWKeo1Itlm/Zi8YY/sCVnH7KaNsSAjo3Rv30G4mNjgrZxaOlSHNiyBWlZWUg+/viQbejW0e2TGffqtGGHh3Ybv+zB/za6sUqtx4BOTUK2YaZfunVsbUPDvRzDMLBjxw5kZ2cjJiZ4WTs8zKwpXXc7PMzUsWOtV2t8BeVFqfldap4zO752zKGVa93XVoQ5zkwd2/OJoDxn6x7Iwuu6XWvdjuu6RPfqxImkfawd8e4kd1/dCHOpmeu62TakXdvMuEuMX6nXNrPxq7N3kLiXM1NH6h5e9LmDsPdtTlnr0vcnkuLdLne7cKlaPHYfMmQILrroIhx77LEoLS3FnXfeiZ9//hnr1q1DUlJSwDrLly/HiSeeiClTpuD000/HzJkz8fDDD2PVqlXo2rVr2Dbz8vKQmpqK3NxcpKSkhCxrFBdj30svI//bb6FcLvx58CAa1a8Pl1JIOu44NLzyiioTWFTqxYuLNmPZpn1wuRRUSRFccfFQyoX+7Rvi6oHtKgSJfxtwu3HYMJDgdgOGEbQN3Tq6fTLjXt027PAw08ahv/YhuUHDkG2Y6ZdundpoIxJ3f7xeLzZu3BhyI2eHh5k1petuh4ddcaK71mtifCXkRan5XWqeq8742jGHVq11fyLJcWbq1FY+kZDnamMPZMV13a61bsd1XaJ7deNEyj7Wjnh3krs/keRSM9f16rQh6dpmxl1i/Eq9tlUnfiPdO0jcy0mNEzvivS6cbVj1vs0pa70u7E+kxLtd7tVF5yy3Vm9d8vnnn2P06NHo0qULunfvjunTp2Pbtm1YuXJl0DpPP/00hgwZgokTJ6Jz58544IEH0LNnTzz77LM13r/85cuR/+23iGvSBJ7WreFNSYGndWvENWmC/G+/Rf7y5VXqLNu0F8s27UNmaj1kZSShYWIssjKSkJlaD8s278OyTXtDtoH09LBt6NbR7ZMZ95poww4PnTbaNExCqgdo0zB0G2b6pVvH7jYiddfFDg8za0rX3Q4PM3XsWOvVHV8peVFqfpea56o7vnbMoRVr3Q5qI59IyXN274Gsuq7btdbtuK5LdK+JOJGwj7Uj3p3krouZ63p125BybTPjLjF+pV7baiN+JezlzNSRuoeXfu4g6X2bU9Z6XdmfSIh3u9ztRNQ9unNzcwEA6enpQcusWLECp556aoXHBg8ejBUrVgQsX1RUhLy8vAo/AFBSUhL25+CSJVAuF5CQAMMwAJT9+RESEqBcLhxcsqRKncW/7IHLpZAQ54YyFNwuF5RR9rsLCot/2ROyDZfLFbYN3Tq6fTLjXhNt2OGh04a/d6g2zPRLt47dbUTq7v9TWlqKmJgYlJaWBi1jh4eZNaXrboeHXXGiu9arO75S8qLU/C41z1V3fO2YQyvWum6Ok5oXpeY5u/dAVl3X7VrrdlzXJbrXRJxI2MfaEe9OctfNpWau69VtQ8q1zYy7xPiVem2rTvxGuneQuJeTGid2xHtdOduw4n2bU9Z6XdmfSIh3u9xr4idSavXWJf4YhoEzzzwTBw4cwNKlS4OW83g8eP311zFixAjfY88//zzuu+8+7Nmzp0r5yZMn47777qvy+MyZM5GYmBiyT5kzZsBVXAJvgI/Fx+TlQXnisHvkyAqPT/vVjRIvkBrgU/q5xUBcDHBZB6NabejW0e0T29Brw446UtvQxQ4PO9atk+JEd7yckhelekjNc3aMr8Q5tAMn5ROJa12qRzTPoVPiRGJedJK7LlLbcEqcSI1FiXtlM0hct2bqOKUN7sfljW+07k/smkM73KtLQUEBLr744ohuXVKrX0bpz7hx4/Dzzz+HPOQ2wx133IGbbrrJ93teXh5atmyJQYMGhR2cP9esQdGvG+Fp1QqGUfZFEi1atIDb7Ubx778jvkM2elb6EsxVaj1+2XMILRsmAVAoLi6BxxMHwAXvvnx0apKMYcM6B2yjcvlgbejW0e2TGffqtmGHh24bhmFg544daP63d7A2zPRLt47dbUTq7o9SCnl5eUhJSYHL5QpYxg4PM2tK190Oj+rOoVVrvbrjKyUvSs3vUvNcdcbXjjm0aq37E0mOM1OnNvKJlDxn9x7Iquu6XWvdjuu6RPfqxomUfawd8e4kd38iyaVmruvVaUPStc2Mu8T4lXptq078Rrp3kLiXM1NH6h5e+rmDpPdtTlnrdWF/IiXe7XKvLuV354gEEbcuGT9+PD799FMsXLgQLVq0CFk2MzOzyie39+zZg8zMzIDl4+PjkZKSUuEHAOLi4sL+1D/hBLiUAg4fhttdNlRutxs4fBgupVD/hBOq1BnQqQmUcuFwSdnH9wsLC+Fylf2u4MKATk2CtuFyuf8uH7oN3Tq6fTLjXt027PDQbcPfO1QbZvqlW8fuNiJ19/+JiYnB3r17ERMTE7SMHR5m1pSuux0edsWJ7lqv7vhKyYtS87vUPFed8bVjDq1a67o5TmpelJrn7N4DWXVdt2ut23Fdl+he3TiRso+1I96d5K6bS81c16vThqRrmxl3ifEr9dpWnfiNdO8gcS8nNU7siPe6cLZh1fs2p6z1urA/kRLvdrnXxE+k1OonupVSuO666/Dhhx9i0aJFyMrKClunb9+++Oqrr3DDDTf4HluwYAH69u1b4/1L6tcPhT+v9X3DaczBgyj+/XffN5wm9etXpU7/9hn4aUculm3eBzcAo6QUf5UUwADQv11D9G+fEbQNuF2AoVB84C/ACN6Gbh3dPplxr24bdnjotuGCwqFiwLsvHwquoG2Y6ZduHbvbiNRdFzs8zKwpXXc7PMzUsWOtV3d8peRFqfldap6rzvjaMYdWrXU7qI18IiXP2b0Hsuq6btdat+O6LtG9unEiZR9rR7w7yV0XM9f16rQh6dpmxl1i/Eq9ttkdv1L2cmbqSN3DSz93kPS+zSlrvS7sT6TEu13udlKr9+i+9tprMXPmTHz00Ufo2LGj7/HU1FQkJCQAAP71r3+hefPmmDJlCgBg+fLlGDBgAKZOnYrhw4dj1qxZeOihh7Bq1Sp07do1bJt5eXlITU2N6L4uAGAUFyN/+XIcXLIE23/6CS2PPhr1TzgBSf36we0JcAMbAEWlXizbtBeLN/yBLTn7kNW0IQZ0bIz+7TMQHxsTtI1DS5fiwJYtSMvKQvLxx4dsQ7eObp/MuFenDTs8tNv4ZQ/+t/F3dM9ujQGdmoRsw0y/dOvY2oaGezlerxcbN25EdnY2YmKCl7XDw8ya0nW3w8NMHTvWerXGV1BelJrfpeY5s+NrxxxaudbLiTTHmaljez4RlOds3QNZeF23a63bcV2X6F6dOJG0j7Uj3p3kXk6kudTMdd1sG9KubWbcJcav1Gub2fjV2TtI3MuZqSN1Dy/63EHY+zanrHXp+xNJ8W6Xe3XQOstVtQiAgD/Tpk3zlRkwYIAaNWpUhXrvvvuu6tChg/J4PKpLly7qs88+i7jN3NxcBUDl5uZq9bW4uFjNmTNHFRcXR1zH6/Wqbdu2Ka/Xa0l5u9rQdZfqoVvHjjk3U8cp7lLHSmK8m6njFHfGO92taiNa412p6HV3SrybqeMUd8Y73SOB8U53q9qQOFZ8z8p4t6oNusuKEyetdTPonOXW+q1LwrFo0aIqj51//vk4//zzLehRzeJ2u9GyZUvLytvVhi5SPeguy13qWOli13xEqzvjne5WtaGLVA+6y3LnWpcXJ7pI9aC7LHepY2WGaHWX6kF3We5Sx0oXqfsTp7g7Jd7N1LHrWqWDiC+jdCqGYWDv3r0wDMOS8na1oYtUD7rLcpc6VrrYNR/R6s54p7tVbegi1YPusty51uXFiS5SPeguy13qWJkhWt2letBdlrvUsdJF6v7EKe5OiXczdey6VunAg24LUUph7969EX1y3Ux5u9rQRaoH3WW5Sx0rXeyaj2h1Z7zT3ao2dJHqQXdZ7lzr8uJEF6kedJflLnWszBCt7lI96C7LXepY6SJ1f+IUd6fEu5k6dl2rdOBBNyGEEEIIIYQQQgghhJA6DQ+6CSGEEEIIIYQQQgghhNRpeNBtIS6XC6mpqXC5XJaUt6sNXaR60F2Wu9Sx0sWu+YhWd8Y73a1qQxepHnSX5c61Li9OdJHqQXdZ7lLHygzR6i7Vg+6y3KWOlS5S9ydOcXdKvJupY9e1SofY2u6Ak3G73WjatKll5e1qQxepHnSX5S51rHSxaz6i1Z3xTner2tBFqgfdZblzrcuLE12ketBdlrvUsTJDtLpL9aC7LHepY6WL1P2JU9ydEu9m6th1rdKBn+i2EMMwkJOTo/VtpTrl7WpDF6kedJflLnWsdLFrPqLVnfFOd6va0EWqB91luXOty4sTXaR60F2Wu9SxMkO0ukv1oLssd6ljpYvU/YlT3J0S72bq2HWt0oEH3RailEJubq7Wt5XqlLerDV2ketBdlrvUsdLFrvmIVnfGO92takMXqR50l+XOtS4vTnSR6kF3We5Sx8oM0eou1YPustyljpUuUvcnTnF3SrybqWPXtUoHHnQTQgghhBBCCCGEEEIIqdNE3T26y/+VIS8vT6teSUkJCgoKkJeXh7i4uIjqeL1eHDp0CHl5eYiJianx8na1oesu1UO3jh1zbqaOU9yljpXEeDdTxynujHe6M95rvk60ujsl3s3UcYo7453ujPfQ0J3xbkUbZuo4xV3qWEmMdzN1nOLulHg3U8dMG2YoP8ON5JPjUXfQffDgQQBAy5Yta7knhBBCCCGEEEIIIYQQQsJx8OBBpKamhizjUpJupGIDhmFg165dqF+/PlwuV8T18vLy0LJlS2zfvh0pKSkR1zv22GPx/fffW1bejjbMuEv00K1j15ybqeMUd4ljJTXezdRxijvjne5WlI/WeAei191J8a5bx0nujHe6h4PxTnfGe+33y442GO/y4t1MHae4OyXezdQx04YuSikcPHgQzZo1g9sd+i7cUfeJbrfbjRYtWpiun5KSohVUMTExlpa3qw1Az12qh5k6Vs+5mTpOcZc6VoC8eDdTxynujHe6W9UGEL3xDkSvuxPi3WwdJ7gz3ukeKYx3ulvRhsSxAvielfFuTRt0lxUngHPWuhnCfZK7HH4ZpcWMGzfO0vJ2taGLVA+6W4tED4nedtVxijvj3fo6VrchNU50kepBd2uR6hGt7hK9zdRxypybqeMUd6ljZYZodZfqQXdrkegh0duuOk5xd0q8m6lj17UqUqLu1iVmycvLQ2pqKnJzc235lwpJRKt7tHoDdKd7dLlHqzdAd7pHl3u0egN0p3t0uUerN0D3aHSPVm+A7nSPLvdo9TYDP9EdIfHx8bj33nsRHx9f212xnWh1j1ZvgO50jy73aPUG6E736HKPVm+A7nSPLvdo9QboHo3u0eoN0J3u0eUerd5m4Ce6CSGEEEIIIYQQQgghhNRp+IluQgghhBBCCCGEEEIIIXUaHnQTQgghhBBCCCGEEEIIqdPwoJsQQgghhBBCCCGEEEJInYYH3YQQQgghhBBCCCGEEELqNDzojoDnnnsObdq0Qb169dCnTx/83//9X213yXImT54Ml8tV4adTp0613S1L+Oabb3DGGWegWbNmcLlcmDNnToXnlVK455570LRpUyQkJODUU0/Fxo0ba6ezNUw499GjR1eJgyFDhtROZ2uQKVOm4Nhjj0X9+vXRuHFjnH322diwYUOFMoWFhRg3bhwaNmyI5ORk/POf/8SePXtqqcc1RyTuAwcOrDLvV199dS31uOZ44YUX0K1bN6SkpCAlJQV9+/bFvHnzfM87dc7DeTt1vgMxdepUuFwu3HDDDb7HnDrvlQnk7tS5D7eHceqch/N26nyXs3PnTlxyySVo2LAhEhIScPTRR+OHH37wPe/k/Vw4d6fu59q0aVPFy+VyYdy4cQCcu9bDeTt5rXu9XkyaNAlZWVlISEhAu3bt8MADD0Ap5Svj1LUeibtT1/rBgwdxww03oHXr1khISEC/fv3w/fff+5536pwD4d2dMuc1cSazf/9+jBw5EikpKUhLS8Pll1+OQ4cO2WhhjppwD3RdmDp1qo0WsuBBdxjeeecd3HTTTbj33nuxatUqdO/eHYMHD8Yff/xR212znC5duiAnJ8f3s3Tp0trukiXk5+eje/fueO655wI+/8gjj+A///kPXnzxRXz33XdISkrC4MGDUVhYaHNPa55w7gAwZMiQCnHw9ttv29hDa1i8eDHGjRuHb7/9FgsWLEBJSQkGDRqE/Px8X5kbb7wRn3zyCd577z0sXrwYu3btwrnnnluLva4ZInEHgCuuuKLCvD/yyCO11OOao0WLFpg6dSpWrlyJH374ASeffDLOOussrF27FoBz5zycN+DM+a7M999/j//+97/o1q1bhcedOu/+BHMHnDv3ofYwTp7zcHs3p873X3/9hf79+yMuLg7z5s3DunXr8Pjjj6NBgwa+Mk7dz0XiDjhzP/f9999XcFqwYAEA4Pzzzwfg3LUezhtw7lp/+OGH8cILL+DZZ5/F+vXr8fDDD+ORRx7BM8884yvj1LUeiTvgzLU+duxYLFiwAG+++SZ++uknDBo0CKeeeip27twJwLlzDoR3B5wx5zVxJjNy5EisXbsWCxYswKeffopvvvkGV155pV0Kpqmp86j777+/Qhxcd911dnRfJoqEpHfv3mrcuHG+371er2rWrJmaMmVKLfbKeu69917VvXv32u6G7QBQH374oe93wzBUZmamevTRR32PHThwQMXHx6u33367FnpoHZXdlVJq1KhR6qyzzqqV/tjJH3/8oQCoxYsXK6XK5jguLk699957vjLr169XANSKFStqq5uWUNldKaUGDBigrr/++trrlI00aNBAvfLKK1E150od8VYqOub74MGDKjs7Wy1YsKCCbzTMezB3pZw796H2ME6e83B7N6fOt1JK3Xbbber4448P+ryT93Ph3JWKnv3c9ddfr9q1a6cMw3D0Wq+Mv7dSzl7rw4cPV2PGjKnw2LnnnqtGjhyplHL2Wg/nrpQz13pBQYGKiYlRn376aYXHe/bsqe666y5Hz3k4d6WcOedmzmTWrVunAKjvv//eV2bevHnK5XKpnTt32tb36mL2PKp169bqySeftLGnsuEnukNQXFyMlStX4tRTT/U95na7ceqpp2LFihW12DN72LhxI5o1a4a2bdti5MiR2LZtW213yXa2bNmC3bt3V4iB1NRU9OnTJypiAAAWLVqExo0bo2PHjrjmmmuwb9++2u5SjZObmwsASE9PBwCsXLkSJSUlFea9U6dOaNWqlePmvbJ7OTNmzEBGRga6du2KO+64AwUFBbXRPcvwer2YNWsW8vPz0bdv36iZ88re5Th9vseNG4fhw4dXmF8gOtZ6MPdynDr3wfYwTp/zcHs3p873xx9/jF69euH8889H48aN0aNHD7z88su+5528nwvnXo7T93PFxcV46623MGbMGLhc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hBBCCCGEEELqNDzoJoQQQgghpI7hcrkwZ84c0/UXLVoEl8uFAwcOVKsfo0ePxtlnn12t1yCEEEIIIaQm4EE3IYQQQgghlfjzzz9xzTXXoFWrVoiPj0dmZiYGDx6MZcuW1XbXaoR+/fohJycHqamptd0VQgghhBBCaoTY2u4AIYQQQggh0vjnP/+J4uJivP7662jbti327NmDr776Cvv27avtrtUIHo8HmZmZtd0NQgghhBBCagx+opsQQgghhBA/Dhw4gCVLluDhhx/GSSedhNatW6N379644447cOaZZ/rKPfHEEzj66KORlJSEli1b4tprr8WhQ4d8z0+fPh1paWn49NNP0bFjRyQmJuK8885DQUEBXn/9dbRp0wYNGjTAhAkT4PV6ffXatGmDBx54ACNGjEBSUhKaN2+O5557LmSft2/fjgsuuABpaWlIT0/HWWedha1btwYtX/nWJeV9/eKLL9C5c2ckJydjyJAhyMnJ8dXxer246aabkJaWhoYNG+LWW2+FUqrC6xqGgSlTpiArKwsJCQno3r073n//fQCAUgqnnnoqBg8e7Ku3f/9+tGjRAvfcc0/oSSGEEEIIISQMPOgmhBBCCCHEj+TkZCQnJ2POnDkoKioKWs7tduM///kP1q5di9dffx1ff/01br311gplCgoK8J///AezZs3C559/jkWLFuGcc87B3LlzMXfuXLz55pv473//6zsMLufRRx9F9+7d8eOPP+L222/H9ddfjwULFgTsR0lJCQYPHoz69etjyZIlWLZsme+guri4OGLvgoICPPbYY3jzzTfxzTffYNu2bbjlllt8zz/++OOYPn06XnvtNSxduhT79+/Hhx9+WOE1pkyZgjfeeAMvvvgi1q5dixtvvBGXXHIJFi9eDJfLhddffx3ff/89/vOf/wAArr76ajRv3pwH3YQQQgghpNq4VOWPYRAAZZ9YKSkpqe1uEEIIIabweDxwu/nv2YSY5YMPPsAVV1yBw4cPo2fPnhgwYAAuuugidOvWLWid999/H1dffTX27t0LoOxT0pdddhk2bdqEdu3aASg72H3zzTexZ88eJCcnAwCGDBmCNm3a4MUXXwRQ9onuzp07Y968eb7Xvuiii5CXl4e5c+cCKPsyyg8//BBnn3023nrrLTz44INYv349XC4XAKC4uBhpaWmYM2cOBg0aVKWvixYtwkknnYS//voLaWlpAfv6/PPP4/7778fu3bsBAM2aNcONN96IiRMnAgBKS0uRlZWFY445xvePAunp6fjyyy/Rt29fX1tjx45FQUEBZs6cCQB477338K9//Qs33HADnnnmGfz444/Izs7WnSJCCCGEEEIqwHt0V0Iphd27d1f7G+gJIYSQ2sTtdiMrKwsej6e2u0JIneSf//wnhg8fjiVLluDbb7/FvHnz8Mgjj+CVV17B6NGjAQBffvklpkyZgl9++QV5eXkoLS1FYWEhCgoKkJiYCABITEz0HRwDQJMmTdCmTRvfIXf5Y3/88UeF9v0Pist/f+qppwL29X//+x82bdqE+vXrV3i8sLAQmzdvjti5cl+bNm3q61dubi5ycnLQp08f3/OxsbHo1auX7zYkmzZtQkFBAU477bQKr1tcXIwePXr4fj///PPx4YcfYurUqXjhhRd4yE0IIYQQQmoEHnRXovyQu3HjxkhMTPR9KoYQQgipKxiGgV27diEnJwetWrXitYwQk9SrVw+nnXYaTjvtNEyaNAljx47Fvffei9GjR2Pr1q04/fTTcc011+Df//430tPTsXTpUlx++eUoLi72HXTHxcVVeE2XyxXwMcMwTPfz0KFDOOaYYzBjxowqzzVq1Cji1wnUL50//iy/P/lnn32G5s2bV3guPj7e9/8FBQVYuXIlYmJisHHjxohfnxBCCCGEkFDwoNsPr9frO+Ru2LBhbXeHEEIIMU2jRo2wa9culJaWVjm8IoSY46ijjsKcOXMAACtXroRhGHj88cd9twl69913a6ytb7/9tsrvnTt3Dli2Z8+eeOedd9C4cWOkpKTUWB/8SU1NRdOmTfHdd9/hxBNPBFB265KVK1eiZ8+eAMrGJz4+Htu2bcOAAQOCvtbNN98Mt9uNefPmYdiwYRg+fDhOPvlkS/pNCCGEEEKiBx50+1F+T+7yT+AQQgghdZXyW5Z4vV4edBOiyb59+3D++edjzJgx6NatG+rXr48ffvgBjzzyCM466ywAQPv27VFSUoJnnnkGZ5xxBpYtW+a7x3ZNsGzZMjzyyCM4++yzsWDBArz33nv47LPPApYdOXIkHn30UZx11lm4//770aJFC/z++++YPXs2br31VrRo0aJG+nT99ddj6tSpyM7ORqdOnfDEE09UuN1f/fr1ccstt+DGG2+EYRg4/vjjkZubi2XLliElJQWjRo3CZ599htdeew0rVqxAz549MXHiRIwaNQpr1qxBgwYNaqSfhBBCCCEkOuG3VAWAf+JNCCGkrsNrGSHmSU5ORp8+ffDkk0/ixBNPRNeuXTFp0iRcccUVePbZZwEA3bt3xxNPPIGHH34YXbt2xYwZMzBlypQa68PNN9+MH374AT169MCDDz6IJ554AoMHDw5YNjExEd988w1atWqFc889F507d8bll1+OwsLCGv2E980334xLL70Uo0aNQt++fVG/fn2cc845Fco88MADmDRpEqZMmYLOnTtjyJAh+Oyzz5CVlYU///wTl19+OSZPnuz7FPh9992HJk2a4Oqrr66xfhJCCCGEkOjEpXRuvOdwCgsLsWXLFmRlZaFevXq13R1CCCHENLymEVJ3adOmDW644QbccMMNtd0VQgghhBBC6gz8RDeJmEWLFsHlclX4E9VwtGnTBk899ZRlfSIkGuFaJIQQQgghhBBCCKkID7odwujRo+FyuQL+2ee4cePgcrkwevRo+zsWITt27IDH40HXrl1ruyuiqevzHA3U1TmaPHkyXC6X7yc1NRUnnHACFi9eXNtdE0ldnWdCCCGEEEIIIcSp8KDbQbRs2RKzZs3C4cOHfY8VFhZi5syZaNWqVS32LDzTp0/HBRdcgLy8PHz33Xe13R3R1OV5jhbq6hx16dIFOTk5yMnJwYoVK5CdnY3TTz8dubm5td01kdTVeSaEyGfr1q28bQkhhBBCCCGa8KDbAopKvfj6lz247+O1GDdjFe77eC2+/mUPikq9lrbbs2dPtGzZErNnz/Y9Nnv2bLRq1Qo9evSo2MeiIkyYMAGNGzdGvXr1cPzxx+P777+vUGbu3Lno0KEDEhIScNJJJ2Hr1q1V2ly6dClOOOEEJCQkoGXLlpgwYQLy8/O1+q2UwrRp03DppZfi4osvxquvvqpVP9qIdJ4Nw8CUKVOQlZWFhIQEdO/eHe+//77vea/Xi8svv9z3fMeOHfH0009XaGv06NE4++yz8dhjj6Fp06Zo2LAhxo0bh5KSEutFawCjuBgHFy3C7n8/hB033Ijd/34IBxctglFcbGm7dXUtxsbGIjMzE5mZmTjqqKNw//3349ChQ/j111+1Xida4FokhBBCCCGEEELkwIPuGqao1IsXF23Gi4t+w/rdB1FY4sX63Qfx4qLf8OKizZYfdo8ZMwbTpk3z/f7aa6/hsssuq1Lu1ltvxQcffIDXX38dq1atQvv27TF48GDs378fALB9+3ace+65OOOMM7B69WqMHTsWt99+e4XX2Lx5M4YMGYJ//vOfWLNmDd555x0sXboU48eP1+rzwoULUVBQgFNPPRWXXHIJZs2apX1AV1MUFJcG/Sks8dZ4WbNEMs9TpkzBG2+8gRdffBFr167FjTfeiEsuucR3KwrDMNCiRQu89957WLduHe655x7ceeedePfddyu8zsKFC7F582YsXLgQr7/+OqZPn47p06eb7rtdGMXF2PfSy9j3yqso2rABqrAQRRs2YN8rr2LfSy9bfthdF9eiP0VFRZg2bRrS0tLQsWNH069jFqOgIPhPUVHkZQsLIyprFq5FQgghhBBCCCFEBi6llKrtTkihsLAQW7ZsQVZWFurVq2fqNb7+ZQ9eXPQbMlPrISk+1vd4flEpducV4uoBbXFypyY11WUfo0ePxoEDB/Dyyy+jZcuW2LBhAwCgU6dO2L59O8aOHYu0tDRMnz4d+fn5aNCgAaZPn46LL74YAFBSUoI2bdrghhtuwMSJE3HnnXfio48+wtq1a31t3H777Xj44Yfx119/IS0tDWPHjkVMTAz++9//+sosXboUAwYMQH5+PurVq+d7zVB/fjty5Eg0btwYTz75JADgH//4B2644YZaub9tm9s/C/rcSR0bYdplvX2/d570OQ6XBP6Hiz5Z6Xjnqr6+33s+sAD786serG6dOlyrf5HO83//+1+kp6fjyy+/RN++R/oxduxYFBQUYObMmQFff/z48di9e7fv06ajR4/GokWLsHnzZsTExAAALrjgArjdbsyaNUur73ZzcNEi7HvlVcQ1aQJ3UpLvcSM/HyV79qDh2MtRf+DAGm+3rq7FyZMn44EHHkBCQgIAoKCgAPXr18c777yDIUOG1Pg4hWN9p85Bn0sacCJa+bn+0qMnlN/tQ/xJPPZYtH7zDd/vv/btB+9ff1Up1/mX9Vr9qwtrsSauaYQQQgghhBBCSF0hNnwRosOSX/fC7XZVOOQGgKT4WLhdZc9bcdBdTqNGjTB8+HBMnz4dSikMHz4cGRkZFcps3rwZJSUl6N+/v++xuLg49O7dG+vXlx32rF+/Hn369KlQz/+QBgD+97//Yc2aNZgxY4bvMaUUDMPAli1b0Llz8IOqcg4cOIDZs2dj6dKlvscuueQSvPrqq/witxCEm+dNmzahoKAAp512WoV6xcXFFW6p8Nxzz+G1117Dtm3bcPjwYRQXF+Mf//hHhTpdunTxHawBQNOmTfHTTz9ZI1aD5C9bDpfbXeGQGwDcSUlwud3IX7bckoPucuraWgSAjh074uOPPwYAHDx4EO+88w7OP/98LFy4EL169YpcPorgWiSEEEIIIYQQQmTAg+4a5o+DRUjyxAR8LskTiz8OFgV8riYZM2aM75YFzz33nGXtHDp0CFdddRUmTJhQ5blIv4ht5syZKCwsrHCQV35A9+uvv6JDhw411t9IWHf/4KDPuV2uCr+vnHRqxGWX3nZS9ToWgFDzfOjQIQDAZ599hubNm1d4Lj4+HgAwa9Ys3HLLLXj88cfRt29f1K9fH48++miVLwONi4ur8LvL5YJhGDXqYgWlf/4Jd2JiwOfciYko/fNPy/tQl9YiAHg8HrRv3973e48ePTBnzhw89dRTeOutt2qkr5HScdXK4E/GVMyxHZYtDVIQgLviHbraf/VldboVEK5FQgghhBBCCCGk9uFBdw3TuH481u8+GPC5/OJStEoPfPBWkwwZMgTFxcVwuVwYPLjqwW27du3g8XiwbNkytG7dGkDZ7RK+//57320NOnfu7PtkZznffvtthd979uyJdevWVTgY0+XVV1/FzTffXOXT29deey1ee+01TJ061fRrmyHRE/mSsKpspISa56OOOgrx8fHYtm0bBgwYELD+smXL0K9fP1x77bW+xzZv3lzj/awtYhs1QtHft5OojFFQgPiWLS3vQ11ai8GIiYnB4SC3BbGSYP9IYWfZSOFaJIQQQgghhBBCah8edNcwJ3TIwNpdecgvKq1yj25DlT1vNTExMb7bHsTEVP10eVJSEq655hpMnDgR6enpaNWqFR555BEUFBTg8ssvBwBcffXVePzxxzFx4kSMHTsWK1eurPKlZ7fddhuOO+44jB8/HmPHjkVSUhLWrVuHBQsW4Nlnnw3bz9WrV2PVqlWYMWMGOnXqVOG5ESNG4P7778eDDz6I2FiGaSBCzXP9+vVxyy234MYbb4RhGDj++OORm5uLZcuWISUlBaNGjUJ2djbeeOMNfPHFF8jKysKbb76J77//HllZWbWhU+Mk9e+HwvXrYeTnV7lHtzIMJPXvZ3kf6spaLKe0tBS7d+8GcOTWJevWrcNtt91mcgSiA65FQgghhBBCCCGk9uEJYg3Tv30GftqRi2Wb98HtKrtdSX5x2SF3/3YN0b+99QfdAJCSkhLy+alTp8IwDFx66aU4ePAgevXqhS+++AINGjQAUHa7gw8++AA33ngjnnnmGfTu3RsPPfQQxowZ43uNbt26YfHixbjrrrtwwgknQCmFdu3a4cILL4yoj6+++iqOOuqoKofcAHDOOedg/PjxmDt3Ls4880wN8+gi1Dw/8MADaNSoEaZMmYLffvsNaWlp6NmzJ+68804AwFVXXYUff/wRF154IVwuF0aMGIFrr70W8+bNs6v7lpLUrx8Kf16L/G+/LbtXd2IijIKCskPu445DUj/rD7qBurEWy1m7di2aNm0KAEhMTES7du3wwgsv4F//+pemdfTBtUgIIYQQQgghhNQuLqWUqu1OSKGwsBBbtmxBVlYW6tWrZ/p1ikq9WLZpL5b8uhd/HCxC4/rxOKFDBvq3z0B8bOD7dxNCah6juBj5y5cjf9lylP75J2IbNUJS/35I6tcPbo+ntrtHiKXU1DWNEEIIIaS2GD16NNq3b4+77767VtofOnQoRo8erf0BEn+2bt2K9u3bo7S0tNr9adOmDd566y0cf/zx1X4tO0hOTsavv/6KZs2amX6N6dOn46233sKXX9b8d+2QIwwcOBBjx47FJZdcYmu7kydPxo4dO/DKK6/Y2q7TWbRoEcaOHYtNmzbVdldsxx2+CNElPjYGJ3dqgnvP7ILnRvbEvWd2wcmdmvCQmxCbcXs8qD9wIDLvuhMtnnoSmXfdifoDB/KQmxBCCCGEkBqkTZs2SExMRHJyMpo1a4YJEybA6/XWdrcCMnnyZHTp0gVut7vKLQErM2/ePO1D7tGjR+PBBx+sRg/tY9GiRVrf87NhwwacccYZaNSoETIyMnDuuedi165dQcsfOnRI+5C7TZs2WLo0xJfN1xF042DdunUYNGgQGjRogDZt2oQsu3XrVrhcLiQnJ/t+ZsyYUc0e24PL5cKOHTtquxuOY+DAgXjrrbdquxsi4EE3IYQQQgghhBBCqsX8+fNx6NAhLFmyBB988AFeffXV2u5SQNq3b48nnniiznwqWhK5ubk499xz8euvv2Lnzp1o0aIFRo8eXdvdqjVq8h9z4uLicNFFF+Hpp5+OqHxMTAwOHTrk+xk5cmSN9YVUpCb+GqMutOkUeNBNCCGEEEIIIYSQGqFdu3bo378/Vq9e7XvsuuuuQ7NmzZCWloZBgwZh27ZtvudcLhdeeOEFZGVlISMjA1OmTAn4unv27EG3bt3w/PPPAwD+/e9/o2nTpkhJScHRRx+NdevWRdS/Sy65BIMHD0ZiYmLYsv6fkvz222/Ro0cPpKSkoHnz5njyySerlH/99dcxY8YMPPDAA0hOTsbVV1/te+7ll19G06ZNkZmZiddff933+OHDhzF+/Hg0a9YMLVq0wNSpU0P2afny5ejQoQMaNmyIW265BYZh+J577rnnkJ2djYyMDIwaNQr5+fkAgF9//RXHH388UlJS0KRJE0ycOBFerxdDhw7Fb7/95vtUcDh69+6Nyy67DA0aNEB8fDzGjx+PFStWBC3v/+nd1157Da1bt0b9+vXRsWNHLFq0qEr5sWPHYtu2bRg0aFCFTykbhoFrrrkGKSkpOOqoo7Bq1SpfnW3btmH48OFo2LAhOnfujM8//zxgX+666y7fd+Ts2rULLpcLb7zxBoCyf6Tp378/gLLb/40bNw6ZmZlo1aoV7r//ft8YT548GSNGjMA///lPJCcn4+uvvw7oFSoOgpGdnY0xY8agQ4cOYcvqMHLkSLz00ksAymLH5XLhm2++AQC89NJLFQ7IN27ciF69eiElJQUXXnghioqKfM+9//776NKlC9LT03HmmWfijz/+AHDkrwLuv/9+pKeno02bNvjiiy8C9mXQoEEAgI4dOyI5ORlLliwBULYGzj//fNSvXx99+vTBli1bfHV++uknnHjiiWjQoAGOOeYY/PDDDwFf+88//8TQoUORlpaGjIwMjBgxwvfc119/7fPKzs72tbt9+3YMGzYMDRo0wFFHHYWPPvrIV2fgwIGYNGkSevXqhaSkJJSUlGDx4sU45phjkJaWhoEDB2Lz5s2+/o8YMQLp6elIT0/HCSecELCPf/31F4YMGYKMjAw0atQIV155pW+My8fx3nvvRUZGBu69996Ic8MDDzyAJUuWYOzYsUhOTsZDDz3key7YvOzfvx8XX3wxGjdujLZt21bISf4YhoEJEyYgIyMDaWlpOPbYY7F3714AwEMPPYTWrVsjJSUFffv2xZo1a3z12rRpg0cffRSdO3dG/fr1cc8992DDhg3o1asXUlNTq6yJYLnLFIr4OHz4sFq3bp06fPhwbXeFEEIIqRa8phFCCCHELlq3bq2WLFmilFJqw4YNKjMzUz322GO+599++2114MABVVBQoC677DJ11lln+Z4DoM477zx18OBB9dNPP6n4+Hi1adMmpZRSo0aNUg888IDasWOH6ty5s3r55ZeVUkqtX79etWjRQuXk5CjDMNT69etVTk6OUkqpKVOmqOHDh4ft8+DBg9W0adNClhkwYIB68803lVJK9enTR7311ltKKaX279+vVq1aFbBOeZ/L2bJliwKgJkyYoIqKitQXX3yhkpKSVF5enlJKqWuvvVaNGDFCHTx4UO3cuVMdddRR6pNPPgn42q1bt1b/+Mc/VE5Ojq/sK6+8opRS6t1331Vdu3ZVW7duVQUFBWrEiBHq5ptvVkopdeGFF6qHHnpIGYahDh06pL777jullFILFy5U7dq1CztWwXj55ZdVnz59gj4PQG3fvl0dOnRI1a9fX/36669KKaW2bt2qfvvtt6CO5bGklFLTpk1TsbGxaubMmaq0tFTddddd6sQTT1RKKeX1elW3bt3U008/rUpKStTy5ctVRkaG2r17d5XXnTdvnjr++OOVUkrNmjVLZWVlqSuuuEIppdTdd9+tbrvtNqWUUnfeeacaMGCA2r9/v/r9999Vdna2L07uvfdeFR8fr7744gvl9XpDelWOg0hZsWKFat26dcgy5THVtGlT1apVK3X99der/Pz8gGVfeOEFdckllyillJo6darKyspS//7/9u49KMrq/wP4e1kusuAuKHeEHQQMUPNucftCkoqXMkRBCfCSAnkJQVAjCBV0HEcBdUKcHEczB8fRMAitqSS1Gq+Vt7wWsClbAXlZkGV35fP7g+EZVnY34Psrv9Tn9dfz7HPOec7l4Yif83B2wwYiIoqPj6edO3cSUfuz7ufnRzU1NXT//n0KCAigPXv2EBHR2bNnyd3dnS5fvkwajYYyMzMpOjqaiNqfIbFYTJs2bSKtVku7du0iT09Po3XveCY65ObmkrW1NZ04cYK0Wi0lJCRQYmIiERGpVCpyc3Ojw4cPk06no7KyMvLw8DD4/5vVq1fTm2++SVqtltRqNX3zzTdERPTTTz9R//79qaKignQ6HdXW1tLt27eJiCg4OJgyMjJIrVZTVVUV2draCtfCwsLI29ubbt++TS0tLaRQKMjBwYFOnTpFOp2Otm/fTmPHjhX6+JVXXqHHjx+TVqulU6dOGWx7Q0MDlZeXk1qtprq6Oho1ahQVFhbq9eO6detIo9HQ48ePezQ3dJ6rujMuU6dOpZUrV5Jarabr16+Tq6srXbp0qUu5x48fpzFjxtDDhw9Jp9PRxYsXSaVSERHRkSNH6PfffyeNRkM5OTk0YsQIIZ9cLqewsDBqbGyk69evk5WVFU2cOJEUCgUplUpydnamEydOEJHpuas3ONDdCQcFGGOM/VPwv2mMMcYY+7vI5XKytbUlGxsbAkAzZ840+jvIjRs3aODAgcI5ALpw4YJwPm7cOCorKyOi9mDhokWLyNfXl/bt2yekuX37Njk6OgrBsd7oaaA7JCSE1q5dS42NjSbzGAt0NzQ0CJ85OjrS999/T21tbWRtbU337t0Tru3YsYPmzZtnsGy5XK5X5/fff58mTpwotOfAgQPCtStXrggB0/j4eEpOTqa6ujq98v6bQHfHGHz55ZdG03QOdEulUiorKyO1Wm2yXEOB7mHDhgnn165dI5lMRkTtQWFfX1+9/NHR0QbH9eHDhySRSKilpYWWLl1KxcXF5O/vT0Tt49wRQBw8eLAQgCMiKikpoUmTJhFRe1C245iITLbrrwx0q1QqunjxIul0OqqpqaGwsDBatmyZwbRXr14VApzTpk2j4uJiioyMJKL2vr5y5QoRtfdB58WpzMxMSk1NJSKi5ORkIThORPTo0SMyNzcnrVZLVVVVJJVK6cmTJ0RE1NzcTADo/v37ButjKNA9ffp04byyslIImJaWlgrPd4cxY8ZQVVVVl3Kzs7Pptdde67KAkp+fT3FxcV3SKxQKsrKyosePHwufzZkzhzZu3Cj0R8cxUfsCWsfCSAcHBweqrq6m3bt3U1BQEF29etVgm40pKSnRWzCQSCTCfNbTucFQoNvYuCiVSpJIJKTRaIT0K1eupNzc3C7lfvHFFzRkyBA6e/YstbW1GW1LS0sLiUQiIQgul8vpyJEjwvXx48fT1q1bhfOYmBghyG9q7uoN3rrEgM5/+sMYY4z1RUT0rKvAGGOMsX+R48ePQ6VS4ejRo7hw4QKampqEaxs2bICPjw+kUinGjx+PxsZGvbzOzs7CsUQi0cv78ccfQyKR6H0ppI+PD7Zu3YqsrCw4Oztj0aJFePTo0V/YOmD37t24du0afHx8EBISYnLLjqeJxWIMHDhQOO9oY319PVpaWhAQEAA7OzvY2dkhKysLv/32m9GyPDw89I6VSiWA9i08kpOThXJCQkJQX18PANi8eTM0Gg1GjhyJUaNGoaKioqfN11NXV4dJkyYhLy8PEyZM+NP0NjY2KC0txfbt2+Hs7IzZs2eb/BLLpxl7PhQKBaqrq4U229nZ4dNPPxX6pDOpVIrnnnsO586dw9dff42oqCihLefPnxe2Lqmrq4Onp6eQTy6X69V10KBB/2/t6i1bW1uMHj0aYrEYcrkcmzZtwkcffWQwbUBAAJqamlBTU4NLly5hwYIFuHLlCmpra6FSqTB06FAhral+3rBhg9DHHh4eMDc3x6+//goAcHR0hJmZmZAPgN7P8J8xdd+TJ0/qje/169cN9nFmZiY8PT0RFhYGPz8/4TsC7t69Cy8vry7p6+rq4OjoCGtra+EzU2OtUCiwf/9+vbo0Nzfj3r17SEhIQEREBKKioiCXy41uv6RSqZCYmIhBgwZBKpUiPT1dby50cXGBubk5APRqbniasXFRKBRQq9VwdHQUyt61a5cwnp1FREQgJSUFSUlJcHV1RUZGBrRaLYD27ZiGDh0KmUwGFxcXEJFee5ycnIRja2vrLuedx9nY3NUb5r3O+Q9kaWkJMzMz4YG3tLSESCR61tVijDHGeoSIUF9fD5FIBAsLi2ddHcYYY4z9S4hEIsyYMQPl5eXIz89HUVERTp48ieLiYlRVVcHX1xe3bt2Cn59ft8tcvnw5bt68idjYWBw+fFgIBCUkJCAhIQENDQ2YM2cOCgoKsHbt2r+oZe37Ch86dAg6nQ4lJSWYO3cuampquqTrSQzBwcEBVlZW+PnnnzFgwIBu5fnll1/0jl1dXQEA7u7uyM/Px8yZM7vkcXV1xZ49e0BEKC8vR0xMDO7fv9+reEdDQwNefvllJCUlITk5udv5pk6diqlTp6KpqQkpKSnIysrC3r17u6TrSZ3c3d3h7++vtzewKaGhoaioqEBLSwtcXFwQEhKCoqIiDB48GPb29gAANzc3KBQKeHt7A2gPwrm5uRmtn7F2/Z2xJDMzM6MvuYhEIoSEhGDnzp3w8/NDv379EBAQgPfeew9BQUHdqqe7uzvy8vKQnp7e5dqdO3f+6/qbuu/kyZNRXl7+p2mlUim2bduGbdu24cyZM5gwYQJeeukleHh4GNy/383NDfX19VCr1ejXrx+A9rEePny4kKZz37i7u2Px4sXYvn27wfuvX78e69evx40bNxAeHo7AwECEh4frpSkoKEB9fT1++OEHODg4YNeuXSgtLTV4v57ODT39ubG1te32HJCWloa0tDRhT/Nhw4YhPDwcK1aswMmTJzF69Gi0trbCxsamVy9bmZq7eoMD3Z2YmZnBy8sLSqXyb1mFY4wxxv4qIpEIgwYNglgsftZVYYwxxti/TEZGBsaNG4fs7GyoVCpYWFjAwcEBzc3NyM/P71FZIpEIe/fuRXR0NObPn4/9+/fj1q1bUCqVCAoKgkQigZWVVbd/59FqtXjy5Ana2tqg1WqhVquFl95MOXDgACIjIzFw4ED079/f6P2cnJwMBsANMTMzw7x587By5UoUFhZCKpXi5s2bUKlUGD9+vME8O3bswJQpU9DW1oaioiKsWLECALBw4UJs3LgRI0aMgLe3N5RKJS5duoTIyEgcPnwYQUFBwheCikQiiEQiODk5ob6+Hs3NzbCxsQEA7N27F2vXrjXYhkePHmHy5MmYPn061qxZ0602Au1fJHr+/HlERETAysoKEokET548MZi2o/9CQkL+tNwXXngBbW1t2LlzJ9544w0AwNmzZyGXy/Xeyu4QGhqKhQsXYtasWQCA//znP1iyZAni4+OFNLGxscjLy8PIkSPR1NSEgoICrF69usftMvQchIeHIzw83OCCDBGhtbUVGo0GRAS1Wg0zMzNYWlp2SXvu3DnY29vDx8cHSqUSb7/9Nl599VWj/RQaGor169cjIyNDaPfmzZuRnZ1tNE9nCxYsQHx8PCIiIjBixAj88ccfOH36NGbMmNGt/J119Evnt6WN6XjOjh49iunTpwtfCBkYGAiZTKaXtrKyEv7+/vDy8oJMJoNIJIJYLMbcuXMxcuRIHDt2DJGRkbh37x40Gg28vb0xevRo5ObmIi8vD2fOnEFFRQXWrVtnsC5xcXEICgrC7NmzERwcjObmZnz22WeYNWsWqqqq4OTkBH9/f0ilUpibmxucH1QqFSQSCWQyGWpra1FcXCwssDytp3NDT+Ydd3d3BAYGIjs7G++88w4sLS1x+fJlYRGkswsXLoCIMGrUKPTv3x8WFhYQi8VoamqCmZkZHB0dodPpkJub2617G2Jq7uoNDnQ/xdLSEp6entDpdEYnXsYYY+x/XccvIYwxxhhjfzd/f3+EhYVh27ZtyM3NRXBwMORyORwcHLBq1Sp8+OGHPSrP3Nwchw4dwrRp07B06VKkpKQgMzMTN27cgJWVFSZNmoS0tDQAwMaNG3H69GkcP37cYFmLFy/Gvn37AACff/45kpKSUFVV1eXty6cdO3YMqampaG1txZAhQ/DBBx8YTNcRSLWzs0NcXBxWrVplstzCwkJkZWVh+PDhUKlU8PX1NbkYEBMTg9DQUDQ2NiIxMRELFiwAAMydOxcPHjzAtGnTUFdXBxcXF6SkpCAyMhLnzp3D8uXLoVKp4OnpidLSUlhZWcHf3x8zZsyAh4cH2tra8ODBA9y9e1fYxuNpZWVl+O6773Dz5k0UFxcLn//ZNhVtbW3YvHkz4uLiIBaLERQUhN27dxtMu3r1arz11ltYtmyZ3j0MMTc3R2VlJVJTU/Huu++CiDB27FiUlJQYTB8aGgqVSiUE0Z8+B4CcnBykp6fDz88PFhYWWLRoEebNm9fjdj39HBQXF5vs29raWr0tNqytrREWFoavvvoKADB06FBkZWXh9ddfx507d5CVlYX6+nrY29sjKirK6HYZxtqdk5PTrcUEAAgKCsKWLVuQmJiI6upqDBgwADExMb0KdL/77ruIjo5Ga2srPvnkE5NpZTIZKisrkZaWhoULF8LCwgLBwcEIDAzskvbWrVtYsmQJGhsb4eTkhKKiIsjlcgDAkSNHkJmZidjYWOGvG7y9vXHw4EEkJSXByckJrq6u2LdvH3x9fQ3WxcvLCwcPHhTmHRsbG0yYMAGzZs2CUqlEUlISlEolZDIZUlJSEBoa2qWM1NRUxMbGwt7eHn5+foiKihLG15CezA3Lly/H/PnzsWXLFqxZswYvvviiyb49cOAA0tPTMXjwYGg0GgwbNgyFhYVd0j18+BArVqxAdXU1bGxsEBMTIzzvycnJeP7552FjY4OcnByDizLdYWruUigUCAgIwI8//mhw8coQEfEmnowxxhhjjDHGGGP/elOmTEFBQQH8/f2fdVX+UZRKJaKjo/Htt98+66ow9o/GgW7GGGOMMcYYY4wxxhhjfZrpTagYY4wxxhhjjDHGGGOMsf9xHOhmjDHGGGOMMcYYY4wx1qdxoJsxxhhjjDHGGGOMMcZYn8aBbsYYY4wxxhhjjDHGGGN9Gge6GWOMMcYYY4wxxhhjjPVpHOhmjDHGGGOMMcYYY4wx1qdxoJsxxhhjjDHGGGOMMcZYn8aBbsYYY4wxxhhjjDHGGGN9Gge6GWOMMcYYY4wxxhhjjPVpHOhmjDHGGGOMMcYYY4wx1qdxoJsxxhhjjDHGGGOMMcZYn/Z/nz9xvelqlskAAAAASUVORK5CYII=", + "text/plain": [ + "
" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# the `-` sign is to sort in descending order because higher AUPIMO is better\n", + "# the rank values are 1 or 2 because there are only two models\n", + "# where 1 is the best and 2 is the worst\n", + "# when the scores are the same, 1.5 is assigned to both models\n", + "ranks = stats.rankdata(-np.stack([modela, modelb], axis=1), method=\"average\", axis=1)\n", + "ranksa, ranksb = ranks[:, 0], ranks[:, 1]\n", + "\n", + "num_samples = ranks.shape[0]\n", + "indexes = np.arange(num_samples)\n", + "\n", + "fig, ax = plt.subplots(figsize=(18, 2.5))\n", + "\n", + "# plot sample index vs score and their mean\n", + "ax.scatter(indexes, ranksa, s=30, color=\"tab:blue\", marker=\"o\", label=\"Model A\", zorder=3, alpha=0.6)\n", + "ax.axhline(ranksa.mean(), color=\"tab:blue\", linestyle=\"--\", label=\"Mean\", zorder=3)\n", + "ax.scatter(indexes, ranksb, s=30, color=\"tab:red\", marker=\"o\", label=\"Model B\", zorder=3, alpha=0.6)\n", + "ax.axhline(ranksb.mean(), color=\"tab:red\", linestyle=\"--\", label=\"Mean\", zorder=3)\n", + "\n", + "# configure the x-axis\n", + "ax.set_xlabel(\"Sample index\")\n", + "ax.set_xlim(0 - (eps := 0.01 * num_samples), num_samples + eps)\n", + "ax.xaxis.set_major_locator(IndexLocator(5, 0))\n", + "ax.xaxis.set_minor_locator(IndexLocator(1, 0))\n", + "\n", + "# configure the y-axis\n", + "ax.set_ylabel(\"AUPIMO Rank\")\n", + "ax.set_ylim(1 - 0.1, 2 + 0.1)\n", + "ax.yaxis.set_major_locator(FixedLocator([1, 1.5, 2]))\n", + "ax.invert_yaxis()\n", + "\n", + "# configure the grid, legend, etc\n", + "ax.grid(axis=\"both\", which=\"major\", linestyle=\"-\")\n", + "ax.grid(axis=\"x\", which=\"minor\", linestyle=\"--\", alpha=0.5)\n", + "ax.legend(ncol=4, loc=\"upper left\", bbox_to_anchor=(0, -0.15))\n", + "ax.set_title(\"AUPIMO scores ranks\")\n", + "\n", + "fig.text(\n", + " 0.9,\n", + " -0.1,\n", + " \"Ranks: 1 is the best, 2 is the worst, 1.5 when the scores are the same.\",\n", + " ha=\"right\",\n", + " va=\"top\",\n", + " fontsize=\"small\",\n", + ")\n", + "\n", + "fig # noqa: B018, RUF100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Again, blue seems to have a slight advantage, but -- again -- is it significant enough to be sure that model A is better than model B?\n", + "\n", + "Remember that AUPIMO is a recall metric, so it is basically a ratio of the area of anomalies. \n", + "\n", + "Is it relevant if model A has 1% more recall than model B in a given image?\n", + "\n", + "> You can check that out in [`701b_aupimo_advanced_i.ipybn`](./701b_aupimo_advanced_i.ipynb).\n", + "\n", + "We'll --arbitrarily -- assume that only differences above 5% are relevant." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "data": { + "image/png": "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XW3oItWD7rLcpY6VLnbNR7y6M97pblUbukj1oLssd651eXGii1QPustylzpWZohXd6kedJflLnWsdJG6P3GKu1Pi3Uwdu65VOmgfdJ955pm46aabcPPNN+Pcc8/FGWecEfSPHMLn82Hbtm1a31aqU96uNnSR6kF3We5Sx0oXu+YjXt0Z73S3qg1dpHrQXZY717q8ONFFqgfdZblLHSszxKu7VA+6y3KXOla6SN2fOMXdKfFupo5d1yodtL+M8p577rGiH4QQQgghhBBCCCGEEEKIKWTdMZwQQgghhBBCCCGEEEII0UT7E91erxdPPPEE3n33XWzduhWVlZVBz+/duzdmnWvouFwuJCcnw+VyWVLerjZ0kepBd1nuUsdKF7vmI17dGe90t6oNXaR60F2WO9e6vDjRRaoH3WW5Sx0rM8Sru1QPustylzpWukjdnzjF3SnxbqaOXdcqHbQPuu+99168/PLLuOmmm3DnnXfin//8J7Zs2YJZs2bh7rvvtqKPDRa3241OnTpZVt6uNnSR6kF3We5Sx0oXu+YjXt0Z73S3qg1dpHrQXZY717q8ONFFqgfdZblLHSszxKu7VA+6y3KXOla6SN2fOMXdKfFupo5d1yodtG9dMm3aNLz00ku46aabkJiYiFGjRuHll1/G3Xffja+//tqKPjZYlFLYt2+f1reV6pS3qw1dpHrQXZa71LHSxa75iFd3xjvdrWpDF6kedJflzrUuL050kepBd1nuUsfKDPHqLtWD7rLcpY6VLlL3J05xd0q8m6lj17VKB+2D7p07d+KII44AAGRkZKCoqAgA8Ne//hWffvppbHvXwPH5fNi5c6fWt5XqlLerDV2ketBdlrvUsdLFrvmIV3fGO92takMXqR50l+XOtS4vTnSR6kF3We5Sx8oM8eou1YPustyljpUuUvcnTnF3SrybqWPXtUoH7VuXtG3bFoWFhWjfvj06d+6Mzz//HH369MG3336LlJQUK/oYc1xJKSir9CKxsqrWc26XC42SEvw/l/1ZxuOpQoX34M9JyhWxbDVerxflHt/BOokIKnug0gsFFbL8gUovMlIjl63G5w0OpnKPF74Iv0lJSXAZLpuWfCg8PL5g90hlKzyHvBMSar9+alKC//49FVVeeH0qaKwC64QqCyBk+UaJCXC7D5atrPKhqsZCC6yTnuKOWBY4NOden0LS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IYQQQgghhBBCCCGRMXzrkl27diEvLw8A0KJFC6SlpeHUU0+1rGNOwOVyISsrS+sb2HXK29WGLlI96C7LXepY6WLXfMSrO+Od7la1oYtUD7rLcudalxcnukj1oLssd6ljZYZ4dZfqQXdZ7lLHShep+xOnuDsl3s3UsetapYPWl1EGfyGUG8nJyTHvkJNwu91o1aqVZeXtakMXqR50l+Uudax0sWs+4tWd8U53q9rQRaoH3WW5c63LixNdpHrQXZa71LEyQ7y6S/Wguyx3qWOli9T9iVPcnRLvZurYda3SwfAnupVS6Nq1K7Kzs5GdnY39+/ejd+/e/p+r/5FD+Hw+FBYWwufzRS9sorxdbegi1YPustyljpUuds1HvLoz3uluVRu6SPWguyx3rnV5caKLVA+6y3KXOlZmiFd3qR50l+Uudax0kbo/cYq7U+LdTB27rlU6GD7ofu211/Dvf/8bTzzxBJ544gm89tprePLJJ/0/V/8jh1BKoaioSOvbSnXK29WGLlI96C7LXepY6WLXfMSrO+Od7la1oYtUD7rLcudalxcnukj1oLssd6ljZYZ4dZfqQXdZ7lLHShep+xOnuDsl3s3UsetapYPhW5eMGTPGyn4QQgghhBBCCCGEEEIIIaYw/IluQgghhBBCCCGEEEIIIUQiPOi2EJfLhZycHK1vK9Upb1cbukj1oLssd6ljpYtd8xGv7ox3ulvVhi5SPeguy51rXV6c6CLVg+6y3KWOlRni1V2qB91luUsdK12k7k+c4u6UeDdTx65rlQ6Gb11C9HG73cjJybGsvF1t6CLVg+6y3KWOlS52zUe8ujPe6W5VG7pI9aC7LHeudXlxootUD7rLcpc6VmaIV3epHnSX5S51rHSRuj9xirtT4t1MHbuuVTrwE90W4vP58Ouvv2p9W6lOebva0EWqB91luUsdK13smo94dWe8092qNnSR6kF3We5c6/LiRBepHnSX5S51rMwQr+5SPeguy13qWOkidX/iFHenxLuZOnZdq3QwfdC9e/du7N69O5Z9cRxKKZSWlmp9W6lOebva0EWqB91luUsdK13smo94dWe8092qNnSR6kF3We5c6/LiRBepHnSX5S51rMwQr+5SPeguy13qWOkidX/iFHenxLuZOnZdq3TQOujet28fJkyYgJycHLRs2RItW7ZETk4OrrnmGuzbt8+i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++23/jJz5sxRLpdLbd++3ba+1xWz51EdOnRQTzzxhI09lQ0/0R2ByspKrFixAqeccor/MbfbjVNOOQVfffVVPfbMHjZu3IjWrVujU6dOGD16NLZu3VrfXbKdgoIC7Ny5MygGsrKy0K9fv7iIAQBYsGABWrRogW7duuGqq67Cnj176rtLMaeoqAgAkJ2dDQBYsWIFPB5P0Lx3794d7du3d9y813SvZtq0acjJycHhhx+O22+/HWVlZfXRPcvwer2YPn06SktL0b9//7iZ85re1Th9vidMmICRI0cGzS8QH2s9nHs1Tp37cHsYp895tL2bU+f7o48+Qt++fXHeeeehRYsW6N27N1566SX/807ez0Vzr8bp+7nKykq8+eabGDduHFwul+PXejU1vatx6lofMGAA5s2bhw0bNgAA/u///g9LlizBqaeeCsDZaz2aezVOW+tVVVXwer1o1KhR0OOpqalYsmSJo+c8mns1TpvzmhiZ46+++gpNmjRB3759/WVOOeUUuN1uLF++3PY+xwqd+J4yZQqaNWuG3r1745FHHkFVVZXd3RVDYn13QDK7d++G1+tFy5Ytgx5v2bIl1q1bV0+9sod+/fph6tSp6NatGwoLC3Hvvffi+OOPx08//YTGjRvXd/dsY+fOnQAQMgaqn3Myw4cPx9lnn428vDxs3rwZd9xxB0499VR89dVXSEhIqO/uxQSfz4frr78eAwcOxOGHHw7g4LwnJyejSZMmQWWdNu+h3AHgwgsvRIcOHdC6dWusWrUKt956K9avX48ZM2bUY29jw48//oj+/fujvLwcGRkZmDlzJg477DCsXLnS0XMezhtw9nwDwPTp0/H9998H3c+wGqev9UjugHPnPtIexslzHm3v5tT5BoCff/4Zzz33HG688Ubccccd+Pbbb3HttdciOTkZY8aMcfR+Lpo7EB/7uVmzZmHfvn0YO3YsAOfn92pqegPOze0AcNttt6G4uBjdu3dHQkICvF4v/vWvf2H06NEAnP3eLZo74My13rhxY/Tv3x/3338/evTogZYtW+Ltt9/GV199hS5dujh6zqO5A86c85oYmeOdO3eiRYsWQc8nJiYiOzu7QceB0fi+9tpr0adPH2RnZ2PZsmW4/fbbUVhYiMcff9zW/kqBB90kJIG/GT7yyCPRr18/dOjQAe+++y4uvfTSeuwZsZO//e1v/v8/4ogjcOSRR6Jz585YsGABTj755HrsWeyYMGECfvrpJ8fegz4S4dwD72V2xBFHoFWrVjj55JOxefNmdO7c2e5uxpRu3bph5cqVKCoqwvvvv48xY8Zg4cKF9d0tywnnfdhhhzl6vn/99Vdcd911mDt3bq1PwzgdI+5OnftIe5jU1NR67Jm1RNu7OXW+gYO/uO3bty8efPBBAEDv3r3x008/4fnnn/cf9joVI+7xsJ975ZVXcOqpp6J169b13RVbCeXt5LX+7rvvYtq0aXjrrbfQs2dPrFy5Etdffz1at27t+LVuxN2pa/2NN97AuHHj0KZNGyQkJKBPnz4YNWoUVqxYUd9ds5xo7k6dc6LHjTfe6P//I488EsnJybjiiiswefJkpKSk1GPP6gfeuiQCOTk5SEhIqPXt3Lt27UJubm499ap+aNKkCbp27YpNmzbVd1dspXqeGQMH6dSpE3JychwTB9dccw0++eQTzJ8/H23btvU/npubi8rKSuzbty+ovJPmPZx7KPr16wcAjpj35ORkdOnSBUcddRQmT56MXr164cknn3T8nIfzDoWT5nvFihX47bff0KdPHyQmJiIxMRELFy7Ef/7zHyQmJqJly5aOnfdo7l6vt1YdJ819IIF7GKev9UCi7d2cNN+tWrXy/5VKNT169PDfusXJ+7lo7qFw2n7ul19+wRdffIHx48f7H4uHtR7KOxROWusTJ07Ebbfdhr/97W844ogjcPHFF+OGG27A5MmTATh7rUdzD4VT1nrnzp2xcOFC7N+/H7/++iu++eYbeDwedOrUydFzDkR2D4VT5jwQI3Ocm5uL3377Lej5qqoq7N27t0HHgdn47tevH6qqqrBlyxYruycWHnRHIDk5GUcddRTmzZvnf8zn82HevHlB9zaNB/bv34/NmzejVatW9d0VW8nLy0Nubm5QDBQXF2P58uVxFwMAsG3bNuzZs6fBx4FSCtdccw1mzpyJL7/8Enl5eUHPH3XUUUhKSgqa9/Xr12Pr1q0Nft6juYdi5cqVANDg5z0UPp8PFRUVjp7zUFR7h8JJ833yySfjxx9/xMqVK/3/+vbti9GjR/v/36nzHs091J+zOmnuAwncw8TTWo+2d3PSfA8cOBDr168PemzDhg3o0KEDAGfv56K5h8Ip+7lqXnvtNbRo0QIjR470PxYPaz2UdyictNbLysrgdgcfYSQkJMDn8wFw9lqP5h4Kp6319PR0tGrVCn/88Qc+++wznHHGGY6e80BCuYfCaXMOGFvX/fv3x759+4I+5f/ll1/C5/P5f9nXEDEb3ytXroTb7a51O5e4ob6/DVM606dPVykpKWrq1KlqzZo16vLLL1dNmjRRO3furO+uWcpNN92kFixYoAoKCtTSpUvVKaeconJyctRvv/1W312LOSUlJeqHH35QP/zwgwKgHn/8cfXDDz+oX375RSml1JQpU1STJk3Uhx9+qFatWqXOOOMMlZeXpw4cOFDPPa87kdxLSkrUzTffrL766itVUFCgvvjiC9WnTx+Vn5+vysvL67vrdeKqq65SWVlZasGCBaqwsND/r6yszF/myiuvVO3bt1dffvml+u6771T//v1V//7967HXsSGa+6ZNm9R9992nvvvuO1VQUKA+/PBD1alTJ3XCCSfUc8/rzm233aYWLlyoCgoK1KpVq9Rtt92mXC6X+vzzz5VSzp3zSN5Onu9wDBo0SF133XX+n50676EIdHfy3Efbwzh1ziN5O3m+lVLqm2++UYmJiepf//qX2rhxo5o2bZpKS0tTb775pr+MU/dz0dydvJ9TSimv16vat2+vbr311lrPOXWtKxXe2+lrfcyYMapNmzbqk08+UQUFBWrGjBkqJydH3XLLLf4yTl3r0dydvNb/97//qTlz5qiff/5Zff7556pXr16qX79+qrKyUinl3DlXKrK7k+Y8Fmcyw4cPV71791bLly9XS5YsUfn5+WrUqFH1pWSYurovW7ZMPfHEE2rlypVq8+bN6s0331TNmzdXf//73+tTq17hQbcBnnrqKdW+fXuVnJysjjnmGPX111/Xd5cs54ILLlCtWrVSycnJqk2bNuqCCy5QmzZtqu9uWcL8+fMVgFr/xowZo5RSyufzqbvuuku1bNlSpaSkqJNPPlmtX7++fjsdIyK5l5WVqaFDh6rmzZurpKQk1aFDB3XZZZc54pc8oZwBqNdee81f5sCBA+rqq69WTZs2VWlpaeqss85ShYWF9dfpGBHNfevWreqEE05Q2dnZKiUlRXXp0kVNnDhRFRUV1W/HY8C4ceNUhw4dVHJysmrevLk6+eST/YfcSjl3ziN5O3m+w1HzoNup8x6KQHcnz320PYxT5zySt5Pnu5qPP/5YHX744SolJUV1795dvfjii0HPO3k/F8ndyfs5pZT67LPPFICQc+nUta5UeG+nr/Xi4mJ13XXXqfbt26tGjRqpTp06qX/+85+qoqLCX8apaz2au5PX+jvvvKM6deqkkpOTVW5urpowYYLat2+f/3mnzrlSkd2dNOexOJPZs2ePGjVqlMrIyFCZmZnqkksuUSUlJfVgo0dd3VesWKH69eunsrKyVKNGjVSPHj3Ugw8+2OB+2RFLXEopZeUnxgkhhBBCCCGEEEIIIYQQK+E9ugkhhBBCCCGEEEIIIYQ0aHjQTQghhBBCCCGEEEIIIaRBw4NuQgghhBBCCCGEEEIIIQ0aHnQTQgghhBBCCCGEEEIIadDwoJsQQgghhBBCCCGEEEJIg4YH3YQQQgghhBBCCCGEEEIaNDzoJoQQQgghhBBCCCGEENKg4UE3IYQQQgghDQyXy4VZs2aZrr9gwQK4XC7s27evTv0YO3YszjzzzDq9BiGEEEIIIbGAB92EEEIIIYTU4Pfff8dVV12F9u3bIyUlBbm5uRg2bBiWLl1a312LCQMGDEBhYSGysrLquyuEEEIIIYTEhMT67gAhhBBCCCHSOOecc1BZWYnXX38dnTp1wq5duzBv3jzs2bOnvrsWE5KTk5Gbm1vf3SCEEEIIISRm8BPdhBBCCCGEBLBv3z4sXrwYDz30EE488UR06NABxxxzDG6//Xacfvrp/nKPP/44jjjiCKSnp6Ndu3a4+uqrsX//fv/zU6dORZMmTfDJJ5+gW7duSEtLw7nnnouysjK8/vrr6NixI5o2bYprr70WXq/XX69jx464//77MWrUKKSnp6NNmzZ45plnIvb5119/xfnnn48mTZogOzsbZ5xxBrZs2RK2fM1bl1T39bPPPkOPHj2QkZGB4cOHo7Cw0F/H6/XixhtvRJMmTdCsWTPccsstUEoFva7P58PkyZORl5eH1NRU9OrVC++//z4AQCmFU045BcOGDfPX27t3L9q2bYu777478qQQQgghhBASBR50E0IIIYQQEkBGRgYyMjIwa9YsVFRUhC3ndrvxn//8B6tXr8brr7+OL7/8ErfccktQmbKyMvznP//B9OnT8b///Q8LFizAWWedhdmzZ2P27Nl444038MILL/gPg6t55JFH0KtXL/zwww+47bbbcN1112Hu3Lkh++HxeDBs2DA0btwYixcvxtKlS/0H1ZWVlYa9y8rK8Oijj+KNN97AokWLsHXrVtx8883+5x977DFMnToVr776KpYsWYK9e/di5syZQa8xefJk/Pe//8Xzzz+P1atX44YbbsBFF12EhQsXwuVy4fXXX8e3336L//znPwCAK6+8Em3atOFBNyGEEEIIqTMuVfNjGATAwU+seDye+u4GIYQQYork5GS43fx9NiFm+eCDD3DZZZfhwIED6NOnDwYNGoS//e1vOPLII8PWef/993HllVdi9+7dAA5+SvqSSy7Bpk2b0LlzZwAHD3bfeOMN7Nq1CxkZGQCA4cOHo2PHjnj++ecBHPxEd48ePTBnzhz/a//tb39DcXExZs+eDeDgl1HOnDkTZ555Jt5880088MADWLt2LVwuFwCgsrISTZo0waxZszB06NBafV2wYAFOPPFE/PHHH2jSpEnIvj777LO47777sHPnTgBA69atccMNN2DixIkAgKqqKuTl5eGoo47y/1IgOzsbX3zxBfr37+9va/z48SgrK8Nbb70FAHjvvffw97//Hddffz2eeuop/PDDD8jPz9edIkIIIYQQQoLgPbproJTCzp076/wN9IQQQkh94na7kZeXh+Tk5PruCiENknPOOQcjR47E4sWL8fXXX2POnDl4+OGH8fLLL2Ps2LEAgC+++AKTJ0/GunXrUFxcjKqqKpSXl6OsrAxpaWkAgLS0NP/BMQC0bNkSHTt29B9yVz/222+/BbUfeFBc/fO///3vkH39v//7P2zatAmNGzcOery8vBybN2827Fyzr61atfL3q6ioCIWFhejXr5//+cTERPTt29d/G5JNmzahrKwMQ4YMCXrdyspK9O7d2//zeeedh5kzZ2LKlCl47rnneMhNCCGEEEJiAg+6a1B9yN2iRQukpaX5PxVDCCGENBR8Ph927NiBwsJCtG/fntcyQkzSqFEjDBkyBEOGDMFdd92F8ePH45577sHYsWOxZcsW/PWvf8VVV12Ff/3rX8jOzsaSJUtw6aWXorKy0n/QnZSUFPSaLpcr5GM+n890P/fv34+jjjoK06ZNq/Vc8+bNDb9OqH7p/PFn9f3JP/30U7Rp0ybouZSUFP//l5WVYcWKFUhISMDGjRsNvz4hhBBCCCGR4EF3AF6v13/I3axZs/ruDiGEEGKa5s2bY8eOHaiqqqp1eEUIMcdhhx2GWbNmAQBWrFgBn8+Hxx57zH+boHfffTdmbX399de1fu7Ro0fIsn369ME777yDFi1aIDMzM2Z9CCQrKwutWrXC8uXLccIJJwA4eOuSFStWoE+fPgAOjk9KSgq2bt2KQYMGhX2tm266CW63G3PmzMGIESMwcuRInHTSSZb0mxBCCCGExA886A6g+p7c1Z/AIYQQQhoq1bcs8Xq9POgmRJM9e/bgvPPOw7hx43DkkUeicePG+O677/Dwww/jjDPOAAB06dIFHo8HTz31FE477TQsXbrUf4/tWLB06VI8/PDDOPPMMzF37ly89957+PTTT0OWHT16NB555BGcccYZuO+++9C2bVv88ssvmDFjBm655Ra0bds2Jn267rrrMGXKFOTn56N79+54/PHHg27317hxY9x888244YYb4PP5cNxxx6GoqAhLly5FZmYmxowZg08//RSvvvoqvvrqK/Tp0wcTJ07EmDFjsGrVKjRt2jQm/SSEEEIIIfEJv6UqBPwTb0IIIQ0dXssIMU9GRgb69euHJ554AieccAIOP/xw3HXXXbjsssvw9NNPAwB69eqFxx9/HA899BAOP/xwTJs2DZMnT45ZH2666SZ899136N27Nx544AE8/vjjGDZsWMiyaWlpWLRoEdq3b4+zzz4bPXr0wKWXXory8vKYfsL7pptuwsUXX4wxY8agf//+aNy4Mc4666ygMvfffz/uuusuTJ48GT169MDw4cPx6aefIi8vD7///jsuvfRSTJo0yf8p8HvvvRctW7bElVdeGbN+EkIIIYSQ+MSldG6853DKy8tRUFCAvLw8NGrUqL67QwghhJiG1zRCGi4dO3bE9ddfj+uvv76+u0IIIYQQQkiDgZ/oJoZZsGABXC5X0J+oRqNjx47497//bVmfCIlHuBYJIYQQQgghhBBCguFBt0MYO3YsXC5XyD/7nDBhAlwuF8aOHWt/xwyybds2JCcn4/DDD6/vroimoc9zPNBQ52jSpElwuVz+f1lZWTj++OOxcOHC+u6aSBrqPBNCCCGEEEIIIU6FB90Ool27dpg+fToOHDjgf6y8vBxvvfUW2rdvX489i87UqVNx/vnno7i4GMuXL6/v7oimIc9zvNBQ56hnz54oLCxEYWEhvvrqK+Tn5+Ovf/0rioqK6rtrImmo80wIkc+WLVt42xJCCCGEEEI04UG3BVRUefHlul2496PVmDDte9z70Wp8uW4XKqq8lrbb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Cvx1fvzxR5xwwglo2rQpjjrqKHz33XchX/v333/HqaeeiiZNmiAnJwejRo3yP/fll1/6vfLz8/3t/vrrrxgxYgSaNm2Kww47DB9++KG/zuDBg3HXXXehb9++SE9Ph8fjwcKFC3HUUUehSZMmGDx4MDZv3uzv/6hRo5CdnY3s7Gwcf/zxIfv4xx9/YPjw4cjJyUHz5s1x+eWX+8e4ehzvuece5OTk4J577jGcG+6//34sXrwY48ePR0ZGBh588EH/c+HmZe/evbjwwgvRokULdOrUKSgnBeLz+XDttdciJycHTZo0wdFHH43du3cDAB588EF06NABmZmZ6N+/P1atWuWv17FjRzzyyCPo0aMHGjdujLvvvhvr169H3759kZWVVWtNhMtdplDEz4EDB9SaNWvUgQMH6rsrhBBCSJ3gNY0QQgghdtGhQwe1ePFipZRS69evV7m5uerRRx/1P//222+rffv2qbKyMnXJJZeoM844w/8cAHXuueeqkpIS9eOPP6qUlBS1adMmpZRSY8aMUffff7/atm2b6tGjh3rppZeUUkqtXbtWtW3bVhUWFiqfz6fWrl2rCgsLlVJKTZ48WY0cOTJqn4cNG6Zee+21iGUGDRqk3njjDaWUUv369VNvvvmmUkqpvXv3qu+//z5kneo+V1NQUKAAqGuvvVZVVFSozz77TKWnp6vi4mKllFJXX321GjVqlCopKVHbt29Xhx12mPr4449DvnaHDh3UX/7yF1VYWOgv+/LLLyullHr33XfV4YcfrrZs2aLKysrUqFGj1E033aSUUuqCCy5QDz74oPL5fGr//v1q+fLlSiml5s+frzp37hx1rMLx0ksvqX79+oV9HoD69ddf1f79+1Xjxo3Vhg0blFJKbdmyRf38889hHatjSSmlXnvtNZWYmKjeeustVVVVpf75z3+qE044QSmllNfrVUceeaR68sknlcfjUcuWLVM5OTlq586dtV53zpw56rjjjlNKKTV9+nSVl5enLrvsMqWUUnfeeae69dZblVJK3XHHHWrQoEFq79696pdfflH5+fn+OLnnnntUSkqK+uyzz5TX643oVTMOjPLVV1+pDh06RCxTHVOtWrVS7du3V9ddd50qLS0NWfa5555TF110kVJKqSlTpqi8vDz1r3/9Syml1EUXXaSee+45pdTBWO/evbvasmWL+uOPP9Rh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", + "text/plain": [ + "
" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "MIN_ABS_DIFF = 0.05\n", + "scores = np.stack([modela, modelb], axis=1)\n", + "ranks = stats.rankdata(-scores, method=\"average\", axis=1)\n", + "abs_diff = np.abs(np.diff(scores, axis=1)).flatten()\n", + "ranks[abs_diff < MIN_ABS_DIFF, :] = 1.5\n", + "ranksa, ranksb = ranks[:, 0], ranks[:, 1]\n", + "\n", + "num_samples = ranks.shape[0]\n", + "indexes = np.arange(num_samples)\n", + "\n", + "fig, ax = plt.subplots(figsize=(18, 2.5))\n", + "\n", + "# plot sample index vs score and their mean\n", + "ax.scatter(indexes, ranksa, s=30, color=\"tab:blue\", marker=\"o\", label=\"Model A\", zorder=3, alpha=0.6)\n", + "ax.axhline(ranksa.mean(), color=\"tab:blue\", linestyle=\"--\", label=\"Mean\", zorder=3)\n", + "ax.scatter(indexes, ranksb, s=30, color=\"tab:red\", marker=\"o\", label=\"Model B\", zorder=3, alpha=0.6)\n", + "ax.axhline(ranksb.mean(), color=\"tab:red\", linestyle=\"--\", label=\"Mean\", zorder=3)\n", + "\n", + "# configure the x-axis\n", + "ax.set_xlabel(\"Sample index\")\n", + "ax.set_xlim(0 - (eps := 0.01 * num_samples), num_samples + eps)\n", + "ax.xaxis.set_major_locator(IndexLocator(5, 0))\n", + "ax.xaxis.set_minor_locator(IndexLocator(1, 0))\n", + "\n", + "# configure the y-axis\n", + "ax.set_ylabel(\"AUPIMO Rank\")\n", + "ax.set_ylim(1 - 0.1, 2 + 0.1)\n", + "ax.yaxis.set_major_locator(FixedLocator([1, 1.5, 2]))\n", + "ax.invert_yaxis()\n", + "\n", + "# configure the grid, legend, etc\n", + "ax.grid(axis=\"both\", which=\"major\", linestyle=\"-\")\n", + "ax.grid(axis=\"x\", which=\"minor\", linestyle=\"--\", alpha=0.5)\n", + "ax.legend(ncol=4, loc=\"upper left\", bbox_to_anchor=(0, -0.15))\n", + "ax.set_title(\"AUPIMO scores ranks\")\n", + "\n", + "fig.text(\n", + " 0.9,\n", + " -0.1,\n", + " \"Ranks: 1 is the best, 2 is the worst, 1.5 when the scores are the same.\",\n", + " ha=\"right\",\n", + " va=\"top\",\n", + " fontsize=\"small\",\n", + ")\n", + "\n", + "fig # noqa: B018, RUF100" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The advantage of A over B is clearer now.\n", + "\n", + "Most of cases where B was better were within the difference margin of 5%.\n", + "\n", + "The average ranks also got more distant.\n", + "\n", + "Could it be by chance or can we be confident that model A is better than model B?\n", + "\n", + "> **Wilcoxon signed rank test**\n", + "> \n", + "> - null hypothesis: `average(rankA) == average(rankB)` \n", + "> - alternative hypothesis: `average(rankA) != average(rankB)`\n", + "> \n", + "> See [`scipy.stats.wilcoxon`](https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.wilcoxon.html#scipy.stats.wilcoxon) and [\"Wilcoxon signed-rank test\" in Wikipedia](https://en.wikipedia.org/wiki/Wilcoxon_signed-rank_test).\n", + ">\n", + "> Confidence Level (reminder): *higher* confidence level *more confident* that `average(rankA) > average(rankB)`.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "test_result=WilcoxonResult(statistic=1823.0, pvalue=0.0002876893285960681)\n", + "confidence=100.0%\n" + ] + } + ], + "source": [ + "MIN_ABS_DIFF = 0.05\n", + "differences = modela - modelb\n", + "differences[abs_diff < MIN_ABS_DIFF] = 0.0\n", + "test_result = stats.wilcoxon(differences, zero_method=\"zsplit\")\n", + "confidence = 1.0 - float(test_result.pvalue)\n", + "print(f\"{test_result=}\")\n", + "print(f\"{confidence=:.1%}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "We got such a high confidence that we can say for sure that these differences are not due to chance.\n", + "\n", + "So we can say that model A is _consistently_ better than model B -- even though some counter examples exist as we saw in the image by image comparison." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Cite Us\n", + "\n", + "AUPIMO was developed during [Google Summer of Code 2023 (GSoC 2023)](https://summerofcode.withgoogle.com/archive/2023/projects/SPMopugd) with the `anomalib` team from Intel's OpenVINO Toolkit.\n", + "\n", + "arXiv: [arxiv.org/abs/2401.01984](https://arxiv.org/abs/2401.01984) (accepted to BMVC 2024)\n", + "\n", + "Official repository: [github.com/jpcbertoldo/aupimo](https://github.com/jpcbertoldo/aupimo) (numpy-only API and numba-accelerated versions available)\n", + "\n", + "```bibtex\n", + "@misc{bertoldo2024aupimo,\n", + " author={Joao P. C. Bertoldo and Dick Ameln and Ashwin Vaidya and Samet Akçay},\n", + " title={{AUPIMO: Redefining Visual Anomaly Detection Benchmarks with High Speed and Low Tolerance}}, \n", + " year={2024},\n", + " url={https://arxiv.org/abs/2401.01984}, \n", + "}\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "---" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Utils\n", + "\n", + "Some utility functions to expand what this notebook shows." + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Save AUPIMO scores\n", + "\n", + "At the begin of the notebook we defined a function `load_aupimo_result_from_json_dict()` that deserializes `AUPIMOResult` objects.\n", + "\n", + "Let's define the opposite operator so you can save and publish your AUPIMO scores." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "payload.keys()=dict_keys(['fpr_lower_bound', 'fpr_upper_bound', 'num_thresholds', 'thresh_lower_bound', 'thresh_upper_bound', 'aupimos'])\n" + ] + } + ], + "source": [ + "def save_aupimo_result_to_json_dict(\n", + " aupimo_result: AUPIMOResult,\n", + " paths: list[str | Path] | None = None,\n", + ") -> dict[str, str | float | int | list[str]]:\n", + " \"\"\"Convert the AUPIMOResult dataclass to a JSON payload.\"\"\"\n", + " payload = {\n", + " \"fpr_lower_bound\": aupimo_result.fpr_lower_bound,\n", + " \"fpr_upper_bound\": aupimo_result.fpr_upper_bound,\n", + " \"num_thresholds\": aupimo_result.num_thresholds,\n", + " \"thresh_lower_bound\": aupimo_result.thresh_lower_bound,\n", + " \"thresh_upper_bound\": aupimo_result.thresh_upper_bound,\n", + " \"aupimos\": aupimo_result.aupimos.tolist(),\n", + " }\n", + " if paths is not None:\n", + " if len(paths) != aupimo_result.aupimos.shape[0]:\n", + " msg = (\n", + " \"Invalid paths. It must have the same length as the AUPIMO scores. \"\n", + " f\"Got {len(paths)} paths and {aupimo_result.aupimos.shape[0]} scores.\"\n", + " )\n", + " raise ValueError(msg)\n", + " # make sure the paths are strings, not pathlib.Path objects\n", + " payload[\"paths\"] = [str(p) for p in paths]\n", + " return payload\n", + "\n", + "\n", + "payload = save_aupimo_result_to_json_dict(aupimo_result_model_a)\n", + "print(f\"{payload.keys()=}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "payload.keys()=dict_keys(['fpr_lower_bound', 'fpr_upper_bound', 'num_thresholds', 'thresh_lower_bound', 'thresh_upper_bound', 'aupimos'])\n" + ] + } + ], + "source": [ + "payload = save_aupimo_result_to_json_dict(aupimo_result_model_a)\n", + "print(f\"{payload.keys()=}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "payload.keys()=dict_keys(['fpr_lower_bound', 'fpr_upper_bound', 'num_thresholds', 'thresh_lower_bound', 'thresh_upper_bound', 'aupimos', 'paths'])\n" + ] + } + ], + "source": [ + "# you can optionally save the paths to the images\n", + "# where the AUPIMO scores were computed from\n", + "payload = save_aupimo_result_to_json_dict(aupimo_result_model_a, paths)\n", + "print(f\"{payload.keys()=}\")" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "8,0K\t/tmp/tmpsuauy_de/aupimo_result.json\n" + ] + } + ], + "source": [ + "# let's check that it can be saved to a file and loaded back\n", + "\n", + "from tempfile import TemporaryDirectory\n", + "\n", + "with TemporaryDirectory() as tmpdir:\n", + " cache_dir = Path(tmpdir)\n", + "\n", + " with (cache_dir / \"aupimo_result.json\").open(\"w\") as file:\n", + " json.dump(payload, file)\n", + "\n", + " !du -sh {cache_dir / \"aupimo_result.json\"}\n", + "\n", + " with (cache_dir / \"aupimo_result.json\").open(\"r\") as file:\n", + " payload_reloaded = json.load(file)\n", + "\n", + "aupimo_result_reloaded = load_aupimo_result_from_json_dict(payload_reloaded)\n", + "assert torch.allclose(aupimo_result_model_a.aupimos, aupimo_result_reloaded.aupimos, equal_nan=True)" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Pairwise statistical tests (multiple models)\n", + "\n", + "What if you have multiple models to compare?\n", + "\n", + "Here we define a functions that will return all the pairwise comparisons between the models." + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "import itertools\n", + "from typing import Any, Literal\n", + "\n", + "import numpy as np\n", + "from numpy import ndarray\n", + "from scipy import stats\n", + "from torch import Tensor\n", + "\n", + "\n", + "def _validate_models(models: dict[str, Tensor | ndarray]) -> dict[str, ndarray]:\n", + " \"\"\"Make sure the input `models` is valid and convert all the dict's values to `ndarray`.\n", + "\n", + " Args:\n", + " models (dict[str, Tensor | ndarray]): {\"model name\": sequence of shape (num_images,)}.\n", + " Validations:\n", + " - keys are strings (model names)\n", + " - there are at least two models\n", + " - values are sequences of floats in [0, 1] or `nan`\n", + " - all sequences have the same shape\n", + " - all `nan` values are at the positions\n", + " Returns:\n", + " dict[str, ndarray]: {\"model name\": array (num_images,)}.\n", + " \"\"\"\n", + " if not isinstance(models, dict):\n", + " msg = f\"Expected argument `models` to be a dict, but got {type(models)}.\"\n", + " raise TypeError(msg)\n", + "\n", + " if len(models) < 2:\n", + " msg = \"Expected argument `models` to have at least one key, but got none.\"\n", + " raise ValueError(msg)\n", + "\n", + " ref_num_samples = None\n", + " ref_nans = None\n", + " for key in models:\n", + " if not isinstance(key, str):\n", + " msg = f\"Expected argument `models` to have all keys of type str. Found {type(key)}.\"\n", + " raise TypeError(msg)\n", + "\n", + " value = models[key]\n", + "\n", + " if not isinstance(value, Tensor | ndarray):\n", + " msg = (\n", + " \"Expected argument `models` to have all values of type Tensor or ndarray. \"\n", + " f\"Found {type(value)} on {key=}.\"\n", + " )\n", + " raise TypeError(msg)\n", + "\n", + " if isinstance(value, Tensor):\n", + " models[key] = value = value.numpy()\n", + "\n", + " if not np.issubdtype(value.dtype, np.floating):\n", + " msg = f\"Expected argument `models` to have all values of floating type. Found {value.dtype} on {key=}.\"\n", + " raise ValueError(msg)\n", + "\n", + " if value.ndim != 1:\n", + " msg = f\"Expected argument `models` to have all values of 1D arrays. Found {value.ndim} on {key=}.\"\n", + " raise ValueError(msg)\n", + "\n", + " if ref_num_samples is None:\n", + " ref_num_samples = num_samples = value.shape[0]\n", + " ref_nans = nans = np.isnan(value)\n", + "\n", + " if num_samples != ref_num_samples:\n", + " msg = \"Argument `models` has inconsistent number of samples.\"\n", + " raise ValueError(msg)\n", + "\n", + " if (nans != ref_nans).any():\n", + " msg = \"Argument `models` has inconsistent `nan` values (in different positions).\"\n", + " raise ValueError(msg)\n", + "\n", + " if (value[~nans] < 0).any() or (value[~nans] > 1).any():\n", + " msg = (\n", + " \"Expected argument `models` to have all sequences of floats \\\\in [0, 1]. \"\n", + " f\"Key {key} has values outside this range.\"\n", + " )\n", + " raise ValueError(msg)\n", + "\n", + " return models\n", + "\n", + "\n", + "def test_pairwise(\n", + " models: dict[str, Tensor | ndarray],\n", + " *,\n", + " test: Literal[\"ttest_rel\", \"wilcoxon\"],\n", + " min_abs_diff: float | None = None,\n", + ") -> list[dict[str, Any]]:\n", + " \"\"\"Compare all pairs of models using statistical tests.\n", + "\n", + " Scores are assumed to be *higher is better*.\n", + "\n", + " General hypothesis in the tests:\n", + " - Null hypothesis: two models are equivalent on average.\n", + " - Alternative hypothesis: one model is better than the other (two-sided test).\n", + "\n", + " Args:\n", + " models (dict[str, Tensor | ndarray]): {\"model name\": sequence of shape (num_images,)}.\n", + " test (Literal[\"ttest_rel\", \"wilcoxon\"]): The statistical test to use.\n", + " - \"ttest_rel\": Paired Student's t-test (parametric).\n", + " - \"wilcoxon\": Wilcoxon signed-rank test (non-parametric).\n", + " min_abs_diff (float | None): Minimum absolute difference to consider in the Wilcoxon test. If `None`, all\n", + " differences are considered. Default is `None`. Ignored in the t-test.\n", + " \"\"\"\n", + " models = _validate_models(models)\n", + " if test not in {\"ttest_rel\", \"wilcoxon\"}:\n", + " msg = f\"Expected argument `test` to be 'ttest_rel' or 'wilcoxon', but got '{test}'.\"\n", + " raise ValueError(msg)\n", + " # remove nan values\n", + " models = {k: v[~np.isnan(v)] for k, v in models.items()}\n", + " models_names = sorted(models.keys())\n", + " num_models = len(models)\n", + " comparisons = list(itertools.combinations(range(num_models), 2))\n", + "\n", + " # for each comparison, compute the test and confidence (1 - p-value)\n", + " test_results = []\n", + " for modela_idx, modelb_idx in comparisons: # indices of the sorted model names\n", + " modela = models_names[modela_idx]\n", + " modelb = models_names[modelb_idx]\n", + " modela_scores = models[modela]\n", + " modelb_scores = models[modelb]\n", + " if test == \"ttest_rel\":\n", + " test_result = stats.ttest_rel(modela_scores, modelb_scores, alternative=\"two-sided\")\n", + " else: # test == \"wilcoxon\"\n", + " differences = modela_scores - modelb_scores\n", + " if min_abs_diff is not None:\n", + " differences[np.abs(differences) < min_abs_diff] = 0.0\n", + " # extreme case\n", + " if (differences == 0).all():\n", + " test_result = stats._morestats.WilcoxonResult(np.nan, 1.0) # noqa: SLF001\n", + " else:\n", + " test_result = stats.wilcoxon(differences, zero_method=\"zsplit\", alternative=\"two-sided\")\n", + " test_results.append({\n", + " \"modela\": modela,\n", + " \"modelb\": modelb,\n", + " \"confidence\": 1 - test_result.pvalue,\n", + " \"pvalue\": test_result.pvalue,\n", + " \"statistic\": test_result.statistic,\n", + " })\n", + "\n", + " return test_results" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Let's first test it with the same two models we used before." + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
modelamodelbconfidencepvaluestatistic
0AB0.9950.0052.872
\n", + "
" + ], + "text/plain": [ + " modela modelb confidence pvalue statistic\n", + "0 A B 0.995 0.005 2.872" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# parametric test\n", + "pd.DataFrame.from_records(test_pairwise({\"A\": modela, \"B\": modelb}, test=\"ttest_rel\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
modelamodelbconfidencepvaluestatistic
0AB0.9980.0021965.500
\n", + "
" + ], + "text/plain": [ + " modela modelb confidence pvalue statistic\n", + "0 A B 0.998 0.002 1965.500" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# non-parametric test\n", + "pd.DataFrame.from_records(test_pairwise({\"A\": modela, \"B\": modelb}, test=\"wilcoxon\"))" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
modelamodelbconfidencepvaluestatistic
0AB1.0000.0001823.000
\n", + "
" + ], + "text/plain": [ + " modela modelb confidence pvalue statistic\n", + "0 A B 1.000 0.000 1823.000" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# non-parametric test with a minimum absolute difference\n", + "pd.DataFrame.from_records(test_pairwise({\"A\": modela, \"B\": modelb}, test=\"wilcoxon\", min_abs_diff=0.05))" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Now let's get the best models from the benchmark in our paper and compare them two by two.\n", + "\n", + "We'll look at the dataset `cashew` from `VisA`.\n", + "\n", + "> More details in the paper (see the last cell)." + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
 modelamodelbconfidencepvaluestatistic
0efficientad_wr101_s_extpatchcore_wr1010.9994020.0005981580.000000
1efficientad_wr101_s_extrd++_wr50_ext0.7736590.2263412193.500000
2efficientad_wr101_s_extsimplenet_wr50_ext1.0000000.000000690.500000
3efficientad_wr101_s_extuflow_ext0.9994470.0005531550.500000
4patchcore_wr101rd++_wr50_ext0.9999800.0000201333.000000
5patchcore_wr101simplenet_wr50_ext1.0000000.000000351.500000
6patchcore_wr101uflow_ext0.7318750.2681252213.000000
7rd++_wr50_extsimplenet_wr50_ext1.0000000.000000967.000000
8rd++_wr50_extuflow_ext0.9999450.0000551383.000000
9simplenet_wr50_extuflow_ext1.0000000.000000318.500000
\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "models = {\n", + " model_name: get_benchmark_aupimo_scores(model_name, \"visa/cashew\", verbose=False)[1].aupimos.numpy()\n", + " for model_name in [\n", + " \"efficientad_wr101_s_ext\",\n", + " \"patchcore_wr101\",\n", + " \"rd++_wr50_ext\",\n", + " \"simplenet_wr50_ext\",\n", + " \"uflow_ext\",\n", + " ]\n", + "}\n", + "models = test_pairwise(models, test=\"wilcoxon\", min_abs_diff=0.1)\n", + "pd.DataFrame.from_records(models).style.background_gradient(cmap=\"jet\", vmin=0, vmax=1, subset=[\"confidence\"])" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Compare to the benchmark (coming up)\n", + "\n", + "Compare your freshly trained models to the benchmark datasets in our paper." + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "metadata": {}, + "outputs": [], + "source": [ + "# TODO(jpcbertoldo): implement utility function to load and compare to the results from the benchmark # noqa: TD003" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Cite Us\n", + "\n", + "AUPIMO was developed during [Google Summer of Code 2023 (GSoC 2023)](https://summerofcode.withgoogle.com/archive/2023/projects/SPMopugd) with the `anomalib` team from Intel's OpenVINO Toolkit.\n", + "\n", + "arXiv: [arxiv.org/abs/2401.01984](https://arxiv.org/abs/2401.01984) (accepted to BMVC 2024)\n", + "\n", + "Official repository: [github.com/jpcbertoldo/aupimo](https://github.com/jpcbertoldo/aupimo) (numpy-only API and numba-accelerated versions available)\n", + "\n", + "```bibtex\n", + "@misc{bertoldo2024aupimo,\n", + " author={Joao P. C. Bertoldo and Dick Ameln and Ashwin Vaidya and Samet Akçay},\n", + " title={{AUPIMO: Redefining Visual Anomaly Detection Benchmarks with High Speed and Low Tolerance}}, \n", + " year={2024},\n", + " url={https://arxiv.org/abs/2401.01984}, \n", + "}\n", + "```" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "anomalib-dev", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.14" + }, + "orig_nbformat": 4 + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/notebooks/README.md b/notebooks/README.md index 15935b93cf..de33e5b7e9 100644 --- a/notebooks/README.md +++ b/notebooks/README.md @@ -60,3 +60,4 @@ To install Python, Git and other required tools, [OpenVINO Notebooks](https://gi | AUPIMO representative samples and visualization | [701b_aupimo_advanced_i](/notebooks/700_metrics/701b_aupimo_advanced_i.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/openvinotoolkit/anomalib/blob/main/notebooks/700_metrics/701b_aupimo_advanced_i.ipynb) | | PIMO curve and integration bounds | [701c_aupimo_advanced_ii](/notebooks/700_metrics/701c_aupimo_advanced_ii.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/openvinotoolkit/anomalib/blob/main/notebooks/700_metrics/701c_aupimo_advanced_ii.ipynb) | | (AU)PIMO of a random model | [701d_aupimo_advanced_iii](/notebooks/700_metrics/701d_aupimo_advanced_iii.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/openvinotoolkit/anomalib/blob/main/notebooks/700_metrics/701d_aupimo_advanced_iii.ipynb) | +| AUPIMO load/save, statistical comparison | [701e_aupimo_advanced_iv](/notebooks/700_metrics/701e_aupimo_advanced_iv.ipynb) | [![Open In Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/openvinotoolkit/anomalib/blob/main/notebooks/700_metrics/701e_aupimo_advanced_iv.ipynb) | diff --git a/pyproject.toml b/pyproject.toml index 2893ad20c4..e47f7e55d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ dependencies = [ "jsonargparse[signatures]>=4.27.7", "docstring_parser", # CLI help-formatter "rich_argparse", # CLI help-formatter + "lightning-utilities", ] [project.optional-dependencies] @@ -56,6 +57,7 @@ core = [ "open-clip-torch>=2.23.0,<2.26.1", ] openvino = ["openvino>=2024.0", "nncf>=2.10.0", "onnx>=1.16.0"] +vlm = ["ollama", "openai", "python-dotenv","transformers"] loggers = [ "comet-ml>=3.31.7", "gradio>=4", @@ -84,7 +86,7 @@ test = [ "coverage[toml]", "tox", ] -full = ["anomalib[core,openvino,loggers,notebooks]"] +full = ["anomalib[core,openvino,loggers,notebooks, vlm]"] dev = ["anomalib[full,docs,test]"] [project.scripts] @@ -292,11 +294,15 @@ pythonpath = "src" # COVERAGE CONFIGURATION # [tool.coverage.report] exclude_lines = [ - "except ImportError", + "pragma: no cover", + "def __repr__", + "raise NotImplementedError", + "if TYPE_CHECKING:", + "@abstractmethod", + "pass", "raise ImportError", - "except ApiException", - "raise ApiException", "raise ValueError", + "except ImportError:", ] [tool.coverage.paths] diff --git a/src/anomalib/__init__.py b/src/anomalib/__init__.py index cd82b638b9..281e5df759 100644 --- a/src/anomalib/__init__.py +++ b/src/anomalib/__init__.py @@ -5,7 +5,7 @@ from enum import Enum -__version__ = "1.2.0dev" +__version__ = "2.0.0dev" class LearningType(str, Enum): diff --git a/src/anomalib/cli/pipelines.py b/src/anomalib/cli/pipelines.py index 8cfb04fd2e..ba6030491b 100644 --- a/src/anomalib/cli/pipelines.py +++ b/src/anomalib/cli/pipelines.py @@ -6,13 +6,13 @@ import logging from jsonargparse import Namespace -from lightning_utilities.core.imports import package_available +from lightning_utilities.core.imports import module_available from anomalib.cli.utils.help_formatter import get_short_docstring logger = logging.getLogger(__name__) -if package_available("anomalib.pipelines"): +if module_available("anomalib.pipelines"): from anomalib.pipelines import Benchmark from anomalib.pipelines.components.base import Pipeline diff --git a/src/anomalib/cli/utils/openvino.py b/src/anomalib/cli/utils/openvino.py index ee54bf09b2..50a894c304 100644 --- a/src/anomalib/cli/utils/openvino.py +++ b/src/anomalib/cli/utils/openvino.py @@ -6,12 +6,12 @@ import logging from jsonargparse import ArgumentParser -from lightning_utilities.core.imports import package_available +from lightning_utilities.core.imports import module_available logger = logging.getLogger(__name__) -if package_available("openvino"): +if module_available("openvino"): from openvino.tools.ovc.cli_parser import get_common_cli_parser else: get_common_cli_parser = None diff --git a/src/anomalib/data/__init__.py b/src/anomalib/data/__init__.py index e5ee6bae4c..9c9be7eb5b 100644 --- a/src/anomalib/data/__init__.py +++ b/src/anomalib/data/__init__.py @@ -32,16 +32,15 @@ # Datamodules from .datamodules.base import AnomalibDataModule from .datamodules.depth import DepthDataFormat, Folder3D, MVTec3D -from .datamodules.image import BTech, Folder, ImageDataFormat, Kolektor, MVTec, Visa +from .datamodules.image import BTech, Datumaro, Folder, ImageDataFormat, Kolektor, MVTec, Visa from .datamodules.video import Avenue, ShanghaiTech, UCSDped, VideoDataFormat # Datasets from .datasets import AnomalibDataset from .datasets.depth import Folder3DDataset, MVTec3DDataset -from .datasets.image import BTechDataset, FolderDataset, KolektorDataset, MVTecDataset, VisaDataset +from .datasets.image import BTechDataset, DatumaroDataset, FolderDataset, KolektorDataset, MVTecDataset, VisaDataset from .datasets.video import AvenueDataset, ShanghaiTechDataset, UCSDpedDataset from .predict import PredictDataset -from .utils import LabelName logger = logging.getLogger(__name__) @@ -106,6 +105,7 @@ def get_datamodule(config: DictConfig | ListConfig | dict) -> AnomalibDataModule "Folder3DDataset", "MVTec3DDataset", "BTechDataset", + "DatumaroDataset", "FolderDataset", "KolektorDataset", "MVTecDataset", @@ -121,6 +121,7 @@ def get_datamodule(config: DictConfig | ListConfig | dict) -> AnomalibDataModule "VideoDataFormat", "get_datamodule", "BTech", + "Datumaro", "Folder", "Folder3D", "Kolektor", @@ -131,4 +132,5 @@ def get_datamodule(config: DictConfig | ListConfig | dict) -> AnomalibDataModule "ShanghaiTech", "Visa", "LabelName", + "PredictDataset", ] diff --git a/src/anomalib/data/dataclasses/generic.py b/src/anomalib/data/dataclasses/generic.py index 3244fce6cf..5f9dca9dc9 100644 --- a/src/anomalib/data/dataclasses/generic.py +++ b/src/anomalib/data/dataclasses/generic.py @@ -343,7 +343,7 @@ def validate_depth_path(depth_path: PathT) -> PathT | None: @dataclass -class _OutputFields(Generic[T, MaskT], ABC): +class _OutputFields(Generic[T, MaskT, PathT], ABC): """Generic dataclass that defines the standard output fields for Anomalib. This class defines the standard output fields used in Anomalib, including @@ -390,6 +390,7 @@ class _OutputFields(Generic[T, MaskT], ABC): pred_score: FieldDescriptor[T | None] = FieldDescriptor(validator_name="validate_pred_score") pred_mask: FieldDescriptor[MaskT | None] = FieldDescriptor(validator_name="validate_pred_mask") pred_label: FieldDescriptor[T | None] = FieldDescriptor(validator_name="validate_pred_label") + explanation: FieldDescriptor[PathT | None] = FieldDescriptor(validator_name="validate_explanation") @staticmethod @abstractmethod @@ -415,6 +416,12 @@ def validate_pred_label(pred_label: T) -> T | None: """Validate the predicted label.""" raise NotImplementedError + @staticmethod + @abstractmethod + def validate_explanation(explanation: PathT) -> PathT | None: + """Validate the explanation.""" + raise NotImplementedError + @dataclass class UpdateMixin: @@ -471,7 +478,7 @@ def update(self, in_place: bool = True, **changes) -> Any: # noqa: ANN401 class _GenericItem( UpdateMixin, Generic[T, ImageT, MaskT, PathT], - _OutputFields[T, MaskT], + _OutputFields[T, MaskT, PathT], _InputFields[T, ImageT, MaskT, PathT], ): """Generic dataclass for a single item in Anomalib datasets. @@ -520,7 +527,7 @@ class _GenericItem( class _GenericBatch( UpdateMixin, Generic[T, ImageT, MaskT, PathT], - _OutputFields[T, MaskT], + _OutputFields[T, MaskT, PathT], _InputFields[T, ImageT, MaskT, PathT], ): """Generic dataclass for a batch of items in Anomalib datasets. diff --git a/src/anomalib/data/datamodules/__init__.py b/src/anomalib/data/datamodules/__init__.py index c81666db5e..4072428384 100644 --- a/src/anomalib/data/datamodules/__init__.py +++ b/src/anomalib/data/datamodules/__init__.py @@ -4,13 +4,14 @@ # SPDX-License-Identifier: Apache-2.0 from .depth import Folder3D, MVTec3D -from .image import BTech, Folder, Kolektor, MVTec, Visa +from .image import BTech, Datumaro, Folder, Kolektor, MVTec, Visa from .video import Avenue, ShanghaiTech, UCSDped __all__ = [ "Folder3D", "MVTec3D", "BTech", + "Datumaro", "Folder", "Kolektor", "MVTec", diff --git a/src/anomalib/data/datamodules/image/__init__.py b/src/anomalib/data/datamodules/image/__init__.py index ca57cf6868..69221f863c 100644 --- a/src/anomalib/data/datamodules/image/__init__.py +++ b/src/anomalib/data/datamodules/image/__init__.py @@ -6,6 +6,7 @@ from enum import Enum from .btech import BTech +from .datumaro import Datumaro from .folder import Folder from .kolektor import Kolektor from .mvtec import MVTec @@ -15,13 +16,14 @@ class ImageDataFormat(str, Enum): """Supported Image Dataset Types.""" - MVTEC = "mvtec" - MVTEC_3D = "mvtec_3d" BTECH = "btech" - KOLEKTOR = "kolektor" + DATUMARO = "datumaro" FOLDER = "folder" FOLDER_3D = "folder_3d" + KOLEKTOR = "kolektor" + MVTEC = "mvtec" + MVTEC_3D = "mvtec_3d" VISA = "visa" -__all__ = ["BTech", "Folder", "Kolektor", "MVTec", "Visa"] +__all__ = ["BTech", "Datumaro", "Folder", "Kolektor", "MVTec", "Visa"] diff --git a/src/anomalib/data/datamodules/image/datumaro.py b/src/anomalib/data/datamodules/image/datumaro.py new file mode 100644 index 0000000000..f7496982da --- /dev/null +++ b/src/anomalib/data/datamodules/image/datumaro.py @@ -0,0 +1,104 @@ +"""DataModule for Datumaro format. + +Note: This currently only works for annotations exported from Intel Geti™. +""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path + +from anomalib import TaskType +from anomalib.data.datamodules.base import AnomalibDataModule +from anomalib.data.datasets.image.datumaro import DatumaroDataset +from anomalib.data.utils import Split, TestSplitMode, ValSplitMode + + +class Datumaro(AnomalibDataModule): + """Datumaro datamodule. + + Args: + root (str | Path): Path to the dataset root directory. + train_batch_size (int): Batch size for training dataloader. + Defaults to ``32``. + eval_batch_size (int): Batch size for evaluation dataloader. + Defaults to ``32``. + num_workers (int): Number of workers for dataloaders. + Defaults to ``8``. + task (TaskType): Task type, ``classification``, ``detection`` or ``segmentation``. + Defaults to ``TaskType.CLASSIFICATION``. Currently only supports classification. + image_size (tuple[int, int], optional): Size to which input images should be resized. + Defaults to ``None``. + transform (Transform, optional): Transforms that should be applied to the input images. + Defaults to ``None``. + train_transform (Transform, optional): Transforms that should be applied to the input images during training. + Defaults to ``None``. + eval_transform (Transform, optional): Transforms that should be applied to the input images during evaluation. + Defaults to ``None``. + test_split_mode (TestSplitMode): Setting that determines how the testing subset is obtained. + Defaults to ``TestSplitMode.FROM_DIR``. + test_split_ratio (float): Fraction of images from the train set that will be reserved for testing. + Defaults to ``0.2``. + val_split_mode (ValSplitMode): Setting that determines how the validation subset is obtained. + Defaults to ``ValSplitMode.SAME_AS_TEST``. + val_split_ratio (float): Fraction of train or test images that will be reserved for validation. + Defaults to ``0.5``. + seed (int | None, optional): Seed which may be set to a fixed value for reproducibility. + Defualts to ``None``. + + Examples: + To create a Datumaro datamodule + + >>> from pathlib import Path + >>> from torchvision.transforms.v2 import Resize + >>> root = Path("path/to/dataset") + >>> datamodule = Datumaro(root, transform=Resize((256, 256))) + >>> datamodule.setup() + >>> i, data = next(enumerate(datamodule.train_dataloader())) + >>> data.keys() + dict_keys(['image_path', 'label', 'image']) + + >>> data["image"].shape + torch.Size([32, 3, 256, 256]) + """ + + def __init__( + self, + root: str | Path, + train_batch_size: int = 32, + eval_batch_size: int = 32, + num_workers: int = 8, + task: TaskType = TaskType.CLASSIFICATION, + test_split_mode: TestSplitMode | str = TestSplitMode.FROM_DIR, + test_split_ratio: float = 0.5, + val_split_mode: ValSplitMode | str = ValSplitMode.FROM_TEST, + val_split_ratio: float = 0.5, + seed: int | None = None, + ) -> None: + if task != TaskType.CLASSIFICATION: + msg = "Datumaro dataloader currently only supports classification task." + raise ValueError(msg) + super().__init__( + train_batch_size=train_batch_size, + eval_batch_size=eval_batch_size, + num_workers=num_workers, + val_split_mode=val_split_mode, + val_split_ratio=val_split_ratio, + test_split_mode=test_split_mode, + test_split_ratio=test_split_ratio, + seed=seed, + ) + self.root = root + self.task = task + + def _setup(self, _stage: str | None = None) -> None: + self.train_data = DatumaroDataset( + task=self.task, + root=self.root, + split=Split.TRAIN, + ) + self.test_data = DatumaroDataset( + task=self.task, + root=self.root, + split=Split.TEST, + ) diff --git a/src/anomalib/data/datasets/__init__.py b/src/anomalib/data/datasets/__init__.py index 3208bda54a..32e3995ea5 100644 --- a/src/anomalib/data/datasets/__init__.py +++ b/src/anomalib/data/datasets/__init__.py @@ -5,7 +5,7 @@ from .base import AnomalibDataset, AnomalibDepthDataset, AnomalibVideoDataset from .depth import Folder3DDataset, MVTec3DDataset -from .image import BTechDataset, FolderDataset, KolektorDataset, MVTecDataset, VisaDataset +from .image import BTechDataset, DatumaroDataset, FolderDataset, KolektorDataset, MVTecDataset, VisaDataset from .video import AvenueDataset, ShanghaiTechDataset, UCSDpedDataset __all__ = [ @@ -18,6 +18,7 @@ "MVTec3DDataset", # Image "BTechDataset", + "DatumaroDataset", "FolderDataset", "KolektorDataset", "MVTecDataset", diff --git a/src/anomalib/data/datasets/image/__init__.py b/src/anomalib/data/datasets/image/__init__.py index c3b5c41dc7..b7749dad18 100644 --- a/src/anomalib/data/datasets/image/__init__.py +++ b/src/anomalib/data/datasets/image/__init__.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: Apache-2.0 from .btech import BTechDataset +from .datumaro import DatumaroDataset from .folder import FolderDataset from .kolektor import KolektorDataset from .mvtec import MVTecDataset @@ -11,6 +12,7 @@ __all__ = [ "BTechDataset", + "DatumaroDataset", "FolderDataset", "KolektorDataset", "MVTecDataset", diff --git a/src/anomalib/data/datasets/image/datumaro.py b/src/anomalib/data/datasets/image/datumaro.py new file mode 100644 index 0000000000..6c67c61359 --- /dev/null +++ b/src/anomalib/data/datasets/image/datumaro.py @@ -0,0 +1,126 @@ +"""Dataloader for Datumaro format. + +Note: This currently only works for annotations exported from Intel Geti™. +""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import json +from pathlib import Path + +import pandas as pd +from torchvision.transforms.v2 import Transform + +from anomalib import TaskType +from anomalib.data.datasets.base import AnomalibDataset +from anomalib.data.utils import LabelName, Split + + +def make_datumaro_dataset(root: str | Path, split: str | Split | None = None) -> pd.DataFrame: + """Make Datumaro Dataset. + + Assumes the following directory structure: + + dataset + ├── annotations + │ └── default.json + └── images + └── default + ├── image1.jpg + ├── image2.jpg + └── ... + + Args: + root (str | Path): Path to the dataset root directory. + split (str | Split | None): Split of the dataset, usually Split.TRAIN or Split.TEST. + Defaults to ``None``. + + Examples: + >>> root = Path("path/to/dataset") + >>> samples = make_datumaro_dataset(root) + >>> samples.head() + image_path label label_index split mask_path + 0 path/to/dataset... Normal 0 Split.TRAIN + 1 path/to/dataset... Normal 0 Split.TRAIN + 2 path/to/dataset... Normal 0 Split.TRAIN + 3 path/to/dataset... Normal 0 Split.TRAIN + 4 path/to/dataset... Normal 0 Split.TRAIN + + + Returns: + DataFrame: an output dataframe containing samples for the requested split (ie., train or test). + """ + annotation_file = Path(root) / "annotations" / "default.json" + with annotation_file.open() as f: + annotations = json.load(f) + + categories = annotations["categories"] + categories = {idx: label["name"] for idx, label in enumerate(categories["label"]["labels"])} + + samples = [] + for item in annotations["items"]: + image_path = Path(root) / "images" / "default" / item["image"]["path"] + label_index = item["annotations"][0]["label_id"] + label = categories[label_index] + samples.append({ + "image_path": str(image_path), + "label": label, + "label_index": label_index, + "split": None, + "mask_path": "", # mask is provided in the annotation file and is not on disk. + }) + samples_df = pd.DataFrame( + samples, + columns=["image_path", "label", "label_index", "split", "mask_path"], + index=range(len(samples)), + ) + # Create test/train split + # By default assign all "Normal" samples to train and all "Anomalous" samples to test + samples_df.loc[samples_df["label_index"] == LabelName.NORMAL, "split"] = Split.TRAIN + samples_df.loc[samples_df["label_index"] == LabelName.ABNORMAL, "split"] = Split.TEST + + # Get the data frame for the split. + if split: + samples_df = samples_df[samples_df.split == split].reset_index(drop=True) + + return samples_df + + +class DatumaroDataset(AnomalibDataset): + """Datumaro dataset class. + + Args: + task (TaskType): Task type, ``classification``, ``detection`` or ``segmentation``. + root (str | Path): Path to the dataset root directory. + transform (Transform, optional): Transforms that should be applied to the input images. + Defaults to ``None``. + split (str | Split | None): Split of the dataset, usually Split.TRAIN or Split.TEST + Defaults to ``None``. + + + Examples: + .. code-block:: python + + from anomalib.data.image.datumaro import DatumaroDataset + from torchvision.transforms.v2 import Resize + + dataset = DatumaroDataset(root=root, + task="classification", + transform=Resize((256, 256)), + ) + print(dataset[0].keys()) + # Output: dict_keys(['dm_format_version', 'infos', 'categories', 'items']) + + """ + + def __init__( + self, + task: TaskType, + root: str | Path, + transform: Transform | None = None, + split: str | Split | None = None, + ) -> None: + super().__init__(task, transform) + self.split = split + self.samples = make_datumaro_dataset(root, split) diff --git a/src/anomalib/data/image/datumaro.py b/src/anomalib/data/image/datumaro.py new file mode 100644 index 0000000000..b4836990ec --- /dev/null +++ b/src/anomalib/data/image/datumaro.py @@ -0,0 +1,226 @@ +"""Dataloader for Datumaro format. + +Note: This currently only works for annotations exported from Intel Geti™. +""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import json +from pathlib import Path + +import pandas as pd +from torchvision.transforms.v2 import Transform + +from anomalib import TaskType +from anomalib.data.base import AnomalibDataModule, AnomalibDataset +from anomalib.data.utils import LabelName, Split, TestSplitMode, ValSplitMode + + +def make_datumaro_dataset(root: str | Path, split: str | Split | None = None) -> pd.DataFrame: + """Make Datumaro Dataset. + + Assumes the following directory structure: + + dataset + ├── annotations + │ └── default.json + └── images + └── default + ├── image1.jpg + ├── image2.jpg + └── ... + + Args: + root (str | Path): Path to the dataset root directory. + split (str | Split | None): Split of the dataset, usually Split.TRAIN or Split.TEST. + Defaults to ``None``. + + Examples: + >>> root = Path("path/to/dataset") + >>> samples = make_datumaro_dataset(root) + >>> samples.head() + image_path label label_index split mask_path + 0 path/to/dataset... Normal 0 Split.TRAIN + 1 path/to/dataset... Normal 0 Split.TRAIN + 2 path/to/dataset... Normal 0 Split.TRAIN + 3 path/to/dataset... Normal 0 Split.TRAIN + 4 path/to/dataset... Normal 0 Split.TRAIN + + + Returns: + DataFrame: an output dataframe containing samples for the requested split (ie., train or test). + """ + annotation_file = Path(root) / "annotations" / "default.json" + with annotation_file.open() as f: + annotations = json.load(f) + + categories = annotations["categories"] + categories = {idx: label["name"] for idx, label in enumerate(categories["label"]["labels"])} + + samples = [] + for item in annotations["items"]: + image_path = Path(root) / "images" / "default" / item["image"]["path"] + label_index = item["annotations"][0]["label_id"] + label = categories[label_index] + samples.append({ + "image_path": str(image_path), + "label": label, + "label_index": label_index, + "split": None, + "mask_path": "", # mask is provided in the annotation file and is not on disk. + }) + samples_df = pd.DataFrame( + samples, + columns=["image_path", "label", "label_index", "split", "mask_path"], + index=range(len(samples)), + ) + # Create test/train split + # By default assign all "Normal" samples to train and all "Anomalous" samples to test + samples_df.loc[samples_df["label_index"] == LabelName.NORMAL, "split"] = Split.TRAIN + samples_df.loc[samples_df["label_index"] == LabelName.ABNORMAL, "split"] = Split.TEST + + # Get the data frame for the split. + if split: + samples_df = samples_df[samples_df.split == split].reset_index(drop=True) + + return samples_df + + +class DatumaroDataset(AnomalibDataset): + """Datumaro dataset class. + + Args: + task (TaskType): Task type, ``classification``, ``detection`` or ``segmentation``. + root (str | Path): Path to the dataset root directory. + transform (Transform, optional): Transforms that should be applied to the input images. + Defaults to ``None``. + split (str | Split | None): Split of the dataset, usually Split.TRAIN or Split.TEST + Defaults to ``None``. + + + Examples: + .. code-block:: python + + from anomalib.data.image.datumaro import DatumaroDataset + from torchvision.transforms.v2 import Resize + + dataset = DatumaroDataset(root=root, + task="classification", + transform=Resize((256, 256)), + ) + print(dataset[0].keys()) + # Output: dict_keys(['dm_format_version', 'infos', 'categories', 'items']) + + """ + + def __init__( + self, + task: TaskType, + root: str | Path, + transform: Transform | None = None, + split: str | Split | None = None, + ) -> None: + super().__init__(task, transform) + self.split = split + self.samples = make_datumaro_dataset(root, split) + + +class Datumaro(AnomalibDataModule): + """Datumaro datamodule. + + Args: + root (str | Path): Path to the dataset root directory. + train_batch_size (int): Batch size for training dataloader. + Defaults to ``32``. + eval_batch_size (int): Batch size for evaluation dataloader. + Defaults to ``32``. + num_workers (int): Number of workers for dataloaders. + Defaults to ``8``. + task (TaskType): Task type, ``classification``, ``detection`` or ``segmentation``. + Defaults to ``TaskType.CLASSIFICATION``. Currently only supports classification. + image_size (tuple[int, int], optional): Size to which input images should be resized. + Defaults to ``None``. + transform (Transform, optional): Transforms that should be applied to the input images. + Defaults to ``None``. + train_transform (Transform, optional): Transforms that should be applied to the input images during training. + Defaults to ``None``. + eval_transform (Transform, optional): Transforms that should be applied to the input images during evaluation. + Defaults to ``None``. + test_split_mode (TestSplitMode): Setting that determines how the testing subset is obtained. + Defaults to ``TestSplitMode.FROM_DIR``. + test_split_ratio (float): Fraction of images from the train set that will be reserved for testing. + Defaults to ``0.2``. + val_split_mode (ValSplitMode): Setting that determines how the validation subset is obtained. + Defaults to ``ValSplitMode.SAME_AS_TEST``. + val_split_ratio (float): Fraction of train or test images that will be reserved for validation. + Defaults to ``0.5``. + seed (int | None, optional): Seed which may be set to a fixed value for reproducibility. + Defualts to ``None``. + + Examples: + To create a Datumaro datamodule + + >>> from pathlib import Path + >>> from torchvision.transforms.v2 import Resize + >>> root = Path("path/to/dataset") + >>> datamodule = Datumaro(root, transform=Resize((256, 256))) + >>> datamodule.setup() + >>> i, data = next(enumerate(datamodule.train_dataloader())) + >>> data.keys() + dict_keys(['image_path', 'label', 'image']) + + >>> data["image"].shape + torch.Size([32, 3, 256, 256]) + """ + + def __init__( + self, + root: str | Path, + train_batch_size: int = 32, + eval_batch_size: int = 32, + num_workers: int = 8, + task: TaskType = TaskType.CLASSIFICATION, + image_size: tuple[int, int] | None = None, + transform: Transform | None = None, + train_transform: Transform | None = None, + eval_transform: Transform | None = None, + test_split_mode: TestSplitMode | str = TestSplitMode.FROM_DIR, + test_split_ratio: float = 0.5, + val_split_mode: ValSplitMode | str = ValSplitMode.FROM_TEST, + val_split_ratio: float = 0.5, + seed: int | None = None, + ) -> None: + if task != TaskType.CLASSIFICATION: + msg = "Datumaro dataloader currently only supports classification task." + raise ValueError(msg) + super().__init__( + train_batch_size=train_batch_size, + eval_batch_size=eval_batch_size, + num_workers=num_workers, + val_split_mode=val_split_mode, + val_split_ratio=val_split_ratio, + test_split_mode=test_split_mode, + test_split_ratio=test_split_ratio, + image_size=image_size, + transform=transform, + train_transform=train_transform, + eval_transform=eval_transform, + seed=seed, + ) + self.root = root + self.task = task + + def _setup(self, _stage: str | None = None) -> None: + self.train_data = DatumaroDataset( + task=self.task, + root=self.root, + transform=self.train_transform, + split=Split.TRAIN, + ) + self.test_data = DatumaroDataset( + task=self.task, + root=self.root, + transform=self.eval_transform, + split=Split.TEST, + ) diff --git a/src/anomalib/data/utils/tiler.py b/src/anomalib/data/utils/tiler.py index 089aeaae91..2c1e949e45 100644 --- a/src/anomalib/data/utils/tiler.py +++ b/src/anomalib/data/utils/tiler.py @@ -162,11 +162,11 @@ def __init__( remove_border_count: int = 0, mode: ImageUpscaleMode = ImageUpscaleMode.PADDING, ) -> None: - self.tile_size_h, self.tile_size_w = self.__validate_size_type(tile_size) + self.tile_size_h, self.tile_size_w = self.validate_size_type(tile_size) self.random_tile_count = 4 if stride is not None: - self.stride_h, self.stride_w = self.__validate_size_type(stride) + self.stride_h, self.stride_w = self.validate_size_type(stride) self.remove_border_count = remove_border_count self.overlapping = not (self.stride_h == self.tile_size_h and self.stride_w == self.tile_size_w) @@ -201,7 +201,15 @@ def __init__( self.num_patches_w: int @staticmethod - def __validate_size_type(parameter: int | Sequence) -> tuple[int, ...]: + def validate_size_type(parameter: int | Sequence) -> tuple[int, ...]: + """Validate size type and return tuple of form [tile_h, tile_w]. + + Args: + parameter (int | Sequence): input tile size parameter. + + Returns: + tuple[int, ...]: Validated tile size in tuple form. + """ if isinstance(parameter, int): output = (parameter, parameter) elif isinstance(parameter, Sequence): diff --git a/src/anomalib/data/validators/numpy/depth.py b/src/anomalib/data/validators/numpy/depth.py index d43c1e1750..89d7726182 100644 --- a/src/anomalib/data/validators/numpy/depth.py +++ b/src/anomalib/data/validators/numpy/depth.py @@ -82,6 +82,11 @@ def validate_depth_path(depth_path: str | None) -> str | None: """Validate the depth path.""" return validate_path(depth_path) if depth_path else None + @staticmethod + def validate_explanation(explanation: str | None) -> str | None: + """Validate the explanation.""" + return NumpyImageValidator.validate_explanation(explanation) + class NumpyDepthBatchValidator: """Validate numpy.ndarray data for batches of depth images.""" @@ -156,3 +161,8 @@ def validate_depth_path(depth_path: list[str] | None) -> list[str] | None: msg = f"Depth path must be a list of strings, got {type(depth_path)}." raise TypeError(msg) return [validate_path(path) for path in depth_path] + + @staticmethod + def validate_explanation(explanation: list[str] | None) -> list[str] | None: + """Validate the explanations for a batch.""" + return NumpyImageBatchValidator.validate_explanation(explanation) diff --git a/src/anomalib/data/validators/numpy/image.py b/src/anomalib/data/validators/numpy/image.py index b560bd5f20..455ecde2b0 100644 --- a/src/anomalib/data/validators/numpy/image.py +++ b/src/anomalib/data/validators/numpy/image.py @@ -315,6 +315,30 @@ def validate_pred_label(pred_label: np.ndarray | None) -> np.ndarray | None: raise ValueError(msg) return pred_label.astype(bool) + @staticmethod + def validate_explanation(explanation: str | None) -> str | None: + """Validate the explanation. + + Args: + explanation (str | None): Input explanation. + + Returns: + str | None: Validated explanation, or None. + + Examples: + >>> from anomalib.dataclasses.validators import ImageValidator + >>> explanation = "The image has a crack on the wall." + >>> validated_explanation = ImageValidator.validate_explanation(explanation) + >>> validated_explanation == explanation + True + """ + if explanation is None: + return None + if not isinstance(explanation, str): + msg = f"Explanation must be a string, got {type(explanation)}." + raise TypeError(msg) + return explanation + class NumpyImageBatchValidator: """Validate numpy.ndarray data for batches of images.""" @@ -677,3 +701,30 @@ def validate_image_path(image_path: list[str] | None) -> list[str] | None: msg = f"Image path must be a list of strings, got {type(image_path)}." raise TypeError(msg) return [str(path) for path in image_path] + + @staticmethod + def validate_explanation(explanation: list[str] | None) -> list[str] | None: + """Validate the explanations for a batch. + + Args: + explanation (list[str] | None): Input list of explanations. + + Returns: + list[str] | None: Validated list of explanations, or None. + + Raises: + TypeError: If the input is not a list of strings. + + Examples: + >>> from anomalib.data.validators.torch.image import ImageBatchValidator + >>> explanations = ["The image has a crack on the wall.", "The image has a dent on the car."] + >>> validated_explanations = ImageBatchValidator.validate_explanation(explanations) + >>> print(validated_explanations) + ['The image has a crack on the wall.', 'The image has a dent on the car.'] + """ + if explanation is None: + return None + if not isinstance(explanation, list): + msg = f"Explanation must be a list of strings, got {type(explanation)}." + raise TypeError(msg) + return [str(exp) for exp in explanation] diff --git a/src/anomalib/data/validators/numpy/video.py b/src/anomalib/data/validators/numpy/video.py index a75f17d546..e12682881b 100644 --- a/src/anomalib/data/validators/numpy/video.py +++ b/src/anomalib/data/validators/numpy/video.py @@ -6,6 +6,7 @@ from collections.abc import Sequence import numpy as np +from anomalib.data.validators.numpy.image import NumpyImageBatchValidator, NumpyImageValidator from anomalib.data.validators.path import validate_batch_path, validate_path @@ -350,6 +351,11 @@ def validate_target_frame(target_frame: int | None) -> int | None: raise ValueError(msg) return target_frame + @staticmethod + def validate_explanation(explanation: str | None) -> str | None: + """Validate the explanation string.""" + return NumpyImageValidator.validate_explanation(explanation) + class NumpyVideoBatchValidator: """Validate numpy.ndarray data for batches of videos.""" @@ -692,3 +698,8 @@ def validate_target_frame(target_frame: np.ndarray | None) -> np.ndarray | None: msg = "Target frame indices must be non-negative." raise ValueError(msg) return target_frame + + @staticmethod + def validate_explanation(explanation: list[str] | None) -> list[str] | None: + """Validate the explanation string.""" + return NumpyImageBatchValidator.validate_explanation(explanation) diff --git a/src/anomalib/data/validators/torch/depth.py b/src/anomalib/data/validators/torch/depth.py index a91e3f69ee..6869769ad6 100644 --- a/src/anomalib/data/validators/torch/depth.py +++ b/src/anomalib/data/validators/torch/depth.py @@ -228,6 +228,11 @@ def validate_mask_path(mask_path: str | None) -> str | None: """Validate the mask path.""" return ImageValidator.validate_mask_path(mask_path) + @staticmethod + def validate_explanation(explanation: str | None) -> str | None: + """Validate the explanation.""" + return ImageValidator.validate_explanation(explanation) + class DepthBatchValidator: """Validate torch.Tensor data for batches of depth images.""" @@ -441,3 +446,8 @@ def validate_pred_mask(pred_mask: torch.Tensor | None) -> Mask | None: def validate_pred_label(pred_label: torch.Tensor | None) -> torch.Tensor | None: """Validate the prediction label for a batch.""" return ImageBatchValidator.validate_pred_label(pred_label) + + @staticmethod + def validate_explanation(explanation: list[str] | None) -> list[str] | None: + """Validate the explanations for a batch.""" + return ImageBatchValidator.validate_explanation(explanation) diff --git a/src/anomalib/data/validators/torch/image.py b/src/anomalib/data/validators/torch/image.py index f001180a1d..c9a8ac07cb 100644 --- a/src/anomalib/data/validators/torch/image.py +++ b/src/anomalib/data/validators/torch/image.py @@ -309,6 +309,30 @@ def validate_pred_label(pred_label: torch.Tensor | np.ndarray | float | None) -> raise ValueError(msg) return pred_label.to(torch.bool) + @staticmethod + def validate_explanation(explanation: str | None) -> str | None: + """Validate the explanation. + + Args: + explanation (str | None): Input explanation. + + Returns: + str | None: Validated explanation, or None. + + Examples: + >>> from anomalib.dataclasses.validators import ImageValidator + >>> explanation = "The image has a crack on the wall." + >>> validated_explanation = ImageValidator.validate_explanation(explanation) + >>> validated_explanation == explanation + True + """ + if explanation is None: + return None + if not isinstance(explanation, str): + msg = f"Explanation must be a string, got {type(explanation)}." + raise TypeError(msg) + return explanation + class ImageBatchValidator: """Validate torch.Tensor data for batches of images.""" @@ -623,3 +647,30 @@ def validate_image_path(image_path: list[str] | None) -> list[str] | None: msg = f"Image path must be a list of strings, got {type(image_path)}." raise TypeError(msg) return [str(path) for path in image_path] + + @staticmethod + def validate_explanation(explanation: list[str] | None) -> list[str] | None: + """Validate the explanations for a batch. + + Args: + explanation (list[str] | None): Input list of explanations. + + Returns: + list[str] | None: Validated list of explanations, or None. + + Raises: + TypeError: If the input is not a list of strings. + + Examples: + >>> from anomalib.data.validators.torch.image import ImageBatchValidator + >>> explanations = ["The image has a crack on the wall.", "The image has a dent on the car."] + >>> validated_explanations = ImageBatchValidator.validate_explanation(explanations) + >>> print(validated_explanations) + ['The image has a crack on the wall.', 'The image has a dent on the car.'] + """ + if explanation is None: + return None + if not isinstance(explanation, list): + msg = f"Explanation must be a list of strings, got {type(explanation)}." + raise TypeError(msg) + return [str(exp) for exp in explanation] diff --git a/src/anomalib/data/validators/torch/video.py b/src/anomalib/data/validators/torch/video.py index 0719eb46f2..b7ca50c943 100644 --- a/src/anomalib/data/validators/torch/video.py +++ b/src/anomalib/data/validators/torch/video.py @@ -8,6 +8,7 @@ import torch from anomalib.data.validators.path import validate_batch_path, validate_path +from anomalib.data.validators.torch.image import ImageBatchValidator, ImageValidator class VideoValidator: @@ -487,6 +488,11 @@ def validate_last_frame(last_frame: torch.Tensor | int | float | None) -> torch. msg = f"Last frame must be an int, float, or a torch.Tensor, got {type(last_frame)}." raise TypeError(msg) + @staticmethod + def validate_explanation(explanation: str | None) -> str | None: + """Validate the explanation string.""" + return ImageValidator.validate_explanation(explanation) + class VideoBatchValidator: """Validate torch.Tensor data for video batches.""" @@ -935,3 +941,8 @@ def validate_last_frame(last_frame: torch.Tensor | None) -> torch.Tensor | None: msg = "Last frame indices must be non-negative." raise ValueError(msg) return last_frame + + @staticmethod + def validate_explanation(explanation: list[str] | None) -> list[str] | None: + """Validate the explanation string.""" + return ImageBatchValidator.validate_explanation(explanation) diff --git a/src/anomalib/deploy/inferencers/openvino_inferencer.py b/src/anomalib/deploy/inferencers/openvino_inferencer.py index 08ce792042..61b4a3d0ee 100644 --- a/src/anomalib/deploy/inferencers/openvino_inferencer.py +++ b/src/anomalib/deploy/inferencers/openvino_inferencer.py @@ -4,11 +4,11 @@ # SPDX-License-Identifier: Apache-2.0 import logging -from importlib.util import find_spec from pathlib import Path -from typing import TYPE_CHECKING, Any +from typing import Any import numpy as np +from lightning_utilities.core.imports import module_available from openvino.runtime.utils.data_helpers.wrappers import OVDict from anomalib.data import NumpyImageBatch @@ -17,15 +17,6 @@ logger = logging.getLogger("anomalib") -if find_spec("openvino") is not None: - import openvino as ov - - if TYPE_CHECKING: - from openvino import CompiledModel -else: - logger.warning("OpenVINO is not installed. Please install OpenVINO to use OpenVINOInferencer.") - - class OpenVINOInferencer: """OpenVINO implementation for the inference. @@ -96,12 +87,16 @@ def __init__( device: str | None = "AUTO", config: dict | None = None, ) -> None: + if not module_available("openvino"): + msg = "OpenVINO is not installed. Please install OpenVINO to use OpenVINOInferencer." + raise ImportError(msg) + self.device = device self.config = config self.input_blob, self.output_blob, self.model = self.load_model(path) - def load_model(self, path: str | Path | tuple[bytes, bytes]) -> tuple[Any, Any, "CompiledModel"]: + def load_model(self, path: str | Path | tuple[bytes, bytes]) -> tuple[Any, Any, Any]: """Load the OpenVINO model. Args: @@ -112,6 +107,8 @@ def load_model(self, path: str | Path | tuple[bytes, bytes]) -> tuple[Any, Any, [tuple[str, str, ExecutableNetwork]]: Input and Output blob names together with the Executable network. """ + import openvino as ov + core = ov.Core() # If tuple of bytes is passed if isinstance(path, tuple): diff --git a/src/anomalib/loggers/wandb.py b/src/anomalib/loggers/wandb.py index 55e65e6d54..ff41a0949e 100644 --- a/src/anomalib/loggers/wandb.py +++ b/src/anomalib/loggers/wandb.py @@ -9,12 +9,12 @@ from lightning.fabric.utilities.types import _PATH from lightning.pytorch.loggers.wandb import WandbLogger from lightning.pytorch.utilities import rank_zero_only -from lightning_utilities.core.imports import package_available +from lightning_utilities.core.imports import module_available from matplotlib.figure import Figure from .base import ImageLoggerBase -if package_available("wandb"): +if module_available("wandb"): import wandb if TYPE_CHECKING: diff --git a/src/anomalib/metrics/pimo/dataclasses.py b/src/anomalib/metrics/pimo/dataclasses.py index 0c5aeb025d..3eaa04cd12 100644 --- a/src/anomalib/metrics/pimo/dataclasses.py +++ b/src/anomalib/metrics/pimo/dataclasses.py @@ -120,7 +120,7 @@ class AUPIMOResult: # metadata fpr_lower_bound: float fpr_upper_bound: float - num_thresholds: int + num_thresholds: int | None # data thresh_lower_bound: float = field(repr=False) @@ -169,7 +169,8 @@ def __post_init__(self) -> None: try: _validate.is_rate_range((self.fpr_lower_bound, self.fpr_upper_bound)) # TODO(jpcbertoldo): warn when it's too low (use parameters from the numpy code) # noqa: TD003 - _validate.is_num_thresholds_gte2(self.num_thresholds) + if self.num_thresholds is not None: + _validate.is_num_thresholds_gte2(self.num_thresholds) _validate.is_rates(self.aupimos, nan_allowed=True) # validate is_aupimos _validate.validate_threshold_bounds((self.thresh_lower_bound, self.thresh_upper_bound)) @@ -194,7 +195,6 @@ def from_pimo_result( num_thresholds_auc: number of thresholds used to effectively compute AUPIMO; NOT the number of thresholds used to compute the PIMO curve! aupimos: AUPIMO scores - paths: paths to the source images to which the AUPIMO scores correspond. """ if pimo_result.per_image_tprs.shape[0] != aupimos.shape[0]: msg = ( diff --git a/src/anomalib/models/__init__.py b/src/anomalib/models/__init__.py index b4bb36a875..3b32c83367 100644 --- a/src/anomalib/models/__init__.py +++ b/src/anomalib/models/__init__.py @@ -30,6 +30,7 @@ Rkde, Stfpm, Uflow, + VlmAd, WinClip, ) from .video import AiVad @@ -57,8 +58,9 @@ class UnknownModelError(ModuleNotFoundError): "Rkde", "Stfpm", "Uflow", - "AiVad", + "VlmAd", "WinClip", + "AiVad", ] logger = logging.getLogger(__name__) diff --git a/src/anomalib/models/components/base/anomaly_module.py b/src/anomalib/models/components/base/anomaly_module.py index ff12db0cec..336877f17a 100644 --- a/src/anomalib/models/components/base/anomaly_module.py +++ b/src/anomalib/models/components/base/anomaly_module.py @@ -213,7 +213,7 @@ def configure_pre_processor(cls, image_size: tuple[int, int] | None = None) -> P ]), ) - def default_post_processor(self) -> PostProcessor: + def default_post_processor(self) -> PostProcessor | None: """Default post processor. Override in subclass for model-specific post-processing behaviour. diff --git a/src/anomalib/models/components/base/export_mixin.py b/src/anomalib/models/components/base/export_mixin.py index a0f84d1510..0e455332bd 100644 --- a/src/anomalib/models/components/base/export_mixin.py +++ b/src/anomalib/models/components/base/export_mixin.py @@ -11,7 +11,7 @@ import torch from lightning.pytorch import LightningModule -from lightning_utilities.core.imports import package_available +from lightning_utilities.core.imports import module_available from torch import nn from torchmetrics import Metric @@ -234,7 +234,7 @@ def to_openvino( ... task="segmentation", ... ) """ - if not package_available("openvino"): + if not module_available("openvino"): logger.exception("Could not find OpenVINO. Please check OpenVINO installation.") raise ModuleNotFoundError @@ -282,7 +282,7 @@ def _compress_ov_model( Returns: model (CompiledModel): Model in the OpenVINO format compressed with NNCF quantization. """ - if not package_available("nncf"): + if not module_available("nncf"): logger.exception("Could not find NCCF. Please check NNCF installation.") raise ModuleNotFoundError diff --git a/src/anomalib/models/image/__init__.py b/src/anomalib/models/image/__init__.py index f3a5435038..b09da8b07b 100644 --- a/src/anomalib/models/image/__init__.py +++ b/src/anomalib/models/image/__init__.py @@ -20,6 +20,7 @@ from .rkde import Rkde from .stfpm import Stfpm from .uflow import Uflow +from .vlm_ad import VlmAd from .winclip import WinClip __all__ = [ @@ -40,5 +41,6 @@ "Rkde", "Stfpm", "Uflow", + "VlmAd", "WinClip", ] diff --git a/src/anomalib/models/image/dsr/lightning_model.py b/src/anomalib/models/image/dsr/lightning_model.py index a4ed2df231..a5a6071868 100644 --- a/src/anomalib/models/image/dsr/lightning_model.py +++ b/src/anomalib/models/image/dsr/lightning_model.py @@ -208,4 +208,8 @@ def learning_type(self) -> LearningType: def configure_transforms(image_size: tuple[int, int] | None = None) -> Transform: """Default transform for DSR. Normalization is not needed as the images are scaled to [0, 1] in Dataset.""" image_size = image_size or (256, 256) - return Compose([Resize(image_size, antialias=True)]) + return Compose( + [ + Resize(image_size, antialias=True), + ], + ) diff --git a/src/anomalib/models/image/vlm_ad/__init__.py b/src/anomalib/models/image/vlm_ad/__init__.py new file mode 100644 index 0000000000..46ab8e0fee --- /dev/null +++ b/src/anomalib/models/image/vlm_ad/__init__.py @@ -0,0 +1,8 @@ +"""Visual Anomaly Model.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from .lightning_model import VlmAd + +__all__ = ["VlmAd"] diff --git a/src/anomalib/models/image/vlm_ad/backends/__init__.py b/src/anomalib/models/image/vlm_ad/backends/__init__.py new file mode 100644 index 0000000000..44009f8f83 --- /dev/null +++ b/src/anomalib/models/image/vlm_ad/backends/__init__.py @@ -0,0 +1,11 @@ +"""VLM backends.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from .base import Backend +from .chat_gpt import ChatGPT +from .huggingface import Huggingface +from .ollama import Ollama + +__all__ = ["Backend", "ChatGPT", "Huggingface", "Ollama"] diff --git a/src/anomalib/models/image/vlm_ad/backends/base.py b/src/anomalib/models/image/vlm_ad/backends/base.py new file mode 100644 index 0000000000..b4aadf9a22 --- /dev/null +++ b/src/anomalib/models/image/vlm_ad/backends/base.py @@ -0,0 +1,30 @@ +"""Base backend.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from abc import ABC, abstractmethod +from pathlib import Path + +from anomalib.models.image.vlm_ad.utils import Prompt + + +class Backend(ABC): + """Base backend.""" + + @abstractmethod + def __init__(self, model_name: str) -> None: + """Initialize the backend.""" + + @abstractmethod + def add_reference_images(self, image: str | Path) -> None: + """Add reference images for k-shot.""" + + @abstractmethod + def predict(self, image: str | Path, prompt: Prompt) -> str: + """Predict the anomaly label.""" + + @property + @abstractmethod + def num_reference_images(self) -> int: + """Get the number of reference images.""" diff --git a/src/anomalib/models/image/vlm_ad/backends/chat_gpt.py b/src/anomalib/models/image/vlm_ad/backends/chat_gpt.py new file mode 100644 index 0000000000..53648e688a --- /dev/null +++ b/src/anomalib/models/image/vlm_ad/backends/chat_gpt.py @@ -0,0 +1,109 @@ +"""ChatGPT backend.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import base64 +import logging +import os +from pathlib import Path +from typing import TYPE_CHECKING + +from dotenv import load_dotenv +from lightning_utilities.core.imports import module_available + +from anomalib.models.image.vlm_ad.utils import Prompt + +from .base import Backend + +if module_available("openai"): + from openai import OpenAI +else: + OpenAI = None + +if TYPE_CHECKING: + from openai.types.chat import ChatCompletion + +logger = logging.getLogger(__name__) + + +class ChatGPT(Backend): + """ChatGPT backend.""" + + def __init__(self, model_name: str, api_key: str | None = None) -> None: + """Initialize the ChatGPT backend.""" + self._ref_images_encoded: list[str] = [] + self.model_name: str = model_name + self._client: OpenAI | None = None + self.api_key = self._get_api_key(api_key) + + @property + def client(self) -> OpenAI: + """Get the OpenAI client.""" + if OpenAI is None: + msg = "OpenAI is not installed. Please install it to use ChatGPT backend." + raise ImportError(msg) + if self._client is None: + self._client = OpenAI(api_key=self.api_key) + return self._client + + def add_reference_images(self, image: str | Path) -> None: + """Add reference images for k-shot.""" + self._ref_images_encoded.append(self._encode_image_to_url(image)) + + @property + def num_reference_images(self) -> int: + """Get the number of reference images.""" + return len(self._ref_images_encoded) + + def predict(self, image: str | Path, prompt: Prompt) -> str: + """Predict the anomaly label.""" + image_encoded = self._encode_image_to_url(image) + messages = [] + + # few-shot + if len(self._ref_images_encoded) > 0: + messages.append(self._generate_message(content=prompt.few_shot, images=self._ref_images_encoded)) + + messages.append(self._generate_message(content=prompt.predict, images=[image_encoded])) + + response: ChatCompletion = self.client.chat.completions.create(messages=messages, model=self.model_name) + return response.choices[0].message.content + + @staticmethod + def _generate_message(content: str, images: list[str] | None) -> dict: + """Generate a message.""" + message: dict[str, list[dict] | str] = {"role": "user"} + if images is not None: + _content: list[dict[str, str | dict]] = [{"type": "text", "text": content}] + _content.extend([{"type": "image_url", "image_url": {"url": image}} for image in images]) + message["content"] = _content + else: + message["content"] = content + return message + + def _encode_image_to_url(self, image: str | Path) -> str: + """Encode the image to base64 and embed in url string.""" + image_path = Path(image) + extension = image_path.suffix + base64_encoded = self._encode_image_to_base_64(image_path) + return f"data:image/{extension};base64,{base64_encoded}" + + @staticmethod + def _encode_image_to_base_64(image: str | Path) -> str: + """Encode the image to base64.""" + image = Path(image) + return base64.b64encode(image.read_bytes()).decode("utf-8") + + def _get_api_key(self, api_key: str | None = None) -> str: + if api_key is None: + load_dotenv() + api_key = os.getenv("OPENAI_API_KEY") + if api_key is None: + msg = ( + f"OpenAI API key must be provided to use {self.model_name}." + " Please provide the API key in the constructor, or set the OPENAI_API_KEY environment variable" + " or in a `.env` file." + ) + raise ValueError(msg) + return api_key diff --git a/src/anomalib/models/image/vlm_ad/backends/huggingface.py b/src/anomalib/models/image/vlm_ad/backends/huggingface.py new file mode 100644 index 0000000000..e8d3c1e84b --- /dev/null +++ b/src/anomalib/models/image/vlm_ad/backends/huggingface.py @@ -0,0 +1,98 @@ +"""Huggingface backend.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import logging +from pathlib import Path +from typing import TYPE_CHECKING + +from lightning_utilities.core.imports import module_available +from PIL import Image + +from anomalib.models.image.vlm_ad.utils import Prompt + +from .base import Backend + +if TYPE_CHECKING: + from transformers.modeling_utils import PreTrainedModel + from transformers.processing_utils import ProcessorMixin + +if module_available("transformers"): + import transformers +else: + transformers = None + + +logger = logging.getLogger(__name__) + + +class Huggingface(Backend): + """Huggingface backend.""" + + def __init__( + self, + model_name: str, + ) -> None: + """Initialize the Huggingface backend.""" + self.model_name: str = model_name + self._ref_images: list[str] = [] + self._processor: ProcessorMixin | None = None + self._model: PreTrainedModel | None = None + + @property + def processor(self) -> "ProcessorMixin": + """Get the Huggingface processor.""" + if self._processor is None: + if transformers is None: + msg = "transformers is not installed." + raise ValueError(msg) + self._processor = transformers.LlavaNextProcessor.from_pretrained(self.model_name) + return self._processor + + @property + def model(self) -> "PreTrainedModel": + """Get the Huggingface model.""" + if self._model is None: + if transformers is None: + msg = "transformers is not installed." + raise ValueError(msg) + self._model = transformers.LlavaNextForConditionalGeneration.from_pretrained(self.model_name) + return self._model + + @staticmethod + def _generate_message(content: str, images: list[str] | None) -> dict: + """Generate a message.""" + message: dict[str, str | list[dict]] = {"role": "user"} + _content: list[dict[str, str]] = [{"type": "text", "text": content}] + if images is not None: + _content.extend([{"type": "image"} for _ in images]) + message["content"] = _content + return message + + def add_reference_images(self, image: str | Path) -> None: + """Add reference images for k-shot.""" + self._ref_images.append(Image.open(image)) + + @property + def num_reference_images(self) -> int: + """Get the number of reference images.""" + return len(self._ref_images) + + def predict(self, image_path: str | Path, prompt: Prompt) -> str: + """Predict the anomaly label.""" + image = Image.open(image_path) + messages: list[dict] = [] + + if len(self._ref_images) > 0: + messages.append(self._generate_message(content=prompt.few_shot, images=self._ref_images)) + + messages.append(self._generate_message(content=prompt.predict, images=[image])) + processed_prompt = [self.processor.apply_chat_template(messages, add_generation_prompt=True)] + + images = [*self._ref_images, image] + inputs = self.processor(images, processed_prompt, return_tensors="pt", padding=True).to(self.model.device) + outputs = self.model.generate(**inputs, max_new_tokens=100) + result = self.processor.decode(outputs[0], skip_special_tokens=True) + print(result) + return result diff --git a/src/anomalib/models/image/vlm_ad/backends/ollama.py b/src/anomalib/models/image/vlm_ad/backends/ollama.py new file mode 100644 index 0000000000..ff680bee3b --- /dev/null +++ b/src/anomalib/models/image/vlm_ad/backends/ollama.py @@ -0,0 +1,73 @@ +"""Ollama backend. + +Assumes that the Ollama service is running in the background. +See: https://github.com/ollama/ollama +Ensure that ollama is running. On linux: `ollama serve` +On Mac and Windows ensure that the ollama service is running by launching from the application list. +""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import logging +from pathlib import Path + +from lightning_utilities.core.imports import module_available + +from anomalib.models.image.vlm_ad.utils import Prompt + +from .base import Backend + +if module_available("ollama"): + from ollama import chat + from ollama._client import _encode_image +else: + chat = None + +logger = logging.getLogger(__name__) + + +class Ollama(Backend): + """Ollama backend.""" + + def __init__(self, model_name: str) -> None: + """Initialize the Ollama backend.""" + self.model_name: str = model_name + self._ref_images_encoded: list[str] = [] + + def add_reference_images(self, image: str | Path) -> None: + """Encode the image to base64.""" + self._ref_images_encoded.append(_encode_image(image)) + + @property + def num_reference_images(self) -> int: + """Get the number of reference images.""" + return len(self._ref_images_encoded) + + @staticmethod + def _generate_message(content: str, images: list[str] | None) -> dict: + """Generate a message.""" + message: dict[str, str | list[str]] = {"role": "user", "content": content} + if images: + message["images"] = images + return message + + def predict(self, image: str | Path, prompt: Prompt) -> str: + """Predict the anomaly label.""" + if not chat: + msg = "Ollama is not installed. Please install it using `pip install ollama`." + raise ImportError(msg) + image_encoded = _encode_image(image) + messages = [] + + # few-shot + if len(self._ref_images_encoded) > 0: + messages.append(self._generate_message(content=prompt.few_shot, images=self._ref_images_encoded)) + + messages.append(self._generate_message(content=prompt.predict, images=[image_encoded])) + + response = chat( + model=self.model_name, + messages=messages, + ) + return response["message"]["content"].strip() diff --git a/src/anomalib/models/image/vlm_ad/lightning_model.py b/src/anomalib/models/image/vlm_ad/lightning_model.py new file mode 100644 index 0000000000..0c072f1330 --- /dev/null +++ b/src/anomalib/models/image/vlm_ad/lightning_model.py @@ -0,0 +1,132 @@ +"""Visual Anomaly Model for Zero/Few-Shot Anomaly Classification.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +import logging + +import torch +from torch.utils.data import DataLoader + +from anomalib import LearningType +from anomalib.data import ImageBatch +from anomalib.metrics import Evaluator, F1Score +from anomalib.models import AnomalyModule +from anomalib.post_processing import PostProcessor + +from .backends import Backend, ChatGPT, Huggingface, Ollama +from .utils import ModelName, Prompt + +logger = logging.getLogger(__name__) + + +class VlmAd(AnomalyModule): + """Visual anomaly model.""" + + def __init__( + self, + model: ModelName | str = ModelName.LLAMA_OLLAMA, + api_key: str | None = None, + k_shot: int = 0, + ) -> None: + super().__init__() + self.k_shot = k_shot + model = ModelName(model) + self.vlm_backend: Backend = self._setup_vlm_backend(model, api_key) + + @staticmethod + def _setup_vlm_backend(model_name: ModelName, api_key: str | None) -> Backend: + if model_name == ModelName.LLAMA_OLLAMA: + return Ollama(model_name=model_name.value) + if model_name == ModelName.GPT_4O_MINI: + return ChatGPT(api_key=api_key, model_name=model_name.value) + if model_name in {ModelName.VICUNA_7B_HF, ModelName.VICUNA_13B_HF, ModelName.MISTRAL_7B_HF}: + return Huggingface(model_name=model_name.value) + + msg = f"Unsupported VLM model: {model_name}" + raise ValueError(msg) + + def _setup(self) -> None: + if self.k_shot > 0 and self.vlm_backend.num_reference_images != self.k_shot: + logger.info("Collecting reference images from training dataset.") + dataloader = self.trainer.datamodule.train_dataloader() + self.collect_reference_images(dataloader) + + def collect_reference_images(self, dataloader: DataLoader) -> None: + """Collect reference images for few-shot inference.""" + for batch in dataloader: + for img_path in batch.image_path: + self.vlm_backend.add_reference_images(img_path) + if self.vlm_backend.num_reference_images == self.k_shot: + return + + @property + def prompt(self) -> Prompt: + """Get the prompt.""" + return Prompt( + predict=( + "You are given an image. It is either normal or anomalous." + " First say 'YES' if the image is anomalous, or 'NO' if it is normal.\n" + "Then give the reason for your decision.\n" + "For example, 'YES: The image has a crack on the wall.'" + ), + few_shot=( + "These are a few examples of normal picture without any anomalies." + " You have to use these to determine if the image I provide in the next" + " chat is normal or anomalous." + ), + ) + + def validation_step(self, batch: ImageBatch, *args, **kwargs) -> ImageBatch: + """Validation step.""" + del args, kwargs # These variables are not used. + assert batch.image_path is not None + responses = [(self.vlm_backend.predict(img_path, self.prompt)) for img_path in batch.image_path] + batch.explanation = responses + batch.pred_label = torch.tensor([1.0 if r.startswith("Y") else 0.0 for r in responses], device=self.device) + return batch + + @property + def learning_type(self) -> LearningType: + """The learning type of the model.""" + return LearningType.ZERO_SHOT if self.k_shot == 0 else LearningType.FEW_SHOT + + @property + def trainer_arguments(self) -> dict[str, int | float]: + """Doesn't need training.""" + return {} + + @staticmethod + def configure_transforms(image_size: tuple[int, int] | None = None) -> None: + """This modes does not require any transforms.""" + if image_size is not None: + logger.warning("Ignoring image_size argument as each backend has its own transforms.") + + def default_post_processor(self) -> PostProcessor | None: # noqa: PLR6301 + """Post processing is not required for this model.""" + return None + + @staticmethod + def configure_evaluator() -> Evaluator: + """Default evaluator. + + Override in subclass for model-specific evaluator behaviour. + """ + image_f1score = F1Score(fields=["pred_label", "gt_label"], prefix="image_") + return Evaluator(test_metrics=image_f1score) + + @staticmethod + def _export_not_supported_message() -> None: + logging.warning("Exporting the model is not supported for VLM-AD model. Skipping...") + + def to_torch(self, *_, **__) -> None: # type: ignore[override] + """Skip export to torch.""" + return self._export_not_supported_message() + + def to_onnx(self, *_, **__) -> None: # type: ignore[override] + """Skip export to onnx.""" + return self._export_not_supported_message() + + def to_openvino(self, *_, **__) -> None: # type: ignore[override] + """Skip export to openvino.""" + return self._export_not_supported_message() diff --git a/src/anomalib/models/image/vlm_ad/utils.py b/src/anomalib/models/image/vlm_ad/utils.py new file mode 100644 index 0000000000..ce9b9067ac --- /dev/null +++ b/src/anomalib/models/image/vlm_ad/utils.py @@ -0,0 +1,25 @@ +"""Dataclasses.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from dataclasses import dataclass +from enum import Enum + + +@dataclass +class Prompt: + """Prompt.""" + + few_shot: str + predict: str + + +class ModelName(Enum): + """List of supported models.""" + + LLAMA_OLLAMA = "llava" + GPT_4O_MINI = "gpt-4o-mini" + VICUNA_7B_HF = "llava-hf/llava-v1.6-vicuna-7b-hf" + VICUNA_13B_HF = "llava-hf/llava-v1.6-vicuna-13b-hf" + MISTRAL_7B_HF = "llava-hf/llava-v1.6-mistral-7b-hf" diff --git a/src/anomalib/pipelines/benchmark/generator.py b/src/anomalib/pipelines/benchmark/generator.py index 922dfa06cb..988e0111b7 100644 --- a/src/anomalib/pipelines/benchmark/generator.py +++ b/src/anomalib/pipelines/benchmark/generator.py @@ -10,6 +10,7 @@ from anomalib.pipelines.components import JobGenerator from anomalib.pipelines.components.utils import get_iterator_from_grid_dict from anomalib.pipelines.types import PREV_STAGE_RESULT +from anomalib.utils.config import flatten_dict from anomalib.utils.logging import hide_output from .job import BenchmarkJob @@ -39,9 +40,12 @@ def generate_jobs( """Return iterator based on the arguments.""" del previous_stage_result # Not needed for this job for _container in get_iterator_from_grid_dict(args): + # Pass experimental configs as a flatten dictionary to the job runner. + flat_cfg = flatten_dict(_container) yield BenchmarkJob( accelerator=self.accelerator, seed=_container["seed"], model=get_model(_container["model"]), datamodule=get_datamodule(_container["data"]), + flat_cfg=flat_cfg, ) diff --git a/src/anomalib/pipelines/benchmark/job.py b/src/anomalib/pipelines/benchmark/job.py index ab443cfa8a..f56899ac5d 100644 --- a/src/anomalib/pipelines/benchmark/job.py +++ b/src/anomalib/pipelines/benchmark/job.py @@ -4,6 +4,7 @@ # SPDX-License-Identifier: Apache-2.0 import logging +import time from datetime import datetime from pathlib import Path from tempfile import TemporaryDirectory @@ -31,16 +32,25 @@ class BenchmarkJob(Job): model (AnomalyModule): The model to use. datamodule (AnomalibDataModule): The data module to use. seed (int): The seed to use. + flat_cfg (dict): The flat dictionary of configs with dotted keys. """ name = "benchmark" - def __init__(self, accelerator: str, model: AnomalyModule, datamodule: AnomalibDataModule, seed: int) -> None: + def __init__( + self, + accelerator: str, + model: AnomalyModule, + datamodule: AnomalibDataModule, + seed: int, + flat_cfg: dict, + ) -> None: super().__init__() self.accelerator = accelerator self.model = model self.datamodule = datamodule self.seed = seed + self.flat_cfg = flat_cfg @hide_output def run( @@ -48,6 +58,7 @@ def run( task_id: int | None = None, ) -> dict[str, Any]: """Run the benchmark.""" + job_start_time = time.time() devices: str | list[int] = "auto" if task_id is not None: devices = [task_id] @@ -59,16 +70,22 @@ def run( devices=devices, default_root_dir=temp_dir, ) + fit_start_time = time.time() engine.fit(self.model, self.datamodule) + test_start_time = time.time() test_results = engine.test(self.model, self.datamodule) + job_end_time = time.time() + durations = { + "job_duration": job_end_time - job_start_time, + "fit_duration": test_start_time - fit_start_time, + "test_duration": job_end_time - test_start_time, + } # TODO(ashwinvaidya17): Restore throughput # https://github.com/openvinotoolkit/anomalib/issues/2054 output = { - "seed": self.seed, "accelerator": self.accelerator, - "model": self.model.__class__.__name__, - "data": self.datamodule.__class__.__name__, - "category": self.datamodule.category, + **durations, + **self.flat_cfg, **test_results[0], } logger.info(f"Completed with result {output}") diff --git a/src/anomalib/pipelines/benchmark/pipeline.py b/src/anomalib/pipelines/benchmark/pipeline.py index 730b3ecccc..3b27caeec1 100644 --- a/src/anomalib/pipelines/benchmark/pipeline.py +++ b/src/anomalib/pipelines/benchmark/pipeline.py @@ -20,11 +20,12 @@ def _setup_runners(args: dict) -> list[Runner]: accelerators = args["accelerator"] if isinstance(args["accelerator"], list) else [args["accelerator"]] runners: list[Runner] = [] for accelerator in accelerators: - if accelerator == "cpu": - runners.append(SerialRunner(BenchmarkJobGenerator("cpu"))) - elif accelerator == "cuda": - runners.append(ParallelRunner(BenchmarkJobGenerator("cuda"), n_jobs=torch.cuda.device_count())) - else: + if accelerator not in {"cpu", "cuda"}: msg = f"Unsupported accelerator: {accelerator}" raise ValueError(msg) + device_count = torch.cuda.device_count() + if device_count <= 1 or accelerator == "cpu": + runners.append(SerialRunner(BenchmarkJobGenerator(accelerator))) + else: + runners.append(ParallelRunner(BenchmarkJobGenerator(accelerator), n_jobs=device_count)) return runners diff --git a/src/anomalib/utils/exceptions/imports.py b/src/anomalib/utils/exceptions/imports.py index dac22ba056..6ef8dbd89d 100644 --- a/src/anomalib/utils/exceptions/imports.py +++ b/src/anomalib/utils/exceptions/imports.py @@ -22,7 +22,7 @@ def try_import(import_path: str) -> bool: warnings.warn( "The 'try_import' function is deprecated and will be removed in v2.0.0. " - "Use 'package_available' from lightning-utilities instead.", + "Use 'module_available' from lightning-utilities instead.", DeprecationWarning, stacklevel=2, ) diff --git a/src/anomalib/utils/logging.py b/src/anomalib/utils/logging.py index 21f7994fbf..d73ef440c4 100644 --- a/src/anomalib/utils/logging.py +++ b/src/anomalib/utils/logging.py @@ -74,10 +74,8 @@ def redirect_logs(log_file: str) -> None: """ Path(log_file).parent.mkdir(exist_ok=True, parents=True) logger_file_handler = logging.FileHandler(log_file) - root_logger = logging.getLogger() - root_logger.setLevel(logging.DEBUG) format_string = "%(asctime)s - %(name)s - %(levelname)s - %(message)s" - logging.basicConfig(format=format_string, level=logging.DEBUG, handlers=[logger_file_handler]) + logging.basicConfig(format=format_string, handlers=[logger_file_handler]) logging.captureWarnings(capture=True) # remove other handlers from all loggers loggers = [logging.getLogger(name) for name in logging.root.manager.loggerDict] diff --git a/src/anomalib/utils/visualization/__init__.py b/src/anomalib/utils/visualization/__init__.py index f68036ed78..404036dfad 100644 --- a/src/anomalib/utils/visualization/__init__.py +++ b/src/anomalib/utils/visualization/__init__.py @@ -4,11 +4,13 @@ # SPDX-License-Identifier: Apache-2.0 from .base import BaseVisualizer, GeneratorResult, VisualizationStep +from .explanation import ExplanationVisualizer from .image import ImageResult, ImageVisualizer from .metrics import MetricsVisualizer __all__ = [ "BaseVisualizer", + "ExplanationVisualizer", "ImageResult", "ImageVisualizer", "GeneratorResult", diff --git a/src/anomalib/utils/visualization/explanation.py b/src/anomalib/utils/visualization/explanation.py new file mode 100644 index 0000000000..10904161e3 --- /dev/null +++ b/src/anomalib/utils/visualization/explanation.py @@ -0,0 +1,106 @@ +"""Explanation visualization generator. + +Note: This is a temporary visualizer, and will be replaced with the new visualizer in the future. +""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from collections.abc import Iterator +from pathlib import Path + +import numpy as np +from PIL import Image, ImageDraw, ImageFont + +from .base import BaseVisualizer, GeneratorResult, VisualizationStep + + +class ExplanationVisualizer(BaseVisualizer): + """Explanation visualization generator.""" + + def __init__(self) -> None: + super().__init__(visualize_on=VisualizationStep.BATCH) + self.padding = 3 + self.font = ImageFont.load_default(size=16) + + def generate(self, **kwargs) -> Iterator[GeneratorResult]: + """Generate images and return them as an iterator.""" + outputs = kwargs.get("outputs", None) + if outputs is None: + msg = "Outputs must be provided to generate images." + raise ValueError(msg) + return self._visualize_batch(outputs) + + def _visualize_batch(self, batch: dict) -> Iterator[GeneratorResult]: + """Visualize batch of images.""" + batch_size = batch["image"].shape[0] + height, width = batch["image"].shape[-2:] + for i in range(batch_size): + image = batch["image"][i] + explanation = batch["explanation"][i] + file_name = Path(batch["image_path"][i]) + image = Image.open(file_name) + image = image.resize((width, height)) + image = self._draw_image(width, height, image=image, explanation=explanation) + yield GeneratorResult(image=image, file_name=file_name) + + def _draw_image(self, width: int, height: int, image: Image, explanation: str) -> np.ndarray: + text_canvas: Image = self._get_explanation_image(width, height, image, explanation) + label_canvas: Image = self._get_label_image(explanation) + + final_width = max(text_canvas.size[0], width) + final_height = height + text_canvas.size[1] + combined_image = Image.new("RGB", (final_width, final_height), (255, 255, 255)) + combined_image.paste(image, (self.padding, 0)) + combined_image.paste(label_canvas, (10, 10)) + combined_image.paste(text_canvas, (0, height)) + return np.array(combined_image) + + def _get_label_image(self, explanation: str) -> Image: + # Draw label + # Can't use pred_labels as it is computed from the pred_scores using image_threshold. It gives incorrect value. + # So, using explanation. This will probably change with the new design. + label = "Anomalous" if explanation.startswith("Y") else "Normal" + label_color = "red" if label == "Anomalous" else "green" + label_canvas = Image.new("RGB", (100, 20), color=label_color) + draw = ImageDraw.Draw(label_canvas) + draw.text((0, 0), label, font=self.font, fill="white", align="center") + return label_canvas + + def _get_explanation_image(self, width: int, height: int, image: Image, explanation: str) -> Image: + # compute wrap width + text_canvas = Image.new("RGB", (width, height), color="white") + dummy_image = ImageDraw.Draw(image) + text_bbox = dummy_image.textbbox((0, 0), explanation, font=self.font, align="center") + text_canvas_width = text_bbox[2] - text_bbox[0] + self.padding + + # split lines based on the width + lines = list(explanation.split("\n")) + line_with_max_len = max(lines, key=len) + new_width = int(width * len(line_with_max_len) // text_canvas_width) + + # wrap text based on the new width + lines = [] + current_line: list[str] = [] + for word in explanation.split(" "): + test_line = " ".join([*current_line, word]) + if len(test_line) <= new_width: + current_line.append(word) + else: + lines.append(" ".join(current_line)) + current_line = [word] + lines.append(" ".join(current_line)) + wrapped_lines = "\n".join(lines) + + # recompute height + dummy_image = Image.new("RGB", (new_width, height), color="white") + draw = ImageDraw.Draw(dummy_image) + text_bbox = draw.textbbox((0, 0), wrapped_lines, font=self.font, align="center") + new_width = int(text_bbox[2] - text_bbox[0] + self.padding) + new_height = int(text_bbox[3] - text_bbox[1] + self.padding) + + # Final text image + text_canvas = Image.new("RGB", (new_width, new_height), color="white") + draw = ImageDraw.Draw(text_canvas) + draw.text((self.padding // 2, 0), wrapped_lines, font=self.font, fill="black", align="center") + return text_canvas diff --git a/tests/helpers/data.py b/tests/helpers/data.py index 0ad699fb2f..60433df9eb 100644 --- a/tests/helpers/data.py +++ b/tests/helpers/data.py @@ -5,6 +5,7 @@ from __future__ import annotations +import json import shutil from contextlib import ContextDecorator from pathlib import Path @@ -319,6 +320,43 @@ def __init__( self.min_size = min_size self.image_generator = DummyImageGenerator(image_shape=image_shape, rng=self.rng) + def _generate_dummy_datumaro_dataset(self) -> None: + """Generates dummy Datumaro dataset in a temporary directory.""" + # generate images + image_root = self.dataset_root / "images" / "default" + image_root.mkdir(parents=True, exist_ok=True) + + file_names: list[str] = [] + + # Create normal images + for i in range(self.num_train + self.num_test): + label = LabelName.NORMAL + image_filename = image_root / f"normal_{i:03}.png" + file_names.append(image_filename) + self.image_generator.generate_image(label, image_filename) + + # Create abnormal images + for i in range(self.num_test): + label = LabelName.ABNORMAL + image_filename = image_root / f"abnormal_{i:03}.png" + file_names.append(image_filename) + self.image_generator.generate_image(label, image_filename) + + # create annotation file + annotation_file = self.dataset_root / "annotations" / "default.json" + annotation_file.parent.mkdir(parents=True, exist_ok=True) + annotations = { + "categories": {"label": {"labels": [{"name": "Normal"}, {"name": "Anomalous"}]}}, + "items": [], + } + for file_name in file_names: + annotations["items"].append({ + "annotations": [{"label_id": 1 if "abnormal" in str(file_name) else 0}], + "image": {"path": file_name.name}, + }) + with annotation_file.open("w") as f: + json.dump(annotations, f) + def _generate_dummy_mvtec_dataset( self, normal_dir: str = "good", diff --git a/tests/integration/model/test_models.py b/tests/integration/model/test_models.py index 2ffd2188f4..9c9c203d45 100644 --- a/tests/integration/model/test_models.py +++ b/tests/integration/model/test_models.py @@ -7,6 +7,7 @@ # SPDX-License-Identifier: Apache-2.0 from pathlib import Path +from unittest.mock import MagicMock import pytest @@ -202,6 +203,11 @@ def _get_objects( ) model = get_model(model_name, **extra_args) + + if model_name == "vlm_ad": + model.vlm_backend = MagicMock() + model.vlm_backend.predict.return_value = "YES: Because reasons..." + engine = Engine( logger=False, default_root_dir=project_path, diff --git a/tests/unit/data/datamodule/image/test_datumaro.py b/tests/unit/data/datamodule/image/test_datumaro.py new file mode 100644 index 0000000000..789d4571c0 --- /dev/null +++ b/tests/unit/data/datamodule/image/test_datumaro.py @@ -0,0 +1,39 @@ +"""Unit tests - Datumaro Datamodule.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path + +import pytest + +from anomalib import TaskType +from anomalib.data import Datumaro +from tests.unit.data.datamodule.base.image import _TestAnomalibImageDatamodule + + +class TestDatumaro(_TestAnomalibImageDatamodule): + """Datumaro Datamodule Unit Tests.""" + + @pytest.fixture() + @staticmethod + def datamodule(dataset_path: Path, task_type: TaskType) -> Datumaro: + """Create and return a Datumaro datamodule.""" + if task_type != TaskType.CLASSIFICATION: + pytest.skip("Datumaro only supports classification tasks.") + + _datamodule = Datumaro( + root=dataset_path / "datumaro", + task=task_type, + train_batch_size=4, + eval_batch_size=4, + ) + _datamodule.setup() + + return _datamodule + + @pytest.fixture() + @staticmethod + def fxt_data_config_path() -> str: + """Return the path to the test data config.""" + return "configs/data/datumaro.yaml" diff --git a/tests/unit/pipelines/__init__.py b/tests/unit/pipelines/__init__.py new file mode 100644 index 0000000000..46de40af76 --- /dev/null +++ b/tests/unit/pipelines/__init__.py @@ -0,0 +1,4 @@ +"""Pipeline unit tests.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 diff --git a/tools/tiled_ensemble/ens_config.yaml b/tools/tiled_ensemble/ens_config.yaml new file mode 100644 index 0000000000..2490b22e9a --- /dev/null +++ b/tools/tiled_ensemble/ens_config.yaml @@ -0,0 +1,43 @@ +seed: 42 +accelerator: "gpu" +default_root_dir: "results" + +tiling: + tile_size: [128, 128] + stride: 128 + +normalization_stage: image # on what level we normalize, options: [tile, image, none] +thresholding: + method: F1AdaptiveThreshold # refer to documentation for thresholding methods + stage: image # stage at which we apply threshold, options: [tile, image] + +data: + class_path: anomalib.data.MVTec + init_args: + root: ./datasets/MVTec + category: bottle + train_batch_size: 32 + eval_batch_size: 32 + num_workers: 8 + task: segmentation + transform: null + train_transform: null + eval_transform: null + test_split_mode: from_dir + test_split_ratio: 0.2 + val_split_mode: same_as_test + val_split_ratio: 0.5 + image_size: [256, 256] + +SeamSmoothing: + apply: True # if this is applied, area around tile seams are is smoothed + sigma: 2 # sigma of gaussian filter used to smooth this area + width: 0.1 # width factor, multiplied by tile dimension gives the region width around seam which will be smoothed + +TrainModels: + model: + class_path: Padim + + metrics: + pixel: AUROC + image: AUROC diff --git a/tools/tiled_ensemble/eval.py b/tools/tiled_ensemble/eval.py new file mode 100644 index 0000000000..58be27c25c --- /dev/null +++ b/tools/tiled_ensemble/eval.py @@ -0,0 +1,28 @@ +"""Run tiled ensemble prediction.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from pathlib import Path + +from jsonargparse import ArgumentParser + +from anomalib.pipelines.tiled_ensemble import EvalTiledEnsemble + + +def get_parser() -> ArgumentParser: + """Create a new parser if none is provided.""" + parser = ArgumentParser() + parser.add_argument("--config", type=str | Path, help="Configuration file path.", required=True) + parser.add_argument("--root", type=str | Path, help="Weights file path.", required=True) + + return parser + + +if __name__ == "__main__": + args = get_parser().parse_args() + + print("Running tiled ensemble test pipeline.") + # pass the path to root dir with checkpoints + test_pipeline = EvalTiledEnsemble(args.root) + test_pipeline.run(args) diff --git a/tools/tiled_ensemble/train.py b/tools/tiled_ensemble/train.py new file mode 100644 index 0000000000..8aed47ea0d --- /dev/null +++ b/tools/tiled_ensemble/train.py @@ -0,0 +1,17 @@ +"""Run tiled ensemble training.""" + +# Copyright (C) 2024 Intel Corporation +# SPDX-License-Identifier: Apache-2.0 + +from anomalib.pipelines.tiled_ensemble import EvalTiledEnsemble, TrainTiledEnsemble + +if __name__ == "__main__": + print("Running tiled ensemble train pipeline") + train_pipeline = TrainTiledEnsemble() + # run training + train_pipeline.run() + + print("Running tiled ensemble test pipeline.") + # pass the root dir from train run to load checkpoints + test_pipeline = EvalTiledEnsemble(train_pipeline.root_dir) + test_pipeline.run()