From 83d31dc8bc9bfa652f08b45fd810e6ad837ff6a6 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Fri, 26 Sep 2025 01:04:30 +0800 Subject: [PATCH 01/26] refactor: rename parameter pretrained_model_name_or_path --- cccv/auto/config.py | 12 +++++++++--- cccv/auto/model.py | 15 +++++++++++---- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index e625835..ca15912 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -7,16 +7,22 @@ class AutoConfig: @staticmethod def from_pretrained( - pretrained_model_name: Union[ConfigType, str], + pretrained_model_name_or_path: Union[ConfigType, str], **kwargs: Any, ) -> Any: """ Get a config instance of a pretrained model configuration. - :param pretrained_model_name: The name of the pretrained model configuration + :param pretrained_model_name_or_path: The name or path of the pretrained model configuration :return: """ - return CONFIG_REGISTRY.get(pretrained_model_name) + if "pretrained_model_name" in kwargs: + print( + "[CCCV] warning: 'pretrained_model_name' is deprecated, please use 'pretrained_model_name_or_path' instead." + ) + pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") + + return CONFIG_REGISTRY.get(pretrained_model_name_or_path) @staticmethod def register(config: Union[BaseConfig, Any], name: Optional[str] = None) -> None: diff --git a/cccv/auto/model.py b/cccv/auto/model.py index b6d0885..2a7c1f2 100644 --- a/cccv/auto/model.py +++ b/cccv/auto/model.py @@ -10,7 +10,8 @@ class AutoModel: @staticmethod def from_pretrained( - pretrained_model_name: Union[ConfigType, str], + pretrained_model_name_or_path: Union[ConfigType, str], + *, device: Optional[torch.device] = None, fp16: bool = True, compile: bool = False, @@ -23,9 +24,9 @@ def from_pretrained( **kwargs: Any, ) -> Any: """ - Get a model instance from a pretrained model name. + Get a model instance from a pretrained model name or path. - :param pretrained_model_name: The name of the pretrained model. It should be registered in CONFIG_REGISTRY. + :param pretrained_model_name_or_path: The name or path of the pretrained model. It should be registered in CONFIG_REGISTRY. :param device: inference device :param fp16: use fp16 precision or not :param compile: use torch.compile or not @@ -37,8 +38,13 @@ def from_pretrained( :param gh_proxy: The proxy for downloading from github release. Example: https://github.abskoop.workers.dev/ :return: """ + if "pretrained_model_name" in kwargs: + print( + "[CCCV] warning: 'pretrained_model_name' is deprecated, please use 'pretrained_model_name_or_path' instead." + ) + pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") - config = CONFIG_REGISTRY.get(pretrained_model_name) + config = CONFIG_REGISTRY.get(pretrained_model_name_or_path) return AutoModel.from_config( config=config, device=device, @@ -56,6 +62,7 @@ def from_pretrained( @staticmethod def from_config( config: Union[BaseConfig, Any], + *, device: Optional[torch.device] = None, fp16: bool = True, compile: bool = False, From c67e3a8fb6ac67c01bd23716a8539d4a081c0c87 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Fri, 26 Sep 2025 01:17:26 +0800 Subject: [PATCH 02/26] refactor: rename parameter pretrained_model_name_or_path --- cccv/auto/config.py | 17 ++--------------- cccv/auto/model.py | 26 -------------------------- example/register.py | 6 +++--- 3 files changed, 5 insertions(+), 44 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index ca15912..315c941 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -1,6 +1,6 @@ -from typing import Any, Optional, Union +from typing import Any, Union -from cccv.config import CONFIG_REGISTRY, BaseConfig +from cccv.config import CONFIG_REGISTRY from cccv.type import ConfigType @@ -23,16 +23,3 @@ def from_pretrained( pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") return CONFIG_REGISTRY.get(pretrained_model_name_or_path) - - @staticmethod - def register(config: Union[BaseConfig, Any], name: Optional[str] = None) -> None: - """ - Register the given config class instance under the name BaseConfig.name or the given name. - Can be used as a function call. See docstring of this class for usage. - - :param config: The config class instance to register - :param name: The name to register the config class instance under. If None, use BaseConfig.name - :return: - """ - # used as a function call - CONFIG_REGISTRY.register(obj=config, name=name) diff --git a/cccv/auto/model.py b/cccv/auto/model.py index 2a7c1f2..8e551f6 100644 --- a/cccv/auto/model.py +++ b/cccv/auto/model.py @@ -106,29 +106,3 @@ def from_config( ) return model - - @staticmethod - def register(obj: Optional[Any] = None, name: Optional[str] = None) -> Any: - """ - Register the given object under the name `obj.__name__` or the given name. - Can be used as either a decorator or not. See docstring of this class for usage. - - :param obj: The object to register. If None, this is being used as a decorator. - :param name: The name to register the object under. If None, use `obj.__name__`. - :return: - """ - if obj is None: - # used as a decorator - def deco(func_or_class: Any) -> Any: - _name = name - if _name is None: - _name = func_or_class.__name__ - MODEL_REGISTRY.register(obj=func_or_class, name=_name) - return func_or_class - - return deco - - # used as a function call - if name is None: - name = obj.__name__ - MODEL_REGISTRY.register(obj=obj, name=name) diff --git a/example/register.py b/example/register.py index 7cb8245..1b11aec 100644 --- a/example/register.py +++ b/example/register.py @@ -1,6 +1,6 @@ from typing import Any -from cccv import ArchType, AutoConfig, AutoModel, SRBaseModel +from cccv import CONFIG_REGISTRY, MODEL_REGISTRY, ArchType, AutoModel, SRBaseModel from cccv.config import RealESRGANConfig # define your own config name and model name @@ -17,11 +17,11 @@ scale=2, ) -AutoConfig.register(cfg) +CONFIG_REGISTRY.register(cfg) # extend from cccv.SRBaseModel then implement your own model -@AutoModel.register(name=model_name) +@MODEL_REGISTRY.register(name=model_name) class TESTMODEL(SRBaseModel): def load_model(self) -> Any: print("Override load_model function here") From 5ef996f6d02770b76823c896155feb1f6098075a Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Fri, 26 Sep 2025 01:20:50 +0800 Subject: [PATCH 03/26] refactor: rename parameter pretrained_model_name_or_path --- tests/test_auto_class.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/test_auto_class.py b/tests/test_auto_class.py index 326c255..38de49e 100644 --- a/tests/test_auto_class.py +++ b/tests/test_auto_class.py @@ -1,6 +1,6 @@ from typing import Any -from cccv import ArchType, AutoConfig, AutoModel +from cccv import CONFIG_REGISTRY, MODEL_REGISTRY, ArchType, AutoModel from cccv.config import RealESRGANConfig from cccv.model import SRBaseModel @@ -17,9 +17,9 @@ def test_auto_class_register() -> None: scale=2, ) - AutoConfig.register(cfg) + CONFIG_REGISTRY.register(cfg) - @AutoModel.register(name=model_name) + @MODEL_REGISTRY.register(name=model_name) class TESTMODEL(SRBaseModel): def get_cfg(self) -> Any: return self.config From c34d5e713e4c07882de4162e7bed2c0e3f692255 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Fri, 26 Sep 2025 02:56:00 +0800 Subject: [PATCH 04/26] feat: enhance AutoConfig to support path-based model configuration --- .gitignore | 2 ++ cccv/auto/config.py | 43 ++++++++++++++++++++++++++++++++-- cccv/auto/model.py | 9 ++++--- cccv/config/sr/srcnn_config.py | 2 +- cccv/util/registry.py | 4 ++-- tests/test_auto_class.py | 35 ++++++++++++++++++++++++++- tests/test_tile.py | 3 +-- tests/test_util.py | 3 +-- 8 files changed, 88 insertions(+), 13 deletions(-) diff --git a/.gitignore b/.gitignore index 4046166..e092669 100644 --- a/.gitignore +++ b/.gitignore @@ -170,3 +170,5 @@ cython_debug/ *.mp4 *.mkv + +/cccv/cache_models/cccv_demo_remote_model diff --git a/cccv/auto/config.py b/cccv/auto/config.py index 315c941..08c0aa5 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -1,3 +1,6 @@ +import importlib.util +import json +from pathlib import Path from typing import Any, Union from cccv.config import CONFIG_REGISTRY @@ -7,7 +10,7 @@ class AutoConfig: @staticmethod def from_pretrained( - pretrained_model_name_or_path: Union[ConfigType, str], + pretrained_model_name_or_path: Union[ConfigType, str, Path], **kwargs: Any, ) -> Any: """ @@ -22,4 +25,40 @@ def from_pretrained( ) pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") - return CONFIG_REGISTRY.get(pretrained_model_name_or_path) + # 1. check if it's a registered config name + if any(pretrained_model_name_or_path == item for item in ConfigType): + return CONFIG_REGISTRY.get(str(pretrained_model_name_or_path)) + + # 2. check if it's a real path + dir_path = Path(str(pretrained_model_name_or_path)) + + if not dir_path.exists() or not dir_path.is_dir(): + raise ValueError( + f"[CCCV] model configuration '{pretrained_model_name_or_path}' is not a valid config name or path" + ) + + # load config,json from the directory + config_path = dir_path / "config.json" + # check if config.json exists + if not config_path.exists(): + raise FileNotFoundError(f"[CCCV] no valid config.json not found in {dir_path}") + + with open(config_path, "r", encoding="utf-8") as f: + config_dict = json.load(f) + + if "name" not in config_dict: + raise KeyError( + f"[CCCV] no key 'name' in config.json in {dir_path}, you should provide a valid config.json contain a key 'name'" + ) + + # auto import all .py files in the directory to register the arch, model and config + for py_file in dir_path.glob("*.py"): + spec = importlib.util.spec_from_file_location(py_file.stem, py_file) + if spec is None or spec.loader is None: + continue + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + + cfg = CONFIG_REGISTRY.get(config_dict["name"]) + cfg.path = dir_path / cfg.name + return cfg diff --git a/cccv/auto/model.py b/cccv/auto/model.py index 8e551f6..1795748 100644 --- a/cccv/auto/model.py +++ b/cccv/auto/model.py @@ -1,8 +1,10 @@ +from pathlib import Path from typing import Any, Optional, Tuple, Union import torch -from cccv.config import CONFIG_REGISTRY, BaseConfig +from cccv.auto.config import AutoConfig +from cccv.config import BaseConfig from cccv.model import MODEL_REGISTRY from cccv.type import ConfigType @@ -10,7 +12,7 @@ class AutoModel: @staticmethod def from_pretrained( - pretrained_model_name_or_path: Union[ConfigType, str], + pretrained_model_name_or_path: Union[ConfigType, str, Path], *, device: Optional[torch.device] = None, fp16: bool = True, @@ -44,7 +46,8 @@ def from_pretrained( ) pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") - config = CONFIG_REGISTRY.get(pretrained_model_name_or_path) + config = AutoConfig.from_pretrained(pretrained_model_name_or_path, **kwargs) + return AutoModel.from_config( config=config, device=device, diff --git a/cccv/config/sr/srcnn_config.py b/cccv/config/sr/srcnn_config.py index 7d04f11..1a6403b 100644 --- a/cccv/config/sr/srcnn_config.py +++ b/cccv/config/sr/srcnn_config.py @@ -6,7 +6,7 @@ class SRCNNConfig(SRBaseConfig): - arch: ArchType = ArchType.SRCNN + arch: Union[ArchType, str] = ArchType.SRCNN model: Union[ModelType, str] = ModelType.SRBaseModel scale: int = 2 num_channels: int = 1 diff --git a/cccv/util/registry.py b/cccv/util/registry.py index c160600..ff6c3a9 100644 --- a/cccv/util/registry.py +++ b/cccv/util/registry.py @@ -41,7 +41,7 @@ def __init__(self, name: str) -> None: def _do_register(self, name: str, obj: Any) -> None: if name in self._obj_map: - print("[CCCV] An object named '{}' was already registered in '{}' registry!".format(name, self._name)) + raise KeyError(f"[CCCV] An object named '{name}' was already registered in '{self._name}' registry!") else: self._obj_map[name] = obj @@ -69,7 +69,7 @@ def deco(func_or_class: Any) -> Any: def get(self, name: str) -> Any: ret = self._obj_map.get(name) if ret is None: - raise KeyError("[CCCV] No object named '{}' found in '{}' registry!".format(name, self._name)) + raise KeyError(f"[CCCV] No object named '{name}' found in '{self._name}' registry!") return ret def __contains__(self, name: str) -> bool: diff --git a/tests/test_auto_class.py b/tests/test_auto_class.py index 38de49e..4f9b9cf 100644 --- a/tests/test_auto_class.py +++ b/tests/test_auto_class.py @@ -1,8 +1,19 @@ from typing import Any -from cccv import CONFIG_REGISTRY, MODEL_REGISTRY, ArchType, AutoModel +import cv2 + +from cccv import CONFIG_REGISTRY, MODEL_REGISTRY, ArchType, AutoConfig, AutoModel, BaseConfig, ConfigType from cccv.config import RealESRGANConfig from cccv.model import SRBaseModel +from tests.util import ( + ASSETS_PATH, + CCCV_DEVICE, + CCCV_FP16, + CCCV_TILE, + calculate_image_similarity, + compare_image_size, + load_image, +) def test_auto_class_register() -> None: @@ -26,3 +37,25 @@ def get_cfg(self) -> Any: model: TESTMODEL = AutoModel.from_pretrained(cfg_name) assert model.get_cfg() == cfg + + +class Test_AutoConfig: + def test_registered_config(self) -> None: + cfg = AutoConfig.from_pretrained(ConfigType.RealESRGAN_AnimeJaNai_HD_V3_Compact_2x) + assert isinstance(cfg, BaseConfig) + assert cfg.name == ConfigType.RealESRGAN_AnimeJaNai_HD_V3_Compact_2x + + def test_config_from_path(self) -> None: + cfg: BaseConfig = AutoConfig.from_pretrained( + r"C:\Users\haiqu\Desktop\cccv\cccv\cache_models\cccv_demo_remote_model" + ) + + img1 = load_image() + model: SRBaseModel = AutoModel.from_config(config=cfg, device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE) + + img2 = model.inference_image(img1) + + cv2.imwrite(str(ASSETS_PATH / f"test_{cfg.name}_out.jpg"), img2) + + assert calculate_image_similarity(img1, img2) + assert compare_image_size(img1, img2, cfg.scale) diff --git a/tests/test_tile.py b/tests/test_tile.py index e0e4b6f..0fa37d1 100644 --- a/tests/test_tile.py +++ b/tests/test_tile.py @@ -6,8 +6,7 @@ from cccv import AutoModel, ConfigType from cccv.model import SRBaseModel, tile_sr - -from .util import ASSETS_PATH, CCCV_DEVICE, calculate_image_similarity, compare_image_size, load_image +from tests.util import ASSETS_PATH, CCCV_DEVICE, calculate_image_similarity, compare_image_size, load_image def test_tile_sr() -> None: diff --git a/tests/test_util.py b/tests/test_util.py index a767cef..fa9ff81 100644 --- a/tests/test_util.py +++ b/tests/test_util.py @@ -15,8 +15,7 @@ resize, ssim_matlab, ) - -from .util import calculate_image_similarity, load_image +from tests.util import calculate_image_similarity, load_image def test_device() -> None: From 0315580195b53bd57d078ba0cdd59a3a44f2192e Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:25:29 +0800 Subject: [PATCH 05/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index 08c0aa5..e20b707 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -26,16 +26,16 @@ def from_pretrained( pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") # 1. check if it's a registered config name - if any(pretrained_model_name_or_path == item for item in ConfigType): + if isinstance(pretrained_model_name_or_path, ConfigType): + pretrained_model_name_or_path = pretrained_model_name_or_path.value + if str(pretrained_model_name_or_path) in CONFIG_REGISTRY: return CONFIG_REGISTRY.get(str(pretrained_model_name_or_path)) # 2. check if it's a real path dir_path = Path(str(pretrained_model_name_or_path)) if not dir_path.exists() or not dir_path.is_dir(): - raise ValueError( - f"[CCCV] model configuration '{pretrained_model_name_or_path}' is not a valid config name or path" - ) + raise ValueError(f"[CCCV] model configuration '{dir_path}' is not a valid config name or path") # load config,json from the directory config_path = dir_path / "config.json" From 6292e2ff59dda3607ce8e30b565be4cd12a7bdcf Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Fri, 26 Sep 2025 03:59:42 +0800 Subject: [PATCH 06/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 17 ++++++++++------- cccv/config/base_config.py | 4 +++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index e20b707..cc31339 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Any, Union -from cccv.config import CONFIG_REGISTRY +from cccv.config import CONFIG_REGISTRY, BaseConfig from cccv.type import ConfigType @@ -46,10 +46,11 @@ def from_pretrained( with open(config_path, "r", encoding="utf-8") as f: config_dict = json.load(f) - if "name" not in config_dict: - raise KeyError( - f"[CCCV] no key 'name' in config.json in {dir_path}, you should provide a valid config.json contain a key 'name'" - ) + for k in ["arch", "model", "name"]: + if k not in config_dict: + raise KeyError( + f"[CCCV] no key '{k}' in config.json in {dir_path}, you should provide a valid config.json contain a key '{k}'" + ) # auto import all .py files in the directory to register the arch, model and config for py_file in dir_path.glob("*.py"): @@ -59,6 +60,8 @@ def from_pretrained( module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) - cfg = CONFIG_REGISTRY.get(config_dict["name"]) - cfg.path = dir_path / cfg.name + config_dict["path"] = str(dir_path / config_dict["name"]) + + # convert config_dict to pydantic model + cfg = BaseConfig.model_validate(config_dict, strict=False) return cfg diff --git a/cccv/config/base_config.py b/cccv/config/base_config.py index e34b75f..96b9bea 100644 --- a/cccv/config/base_config.py +++ b/cccv/config/base_config.py @@ -1,12 +1,14 @@ from typing import Optional, Union -from pydantic import BaseModel, FilePath, HttpUrl +from pydantic import BaseModel, ConfigDict, FilePath, HttpUrl from cccv.type.arch import ArchType from cccv.type.model import ModelType class BaseConfig(BaseModel): + model_config = ConfigDict(extra="allow") + name: str url: Optional[HttpUrl] = None path: Optional[FilePath] = None From c448ec18e0c9a3d0eb1a924df98a4823c6dadb67 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Fri, 26 Sep 2025 04:05:41 +0800 Subject: [PATCH 07/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 2 +- tests/test_auto_class.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index cc31339..b15aa7a 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -63,5 +63,5 @@ def from_pretrained( config_dict["path"] = str(dir_path / config_dict["name"]) # convert config_dict to pydantic model - cfg = BaseConfig.model_validate(config_dict, strict=False) + cfg = BaseConfig.model_validate(config_dict) return cfg diff --git a/tests/test_auto_class.py b/tests/test_auto_class.py index 4f9b9cf..ebaac6c 100644 --- a/tests/test_auto_class.py +++ b/tests/test_auto_class.py @@ -49,7 +49,7 @@ def test_config_from_path(self) -> None: cfg: BaseConfig = AutoConfig.from_pretrained( r"C:\Users\haiqu\Desktop\cccv\cccv\cache_models\cccv_demo_remote_model" ) - + print(cfg) img1 = load_image() model: SRBaseModel = AutoModel.from_config(config=cfg, device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE) From 65bafa73cf6ba801325311c397db17a11d66e836 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Fri, 26 Sep 2025 23:52:21 +0800 Subject: [PATCH 08/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/__init__.py | 2 +- cccv/auto/config.py | 4 ++-- cccv/config/__init__.py | 2 +- cccv/config/base_config.py | 6 ++++-- 4 files changed, 8 insertions(+), 6 deletions(-) diff --git a/cccv/__init__.py b/cccv/__init__.py index 7a602b2..91d0965 100644 --- a/cccv/__init__.py +++ b/cccv/__init__.py @@ -26,6 +26,6 @@ from cccv.arch import ARCH_REGISTRY from cccv.auto import AutoConfig, AutoModel -from cccv.config import CONFIG_REGISTRY, BaseConfig, SRBaseConfig, VFIBaseConfig, VSRBaseConfig +from cccv.config import CONFIG_REGISTRY, AutoBaseConfig, BaseConfig, SRBaseConfig, VFIBaseConfig, VSRBaseConfig from cccv.model import MODEL_REGISTRY, AuxiliaryBaseModel, CCBaseModel, SRBaseModel, VFIBaseModel, VSRBaseModel from cccv.type import ArchType, BaseModelInterface, ConfigType, ModelType diff --git a/cccv/auto/config.py b/cccv/auto/config.py index b15aa7a..9fd0c32 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -3,7 +3,7 @@ from pathlib import Path from typing import Any, Union -from cccv.config import CONFIG_REGISTRY, BaseConfig +from cccv.config import CONFIG_REGISTRY, AutoBaseConfig from cccv.type import ConfigType @@ -63,5 +63,5 @@ def from_pretrained( config_dict["path"] = str(dir_path / config_dict["name"]) # convert config_dict to pydantic model - cfg = BaseConfig.model_validate(config_dict) + cfg = AutoBaseConfig.model_validate(config_dict) return cfg diff --git a/cccv/config/__init__.py b/cccv/config/__init__.py index 283a196..4c43937 100644 --- a/cccv/config/__init__.py +++ b/cccv/config/__init__.py @@ -3,7 +3,7 @@ CONFIG_REGISTRY: RegistryConfigInstance = RegistryConfigInstance("CONFIG") -from cccv.config.base_config import BaseConfig, SRBaseConfig, VSRBaseConfig, VFIBaseConfig +from cccv.config.base_config import BaseConfig, SRBaseConfig, VSRBaseConfig, VFIBaseConfig, AutoBaseConfig # Auxiliary Network diff --git a/cccv/config/base_config.py b/cccv/config/base_config.py index 96b9bea..03c0d02 100644 --- a/cccv/config/base_config.py +++ b/cccv/config/base_config.py @@ -7,8 +7,6 @@ class BaseConfig(BaseModel): - model_config = ConfigDict(extra="allow") - name: str url: Optional[HttpUrl] = None path: Optional[FilePath] = None @@ -17,6 +15,10 @@ class BaseConfig(BaseModel): model: Union[ModelType, str] +class AutoBaseConfig(BaseConfig): + model_config = ConfigDict(extra="allow") + + class AuxiliaryBaseConfig(BaseConfig): pass From bd472bbb41c2b65db12093e8f2096f2855c8cb84 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 00:09:34 +0800 Subject: [PATCH 09/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index 9fd0c32..4519623 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -60,7 +60,9 @@ def from_pretrained( module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) - config_dict["path"] = str(dir_path / config_dict["name"]) + if "path" not in config_dict or config_dict["path"] is None: + # add the path to the config_dict + config_dict["path"] = str(dir_path / config_dict["name"]) # convert config_dict to pydantic model cfg = AutoBaseConfig.model_validate(config_dict) From eb0cad6a953ed2c72619606a0f2800e1b1e24ca9 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 01:00:55 +0800 Subject: [PATCH 10/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/model.py | 4 +- cccv/model/base_model.py | 9 ++-- .../__init__.py => util/remote.py} | 52 +++++++++++-------- tests/test_auto_class.py | 4 +- 4 files changed, 37 insertions(+), 32 deletions(-) rename cccv/{cache_models/__init__.py => util/remote.py} (75%) diff --git a/cccv/auto/model.py b/cccv/auto/model.py index 1795748..304223c 100644 --- a/cccv/auto/model.py +++ b/cccv/auto/model.py @@ -21,7 +21,7 @@ def from_pretrained( tile: Optional[Tuple[int, int]] = (128, 128), tile_pad: int = 8, pad_img: Optional[Tuple[int, int]] = None, - model_dir: Optional[str] = None, + model_dir: Optional[Union[Path, str]] = None, gh_proxy: Optional[str] = None, **kwargs: Any, ) -> Any: @@ -73,7 +73,7 @@ def from_config( tile: Optional[Tuple[int, int]] = (128, 128), tile_pad: int = 8, pad_img: Optional[Tuple[int, int]] = None, - model_dir: Optional[str] = None, + model_dir: Optional[Union[Path, str]] = None, gh_proxy: Optional[str] = None, **kwargs: Any, ) -> Any: diff --git a/cccv/model/base_model.py b/cccv/model/base_model.py index 7c6b333..355fe13 100644 --- a/cccv/model/base_model.py +++ b/cccv/model/base_model.py @@ -1,14 +1,15 @@ import sys from inspect import signature -from typing import Any, Optional, Tuple +from pathlib import Path +from typing import Any, Optional, Tuple, Union import torch from cccv.arch import ARCH_REGISTRY -from cccv.cache_models import load_file_from_url from cccv.config import BaseConfig from cccv.type import BaseModelInterface from cccv.util.device import DEFAULT_DEVICE +from cccv.util.remote import load_file_from_url class CCBaseModel(BaseModelInterface): @@ -37,7 +38,7 @@ def __init__( tile: Optional[Tuple[int, int]] = (128, 128), tile_pad: int = 8, pad_img: Optional[Tuple[int, int]] = None, - model_dir: Optional[str] = None, + model_dir: Optional[Union[Path, str]] = None, gh_proxy: Optional[str] = None, **kwargs: Any, ) -> None: @@ -56,7 +57,7 @@ def __init__( self.tile: Optional[Tuple[int, int]] = tile self.tile_pad: int = tile_pad self.pad_img: Optional[Tuple[int, int]] = pad_img - self.model_dir: Optional[str] = model_dir + self.model_dir: Optional[Union[Path, str]] = model_dir self.gh_proxy: Optional[str] = gh_proxy # post-hook: edit parameters here if needed diff --git a/cccv/cache_models/__init__.py b/cccv/util/remote.py similarity index 75% rename from cccv/cache_models/__init__.py rename to cccv/util/remote.py index f05df63..76504df 100644 --- a/cccv/cache_models/__init__.py +++ b/cccv/util/remote.py @@ -2,7 +2,7 @@ import os import sys from pathlib import Path -from typing import Any, Optional +from typing import Any, Optional, Union from tenacity import retry, stop_after_attempt, stop_after_delay, wait_random from torch.hub import download_url_to_file @@ -13,12 +13,28 @@ # frozen _IS_FROZEN_ = True CACHE_PATH = Path(sys.executable).parent.absolute() / "cache_models" - if not CACHE_PATH.exists(): - os.makedirs(CACHE_PATH) else: # unfrozen _IS_FROZEN_ = False - CACHE_PATH = Path(__file__).resolve().parent.absolute() + CACHE_PATH = Path(__file__).resolve().parent.parent.absolute() / "cache_models" + + +CCCV_CACHE_MODEL_DIR = os.environ.get("CCCV_CACHE_MODEL_DIR", str(CACHE_PATH)) + +CCCV_REMOTE_MODEL_ZOO = os.environ.get( + "CCCV_REMOTE_MODEL_ZOO", "https://github.com/EutropicAI/cccv/releases/download/model_zoo/" +) + + +def get_cache_dir(model_dir: Optional[Union[Path, str]] = None) -> Path: + if model_dir is None or str(model_dir) == "": + model_dir = str(CCCV_CACHE_MODEL_DIR) + print( + f"[CCCV] Using default cache model path {model_dir}, override it by setting environment variable CCCV_CACHE_MODEL_DIR" + ) + if not os.path.exists(model_dir): + os.makedirs(model_dir) + return Path(model_dir) def get_file_sha256(file_path: str, blocksize: int = 1 << 20) -> str: @@ -36,7 +52,7 @@ def load_file_from_url( config: BaseConfig, force_download: bool = False, progress: bool = True, - model_dir: Optional[str] = None, + model_dir: Optional[Union[Path, str]] = None, gh_proxy: Optional[str] = None, **kwargs: Any, ) -> str: @@ -52,29 +68,19 @@ def load_file_from_url( :param gh_proxy: The proxy for downloading from github release. Example: https://github.abskoop.workers.dev/ :return: """ - - CCCV_REMOTE_MODEL_ZOO = os.environ.get( - "CCCV_REMOTE_MODEL_ZOO", "https://github.com/EutropicAI/cccv/releases/download/model_zoo/" - ) - CCCV_CACHE_MODEL_DIR = os.environ.get("CCCV_CACHE_MODEL_DIR", str(CACHE_PATH)) - - if model_dir is None: - model_dir = str(CCCV_CACHE_MODEL_DIR) - print( - f"[CCCV] Using default cache model path {model_dir}, override it by setting environment variable CCCV_CACHE_MODEL_DIR" - ) - - cached_file_path = os.path.abspath(os.path.join(model_dir, config.name)) + model_dir = get_cache_dir(model_dir) + cached_file_path = str(model_dir / config.name) if config.url is not None: _url: str = str(config.url) else: + remote_zoo = CCCV_REMOTE_MODEL_ZOO print( - f"[CCCV] Fetching models from {CCCV_REMOTE_MODEL_ZOO}, override it by setting environment variable CCCV_REMOTE_MODEL_ZOO" + f"[CCCV] Fetching models from {remote_zoo}, override it by setting environment variable CCCV_REMOTE_MODEL_ZOO" ) - if not CCCV_REMOTE_MODEL_ZOO.endswith("/"): - CCCV_REMOTE_MODEL_ZOO += "/" - _url = CCCV_REMOTE_MODEL_ZOO + config.name + if not remote_zoo.endswith("/"): + remote_zoo += "/" + _url = remote_zoo + config.name _gh_proxy = gh_proxy if _gh_proxy is not None and _url.startswith("https://github.com"): @@ -109,7 +115,7 @@ def _download() -> None: if __name__ == "__main__": # get all model files sha256 - for root, _, files in os.walk(CACHE_PATH): + for root, _, files in os.walk(get_cache_dir()): for file in files: if not file.endswith(".pth") and not file.endswith(".pt"): continue diff --git a/tests/test_auto_class.py b/tests/test_auto_class.py index ebaac6c..7d7bf79 100644 --- a/tests/test_auto_class.py +++ b/tests/test_auto_class.py @@ -46,9 +46,7 @@ def test_registered_config(self) -> None: assert cfg.name == ConfigType.RealESRGAN_AnimeJaNai_HD_V3_Compact_2x def test_config_from_path(self) -> None: - cfg: BaseConfig = AutoConfig.from_pretrained( - r"C:\Users\haiqu\Desktop\cccv\cccv\cache_models\cccv_demo_remote_model" - ) + cfg: BaseConfig = AutoConfig.from_pretrained(r"C:\Users\haiqu\Desktop\cccv\cccv_demo_remote_model") print(cfg) img1 = load_image() model: SRBaseModel = AutoModel.from_config(config=cfg, device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE) From 13e091062b465c19772fadcf719c3e3e53493e65 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 01:12:26 +0800 Subject: [PATCH 11/26] feat: enhance AutoConfig to support path-based model configuration --- .gitignore | 2 +- tests/test_cache_models.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index e092669..67eceef 100644 --- a/.gitignore +++ b/.gitignore @@ -171,4 +171,4 @@ cython_debug/ *.mp4 *.mkv -/cccv/cache_models/cccv_demo_remote_model +/cccv/cache_models/ diff --git a/tests/test_cache_models.py b/tests/test_cache_models.py index 37c0be0..94bab4e 100644 --- a/tests/test_cache_models.py +++ b/tests/test_cache_models.py @@ -1,5 +1,5 @@ from cccv import CONFIG_REGISTRY, ConfigType -from cccv.cache_models import load_file_from_url +from cccv.util.remote import load_file_from_url def test_cache_models() -> None: From 4ad047f54a625600be7ff3fdbdb7039894e6671f Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 02:30:45 +0800 Subject: [PATCH 12/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/model/base_model.py | 2 +- cccv/util/remote.py | 50 +++++++++++++++++-- tests/test_auto_class.py | 4 +- .../{test_cache_models.py => test_remote.py} | 7 ++- 4 files changed, 55 insertions(+), 8 deletions(-) rename tests/{test_cache_models.py => test_remote.py} (71%) diff --git a/cccv/model/base_model.py b/cccv/model/base_model.py index 355fe13..92f0137 100644 --- a/cccv/model/base_model.py +++ b/cccv/model/base_model.py @@ -106,7 +106,7 @@ def get_state_dict(self) -> Any: cfg: BaseConfig = self.config if cfg.path is not None: - state_dict_path = str(cfg.path) + state_dict_path = cfg.path else: try: state_dict_path = load_file_from_url( diff --git a/cccv/util/remote.py b/cccv/util/remote.py index 76504df..6683405 100644 --- a/cccv/util/remote.py +++ b/cccv/util/remote.py @@ -1,5 +1,6 @@ import hashlib import os +import subprocess import sys from pathlib import Path from typing import Any, Optional, Union @@ -37,7 +38,7 @@ def get_cache_dir(model_dir: Optional[Union[Path, str]] = None) -> Path: return Path(model_dir) -def get_file_sha256(file_path: str, blocksize: int = 1 << 20) -> str: +def get_file_sha256(file_path: Union[Path, str], blocksize: int = 1 << 20) -> str: sha256 = hashlib.sha256() with open(file_path, "rb") as f: while True: @@ -55,7 +56,7 @@ def load_file_from_url( model_dir: Optional[Union[Path, str]] = None, gh_proxy: Optional[str] = None, **kwargs: Any, -) -> str: +) -> Path: """ Load file form http url, will download models if necessary. @@ -69,7 +70,7 @@ def load_file_from_url( :return: """ model_dir = get_cache_dir(model_dir) - cached_file_path = str(model_dir / config.name) + cached_file_path = model_dir / config.name if config.url is not None: _url: str = str(config.url) @@ -88,7 +89,7 @@ def load_file_from_url( _gh_proxy += "/" _url = _gh_proxy + _url - if not os.path.exists(cached_file_path) or force_download: + if not cached_file_path.exists() or force_download: if _gh_proxy is not None: print(f"[CCCV] Using github proxy: {_gh_proxy}") print(f"[CCCV] Downloading: {_url} to {cached_file_path}\n") @@ -96,7 +97,7 @@ def load_file_from_url( @retry(wait=wait_random(min=3, max=5), stop=stop_after_delay(10) | stop_after_attempt(30)) def _download() -> None: try: - download_url_to_file(url=_url, dst=cached_file_path, hash_prefix=None, progress=progress) + download_url_to_file(url=_url, dst=str(cached_file_path), hash_prefix=None, progress=progress) except Exception as e: print(f"[CCCV] Download failed: {e}, retrying...") raise e @@ -113,6 +114,45 @@ def _download() -> None: return cached_file_path +def git_clone(git_url: str, model_dir: Optional[Union[Path, str]] = None, **kwargs: Any) -> Path: + """ + Clone or update a git repository. We suggest use HuggingFace repo instead of GitHub repo for larger models. + + :param git_url: GitHub repository URL + :param model_dir: Directory to clone into + :param **kwargs: Additional git options (branch, commit_hash, etc.) + :return: Path to the cloned repository + """ + model_dir = get_cache_dir(model_dir) + # get repo name from url + repo_name = git_url.split("/")[-1].replace(".git", "") + clone_dir = model_dir / repo_name + + if clone_dir.exists() and (clone_dir / ".git").exists(): + print(f"[CCCV] Repository exists, updating: {clone_dir}") + subprocess.run(["git", "-C", str(clone_dir), "pull"], check=True) + + # if branch or commit_hash is specified, checkout to that + if "branch" in kwargs: + subprocess.run(["git", "-C", str(clone_dir), "checkout", kwargs["branch"]], check=True) + if "commit_hash" in kwargs: + subprocess.run(["git", "-C", str(clone_dir), "reset", "--hard", kwargs["commit_hash"]], check=True) + else: + # clone the repo if not exists + print(f"[CCCV] Cloning repository: {git_url} -> {clone_dir}") + command = ["git", "clone", git_url, str(clone_dir)] + + if "branch" in kwargs: + command.extend(["--branch", kwargs["branch"]]) + + subprocess.run(command, check=True) + + if "commit_hash" in kwargs: + subprocess.run(["git", "-C", str(clone_dir), "reset", "--hard", kwargs["commit_hash"]], check=True) + + return clone_dir + + if __name__ == "__main__": # get all model files sha256 for root, _, files in os.walk(get_cache_dir()): diff --git a/tests/test_auto_class.py b/tests/test_auto_class.py index 7d7bf79..09eab62 100644 --- a/tests/test_auto_class.py +++ b/tests/test_auto_class.py @@ -5,6 +5,7 @@ from cccv import CONFIG_REGISTRY, MODEL_REGISTRY, ArchType, AutoConfig, AutoModel, BaseConfig, ConfigType from cccv.config import RealESRGANConfig from cccv.model import SRBaseModel +from cccv.util.remote import git_clone from tests.util import ( ASSETS_PATH, CCCV_DEVICE, @@ -46,7 +47,8 @@ def test_registered_config(self) -> None: assert cfg.name == ConfigType.RealESRGAN_AnimeJaNai_HD_V3_Compact_2x def test_config_from_path(self) -> None: - cfg: BaseConfig = AutoConfig.from_pretrained(r"C:\Users\haiqu\Desktop\cccv\cccv_demo_remote_model") + clone_dir = git_clone("https://github.com/EutropicAI/cccv_demo_remote_model") + cfg: BaseConfig = AutoConfig.from_pretrained(clone_dir) print(cfg) img1 = load_image() model: SRBaseModel = AutoModel.from_config(config=cfg, device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE) diff --git a/tests/test_cache_models.py b/tests/test_remote.py similarity index 71% rename from tests/test_cache_models.py rename to tests/test_remote.py index 94bab4e..49c7ca4 100644 --- a/tests/test_cache_models.py +++ b/tests/test_remote.py @@ -1,5 +1,5 @@ from cccv import CONFIG_REGISTRY, ConfigType -from cccv.util.remote import load_file_from_url +from cccv.util.remote import get_cache_dir, git_clone, load_file_from_url def test_cache_models() -> None: @@ -17,3 +17,8 @@ def test_cache_models_with_gh_proxy() -> None: force_download=True, gh_proxy="https://github.abskoop.workers.dev", ) + + +def test_git_clone() -> None: + clone_dir = git_clone("https://github.com/EutropicAI/cccv_demo_remote_model") + assert clone_dir == get_cache_dir() / "cccv_demo_remote_model" From 459488b6612376411c7bf37bf15eb6dc54dd4340 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 02:31:48 +0800 Subject: [PATCH 13/26] feat: enhance AutoConfig to support path-based model configuration --- tests/test_auto_class.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_auto_class.py b/tests/test_auto_class.py index 09eab62..03f9b09 100644 --- a/tests/test_auto_class.py +++ b/tests/test_auto_class.py @@ -48,6 +48,7 @@ def test_registered_config(self) -> None: def test_config_from_path(self) -> None: clone_dir = git_clone("https://github.com/EutropicAI/cccv_demo_remote_model") + cfg: BaseConfig = AutoConfig.from_pretrained(clone_dir) print(cfg) img1 = load_image() From 93d36e4a997f90b6b0ca2ba4354b12fa07ab9b81 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 02:53:53 +0800 Subject: [PATCH 14/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 19 +++++++++++++++---- cccv/auto/model.py | 6 ------ tests/test_auto_class.py | 23 +++++++++++++++++++---- 3 files changed, 34 insertions(+), 14 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index 4519623..324640f 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -1,22 +1,25 @@ import importlib.util import json from pathlib import Path -from typing import Any, Union +from typing import Any, Optional, Union from cccv.config import CONFIG_REGISTRY, AutoBaseConfig from cccv.type import ConfigType +from cccv.util.remote import git_clone class AutoConfig: @staticmethod def from_pretrained( pretrained_model_name_or_path: Union[ConfigType, str, Path], + model_dir: Optional[Union[Path, str]] = None, **kwargs: Any, ) -> Any: """ - Get a config instance of a pretrained model configuration. + Get a config instance of a pretrained model configuration, can be a registered config name or a local path or a git url. :param pretrained_model_name_or_path: The name or path of the pretrained model configuration + :param model_dir: The path to cache the downloaded model configuration. Should be a full path. If None, use default cache path. :return: """ if "pretrained_model_name" in kwargs: @@ -25,13 +28,21 @@ def from_pretrained( ) pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") - # 1. check if it's a registered config name + # 1. check if it's a registered config name, early return if found if isinstance(pretrained_model_name_or_path, ConfigType): pretrained_model_name_or_path = pretrained_model_name_or_path.value if str(pretrained_model_name_or_path) in CONFIG_REGISTRY: return CONFIG_REGISTRY.get(str(pretrained_model_name_or_path)) - # 2. check if it's a real path + # 2. check is a url or not, if it's a url, git clone it to model_dir then replace pretrained_model_name_or_path with the local path (Path) + if str(pretrained_model_name_or_path).startswith("http"): + pretrained_model_name_or_path = git_clone( + git_url=str(pretrained_model_name_or_path), + model_dir=model_dir, + **kwargs, + ) + + # 3. check if it's a real path dir_path = Path(str(pretrained_model_name_or_path)) if not dir_path.exists() or not dir_path.is_dir(): diff --git a/cccv/auto/model.py b/cccv/auto/model.py index 304223c..e43ee06 100644 --- a/cccv/auto/model.py +++ b/cccv/auto/model.py @@ -40,12 +40,6 @@ def from_pretrained( :param gh_proxy: The proxy for downloading from github release. Example: https://github.abskoop.workers.dev/ :return: """ - if "pretrained_model_name" in kwargs: - print( - "[CCCV] warning: 'pretrained_model_name' is deprecated, please use 'pretrained_model_name_or_path' instead." - ) - pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") - config = AutoConfig.from_pretrained(pretrained_model_name_or_path, **kwargs) return AutoModel.from_config( diff --git a/tests/test_auto_class.py b/tests/test_auto_class.py index 03f9b09..05d63ee 100644 --- a/tests/test_auto_class.py +++ b/tests/test_auto_class.py @@ -12,7 +12,6 @@ CCCV_FP16, CCCV_TILE, calculate_image_similarity, - compare_image_size, load_image, ) @@ -51,12 +50,28 @@ def test_config_from_path(self) -> None: cfg: BaseConfig = AutoConfig.from_pretrained(clone_dir) print(cfg) + + +class Test_AutoModel: + def test_model_from_path(self) -> None: + clone_dir = git_clone("https://github.com/EutropicAI/cccv_demo_remote_model") + model: SRBaseModel = AutoModel.from_pretrained(clone_dir, device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE) + img1 = load_image() - model: SRBaseModel = AutoModel.from_config(config=cfg, device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE) + img2 = model.inference_image(img1) + cv2.imwrite(str(ASSETS_PATH / f"test_{clone_dir}_out.jpg"), img2) + + assert calculate_image_similarity(img1, img2) + + def test_model_from_remote_repo(self) -> None: + model: SRBaseModel = AutoModel.from_pretrained( + "https://github.com/EutropicAI/cccv_demo_remote_model", device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE + ) + + img1 = load_image() img2 = model.inference_image(img1) - cv2.imwrite(str(ASSETS_PATH / f"test_{cfg.name}_out.jpg"), img2) + cv2.imwrite(str(ASSETS_PATH / "test_remote_repo_test_out.jpg"), img2) assert calculate_image_similarity(img1, img2) - assert compare_image_size(img1, img2, cfg.scale) From 7216b9af2013c290303c865c8655128ab528fdbc Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 02:58:11 +0800 Subject: [PATCH 15/26] feat: enhance AutoConfig to support path-based model configuration --- tests/test_auto_class.py | 27 +-------------------------- 1 file changed, 1 insertion(+), 26 deletions(-) diff --git a/tests/test_auto_class.py b/tests/test_auto_class.py index 05d63ee..a63bfcc 100644 --- a/tests/test_auto_class.py +++ b/tests/test_auto_class.py @@ -2,10 +2,9 @@ import cv2 -from cccv import CONFIG_REGISTRY, MODEL_REGISTRY, ArchType, AutoConfig, AutoModel, BaseConfig, ConfigType +from cccv import CONFIG_REGISTRY, MODEL_REGISTRY, ArchType, AutoModel from cccv.config import RealESRGANConfig from cccv.model import SRBaseModel -from cccv.util.remote import git_clone from tests.util import ( ASSETS_PATH, CCCV_DEVICE, @@ -39,31 +38,7 @@ def get_cfg(self) -> Any: assert model.get_cfg() == cfg -class Test_AutoConfig: - def test_registered_config(self) -> None: - cfg = AutoConfig.from_pretrained(ConfigType.RealESRGAN_AnimeJaNai_HD_V3_Compact_2x) - assert isinstance(cfg, BaseConfig) - assert cfg.name == ConfigType.RealESRGAN_AnimeJaNai_HD_V3_Compact_2x - - def test_config_from_path(self) -> None: - clone_dir = git_clone("https://github.com/EutropicAI/cccv_demo_remote_model") - - cfg: BaseConfig = AutoConfig.from_pretrained(clone_dir) - print(cfg) - - class Test_AutoModel: - def test_model_from_path(self) -> None: - clone_dir = git_clone("https://github.com/EutropicAI/cccv_demo_remote_model") - model: SRBaseModel = AutoModel.from_pretrained(clone_dir, device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE) - - img1 = load_image() - img2 = model.inference_image(img1) - - cv2.imwrite(str(ASSETS_PATH / f"test_{clone_dir}_out.jpg"), img2) - - assert calculate_image_similarity(img1, img2) - def test_model_from_remote_repo(self) -> None: model: SRBaseModel = AutoModel.from_pretrained( "https://github.com/EutropicAI/cccv_demo_remote_model", device=CCCV_DEVICE, fp16=CCCV_FP16, tile=CCCV_TILE From c894c2b193c594ced5057cb4d5025266b7d92ca5 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:04:58 +0800 Subject: [PATCH 16/26] feat: enhance AutoConfig to support path-based model configuration --- README.md | 18 +++++++++++++++++- example/remote.py | 10 ++++++++++ 2 files changed, 27 insertions(+), 1 deletion(-) create mode 100644 example/remote.py diff --git a/README.md b/README.md index f63bc63..2f6fde7 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ pip install cccv ### Start -#### cv2 +#### Load a registered model in cccv a simple example to use the SISR (Single Image Super-Resolution) model to process an image @@ -37,6 +37,22 @@ img = model.inference_image(img) cv2.imwrite("test_out.jpg", img) ``` +#### Load a custom model from remote repository or local path + +a simple example to use [remote repository](https://github.com/EutropicAI/cccv_demo_remote_model), auto register then load + +```python +import cv2 +import numpy as np + +from cccv import AutoModel, SRBaseModel + +# remote repo +model: SRBaseModel = AutoModel.from_pretrained("https://github.com/EutropicAI/cccv_demo_remote_model") +# local path +model: SRBaseModel = AutoModel.from_pretrained("/path/to/cccv_demo_model") +``` + #### VapourSynth a simple example to use the VapourSynth to process a video diff --git a/example/remote.py b/example/remote.py new file mode 100644 index 0000000..10fb070 --- /dev/null +++ b/example/remote.py @@ -0,0 +1,10 @@ +import cv2 +import numpy as np + +from cccv import AutoModel, SRBaseModel + +model: SRBaseModel = AutoModel.from_pretrained("https://github.com/EutropicAI/cccv_demo_remote_model") + +img = cv2.imdecode(np.fromfile("../assets/test.jpg", dtype=np.uint8), cv2.IMREAD_COLOR) +img = model.inference_image(img) +cv2.imwrite("../assets/test_remote_example_out.jpg", img) From 9fa55b8398e6da268aa5039a69ecaa912e942c9b Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:05:57 +0800 Subject: [PATCH 17/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index 324640f..911ad53 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -71,7 +71,7 @@ def from_pretrained( module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) - if "path" not in config_dict or config_dict["path"] is None: + if "path" not in config_dict or config_dict["path"] is None or config_dict["path"] == "": # add the path to the config_dict config_dict["path"] = str(dir_path / config_dict["name"]) From 4bfb4a6dc842f49b4f91cd3bc6dc8d5fc8064e90 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:08:36 +0800 Subject: [PATCH 18/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 1 + cccv/auto/model.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index 911ad53..ce8d6fe 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -12,6 +12,7 @@ class AutoConfig: @staticmethod def from_pretrained( pretrained_model_name_or_path: Union[ConfigType, str, Path], + *, model_dir: Optional[Union[Path, str]] = None, **kwargs: Any, ) -> Any: diff --git a/cccv/auto/model.py b/cccv/auto/model.py index e43ee06..4e1757d 100644 --- a/cccv/auto/model.py +++ b/cccv/auto/model.py @@ -40,7 +40,7 @@ def from_pretrained( :param gh_proxy: The proxy for downloading from github release. Example: https://github.abskoop.workers.dev/ :return: """ - config = AutoConfig.from_pretrained(pretrained_model_name_or_path, **kwargs) + config = AutoConfig.from_pretrained(pretrained_model_name_or_path, model_dir=model_dir, **kwargs) return AutoModel.from_config( config=config, From 771c2b3756ced521f3a07151d22246b13403d789 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:17:27 +0800 Subject: [PATCH 19/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 7 +++++-- cccv/util/remote.py | 9 +++++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index ce8d6fe..77eeb35 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -1,5 +1,6 @@ import importlib.util import json +import warnings from pathlib import Path from typing import Any, Optional, Union @@ -24,8 +25,10 @@ def from_pretrained( :return: """ if "pretrained_model_name" in kwargs: - print( - "[CCCV] warning: 'pretrained_model_name' is deprecated, please use 'pretrained_model_name_or_path' instead." + warnings.warn( + "[CCCV] 'pretrained_model_name' is deprecated, please use 'pretrained_model_name_or_path' instead.", + DeprecationWarning, + stacklevel=2, ) pretrained_model_name_or_path = kwargs.pop("pretrained_model_name") diff --git a/cccv/util/remote.py b/cccv/util/remote.py index 6683405..a07ad2c 100644 --- a/cccv/util/remote.py +++ b/cccv/util/remote.py @@ -1,7 +1,9 @@ import hashlib import os +import shutil import subprocess import sys +import warnings from pathlib import Path from typing import Any, Optional, Union @@ -123,6 +125,13 @@ def git_clone(git_url: str, model_dir: Optional[Union[Path, str]] = None, **kwar :param **kwargs: Additional git options (branch, commit_hash, etc.) :return: Path to the cloned repository """ + if not shutil.which("git"): + warnings.warn( + "[CCCV] git is not installed or not in the system's PATH. " + "Please install git to use models from remote git repositories.", + stacklevel=2, + ) + model_dir = get_cache_dir(model_dir) # get repo name from url repo_name = git_url.split("/")[-1].replace(".git", "") From 1d92040a0068631e0bbc775cfb194338821e5116 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:19:19 +0800 Subject: [PATCH 20/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/model/base_model.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/cccv/model/base_model.py b/cccv/model/base_model.py index 92f0137..7cb893d 100644 --- a/cccv/model/base_model.py +++ b/cccv/model/base_model.py @@ -1,4 +1,5 @@ import sys +import warnings from inspect import signature from pathlib import Path from typing import Any, Optional, Tuple, Union @@ -73,7 +74,7 @@ def __init__( try: self.model = self.model.half() except Exception as e: - print(f"[CCCV] Warning: {e}. \nfp16 is not supported on this model, fallback to fp32.") + warnings.warn(f"[CCCV] {e}. fp16 is not supported on this model, fallback to fp32.", stacklevel=2) self.fp16 = False self.model = self.load_model() @@ -87,7 +88,7 @@ def __init__( self.compile_backend = "inductor" self.model = torch.compile(self.model, backend=self.compile_backend) except Exception as e: - print(f"[CCCV] Error: {e}, compile is not supported on this model.") + warnings.warn(f"[CCCV] {e}, compile is not supported on this model.", stacklevel=2) def post_init_hook(self) -> None: """ From 9433b88a0c871e44a3684bb62f4ff9561d51f841 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:24:31 +0800 Subject: [PATCH 21/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/arch/vfi/drba_arch.py | 8 +++++--- cccv/arch/vfi/ifnet_arch.py | 8 +++++--- cccv/util/device.py | 3 ++- cccv/util/remote.py | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/cccv/arch/vfi/drba_arch.py b/cccv/arch/vfi/drba_arch.py index a4c27ee..fa57fb2 100644 --- a/cccv/arch/vfi/drba_arch.py +++ b/cccv/arch/vfi/drba_arch.py @@ -1,4 +1,6 @@ # type: ignore +import warnings + import numpy as np import torch import torch.nn as nn @@ -61,7 +63,7 @@ def inference(self, x, timestep=0.5, scale_list=None, fastmode=True, ensemble=Fa torch.cat((img0[:, :3], img1[:, :3], f0, f1, timestep), 1), None, scale=scale_list[i] ) if ensemble: - print("warning: ensemble is not supported since RIFEv4.21") + warnings.warn("[CCCV] ensemble is not supported since RIFEv4.21", stacklevel=2) else: wf0 = warp(f0, flow[:, :2]) wf1 = warp(f1, flow[:, 2:4]) @@ -71,7 +73,7 @@ def inference(self, x, timestep=0.5, scale_list=None, fastmode=True, ensemble=Fa scale=scale_list[i], ) if ensemble: - print("warning: ensemble is not supported since RIFEv4.21") + warnings.warn("[CCCV] ensemble is not supported since RIFEv4.21", stacklevel=2) else: mask = m0 flow = flow + fd @@ -83,7 +85,7 @@ def inference(self, x, timestep=0.5, scale_list=None, fastmode=True, ensemble=Fa mask = torch.sigmoid(mask) merged[4] = warped_img0 * mask + warped_img1 * (1 - mask) if not fastmode: - print("contextnet is removed") + warnings.warn("[CCCV] contextnet is removed", stacklevel=2) """ c0 = self.contextnet(img0, flow[:, :2]) c1 = self.contextnet(img1, flow[:, 2:4]) diff --git a/cccv/arch/vfi/ifnet_arch.py b/cccv/arch/vfi/ifnet_arch.py index 3d7ab51..40087a3 100644 --- a/cccv/arch/vfi/ifnet_arch.py +++ b/cccv/arch/vfi/ifnet_arch.py @@ -1,4 +1,6 @@ # type: ignore +import warnings + import torch import torch.nn as nn import torch.nn.functional as F @@ -43,7 +45,7 @@ def forward(self, x, timestep=0.5, scale_list=None, fastmode=True, ensemble=Fals torch.cat((img0[:, :3], img1[:, :3], f0, f1, timestep), 1), None, scale=scale_list[i] ) if ensemble: - print("warning: ensemble is not supported since RIFEv4.21") + warnings.warn("[CCCV] ensemble is not supported since RIFEv4.21", stacklevel=2) else: wf0 = warp(f0, flow[:, :2]) wf1 = warp(f1, flow[:, 2:4]) @@ -53,7 +55,7 @@ def forward(self, x, timestep=0.5, scale_list=None, fastmode=True, ensemble=Fals scale=scale_list[i], ) if ensemble: - print("warning: ensemble is not supported since RIFEv4.21") + warnings.warn("[CCCV] ensemble is not supported since RIFEv4.21", stacklevel=2) else: mask = m0 flow = flow + fd @@ -65,7 +67,7 @@ def forward(self, x, timestep=0.5, scale_list=None, fastmode=True, ensemble=Fals mask = torch.sigmoid(mask) merged[4] = warped_img0 * mask + warped_img1 * (1 - mask) if not fastmode: - print("contextnet is removed") + warnings.warn("[CCCV] contextnet is removed", stacklevel=2) """ c0 = self.contextnet(img0, flow[:, :2]) c1 = self.contextnet(img1, flow[:, 2:4]) diff --git a/cccv/util/device.py b/cccv/util/device.py index e1532a5..92894ae 100644 --- a/cccv/util/device.py +++ b/cccv/util/device.py @@ -1,4 +1,5 @@ import sys +import warnings import torch @@ -10,7 +11,7 @@ def default_device() -> torch.device: try: return torch.device("mps" if torch.backends.mps.is_available() else "cpu") except Exception as e: - print(f"[CCCV] Error: {e}, MPS is not available, use CPU instead.") + warnings.warn(f"[CCCV] {e}, MPS is not available, use CPU instead.", stacklevel=2) return torch.device("cpu") diff --git a/cccv/util/remote.py b/cccv/util/remote.py index a07ad2c..1598726 100644 --- a/cccv/util/remote.py +++ b/cccv/util/remote.py @@ -101,7 +101,7 @@ def _download() -> None: try: download_url_to_file(url=_url, dst=str(cached_file_path), hash_prefix=None, progress=progress) except Exception as e: - print(f"[CCCV] Download failed: {e}, retrying...") + warnings.warn(f"[CCCV] Download failed: {e}, retrying...", stacklevel=2) raise e _download() From 1ba22b7fed857435336477c977b988fb0e8ba706 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:29:10 +0800 Subject: [PATCH 22/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/arch/sr/dat_arch.py | 2 +- cccv/arch/sr/upcunet_arch.py | 12 ++++++------ cccv/arch/vfi/vfi_utils/softsplat.py | 6 +++--- cccv/model/base_model.py | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/cccv/arch/sr/dat_arch.py b/cccv/arch/sr/dat_arch.py index ed65e2b..fd8caad 100644 --- a/cccv/arch/sr/dat_arch.py +++ b/cccv/arch/sr/dat_arch.py @@ -365,7 +365,7 @@ def __init__( elif idx == 1: W_sp, H_sp = self.split_size[0], self.split_size[1] else: - print("ERROR MODE", idx) + print("[CCCV] ERROR MODE", idx) exit(0) self.H_sp = H_sp self.W_sp = W_sp diff --git a/cccv/arch/sr/upcunet_arch.py b/cccv/arch/sr/upcunet_arch.py index 56aa39e..7cd4628 100644 --- a/cccv/arch/sr/upcunet_arch.py +++ b/cccv/arch/sr/upcunet_arch.py @@ -376,7 +376,7 @@ def forward(self, x, tile_mode, cache_mode, alpha, pro): t2 = tile_mode * 2 crop_size = (((h0 - 1) // t2 * t2 + t2) // tile_mode, ((w0 - 1) // t2 * t2 + t2) // tile_mode) else: - print("tile_mode config error") + print("[CCCV] tile_mode config error") os._exit(233) ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] @@ -526,7 +526,7 @@ def forward_gap_sync(self, x, tile_mode, alpha, pro): t2 = tile_mode * 2 crop_size = (((h0 - 1) // t2 * t2 + t2) // tile_mode, ((w0 - 1) // t2 * t2 + t2) // tile_mode) else: - print("tile_mode config error") + print("[CCCV] tile_mode config error") os._exit(233) ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] @@ -767,7 +767,7 @@ def forward(self, x, tile_mode, cache_mode, alpha, pro): t4 = tile_mode * 4 crop_size = (((h0 - 1) // t4 * t4 + t4) // tile_mode, ((w0 - 1) // t4 * t4 + t4) // tile_mode) else: - print("tile_mode config error") + print("[CCCV] tile_mode config error") os._exit(233) ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] @@ -916,7 +916,7 @@ def forward_gap_sync(self, x, tile_mode, alpha, pro): t4 = tile_mode * 4 crop_size = (((h0 - 1) // t4 * t4 + t4) // tile_mode, ((w0 - 1) // t4 * t4 + t4) // tile_mode) else: - print("tile_mode config error") + print("[CCCV] tile_mode config error") os._exit(233) ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] @@ -1162,7 +1162,7 @@ def forward(self, x, tile_mode, cache_mode, alpha, pro): t2 = tile_mode * 2 crop_size = (((h0 - 1) // t2 * t2 + t2) // tile_mode, ((w0 - 1) // t2 * t2 + t2) // tile_mode) else: - print("tile_mode config error") + print("[CCCV] tile_mode config error") os._exit(233) ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] @@ -1323,7 +1323,7 @@ def forward_gap_sync(self, x, tile_mode, alpha, pro): t2 = tile_mode * 2 crop_size = (((h0 - 1) // t2 * t2 + t2) // tile_mode, ((w0 - 1) // t2 * t2 + t2) // tile_mode) # 5.6G else: - print("tile_mode config error") + print("[CCCV] tile_mode config error") os._exit(233) ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] diff --git a/cccv/arch/vfi/vfi_utils/softsplat.py b/cccv/arch/vfi/vfi_utils/softsplat.py index 5f82e98..823d523 100644 --- a/cccv/arch/vfi/vfi_utils/softsplat.py +++ b/cccv/arch/vfi/vfi_utils/softsplat.py @@ -61,7 +61,7 @@ def cuda_kernel(strFunction: str, strKernel: str, objVariables: typing.Dict): strKey += str(objValue.stride()) elif True: - print(strVariable, type(objValue)) + print(f"[CCCV] {strVariable}, {type(objValue)}") # end # end @@ -106,10 +106,10 @@ def cuda_kernel(strFunction: str, strKernel: str, objVariables: typing.Dict): strKernel = strKernel.replace("{{type}}", "long") elif isinstance(objValue, torch.Tensor): - print(strVariable, objValue.dtype) + print(f"[CCCV] {strVariable}, {objValue.dtype}") elif True: - print(strVariable, type(objValue)) + print(f"[CCCV] {strVariable}, {type(objValue)}") # end # end diff --git a/cccv/model/base_model.py b/cccv/model/base_model.py index 7cb893d..9c879eb 100644 --- a/cccv/model/base_model.py +++ b/cccv/model/base_model.py @@ -114,7 +114,7 @@ def get_state_dict(self) -> Any: config=cfg, force_download=False, model_dir=self.model_dir, gh_proxy=self.gh_proxy ) except Exception as e: - print(f"[CCCV] Error: {e}, try force download the model...") + warnings.warn(f"[CCCV] Error: {e}, try force download the model...", stacklevel=2) state_dict_path = load_file_from_url( config=cfg, force_download=True, model_dir=self.model_dir, gh_proxy=self.gh_proxy ) From 23d4c4b85beb5a7bb8b4803421aa506f976d28b5 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:33:17 +0800 Subject: [PATCH 23/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 2 +- cccv/auto/model.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index 77eeb35..7ec24e5 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -20,7 +20,7 @@ def from_pretrained( """ Get a config instance of a pretrained model configuration, can be a registered config name or a local path or a git url. - :param pretrained_model_name_or_path: The name or path of the pretrained model configuration + :param pretrained_model_name_or_path: :param model_dir: The path to cache the downloaded model configuration. Should be a full path. If None, use default cache path. :return: """ diff --git a/cccv/auto/model.py b/cccv/auto/model.py index 4e1757d..6fd5f4e 100644 --- a/cccv/auto/model.py +++ b/cccv/auto/model.py @@ -26,9 +26,9 @@ def from_pretrained( **kwargs: Any, ) -> Any: """ - Get a model instance from a pretrained model name or path. + Get a model instance from a registered config name or a local path or a git url. - :param pretrained_model_name_or_path: The name or path of the pretrained model. It should be registered in CONFIG_REGISTRY. + :param pretrained_model_name_or_path: :param device: inference device :param fp16: use fp16 precision or not :param compile: use torch.compile or not @@ -74,7 +74,7 @@ def from_config( """ Get a model instance from a config. - :param config: The config object. It should be registered in CONFIG_REGISTRY. + :param config: The config object. We suggest use cccv.BaseConfig or its subclass. :param device: inference device :param fp16: use fp16 precision or not :param compile: use torch.compile or not From 4bd845ceb002fe0eb093e2a2cd1cc00763be5a46 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:36:51 +0800 Subject: [PATCH 24/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/auto/config.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/cccv/auto/config.py b/cccv/auto/config.py index 7ec24e5..c7e055b 100644 --- a/cccv/auto/config.py +++ b/cccv/auto/config.py @@ -68,12 +68,15 @@ def from_pretrained( ) # auto import all .py files in the directory to register the arch, model and config - for py_file in dir_path.glob("*.py"): - spec = importlib.util.spec_from_file_location(py_file.stem, py_file) - if spec is None or spec.loader is None: - continue - module = importlib.util.module_from_spec(spec) - spec.loader.exec_module(module) + try: + for py_file in dir_path.glob("*.py"): + spec = importlib.util.spec_from_file_location(py_file.stem, py_file) + if spec is None or spec.loader is None: + continue + module = importlib.util.module_from_spec(spec) + spec.loader.exec_module(module) + except Exception as e: + raise ImportError(f"[CCCV] failed register model from {dir_path}, error: {e}, please check your .py files") if "path" not in config_dict or config_dict["path"] is None or config_dict["path"] == "": # add the path to the config_dict From 045ce405edd4e1c824c442ef4e392e58e4cca3e8 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:45:42 +0800 Subject: [PATCH 25/26] feat: enhance AutoConfig to support path-based model configuration --- cccv/arch/sr/dat_arch.py | 3 +-- cccv/arch/sr/upcunet_arch.py | 18 ++++++------------ 2 files changed, 7 insertions(+), 14 deletions(-) diff --git a/cccv/arch/sr/dat_arch.py b/cccv/arch/sr/dat_arch.py index fd8caad..5eec59f 100644 --- a/cccv/arch/sr/dat_arch.py +++ b/cccv/arch/sr/dat_arch.py @@ -365,8 +365,7 @@ def __init__( elif idx == 1: W_sp, H_sp = self.split_size[0], self.split_size[1] else: - print("[CCCV] ERROR MODE", idx) - exit(0) + raise ValueError(f"[CCCV] ERROR MODE: invalid idx {idx}, expected 0 or 1") self.H_sp = H_sp self.W_sp = W_sp diff --git a/cccv/arch/sr/upcunet_arch.py b/cccv/arch/sr/upcunet_arch.py index 7cd4628..af702aa 100644 --- a/cccv/arch/sr/upcunet_arch.py +++ b/cccv/arch/sr/upcunet_arch.py @@ -376,8 +376,7 @@ def forward(self, x, tile_mode, cache_mode, alpha, pro): t2 = tile_mode * 2 crop_size = (((h0 - 1) // t2 * t2 + t2) // tile_mode, ((w0 - 1) // t2 * t2 + t2) // tile_mode) else: - print("[CCCV] tile_mode config error") - os._exit(233) + raise ValueError("[CCCV] tile_mode config error: invalid tile_mode value") ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] @@ -526,8 +525,7 @@ def forward_gap_sync(self, x, tile_mode, alpha, pro): t2 = tile_mode * 2 crop_size = (((h0 - 1) // t2 * t2 + t2) // tile_mode, ((w0 - 1) // t2 * t2 + t2) // tile_mode) else: - print("[CCCV] tile_mode config error") - os._exit(233) + raise ValueError("[CCCV] tile_mode config error: invalid tile_mode value") ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] x = F.pad(x, (18, 18 + pw - w0, 18, 18 + ph - h0), "reflect") @@ -767,8 +765,7 @@ def forward(self, x, tile_mode, cache_mode, alpha, pro): t4 = tile_mode * 4 crop_size = (((h0 - 1) // t4 * t4 + t4) // tile_mode, ((w0 - 1) // t4 * t4 + t4) // tile_mode) else: - print("[CCCV] tile_mode config error") - os._exit(233) + raise ValueError("[CCCV] tile_mode config error: invalid tile_mode value") ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] x = F.pad(x, (14, 14 + pw - w0, 14, 14 + ph - h0), "reflect") @@ -916,8 +913,7 @@ def forward_gap_sync(self, x, tile_mode, alpha, pro): t4 = tile_mode * 4 crop_size = (((h0 - 1) // t4 * t4 + t4) // tile_mode, ((w0 - 1) // t4 * t4 + t4) // tile_mode) else: - print("[CCCV] tile_mode config error") - os._exit(233) + raise ValueError("[CCCV] tile_mode config error: invalid tile_mode value") ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] x = F.pad(x, (14, 14 + pw - w0, 14, 14 + ph - h0), "reflect") @@ -1162,8 +1158,7 @@ def forward(self, x, tile_mode, cache_mode, alpha, pro): t2 = tile_mode * 2 crop_size = (((h0 - 1) // t2 * t2 + t2) // tile_mode, ((w0 - 1) // t2 * t2 + t2) // tile_mode) else: - print("[CCCV] tile_mode config error") - os._exit(233) + raise ValueError("[CCCV] tile_mode config error: invalid tile_mode value") ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] x = F.pad(x, (19, 19 + pw - w0, 19, 19 + ph - h0), "reflect") @@ -1323,8 +1318,7 @@ def forward_gap_sync(self, x, tile_mode, alpha, pro): t2 = tile_mode * 2 crop_size = (((h0 - 1) // t2 * t2 + t2) // tile_mode, ((w0 - 1) // t2 * t2 + t2) // tile_mode) # 5.6G else: - print("[CCCV] tile_mode config error") - os._exit(233) + raise ValueError("[CCCV] tile_mode config error: invalid tile_mode value") ph = ((h0 - 1) // crop_size[0] + 1) * crop_size[0] pw = ((w0 - 1) // crop_size[1] + 1) * crop_size[1] x = F.pad(x, (19, 19 + pw - w0, 19, 19 + ph - h0), "reflect") From 42eed702029d3c875a21f7bae009647952aa9494 Mon Sep 17 00:00:00 2001 From: Tohrusky <65994850+Tohrusky@users.noreply.github.com> Date: Sat, 27 Sep 2025 03:49:59 +0800 Subject: [PATCH 26/26] feat: enhance AutoConfig to support path-based model configuration --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 2f6fde7..bc48edd 100644 --- a/README.md +++ b/README.md @@ -39,7 +39,7 @@ cv2.imwrite("test_out.jpg", img) #### Load a custom model from remote repository or local path -a simple example to use [remote repository](https://github.com/EutropicAI/cccv_demo_remote_model), auto register then load +a simple example to use [remote repository](https://github.com/EutropicAI/cccv_demo_remote_model) or local path, auto register the model then load ```python import cv2