| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
|
|
| from typing import List, Tuple, Union |
|
|
| import numpy as np |
| import torch |
| import torchvision |
| import torchvision.transforms.functional |
| from PIL import Image |
| from transformers import AutoImageProcessor, PretrainedConfig |
| from transformers.image_processing_utils import BaseImageProcessor, BatchFeature |
| from transformers.image_utils import to_numpy_array |
| from transformers.utils import logging |
|
|
| logger = logging.get_logger(__name__) |
|
|
| ImageType = Union[np.ndarray, torch.Tensor, Image.Image] |
| IMAGENET_MEAN = (0.48145466, 0.4578275, 0.40821073) |
| IMAGENET_STD = (0.26862954, 0.26130258, 0.27577711) |
| IMAGENET_INCEPTION_MEAN = (0.5, 0.5, 0.5) |
| IMAGENET_INCEPTION_STD = (0.5, 0.5, 0.5) |
|
|
|
|
| def expand2square(pil_img, background_color): |
| width, height = pil_img.size |
| if width == height: |
| return pil_img |
| elif width > height: |
| result = Image.new(pil_img.mode, (width, width), background_color) |
| result.paste(pil_img, (0, (width - height) // 2)) |
| return result |
| else: |
| result = Image.new(pil_img.mode, (height, height), background_color) |
| result.paste(pil_img, ((height - width) // 2, 0)) |
| return result |
|
|
|
|
| class VLMImageProcessorConfig(PretrainedConfig): |
| model_type = "deepseek_vlm" |
| image_size: int |
| min_size: int |
| image_mean: Union[Tuple[float, float, float], List[float]] |
| image_std: Union[Tuple[float, float, float], List[float]] |
| rescale_factor: float |
| do_normalize: bool |
|
|
| def __init__( |
| self, |
| image_size: int, |
| min_size: int = 14, |
| image_mean: Union[Tuple[float, float, float], List[float]] = ( |
| 0.48145466, |
| 0.4578275, |
| 0.40821073, |
| ), |
| image_std: Union[Tuple[float, float, float], List[float]] = ( |
| 0.26862954, |
| 0.26130258, |
| 0.27577711, |
| ), |
| rescale_factor: float = 1.0 / 255.0, |
| do_normalize: bool = True, |
| **kwargs, |
| ): |
| self.image_size = image_size |
| self.min_size = min_size |
| self.image_mean = image_mean |
| self.image_std = image_std |
| self.rescale_factor = rescale_factor |
| self.do_normalize = do_normalize |
|
|
| super().__init__(**kwargs) |
|
|
|
|
| class VLMImageProcessor(BaseImageProcessor): |
| model_input_names = ["pixel_values"] |
|
|
| def __init__( |
| self, |
| image_size: int, |
| min_size: int = 14, |
| image_mean: Union[Tuple[float, float, float], List[float]] = ( |
| 0.48145466, |
| 0.4578275, |
| 0.40821073, |
| ), |
| image_std: Union[Tuple[float, float, float], List[float]] = ( |
| 0.26862954, |
| 0.26130258, |
| 0.27577711, |
| ), |
| rescale_factor: float = 1.0 / 255.0, |
| do_normalize: bool = True, |
| **kwargs, |
| ): |
| super().__init__(**kwargs) |
|
|
| self.image_size = image_size |
| self.rescale_factor = rescale_factor |
| self.image_mean = image_mean |
| self.image_std = image_std |
| self.min_size = min_size |
| self.do_normalize = do_normalize |
|
|
| if image_mean is None: |
| self.background_color = (127, 127, 127) |
| else: |
| self.background_color = tuple([int(x * 255) for x in image_mean]) |
|
|
| def resize(self, pil_img: Image) -> np.ndarray: |
| """ |
| |
| Args: |
| pil_img (PIL.Image): [H, W, 3] in PIL.Image in RGB |
| |
| Returns: |
| x (np.ndarray): [3, self.image_size, self.image_size] |
| """ |
|
|
| width, height = pil_img.size |
| max_size = max(width, height) |
|
|
| size = [ |
| max(int(height / max_size * self.image_size), self.min_size), |
| max(int(width / max_size * self.image_size), self.min_size), |
| ] |
|
|
| if width <= 0 or height <= 0 or size[0] <= 0 or size[1] <= 0: |
| print(f"orig size = {pil_img.size}, new size = {size}") |
| raise ValueError("Invalid size!") |
|
|
| pil_img = torchvision.transforms.functional.resize( |
| pil_img, |
| size, |
| interpolation=torchvision.transforms.functional.InterpolationMode.BICUBIC, |
| antialias=True, |
| ) |
|
|
| pil_img = expand2square(pil_img, self.background_color) |
| x = to_numpy_array(pil_img) |
|
|
| |
| x = np.transpose(x, (2, 0, 1)) |
|
|
| return x |
|
|
| def preprocess(self, images, return_tensors: str = "pt", **kwargs) -> BatchFeature: |
| |
| |
| images: List[np.ndarray] = [self.resize(image) for image in images] |
|
|
| |
| images = [ |
| self.rescale( |
| image=image, |
| scale=self.rescale_factor, |
| input_data_format="channels_first", |
| ) |
| for image in images |
| ] |
|
|
| |
| if self.do_normalize: |
| images = [ |
| self.normalize( |
| image=image, |
| mean=self.image_mean, |
| std=self.image_std, |
| input_data_format="channels_first", |
| ) |
| for image in images |
| ] |
|
|
| data = {"pixel_values": images} |
| return BatchFeature(data=data, tensor_type=return_tensors) |
|
|
| @property |
| def default_shape(self): |
| return [3, self.image_size, self.image_size] |
|
|
|
|
| AutoImageProcessor.register(VLMImageProcessorConfig, VLMImageProcessor) |
|
|
|
|
| if __name__ == "__main__": |
| image_processor = VLMImageProcessor( |
| image_size=1024, |
| image_mean=IMAGENET_INCEPTION_MEAN, |
| image_std=IMAGENET_INCEPTION_STD, |
| do_normalize=True, |
| ) |
|
|