ColabWan / shared /prompt_enhancer /florence2 /image_processing_florence2.py
1ripon1's picture
Upload folder using huggingface_hub
7344bef verified
Raw
History Blame Contribute Delete
8.8 kB
from __future__ import annotations
import json
from pathlib import Path
from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
import numpy as np
from PIL import Image, ImageOps
import torch
from transformers.image_processing_base import ImageProcessingMixin
def _as_list(val):
if isinstance(val, (list, tuple)):
return list(val)
return [val]
def _to_numpy(image: Any) -> np.ndarray:
if isinstance(image, np.ndarray):
return image
if torch.is_tensor(image):
return image.detach().cpu().numpy()
if isinstance(image, Image.Image):
return np.array(image)
raise TypeError(f"Unsupported image type: {type(image)}")
def _infer_input_format(arr: np.ndarray) -> str:
if arr.ndim == 3 and arr.shape[0] in (1, 3) and arr.shape[-1] not in (1, 3):
return "channels_first"
return "channels_last"
def _to_channels_last(arr: np.ndarray, input_format: str) -> np.ndarray:
if input_format == "channels_first":
return np.transpose(arr, (1, 2, 0))
return arr
def _to_channels_first(arr: np.ndarray, input_format: str) -> np.ndarray:
if input_format == "channels_last":
return np.transpose(arr, (2, 0, 1))
return arr
def _compute_resize_size(image_size: Tuple[int, int], size: Dict[str, int]) -> Tuple[int, int]:
height, width = image_size
if "height" in size and "width" in size:
return int(size["height"]), int(size["width"])
if "shortest_edge" in size:
target = int(size["shortest_edge"])
if height <= width:
new_h = target
new_w = int(round(width * target / max(height, 1)))
else:
new_w = target
new_h = int(round(height * target / max(width, 1)))
return new_h, new_w
raise ValueError(f"Unsupported size dict: {size}")
def _resolve_resample(resample: Optional[int]) -> int:
if resample is None:
return Image.BICUBIC
try:
return Image.Resampling(resample)
except Exception:
return resample
def _center_crop_pil(image: Image.Image, crop_size: Dict[str, int]) -> Image.Image:
target_h = int(crop_size["height"])
target_w = int(crop_size["width"])
width, height = image.size
if width < target_w or height < target_h:
padded_w = max(width, target_w)
padded_h = max(height, target_h)
padded = Image.new(image.mode, (padded_w, padded_h), (0, 0, 0))
padded.paste(image, ((padded_w - width) // 2, (padded_h - height) // 2))
image = padded
width, height = image.size
left = int(round((width - target_w) / 2.0))
top = int(round((height - target_h) / 2.0))
return image.crop((left, top, left + target_w, top + target_h))
def _normalize_return_tensors(value: Optional[Union[str, Any]]) -> Optional[str]:
if value is None:
return None
if isinstance(value, str):
return value.lower()
name = getattr(value, "name", None)
if name:
return name.lower()
return str(value).lower()
class Florence2ImageProcessorLite(ImageProcessingMixin):
model_input_names = ["pixel_values"]
def __init__(
self,
image_seq_length: int,
do_resize: bool = True,
size: Optional[Dict[str, int]] = None,
resample: Optional[int] = None,
do_center_crop: bool = False,
crop_size: Optional[Dict[str, int]] = None,
do_rescale: bool = True,
rescale_factor: float = 1 / 255,
do_normalize: bool = True,
image_mean: Optional[List[float]] = None,
image_std: Optional[List[float]] = None,
do_convert_rgb: Optional[bool] = True,
) -> None:
super().__init__()
self.image_seq_length = int(image_seq_length)
self.do_resize = bool(do_resize)
self.size = size or {"height": 224, "width": 224}
self.resample = resample
self.do_center_crop = bool(do_center_crop)
self.crop_size = crop_size or {"height": 224, "width": 224}
self.do_rescale = bool(do_rescale)
self.rescale_factor = float(rescale_factor)
self.do_normalize = bool(do_normalize)
self.image_mean = image_mean or [0.485, 0.456, 0.406]
self.image_std = image_std or [0.229, 0.224, 0.225]
self.do_convert_rgb = do_convert_rgb
@classmethod
def from_preprocessor_config(cls, model_dir: Union[str, Path]) -> "Florence2ImageProcessorLite":
config_path = Path(model_dir) / "preprocessor_config.json"
if not config_path.exists():
raise FileNotFoundError(f"Missing Florence2 preprocessor_config.json in {model_dir}")
data = json.loads(config_path.read_text(encoding="utf-8"))
return cls(
image_seq_length=data.get("image_seq_length", 0),
do_resize=data.get("do_resize", True),
size=data.get("size") or data.get("crop_size") or {"height": 224, "width": 224},
resample=data.get("resample"),
do_center_crop=data.get("do_center_crop", False),
crop_size=data.get("crop_size") or data.get("size") or {"height": 224, "width": 224},
do_rescale=data.get("do_rescale", True),
rescale_factor=data.get("rescale_factor", 1 / 255),
do_normalize=data.get("do_normalize", True),
image_mean=data.get("image_mean"),
image_std=data.get("image_std"),
do_convert_rgb=data.get("do_convert_rgb"),
)
def __call__(
self,
images: Union[Image.Image, np.ndarray, torch.Tensor, List[Any]],
do_resize: Optional[bool] = None,
size: Optional[Dict[str, int]] = None,
resample: Optional[int] = None,
do_center_crop: Optional[bool] = None,
crop_size: Optional[Dict[str, int]] = None,
do_rescale: Optional[bool] = None,
rescale_factor: Optional[float] = None,
do_normalize: Optional[bool] = None,
image_mean: Optional[Iterable[float]] = None,
image_std: Optional[Iterable[float]] = None,
do_convert_rgb: Optional[bool] = None,
return_tensors: Optional[Union[str, Any]] = "pt",
data_format: Optional[str] = "channels_first",
input_data_format: Optional[str] = None,
**kwargs,
) -> Dict[str, Any]:
do_resize = self.do_resize if do_resize is None else do_resize
size = self.size if size is None else size
resample = self.resample if resample is None else resample
do_center_crop = self.do_center_crop if do_center_crop is None else do_center_crop
crop_size = self.crop_size if crop_size is None else crop_size
do_rescale = self.do_rescale if do_rescale is None else do_rescale
rescale_factor = self.rescale_factor if rescale_factor is None else rescale_factor
do_normalize = self.do_normalize if do_normalize is None else do_normalize
image_mean = list(self.image_mean if image_mean is None else image_mean)
image_std = list(self.image_std if image_std is None else image_std)
do_convert_rgb = self.do_convert_rgb if do_convert_rgb is None else do_convert_rgb
resample = _resolve_resample(resample)
want_torch = _normalize_return_tensors(return_tensors) in ("pt", "pytorch", "tensortype.pytorch")
processed: List[np.ndarray] = []
for image in _as_list(images):
if isinstance(image, Image.Image):
img = image
if do_convert_rgb:
img = ImageOps.exif_transpose(img).convert("RGB")
else:
arr = _to_numpy(image)
input_fmt = input_data_format or _infer_input_format(arr)
arr = _to_channels_last(arr, input_fmt)
img = Image.fromarray(arr.astype(np.uint8))
if do_convert_rgb:
img = img.convert("RGB")
if do_resize:
out_h, out_w = _compute_resize_size((img.size[1], img.size[0]), size)
img = img.resize((out_w, out_h), resample=resample)
if do_center_crop:
img = _center_crop_pil(img, crop_size)
arr = np.array(img).astype(np.float32)
if do_rescale:
arr = arr * float(rescale_factor)
if do_normalize:
mean = np.array(image_mean, dtype=np.float32)
std = np.array(image_std, dtype=np.float32)
arr = (arr - mean) / std
if data_format in ("channels_first", "first"):
arr = _to_channels_first(arr, "channels_last")
processed.append(arr)
batch = np.stack(processed, axis=0)
if want_torch:
batch = torch.from_numpy(batch).float()
return {"pixel_values": batch}