astroclip / processing_astroclip.py
giovannicozzolongo's picture
Add AstroCLIP remote-code model
213ef76
Raw
History Blame Contribute Delete
6.1 kB
# Copyright 2026 The HuggingFace Inc. team.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
"""Processor class for AstroCLIP."""
from __future__ import annotations
import json
from pathlib import Path
from typing import Any, Optional
from transformers.dynamic_module_utils import custom_object_save
from transformers.processing_utils import ProcessorMixin
from transformers.utils import PROCESSOR_NAME, cached_file
from .feature_extraction_astroclip import AstroClipSpectrumFeatureExtractor
from .image_processing_astroclip import AstroClipImageProcessor
class AstroClipProcessor(ProcessorMixin):
"""Compose AstroCLIP image and spectrum preprocessing."""
attributes = ["image_processor", "spectrum_feature_extractor"]
image_processor_class = "AstroClipImageProcessor"
spectrum_feature_extractor_class = "AstroClipSpectrumFeatureExtractor"
def __init__(
self,
image_processor: Optional[AstroClipImageProcessor] = None,
spectrum_feature_extractor: Optional[AstroClipSpectrumFeatureExtractor] = None,
spectrum_processor: Optional[AstroClipSpectrumFeatureExtractor] = None,
**kwargs,
):
if kwargs:
unexpected = ", ".join(sorted(kwargs))
raise TypeError(f"unexpected AstroCLIP processor arguments: {unexpected}")
if spectrum_processor is not None:
if spectrum_feature_extractor is not None:
raise ValueError("pass only one of spectrum_feature_extractor or spectrum_processor")
spectrum_feature_extractor = spectrum_processor
if image_processor is None:
image_processor = AstroClipImageProcessor()
if spectrum_feature_extractor is None:
spectrum_feature_extractor = AstroClipSpectrumFeatureExtractor()
# Older Transformers releases validate processor components through
# global AutoClass mappings, which are not reliable for remote code.
self.image_processor = image_processor
self.spectrum_feature_extractor = spectrum_feature_extractor
@property
def spectrum_processor(self) -> AstroClipSpectrumFeatureExtractor:
return self.spectrum_feature_extractor
def to_dict(self) -> dict[str, Any]:
image_processor = self.image_processor
spectrum_feature_extractor = self.spectrum_feature_extractor
output = {
"processor_class": self.__class__.__name__,
"image_processor": {
"crop_size": image_processor.crop_size,
"bands": image_processor.bands,
"band_indices": image_processor.band_indices,
"m": image_processor.m,
"q": image_processor.q,
},
"spectrum_feature_extractor": {
"section_length": spectrum_feature_extractor.section_length,
"overlap": spectrum_feature_extractor.overlap,
"min_std": spectrum_feature_extractor.min_std,
},
}
if self._auto_class is not None:
output["auto_map"] = {self._auto_class: "processing_astroclip.AstroClipProcessor"}
return output
def save_pretrained(self, save_directory: str | Path, **kwargs):
save_directory = Path(save_directory)
save_directory.mkdir(parents=True, exist_ok=True)
processor_dict = self.to_dict()
if self._auto_class is not None:
custom_object_save(self, save_directory, config=processor_dict)
processor_path = save_directory / PROCESSOR_NAME
processor_path.write_text(
json.dumps(processor_dict, indent=2) + "\n",
encoding="utf-8",
)
@classmethod
def from_pretrained(cls, pretrained_model_name_or_path, **kwargs):
local_path = Path(pretrained_model_name_or_path) / PROCESSOR_NAME
if local_path.exists():
processor_file = local_path
else:
hub_kwargs = {
key: kwargs[key]
for key in (
"cache_dir",
"force_download",
"local_files_only",
"token",
"revision",
"subfolder",
"repo_type",
"user_agent",
)
if key in kwargs
}
processor_file = cached_file(
pretrained_model_name_or_path,
PROCESSOR_NAME,
**hub_kwargs,
)
if processor_file is None:
raise OSError(f"could not find {PROCESSOR_NAME} in {pretrained_model_name_or_path}")
processor_dict = json.loads(Path(processor_file).read_text(encoding="utf-8"))
return cls(
image_processor=AstroClipImageProcessor(**processor_dict.get("image_processor", {})),
spectrum_feature_extractor=AstroClipSpectrumFeatureExtractor(
**processor_dict.get("spectrum_feature_extractor", {})
),
)
def __call__(
self,
images: Optional[Any] = None,
spectra: Optional[Any] = None,
return_tensors: Optional[str] = None,
**kwargs,
):
if images is None and spectra is None:
raise ValueError("provide images, spectra or both")
encoding = {}
if images is not None:
if self.image_processor is None:
raise ValueError("an image_processor is required when images are provided")
image_encoding = self.image_processor(
images=images,
return_tensors=return_tensors,
**kwargs,
)
encoding.update(dict(image_encoding))
if spectra is not None:
spectrum_encoding = self.spectrum_feature_extractor(
spectra,
return_tensors=return_tensors,
)
encoding.update(dict(spectrum_encoding))
return encoding
__all__ = ["AstroClipProcessor"]