import os import random from functools import lru_cache from typing import Tuple import numpy as np import pytest import requests import torch from PIL import Image from uniception.models.encoders import * from uniception.models.encoders.image_normalizations import * @pytest.fixture(scope="module") def norm_types(): return IMAGE_NORMALIZATION_DICT.keys() @pytest.fixture(scope="module") def encoders(): return [ "croco", "dust3r_224", "dust3r_512", "dust3r_512_dpt", "mast3r_512", "dinov2_base", "dinov2_large", "dinov2_large_reg", "dinov2_large_dav2", "dinov2_giant", "dinov2_giant_reg", "radio_v2.5-b", "radio_v2.5-l", "e-radio_v2", "naradio_v2.5-b", "naradio_v2.5-l", "cosmosx8", "patch_embedder", ] @pytest.fixture(scope="module") def encoder_configs(encoders): # Adjust the number of configs to match the number of encoders return [{}] * len(encoders) @pytest.fixture def device(request): # Access the value of the custom option for device device_str = request.config.getoption("--device") if device_str == "gpu" and torch.cuda.is_available(): device = torch.device("cuda") # Use the default CUDA device else: device = torch.device("cpu") print(f"Using device: {device.type.upper()}") return device @pytest.fixture def example_input(device): @lru_cache(maxsize=3) def _get_example_input( image_size: Tuple[int, int], image_norm_type: str = "dummy", img_selection: int = 1, return_viz_img: bool = False, ) -> torch.Tensor: url = f"https://raw.githubusercontent.com/naver/croco/d3d0ab2858d44bcad54e5bfc24f565983fbe18d9/assets/Chateau{img_selection}.png" image = Image.open(requests.get(url, stream=True).raw) image = image.resize(image_size) image = image.convert("RGB") img = torch.from_numpy(np.array(image)) viz_img = img.clone() # Normalize the image image_normalization = IMAGE_NORMALIZATION_DICT[image_norm_type] img_mean = image_normalization.mean img_std = image_normalization.std img = (img.float() / 255.0 - img_mean) / img_std # Convert to BCHW format img = img.permute(2, 0, 1).unsqueeze(0).to(device) if return_viz_img: return img, viz_img else: return img return _get_example_input def inference_encoder(encoder, encoder_input): # Encoder expects a ViTEncoderInput object return encoder(encoder_input).features def test_make_dummy_encoder(device): print(f"Testing Init of Dummy Encoder on {device.type.upper()}") encoder = _make_encoder_test("dummy").to(device) # Check if the encoder has parameters try: params = list(encoder.parameters()) if not params: print("Warning: The encoder has no parameters.") else: # Verify if the model is on the right device assert params[0].is_cuda == (device.type == "cuda") except Exception as e: print(f"Error: {e}") assert False # Fail the test if any error occurs assert encoder is not None def test_all_encoder_basics(encoders, encoder_configs, norm_types, example_input, encoder_name, device): if encoder_name: encoders = [encoder_name] # Override default encoders with the one specified for encoder_name, encoder_config in zip(encoders, encoder_configs): print(f"Testing encoder: {encoder_name} on {device.type.upper()}") encoder = _make_encoder_test(encoder_name, **encoder_config).to(device) _check_baseclass_attribute(encoder, norm_types) _check_norm_check_function(encoder) if isinstance(encoder, UniCeptionViTEncoderBase): _check_vit_encoder_attribute(encoder) _test_vit_encoder_patch_size(encoder, example_input) def _check_baseclass_attribute(encoder, norm_types): assert hasattr(encoder, "name") assert hasattr(encoder, "size") assert hasattr(encoder, "data_norm_type") assert isinstance(encoder.name, str) assert isinstance(encoder.size, str) or encoder.size is None assert isinstance(encoder.data_norm_type, str) # Check if the data_norm_type is in the list of normalization types assert encoder.data_norm_type in norm_types def _check_norm_check_function(encoder): assert hasattr(encoder, "_check_data_normalization_type") encoder_notm_type = encoder.data_norm_type try: encoder._check_data_normalization_type(encoder_notm_type) except AssertionError: assert False try: encoder._check_data_normalization_type("some_nonexistent_norm_type") assert False except AssertionError: pass def _check_vit_encoder_attribute(encoder): assert hasattr(encoder, "patch_size") assert isinstance(encoder.patch_size, int) assert encoder.patch_size > 0 def _test_vit_encoder_patch_size(encoder, example_input): print(f"Testing {encoder.name} inference") image_size = (14 * encoder.patch_size, 14 * encoder.patch_size) img = example_input(image_size, encoder.data_norm_type) # Create an instance of ViTEncoderInput with correct attributes encoder_input = ViTEncoderInput( data_norm_type=encoder.data_norm_type, image=img, ) encoder_output = inference_encoder(encoder, encoder_input) assert isinstance(encoder_output, torch.Tensor) assert encoder_output.shape[2] == 14 assert encoder_output.shape[3] == 14 @pytest.fixture(scope="session", autouse=True) def seed_everything(): seed = 42 random.seed(seed) os.environ["PYTHONHASHSEED"] = str(seed) np.random.seed(seed) torch.manual_seed(seed) torch.backends.cudnn.deterministic = True torch.backends.cudnn.benchmark = False print(f"Seed set to: {seed} (type: {type(seed)})") # Turn XFormers off for testing on CPU os.environ["XFORMERS_DISABLED"] = "1"