Download wevisone_model.py from paralaif/ComfyUI-WeVisOne-VL: direct link, hf CLI and curl.
- Browser
- Download file 22.7 kB
-
https://huggingface.co/paralaif/ComfyUI-WeVisOne-VL/resolve/main/wevisone_model.py
- Command line
-
hf download hf://paralaif/ComfyUI-WeVisOne-VL/wevisone_model.py
-
curl -L -o wevisone_model.py https://huggingface.co/paralaif/ComfyUI-WeVisOne-VL/resolve/main/wevisone_model.py
22.7 kB
| import math | |
| from dataclasses import dataclass | |
| import torch | |
| import torch.nn as nn | |
| import torch.nn.functional as F | |
| import comfy.model_management | |
| import comfy.ops | |
| import comfy.text_encoders.qwen_vl | |
| from comfy import sd1_clip | |
| from comfy.ldm.rt_detr.rtdetr_v4 import SelfAttention, _deformable_attn_v2 | |
| from comfy.text_encoders.llama import Llama2_, Qwen3VL_4BConfig | |
| from comfy.text_encoders.qwen3vl import QWEN3VL_VISION, QWEN3VL_VISION_COMMON, Qwen3VL, Qwen3VLClipModel, Qwen3VLTokenizer, Qwen3VLVisionModel | |
| # Tokens appended to the Qwen vocabulary, in id order starting at 151669. | |
| ADDED_TOKENS = ["<{}>".format(i) for i in range(1001)] + ["<ref>", "</ref>", "<box>", "</box>", "<point>", "</point>", "<polygon>", "</polygon>", "<OBJ>"] | |
| OBJ_TOKEN = 152678 | |
| SYSTEM_PROMPT = "You are a helpful assistant." | |
| MIN_PIXELS = 16 * 32 * 32 | |
| MAX_PIXELS = 2025 * 32 * 32 | |
| FEATURE_LAYERS = (5, 11, 17, 23) | |
| CNN_SIZE = 1024 | |
| # Peak activations of one mask query at the 1024x1024 decoder canvas. | |
| DECODER_MEMORY = 2 * 1024 ** 3 | |
| class WeVisOneConfig(Qwen3VL_4BConfig): | |
| vocab_size: int = 152680 | |
| lm_head: bool = True | |
| # Pixel decoder (mask_decoder.pt), hidden 192, mask features 64, 4 ViT levels, 6 query layers. | |
| def group_norm(channels, groups, device, dtype, ops): | |
| groups = min(groups, channels) | |
| while channels % groups: | |
| groups -= 1 | |
| return ops.GroupNorm(groups, channels, device=device, dtype=dtype) | |
| def fourier(values, num_freqs=32): | |
| freqs = 2.0 ** torch.arange(num_freqs, device=values.device, dtype=torch.float32) | |
| phase = 2.0 * math.pi * values[..., None] * freqs | |
| return torch.cat([phase.sin(), phase.cos()], dim=-1) | |
| def sine_position(h, w, dim, device): | |
| half = dim // 2 | |
| y = (torch.arange(h, device=device, dtype=torch.float32) + 0.5) / h * (2.0 * math.pi) | |
| x = (torch.arange(w, device=device, dtype=torch.float32) + 0.5) / w * (2.0 * math.pi) | |
| yy, xx = torch.meshgrid(y, x, indexing="ij") | |
| dim_t = 10000.0 ** (2.0 * (torch.arange(half, device=device, dtype=torch.float32) // 2) / half) | |
| pos_x = xx[..., None] / dim_t | |
| pos_y = yy[..., None] / dim_t | |
| pos_x = torch.stack([pos_x[..., 0::2].sin(), pos_x[..., 1::2].cos()], dim=-1).flatten(-2) | |
| pos_y = torch.stack([pos_y[..., 0::2].sin(), pos_y[..., 1::2].cos()], dim=-1).flatten(-2) | |
| return torch.cat([pos_y, pos_x], dim=-1).reshape(h * w, dim) | |
| def resize(x, size): | |
| if tuple(x.shape[-2:]) == tuple(size): | |
| return x | |
| return F.interpolate(x, size=size, mode="bilinear", align_corners=False) | |
| class ConvGN(nn.Sequential): | |
| def __init__(self, in_ch, out_ch, kernel, groups, stride=1, act=True, device=None, dtype=None, ops=None): | |
| layers = [ops.Conv2d(in_ch, out_ch, kernel, stride=stride, padding=kernel // 2, bias=False, device=device, dtype=dtype), group_norm(out_ch, groups, device, dtype, ops)] | |
| if act: | |
| layers.append(nn.GELU()) | |
| super().__init__(*layers) | |
| class BoxFourierEncoder(nn.Module): | |
| def __init__(self, hidden, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.mlp = nn.Sequential(ops.Linear(4 * 64, hidden, device=device, dtype=dtype), nn.GELU(), ops.Linear(hidden, hidden, device=device, dtype=dtype)) | |
| def forward(self, boxes): | |
| x1, y1, x2, y2 = boxes.unbind(-1) | |
| w = 0.2 * (x2 - x1).clamp_min(1e-6).log() + 0.5 | |
| h = 0.2 * (y2 - y1).clamp_min(1e-6).log() + 0.5 | |
| return self.mlp(torch.cat([fourier(x1), fourier(y1), fourier(w), fourier(h)], dim=-1)) | |
| class MaskQueryBuilder(nn.Module): | |
| def __init__(self, llm_dim, hidden, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.content_proj = ops.Linear(llm_dim, hidden, device=device, dtype=dtype) | |
| self.box_position = BoxFourierEncoder(hidden, device=device, dtype=dtype, ops=ops) | |
| self.content_norm = ops.LayerNorm(hidden, device=device, dtype=dtype) | |
| self.position_norm = ops.LayerNorm(hidden, device=device, dtype=dtype) | |
| def forward(self, t_seg, boxes): | |
| return self.content_norm(self.content_proj(t_seg)), self.position_norm(self.box_position(boxes)) | |
| class SpatialConvEncoderLayer(nn.Module): | |
| def __init__(self, d=192, ffn=768, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.depthwise = ops.Conv2d(d, d, 3, padding=1, groups=d, bias=False, device=device, dtype=dtype) | |
| self.pointwise = ops.Conv2d(d, d, 1, bias=False, device=device, dtype=dtype) | |
| self.norm1 = ops.LayerNorm(d, device=device, dtype=dtype) | |
| self.linear1 = ops.Linear(d, ffn, device=device, dtype=dtype) | |
| self.linear2 = ops.Linear(ffn, d, device=device, dtype=dtype) | |
| self.norm2 = ops.LayerNorm(d, device=device, dtype=dtype) | |
| def forward(self, src, pos, h, w): | |
| levels = (src + pos).transpose(1, 2).unflatten(2, (-1, h, w)).transpose(1, 2).flatten(0, 1) | |
| mixed = self.pointwise(F.gelu(self.depthwise(levels))) | |
| mixed = mixed.unflatten(0, (src.shape[0], -1)).flatten(3).permute(0, 1, 3, 2).flatten(1, 2) | |
| src = self.norm1(src + mixed) | |
| return self.norm2(src + self.linear2(F.gelu(self.linear1(src)))) | |
| class VisualEncoder(nn.Module): | |
| def __init__(self, in_dim, d=192, levels=4, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.input_proj = nn.ModuleList([ConvGN(in_dim, d, 1, 32, act=False, device=device, dtype=dtype, ops=ops) for _ in range(levels)]) | |
| self.level_embed = nn.Parameter(torch.empty(levels, d, device=device, dtype=dtype)) | |
| self.encoder = nn.Module() | |
| self.encoder.layers = nn.ModuleList([SpatialConvEncoderLayer(d, device=device, dtype=dtype, ops=ops) for _ in range(3)]) | |
| def forward(self, features): | |
| # All levels share the pre-merge ViT grid. | |
| h, w = features[0].shape[-2:] | |
| projected = [proj(f).flatten(2).transpose(1, 2) for proj, f in zip(self.input_proj, features)] | |
| src = torch.cat(projected, dim=1) | |
| pos = sine_position(h, w, src.shape[-1], src.device).to(src) | |
| level_embed = comfy.ops.cast_to_input(self.level_embed, src) | |
| pos = torch.cat([pos + level_embed[i] for i in range(len(projected))], dim=0).unsqueeze(0) | |
| for layer in self.encoder.layers: | |
| src = layer(src, pos, h, w) | |
| maps = [chunk.transpose(1, 2).unflatten(2, (h, w)) for chunk in src.chunk(len(projected), dim=1)] | |
| return maps, src, h, w | |
| class MSDeformableAttention(nn.Module): | |
| def __init__(self, d=192, heads=8, levels=4, points=4, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.heads, self.levels, self.points = heads, levels, points | |
| self.sampling_offsets = ops.Linear(d, heads * levels * points * 2, device=device, dtype=dtype) | |
| self.attention_weights = ops.Linear(d, heads * levels * points, device=device, dtype=dtype) | |
| self.value_proj = ops.Linear(d, d, device=device, dtype=dtype) | |
| self.output_proj = ops.Linear(d, d, device=device, dtype=dtype) | |
| def forward(self, query, references, memory, h, w): | |
| b, q, _ = query.shape | |
| value = self.value_proj(memory).unflatten(2, (self.heads, -1)).permute(0, 2, 3, 1).flatten(0, 1) | |
| value = list(value.unflatten(2, (self.levels, h, w)).unbind(2)) | |
| offsets = self.sampling_offsets(query).view(b, q, self.heads, self.levels * self.points, 2) | |
| weights = self.attention_weights(query).view(b, q, self.heads, -1).softmax(-1) | |
| locations = references[:, :, None, None, :2] + offsets / self.points * references[:, :, None, None, 2:] * 0.5 | |
| out = _deformable_attn_v2(value, [(h, w)] * self.levels, locations, weights, [self.points] * self.levels) | |
| return self.output_proj(out) | |
| class QueryDecoderLayer(nn.Module): | |
| def __init__(self, d=192, heads=8, ffn=768, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.self_attn = SelfAttention(d, heads, device=device, dtype=dtype, operations=ops) | |
| self.cross_attn = MSDeformableAttention(d, heads, device=device, dtype=dtype, ops=ops) | |
| self.norm1 = ops.LayerNorm(d, device=device, dtype=dtype) | |
| self.norm2 = ops.LayerNorm(d, device=device, dtype=dtype) | |
| self.norm3 = ops.LayerNorm(d, device=device, dtype=dtype) | |
| self.linear1 = ops.Linear(d, ffn, device=device, dtype=dtype) | |
| self.linear2 = ops.Linear(ffn, d, device=device, dtype=dtype) | |
| def forward(self, target, query_pos, references, memory, h, w): | |
| qk = target + query_pos | |
| target = self.norm1(target + self.self_attn(qk, qk, target)) | |
| target = self.norm2(target + self.cross_attn(target + query_pos, references, memory, h, w)) | |
| return self.norm3(target + self.linear2(F.gelu(self.linear1(target)))) | |
| class PixelShuffleStage(nn.Module): | |
| def __init__(self, in_dim, out_dim, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.conv = ops.Conv2d(in_dim, out_dim * 4, 3, padding=1, device=device, dtype=dtype) | |
| def forward(self, x): | |
| return F.gelu(F.pixel_shuffle(self.conv(x), 2)) | |
| class ResidualPixelShuffleStage(PixelShuffleStage): | |
| def forward(self, x): | |
| return F.interpolate(x, scale_factor=2.0, mode="bilinear", align_corners=False) + F.pixel_shuffle(self.conv(x), 2) | |
| class SameScaleFPN(nn.Module): | |
| def __init__(self, d, levels, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.lateral = nn.ModuleList([ops.Conv2d(d, d, 1, bias=False, device=device, dtype=dtype) for _ in range(levels)]) | |
| self.output = nn.ModuleList([ConvGN(d, d, 3, 32, device=device, dtype=dtype, ops=ops) for _ in range(levels - 1)]) | |
| def forward(self, features): | |
| fused = self.lateral[-1](features[-1]) | |
| for out, level in zip(self.output, range(len(features) - 2, -1, -1)): | |
| fused = out(fused + self.lateral[level](features[level])) | |
| return fused | |
| class PixelFusion(nn.Module): | |
| def __init__(self, d=192, mask_dim=64, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.fpn = SameScaleFPN(d, 4, device=device, dtype=dtype, ops=ops) | |
| self.upsample = nn.Sequential(PixelShuffleStage(d, d, device=device, dtype=dtype, ops=ops), PixelShuffleStage(d, mask_dim, device=device, dtype=dtype, ops=ops)) | |
| self.s2_upsample = ResidualPixelShuffleStage(mask_dim, mask_dim, device=device, dtype=dtype, ops=ops) | |
| self.cnn = nn.Module() | |
| self.cnn.net = nn.Sequential(*ConvGN(3, 128, 3, 32, stride=2, device=device, dtype=dtype, ops=ops), *ConvGN(128, mask_dim, 3, 32, device=device, dtype=dtype, ops=ops)) | |
| self.fpn_proj = ops.Conv2d(mask_dim, mask_dim, 1, bias=False, device=device, dtype=dtype) | |
| self.cnn_proj = ops.Conv2d(mask_dim, mask_dim, 1, bias=False, device=device, dtype=dtype) | |
| self.smooth = nn.Sequential(group_norm(mask_dim, 32, device, dtype, ops), nn.GELU(), ops.Conv2d(mask_dim, mask_dim, 3, padding=1, bias=False, device=device, dtype=dtype)) | |
| def forward(self, image, levels): | |
| fpn = self.upsample(self.fpn(levels)) | |
| cnn = self.cnn.net(image) | |
| target = (cnn.shape[-2] // 2, cnn.shape[-1] // 2) | |
| pool = (min(fpn.shape[-2], target[0]), min(fpn.shape[-1], target[1])) | |
| if pool != tuple(fpn.shape[-2:]): | |
| fpn = F.adaptive_avg_pool2d(fpn, pool) | |
| fpn = resize(fpn, target) | |
| return self.s2_upsample(self.smooth(self.fpn_proj(fpn))) + self.cnn_proj(cnn) | |
| class AlphaResidualBlock(nn.Module): | |
| def __init__(self, ch, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.net = nn.Sequential(group_norm(ch, 32, device, dtype, ops), nn.GELU(), ops.Conv2d(ch, ch, 3, padding=1, bias=False, device=device, dtype=dtype), | |
| group_norm(ch, 32, device, dtype, ops), nn.GELU(), ops.Conv2d(ch, ch, 3, padding=1, bias=False, device=device, dtype=dtype)) | |
| def forward(self, x): | |
| return x + self.net(x) | |
| class AlphaCascadeHead(nn.Module): | |
| # Refines the S/2 coarse logits at S/4, S/2 and S/1 with RGB, semantic features and the previous alpha. | |
| def __init__(self, d=192, mask_dim=64, ch=48, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| project = lambda i: ConvGN(i, ch, 3, 32, device=device, dtype=dtype, ops=ops) | |
| stack = lambda: nn.Sequential(*[AlphaResidualBlock(ch, device=device, dtype=dtype, ops=ops) for _ in range(2)]) | |
| self.semantic = project(mask_dim) | |
| self.rgb_s4 = project(3) | |
| self.rgb_s2 = project(3) | |
| self.rgb_s1 = project(3) | |
| self.coarse_s4 = project(1) | |
| self.coarse_s2 = project(1) | |
| self.previous_s2 = project(1) | |
| self.previous_s1 = project(1) | |
| self.fuse_s4 = ops.Conv2d(3 * ch, ch, 1, bias=False, device=device, dtype=dtype) | |
| self.fuse_s2 = ops.Conv2d(5 * ch, ch, 1, bias=False, device=device, dtype=dtype) | |
| self.fuse_s1 = ops.Conv2d(4 * ch, ch, 1, bias=False, device=device, dtype=dtype) | |
| self.blocks_s4 = stack() | |
| self.blocks_s2 = stack() | |
| self.blocks_s1 = stack() | |
| self.up_s4_to_s2 = ops.ConvTranspose2d(ch, ch, 4, stride=2, padding=1, bias=False, device=device, dtype=dtype) | |
| self.up_s2_to_s1 = ops.ConvTranspose2d(ch, ch, 4, stride=2, padding=1, bias=False, device=device, dtype=dtype) | |
| self.query_s4 = ops.Linear(d, 2 * ch, device=device, dtype=dtype) | |
| self.query_s2 = ops.Linear(d, 2 * ch, device=device, dtype=dtype) | |
| self.query_s1 = ops.Linear(d, 2 * ch, device=device, dtype=dtype) | |
| self.output_s4 = ops.Conv2d(ch, 1, 1, device=device, dtype=dtype) | |
| self.output_s2 = ops.Conv2d(ch, 1, 1, device=device, dtype=dtype) | |
| self.output_s1 = ops.Conv2d(ch, 1, 1, device=device, dtype=dtype) | |
| def condition(x, queries, affine): | |
| scale, bias = affine(queries)[:, :, None, None].chunk(2, dim=1) | |
| return x * (1.0 + 0.1 * scale.tanh()) + 0.1 * bias | |
| def forward(self, image, pixel_feature, queries, coarse): | |
| n = queries.shape[0] | |
| s2 = tuple(coarse.shape[-2:]) | |
| s4 = (max(1, s2[0] // 2), max(1, s2[1] // 2)) | |
| s1 = tuple(image.shape[-2:]) | |
| coarse = coarse[:, None] | |
| semantic_s2 = self.semantic(pixel_feature) | |
| coarse_s4 = resize(coarse, s4) | |
| x = self.fuse_s4(torch.cat([resize(semantic_s2, s4).expand(n, -1, -1, -1), self.rgb_s4(resize(image, s4)).expand(n, -1, -1, -1), self.coarse_s4(coarse_s4)], dim=1)) | |
| feature_s4 = self.blocks_s4(self.condition(x, queries, self.query_s4)) | |
| logits_s4 = coarse_s4 + self.output_s4(feature_s4) | |
| x = self.fuse_s2(torch.cat([self.up_s4_to_s2(feature_s4), semantic_s2.expand(n, -1, -1, -1), self.rgb_s2(resize(image, s2)).expand(n, -1, -1, -1), | |
| self.coarse_s2(coarse), self.previous_s2(resize(logits_s4, s2).sigmoid())], dim=1)) | |
| feature_s2 = self.blocks_s2(self.condition(x, queries, self.query_s2)) | |
| logits_s2 = coarse + self.output_s2(feature_s2) | |
| previous_s1 = resize(logits_s2, s1) | |
| x = self.fuse_s1(torch.cat([self.up_s2_to_s1(feature_s2), resize(semantic_s2, s1).expand(n, -1, -1, -1), self.rgb_s1(image).expand(n, -1, -1, -1), | |
| self.previous_s1(previous_s1.sigmoid())], dim=1)) | |
| feature_s1 = self.blocks_s1(self.condition(x, queries, self.query_s1)) | |
| return (previous_s1 + self.output_s1(feature_s1))[:, 0] | |
| class WeVisOneMaskDecoder(nn.Module): | |
| def __init__(self, visual_dim=1024, llm_dim=2560, d=192, mask_dim=64, device=None, dtype=None, ops=None): | |
| super().__init__() | |
| self.visual_encoder = VisualEncoder(visual_dim, d, device=device, dtype=dtype, ops=ops) | |
| self.query_builder = MaskQueryBuilder(llm_dim, d, device=device, dtype=dtype, ops=ops) | |
| self.transformer = nn.Module() | |
| self.transformer.layers = nn.ModuleList([QueryDecoderLayer(d, device=device, dtype=dtype, ops=ops) for _ in range(6)]) | |
| self.pixel_fusion = PixelFusion(d, mask_dim, device=device, dtype=dtype, ops=ops) | |
| self.mask_predictor = nn.Module() | |
| self.mask_predictor.kernel = nn.Sequential(ops.Linear(d, d, device=device, dtype=dtype), nn.GELU(), ops.Linear(d, mask_dim + 1, device=device, dtype=dtype)) | |
| self.highres_refine = AlphaCascadeHead(d, mask_dim, device=device, dtype=dtype, ops=ops) | |
| def forward(self, image, visual_features, t_seg, boxes): | |
| # image [1, 3, 1024, 1024] in 0..1, visual_features 4x [1, C, gh, gw], t_seg [N, llm_dim], boxes [N, 4] normalized xyxy | |
| levels, memory, h, w = self.visual_encoder(visual_features) | |
| target, query_pos = self.query_builder(t_seg, boxes) | |
| x1, y1, x2, y2 = boxes.clamp(0.0, 1.0).unbind(-1) | |
| references = torch.stack([(x1 + x2) * 0.5, (y1 + y2) * 0.5, (x2 - x1).clamp_min(1e-4), (y2 - y1).clamp_min(1e-4)], dim=-1)[None] | |
| target, query_pos = target[None], query_pos[None] | |
| for layer in self.transformer.layers: | |
| target = layer(target, query_pos, references, memory, h, w) | |
| queries = target[0] | |
| pixel_feature = self.pixel_fusion(image, levels) | |
| params = self.mask_predictor.kernel(queries) | |
| coarse = torch.einsum("qc,chw->qhw", params[:, :-1], pixel_feature[0]) + params[:, -1, None, None] | |
| return self.highres_refine(image, pixel_feature, queries, coarse) | |
| class WeVisOneQwen3VL(Qwen3VL): | |
| model_type = "qwen3vl_4b" | |
| def __init__(self, config_dict, dtype, device, operations): | |
| super(Qwen3VL, self).__init__() | |
| config = WeVisOneConfig(**config_dict) | |
| self.num_layers = config.num_hidden_layers | |
| self.model = Llama2_(config, device=device, dtype=dtype, ops=operations) | |
| vision_config = {**QWEN3VL_VISION_COMMON, **QWEN3VL_VISION[self.model_type], "out_hidden_size": config.hidden_size} | |
| self.visual = Qwen3VLVisionModel(vision_config, device=device, dtype=dtype, ops=operations) | |
| # The released decoder weights are fp32 and the reference runs it in fp32. It is never quantized, so it | |
| # skips the mixed precision ops of quantized checkpoints, which would store its linears in the compute dtype. | |
| self.mask_decoder = WeVisOneMaskDecoder(device=device, dtype=torch.float32, ops=comfy.ops.manual_cast) | |
| self.dtype = dtype | |
| def preprocess_embed(self, embed, device): | |
| if embed["type"] != "image": | |
| return None, None | |
| image, grid = comfy.text_encoders.qwen_vl.process_qwen2vl_images(embed["data"], min_pixels=MIN_PIXELS, max_pixels=MAX_PIXELS, patch_size=16, image_mean=[0.5, 0.5, 0.5], image_std=[0.5, 0.5, 0.5], interpolation="bicubic") | |
| # The pixel decoder reads layers 5/11/17/23 at the inputs of the DeepStack mergers and the final merger. | |
| premerge = [] | |
| hook = lambda module, args: premerge.append(args[0]) | |
| handles = [m.register_forward_pre_hook(hook) for m in list(self.visual.deepstack_merger_list) + [self.visual.merger]] | |
| try: | |
| merged, deepstack = self.visual(image.to(device, dtype=torch.float32), grid) | |
| finally: | |
| for h in handles: | |
| h.remove() | |
| return merged, {"grid": grid, "deepstack": deepstack, "premerge": premerge} | |
| def decode_masks(self, embeds, embeds_info, input_ids, image, boxes): | |
| # Teacher-forced pass over prompt + response: the <OBJ> hidden states (last layer, before the final norm) seed the mask queries. | |
| position_ids, visual_pos_masks, deepstack = self.build_image_inputs(embeds, embeds_info) | |
| _, hidden = self.model(None, embeds=embeds, intermediate_output=-1, final_layer_norm_intermediate=False, position_ids=position_ids, | |
| embeds_info=embeds_info, visual_pos_masks=visual_pos_masks, deepstack_embeds=deepstack) | |
| info = next(e for e in embeds_info if e.get("type") == "image") | |
| # The image placeholder expands to info["size"] embeddings. | |
| positions = [i + info["size"] - 1 for i, t in enumerate(input_ids) if t == OBJ_TOKEN][:len(boxes)] | |
| t_seg = hidden[0, positions].float() | |
| boxes = boxes[:len(positions)] | |
| gh, gw = (int(v) for v in info["extra"]["grid"][0, 1:]) | |
| m = self.visual.spatial_merge_size | |
| # Undo Qwen's merge-local [gh/m, gw/m, m, m] patch order. | |
| features = [f.float().reshape(gh // m, gw // m, m, m, -1).permute(4, 0, 2, 1, 3).reshape(1, -1, gh, gw) for f in info["extra"]["premerge"]] | |
| cnn_image = F.interpolate(image.movedim(-1, 1)[:, :3].to(embeds.device, torch.float32), size=(CNN_SIZE, CNN_SIZE), mode="bicubic", antialias=True, align_corners=False).clamp(0.0, 1.0) | |
| boxes = torch.tensor(boxes, device=embeds.device, dtype=torch.float32) | |
| alphas = [self.mask_decoder(cnn_image, features, t_seg[i:i + 1], boxes[i:i + 1]).sigmoid() for i in range(t_seg.shape[0])] | |
| return F.interpolate(torch.cat(alphas)[None], size=image.shape[1:3], mode="bilinear", align_corners=False)[0] | |
| class WeVisOneClipModel(Qwen3VLClipModel): | |
| def __init__(self, device="cpu", layer="hidden", layer_idx=-1, dtype=None, attention_mask=True, model_options={}): | |
| sd1_clip.SDClipModel.__init__(self, device=device, layer=layer, layer_idx=layer_idx, textmodel_json_config={}, dtype=dtype, special_tokens={"pad": 151643}, | |
| layer_norm_hidden_state=False, model_class=WeVisOneQwen3VL, enable_attention_masks=attention_mask, | |
| return_attention_masks=attention_mask, model_options=model_options) | |
| def decode_masks(self, tokens, generated_ids, image, boxes): | |
| input_ids = [t[0] for t in next(iter(tokens.values()))[0]] + list(generated_ids) | |
| embeds, _, _, embeds_info = self.process_tokens([input_ids], self.execution_device) | |
| dtype = torch.bfloat16 if comfy.model_management.should_use_bf16(self.execution_device) else torch.float32 | |
| return self.transformer.decode_masks(embeds.to(dtype), embeds_info, input_ids, image, boxes) | |
| class WeVisOneTEModel(sd1_clip.SD1ClipModel): | |
| def __init__(self, device="cpu", dtype=None, model_options={}): | |
| super().__init__(device=device, dtype=dtype, name="qwen3vl_4b", clip_model=WeVisOneClipModel, model_options=model_options) | |
| def decode_masks(self, tokens, generated_ids, image, boxes): | |
| return self.qwen3vl_4b.decode_masks(tokens, generated_ids, image, boxes) | |
| class WeVisOneTokenizer(Qwen3VLTokenizer): | |
| def __init__(self, embedding_directory=None, tokenizer_data={}): | |
| super().__init__(embedding_directory=embedding_directory, tokenizer_data=tokenizer_data, model_type="qwen3vl_4b") | |
| self.qwen3vl_4b.tokenizer.add_tokens(ADDED_TOKENS) | |