Image Segmentation
Transformers
Safetensors
English
falcon_x
feature-extraction
falcon-x
vision-language
custom_code
Instructions to use JonathanJMK/FALCON with libraries, inference providers, notebooks, and local apps. Follow these links to get started.
- Libraries
- Transformers
How to use JonathanJMK/FALCON with Transformers:
# Use a pipeline as a high-level helper from transformers import pipeline pipe = pipeline("image-segmentation", model="JonathanJMK/FALCON", trust_remote_code=True)# pip install -U transformers accelerate # Load model directly from transformers import AutoModel model = AutoModel.from_pretrained("JonathanJMK/FALCON", trust_remote_code=True, device_map="auto") - Notebooks
- Google Colab
- Kaggle
File size: 8,776 Bytes
c645013 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 | """Patch aggregation and mask-aware region encoding for Falcon.
The detector contract used here is deliberately small: boxes are ``xyxy`` in
original-image coordinates and masks are either logits or probabilities. The
module never guesses which representation a mask uses and never applies a
sigmoid; it makes a deterministic binary mask with the declared threshold.
"""
from __future__ import annotations
from collections.abc import Sequence
import torch
from torch import Tensor, nn
from torch.nn import functional as F
from torchvision.ops import roi_align
class PatchTokenAggregator(nn.Module):
"""Concatenate each non-overlapping 2x2 patch block, then project it.
DINOv2 emits patch tokens in row-major order. For a block, concatenation
order is top-left, top-right, bottom-left, bottom-right.
"""
def __init__(self, input_dim: int, output_dim: int) -> None:
super().__init__()
self.input_dim = input_dim
self.output_dim = output_dim
self.projection = nn.Linear(4 * input_dim, output_dim)
def forward(self, patch_tokens: Tensor, grid_size: tuple[int, int]) -> Tensor:
if patch_tokens.ndim != 3:
raise ValueError("patch_tokens must have shape [batch, patches, channels]")
batch, count, channels = patch_tokens.shape
height, width = grid_size
if channels != self.input_dim:
raise ValueError(f"expected {self.input_dim} channels, received {channels}")
if count != height * width:
raise ValueError(f"{count} tokens cannot form the declared {height}x{width} grid")
if height % 2 or width % 2:
raise ValueError("the DINOv2 patch grid must be even for 2x2 aggregation")
grid = patch_tokens.reshape(batch, height // 2, 2, width // 2, 2, channels)
blocks = grid.permute(0, 1, 3, 2, 4, 5).reshape(
batch, height // 2, width // 2, 4 * channels
)
return self.projection(blocks).permute(0, 3, 1, 2).contiguous()
class MaskAwareRegionEncoder(nn.Module):
"""Fuse ROI-aligned and binary-mask-pooled features by concatenation.
Invalid/padded proposals are returned as exact zero vectors. ``image_sizes``
contains ``(height, width)`` for each original detector image.
"""
def __init__(
self,
feature_dim: int,
output_dim: int,
*,
roi_size: int = 4,
sampling_ratio: int = 2,
probability_threshold: float = 0.5,
) -> None:
super().__init__()
if not 0.0 <= probability_threshold <= 1.0:
raise ValueError("probability_threshold must lie in [0, 1]")
self.feature_dim = feature_dim
self.output_dim = output_dim
self.roi_size = roi_size
self.sampling_ratio = sampling_ratio
self.probability_threshold = probability_threshold
self.projection = nn.Linear(2 * feature_dim, output_dim)
@staticmethod
def _validate_inputs(
feature_map: Tensor,
boxes: Tensor,
masks: Tensor,
image_sizes: Tensor,
valid: Tensor,
) -> None:
if feature_map.ndim != 4:
raise ValueError("feature_map must have shape [batch, channels, height, width]")
if boxes.ndim != 3 or boxes.shape[-1] != 4:
raise ValueError("boxes must have shape [batch, proposals, 4]")
if masks.ndim != 4 or masks.shape[:2] != boxes.shape[:2]:
raise ValueError("masks must have shape [batch, proposals, height, width]")
if image_sizes.shape != (feature_map.shape[0], 2):
raise ValueError("image_sizes must have shape [batch, 2] in (height, width) order")
if valid.shape != boxes.shape[:2]:
raise ValueError("valid must have shape [batch, proposals]")
if boxes.shape[0] != feature_map.shape[0]:
raise ValueError("feature_map and detector outputs must have the same batch size")
def pool_components(
self,
feature_map: Tensor,
boxes: Tensor,
masks: Tensor,
image_sizes: Tensor,
valid: Tensor | None = None,
*,
masks_are_logits: bool = False,
mask_sizes: Tensor | None = None,
) -> tuple[Tensor, Tensor, Tensor]:
"""Return valid proposal indices, ROI vectors, and mask vectors."""
if valid is None:
valid = torch.ones(boxes.shape[:2], dtype=torch.bool, device=boxes.device)
else:
valid = valid.to(dtype=torch.bool)
self._validate_inputs(feature_map, boxes, masks, image_sizes, valid)
indices = valid.nonzero(as_tuple=False)
if indices.numel() == 0:
empty = feature_map.new_empty((0, self.feature_dim))
return indices, empty, empty
device = feature_map.device
dtype = feature_map.dtype
indices = indices.to(device=device)
boxes = boxes.to(device=device, dtype=dtype)
masks = masks.to(device=device)
image_sizes = image_sizes.to(device=device, dtype=dtype)
batch_ids, proposal_ids = indices.unbind(dim=1)
selected_boxes = boxes[batch_ids, proposal_ids].clone()
source_h = image_sizes[batch_ids, 0].clamp_min(1)
source_w = image_sizes[batch_ids, 1].clamp_min(1)
feature_h, feature_w = feature_map.shape[-2:]
selected_boxes[:, 0::2] *= feature_w / source_w[:, None]
selected_boxes[:, 1::2] *= feature_h / source_h[:, None]
rois = torch.cat((batch_ids.to(dtype=dtype).unsqueeze(1), selected_boxes), dim=1)
roi_features = roi_align(
feature_map,
rois,
output_size=(self.roi_size, self.roi_size),
spatial_scale=1.0,
sampling_ratio=self.sampling_ratio,
aligned=True,
)
roi_vectors = roi_features.mean(dim=(-2, -1))
threshold = 0.0 if masks_are_logits else self.probability_threshold
if mask_sizes is None:
mask_sizes = torch.tensor([list(masks.shape[-2:])] * feature_map.shape[0])
if mask_sizes.shape != (feature_map.shape[0], 2):
raise ValueError("mask_sizes must have shape [batch, 2]")
binary_masks = feature_map.new_zeros((len(indices), 1, feature_h, feature_w))
for batch_index in range(feature_map.shape[0]):
selected = batch_ids == batch_index
if not bool(selected.any()):
continue
height, width = (int(value) for value in mask_sizes[batch_index])
if not (0 < height <= masks.shape[-2] and 0 < width <= masks.shape[-1]):
raise ValueError("mask_sizes exceed the padded canvas or are nonpositive")
selected_masks = masks[batch_index, proposal_ids[selected], :height, :width].unsqueeze(
1
)
binary = (
selected_masks
if selected_masks.dtype == torch.bool
else selected_masks >= threshold
)
binary_masks[selected] = F.interpolate(
binary.to(dtype=dtype), size=(feature_h, feature_w), mode="nearest"
).to(dtype=dtype)
selected_features = feature_map.index_select(0, batch_ids)
denominator = binary_masks.sum(dim=(-2, -1)).clamp_min(1.0)
mask_vectors = (selected_features * binary_masks).sum(dim=(-2, -1)) / denominator
return indices, roi_vectors, mask_vectors
def forward(
self,
feature_map: Tensor,
boxes: Tensor,
masks: Tensor,
image_sizes: Tensor | Sequence[tuple[int, int]],
valid: Tensor | None = None,
*,
masks_are_logits: bool = False,
mask_sizes: Tensor | None = None,
) -> Tensor:
if not isinstance(image_sizes, Tensor):
image_sizes = torch.as_tensor(image_sizes, device=feature_map.device)
if valid is None:
valid = torch.ones(boxes.shape[:2], dtype=torch.bool, device=boxes.device)
indices, roi_vectors, mask_vectors = self.pool_components(
feature_map,
boxes,
masks,
image_sizes,
valid,
masks_are_logits=masks_are_logits,
mask_sizes=mask_sizes,
)
batch, proposals = boxes.shape[:2]
flat_output = feature_map.new_zeros((batch * proposals, self.output_dim))
if indices.numel() == 0:
return flat_output.reshape(batch, proposals, self.output_dim)
fused = self.projection(torch.cat((roi_vectors, mask_vectors), dim=-1))
flat_indices = indices[:, 0] * proposals + indices[:, 1]
flat_output = flat_output.index_copy(0, flat_indices, fused)
return flat_output.reshape(batch, proposals, self.output_dim)
|