Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Bug]: save_image fails to save images on Windows when trying to use Engine.predict() #1831

Closed
neonesis opened this issue Mar 8, 2024 · 4 comments

Comments

@neonesis
Copy link

neonesis commented Mar 8, 2024

Describe the bug

On Windows, when using a typical flow of engine.fit and then engine.predict (using hazelnut toy dataset), at some point anomalib.data.utils.save_image will be called with absolute filename and root folder. Then, save_image will attempt to remove the top-level directory, but it (for some reason) assumes Linux style directories (forward-slash for example).

Specifically, anomalib.data.utils.image.py line 444: file_path = Path("/".join(str(file_path).split("/")[2:])) - this basically does not work on Windows paths due to wrong string conversion (backslashes appear).

Then, since now the file path is incorrectly modified, it contains no image extension, and OpenCV cv2.imwrite call fails.

Dataset

Other (please specify in the text field below)

Model

PADiM

Steps to reproduce the behavior

pip install anomalib

Execute following script:

from anomalib.models import Padim
from anomalib.engine import Engine

# Import the datamodule
from anomalib.data import Folder
from anomalib.data.utils import TestSplitMode

if __name__ == '__main__':
    # Create the datamodule
    datamodule = Folder(
        name="hazelnut_toy",
        root="datasets/hazelnut_toy",
        normal_dir="good",
        test_split_mode=TestSplitMode.SYNTHETIC,
        task="classification",
    )

    # Setup the datamodule
    datamodule.setup()

    model = Padim()
    engine = Engine(task="classification")

    engine.fit(datamodule=datamodule, model=model)

    from anomalib.data import PredictDataset

    pset = PredictDataset(path="datasets/hazelnut_toy/crack")

    engine.predict(model=model, dataset=pset)

OS information

OS information:

  • OS: Windows 11
  • Python version:3.11.6
  • Anomalib version: 1.0.0
  • PyTorch version: 2.1.2
  • CUDA/cuDNN version: 12.1
  • GPU models and configuration: RTX A1000
  • Any other relevant information: Hazelnut dataset

Expected behavior

I expect to have the prediction images to be saved at some paths.
Instead at this part of the code:

def save_image(filename: Path | str, image: np.ndarray | Figure, root: Path | None = None) -> None:
    """Save an image to the file system.

    Args:
        filename (Path | str): Path or filename to which the image will be saved.
        image (np.ndarray | Figure): Image that will be saved to the file system.
        root (Path, optional): Root directory to save the image. If provided, the top level directory of an absolute
            filename will be overwritten. Defaults to None.
    """
    if isinstance(image, Figure):
        image = figure_to_array(image)

    file_path = Path(filename)
    # if file_path is absolute, then root is ignored
    # so we remove the top level directory from the path
    if file_path.is_absolute() and root:
        file_path = Path("/".join(str(file_path).split("/")[2:]))
    if root:
        file_path = root / file_path

    # Make unique file_path if file already exists
    file_path = duplicate_filename(file_path)

    file_path.parent.mkdir(parents=True, exist_ok=True)
    image = cv2.cvtColor(image, cv2.COLOR_RGB2BGR)
    cv2.imwrite(str(file_path), image)

there is an error at cv2.imwrite.

Going into debugger and checking the values of file_path it turns out that there is no extension for the file, which causes the problem. I think the actual problem comes from the file_path = Path("/".join(str(file_path).split("/")[2:]))file_path = Path("/".join(str(file_path).split("/")[2:])) line, which doesn't work on Windows (forward vs. backslash).

Screenshots

No response

Pip/GitHub

pip

What version/branch did you use?

No response

Configuration YAML

API used, config:

datamodule = Folder(
        name="hazelnut_toy",
        root="datasets/hazelnut_toy",
        normal_dir="good",
        test_split_mode=TestSplitMode.SYNTHETIC,
        task="classification",
    )


### Logs

```shell
Exception has occurred: error       (note: full exception trace is shown but execution is paused at: _run_module_as_main)
OpenCV(4.9.0) D:\a\opencv-python\opencv-python\opencv\modules\imgcodecs\src\loadsave.cpp:696: error: (-2:Unspecified error) could not find a writer for the specified extension in function 'cv::imwrite_'
  File "C:\Users\xxx\.venv\Lib\site-packages\anomalib\data\utils\image.py", line 453, in save_image
    cv2.imwrite(str(file_path), image)
  File "C:\Users\xxx\.venv\Lib\site-packages\anomalib\callbacks\visualizer.py", line 111, in on_test_batch_end
    save_image(image=result.image, root=self.root, filename=filename)
  File "C:\Users\xxx\.venv\Lib\site-packages\anomalib\callbacks\visualizer.py", line 144, in on_predict_batch_end
    return self.on_test_batch_end(trainer, pl_module, outputs, batch, batch_idx, dataloader_idx)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\trainer\call.py", line 208, in _call_callback_hooks
    fn(trainer, trainer.lightning_module, *args, **kwargs)
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\loops\prediction_loop.py", line 263, in _predict_step
    call._call_callback_hooks(trainer, "on_predict_batch_end", predictions, *hook_kwargs.values())
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\loops\prediction_loop.py", line 122, in run
    self._predict_step(batch, batch_idx, dataloader_idx, dataloader_iter)
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\loops\utilities.py", line 182, in _decorator
    return loop_run(self, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\trainer\trainer.py", line 1030, in _run_stage
    return self.predict_loop.run()
           ^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\trainer\trainer.py", line 989, in _run
    results = self._run_stage()
              ^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\trainer\trainer.py", line 903, in _predict_impl
    results = self._run(model, ckpt_path=ckpt_path)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\trainer\call.py", line 44, in _call_and_handle_interrupt
    return trainer_fn(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\.venv\Lib\site-packages\lightning\pytorch\trainer\trainer.py", line 864, in predict
    return call._call_and_handle_interrupt(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\.venv\Lib\site-packages\anomalib\engine\engine.py", line 769, in predict
    return self.trainer.predict(model, dataloaders, datamodule, return_predictions, ckpt_path)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\xxx\anomalib_test\an.py", line 30, in <module>
    engine.predict(model=model, dataset=pset)
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python311\Lib\runpy.py", line 88, in _run_code
    exec(code, run_globals)
  File "C:\Users\xxx\AppData\Local\Programs\Python\Python311\Lib\runpy.py", line 198, in _run_module_as_main (Current frame)
    return _run_code(code, main_globals, None,
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
cv2.error: OpenCV(4.9.0) D:\a\opencv-python\opencv-python\opencv\modules\imgcodecs\src\loadsave.cpp:696: error: (-2:Unspecified error) could not find a writer for the specified extension in function 'cv::imwrite_'


### Code of Conduct

- [X] I agree to follow this project's Code of Conduct
@neonesis
Copy link
Author

neonesis commented Mar 8, 2024

Proposed fix is to change anomalib.data.utils.image.py:444 into something like this:

file_path = Path("/".join(str(file_path).split("/")[2:]))  # BAD, WILL NOT WORK ON WINDOWS
file_path = Path(*file_path.parts[2:])  # OS-AGNOSTIC

@samet-akcay
Copy link
Contributor

Thanks for the proposal @neonesis. Do you want us to fix it, or would you want to create a PR to become a contributor

@neonesis
Copy link
Author

neonesis commented Mar 8, 2024

Unfortunately I'm unable to create a PR in an easy way due to corporate policy - please go forward with the fix.

@samet-akcay
Copy link
Contributor

sure no worries. Thanks for the suggestion

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants