Expose GeoText embeddings and region boxes
Browse files
infer.py
CHANGED
|
@@ -136,14 +136,16 @@ def load_model(source_dir: Path, model_dir: Path, device: torch.device):
|
|
| 136 |
|
| 137 |
|
| 138 |
@torch.inference_mode()
|
| 139 |
-
def
|
| 140 |
model,
|
| 141 |
tokenizer,
|
| 142 |
config: Dict[str, Any],
|
| 143 |
image: Image.Image,
|
| 144 |
queries: List[str],
|
| 145 |
device: torch.device,
|
| 146 |
-
|
|
|
|
|
|
|
| 147 |
image_transform = transforms.Compose(
|
| 148 |
[
|
| 149 |
transforms.Resize(
|
|
@@ -166,14 +168,50 @@ def rank_queries(
|
|
| 166 |
text_embeds = model.get_text_embeds(text_input.input_ids, text_input.attention_mask)
|
| 167 |
image_features, text_features = model.get_features(image_embeds, text_embeds)
|
| 168 |
scores = (image_features @ text_features.T).flatten().float().cpu().tolist()
|
| 169 |
-
|
| 170 |
-
(
|
| 171 |
-
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
|
| 175 |
-
|
| 176 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 177 |
|
| 178 |
|
| 179 |
def main() -> None:
|
|
|
|
| 136 |
|
| 137 |
|
| 138 |
@torch.inference_mode()
|
| 139 |
+
def model_outputs(
|
| 140 |
model,
|
| 141 |
tokenizer,
|
| 142 |
config: Dict[str, Any],
|
| 143 |
image: Image.Image,
|
| 144 |
queries: List[str],
|
| 145 |
device: torch.device,
|
| 146 |
+
include_embeddings: bool = False,
|
| 147 |
+
include_bboxes: bool = False,
|
| 148 |
+
) -> Dict[str, Any]:
|
| 149 |
image_transform = transforms.Compose(
|
| 150 |
[
|
| 151 |
transforms.Resize(
|
|
|
|
| 168 |
text_embeds = model.get_text_embeds(text_input.input_ids, text_input.attention_mask)
|
| 169 |
image_features, text_features = model.get_features(image_embeds, text_embeds)
|
| 170 |
scores = (image_features @ text_features.T).flatten().float().cpu().tolist()
|
| 171 |
+
result = {
|
| 172 |
+
"ranked_queries": sorted(
|
| 173 |
+
(
|
| 174 |
+
{"query": query, "similarity": score}
|
| 175 |
+
for query, score in zip(queries, scores)
|
| 176 |
+
),
|
| 177 |
+
key=lambda item: item["similarity"],
|
| 178 |
+
reverse=True,
|
| 179 |
+
)
|
| 180 |
+
}
|
| 181 |
+
|
| 182 |
+
if include_embeddings:
|
| 183 |
+
result["image_embedding"] = image_features[0].float().cpu().tolist()
|
| 184 |
+
result["text_embeddings"] = [
|
| 185 |
+
{"query": query, "embedding": embedding.float().cpu().tolist()}
|
| 186 |
+
for query, embedding in zip(queries, text_features)
|
| 187 |
+
]
|
| 188 |
+
|
| 189 |
+
if include_bboxes:
|
| 190 |
+
repeated_image_embeds = image_embeds.expand(len(queries), -1, -1)
|
| 191 |
+
boxes = model.predict_bbox(
|
| 192 |
+
repeated_image_embeds, text_embeds, text_input.attention_mask
|
| 193 |
+
).float().cpu().tolist()
|
| 194 |
+
result["text_conditioned_boxes"] = [
|
| 195 |
+
{
|
| 196 |
+
"query": query,
|
| 197 |
+
"format": "normalized_cx_cy_width_height",
|
| 198 |
+
"box": box,
|
| 199 |
+
}
|
| 200 |
+
for query, box in zip(queries, boxes)
|
| 201 |
+
]
|
| 202 |
+
|
| 203 |
+
return result
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def rank_queries(
|
| 207 |
+
model,
|
| 208 |
+
tokenizer,
|
| 209 |
+
config: Dict[str, Any],
|
| 210 |
+
image: Image.Image,
|
| 211 |
+
queries: List[str],
|
| 212 |
+
device: torch.device,
|
| 213 |
+
):
|
| 214 |
+
return model_outputs(model, tokenizer, config, image, queries, device)["ranked_queries"]
|
| 215 |
|
| 216 |
|
| 217 |
def main() -> None:
|
server.py
CHANGED
|
@@ -9,13 +9,17 @@ from typing import Any, Dict, List
|
|
| 9 |
|
| 10 |
import torch
|
| 11 |
from fastapi import FastAPI, HTTPException
|
| 12 |
-
from infer import fetch_rgb_geotiff, load_model,
|
| 13 |
from pydantic import BaseModel, Field
|
| 14 |
|
| 15 |
|
| 16 |
class InferenceRequest(BaseModel):
|
| 17 |
image_url: str = Field(..., description="Public RGB GeoTIFF/COG URL")
|
| 18 |
queries: List[str] = Field(..., min_items=1, description="Text queries to rank")
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
|
| 21 |
@asynccontextmanager
|
|
@@ -47,18 +51,23 @@ def infer(request: InferenceRequest) -> Dict[str, Any]:
|
|
| 47 |
try:
|
| 48 |
image, image_metadata = fetch_rgb_geotiff(request.image_url)
|
| 49 |
runtime = app.state.runtime
|
| 50 |
-
|
| 51 |
"image_url": request.image_url,
|
| 52 |
"image_metadata": image_metadata,
|
| 53 |
"device": str(runtime["device"]),
|
| 54 |
-
|
|
|
|
|
|
|
| 55 |
runtime["model"],
|
| 56 |
runtime["tokenizer"],
|
| 57 |
runtime["config"],
|
| 58 |
image,
|
| 59 |
request.queries,
|
| 60 |
runtime["device"],
|
| 61 |
-
|
| 62 |
-
|
|
|
|
|
|
|
|
|
|
| 63 |
except Exception as error:
|
| 64 |
raise HTTPException(status_code=422, detail=str(error)) from error
|
|
|
|
| 9 |
|
| 10 |
import torch
|
| 11 |
from fastapi import FastAPI, HTTPException
|
| 12 |
+
from infer import fetch_rgb_geotiff, load_model, model_outputs
|
| 13 |
from pydantic import BaseModel, Field
|
| 14 |
|
| 15 |
|
| 16 |
class InferenceRequest(BaseModel):
|
| 17 |
image_url: str = Field(..., description="Public RGB GeoTIFF/COG URL")
|
| 18 |
queries: List[str] = Field(..., min_items=1, description="Text queries to rank")
|
| 19 |
+
include_embeddings: bool = Field(
|
| 20 |
+
False, description="Return 256-dimensional image and text vectors"
|
| 21 |
+
)
|
| 22 |
+
include_bboxes: bool = Field(False, description="Return text-conditioned normalized boxes")
|
| 23 |
|
| 24 |
|
| 25 |
@asynccontextmanager
|
|
|
|
| 51 |
try:
|
| 52 |
image, image_metadata = fetch_rgb_geotiff(request.image_url)
|
| 53 |
runtime = app.state.runtime
|
| 54 |
+
result = {
|
| 55 |
"image_url": request.image_url,
|
| 56 |
"image_metadata": image_metadata,
|
| 57 |
"device": str(runtime["device"]),
|
| 58 |
+
}
|
| 59 |
+
result.update(
|
| 60 |
+
model_outputs(
|
| 61 |
runtime["model"],
|
| 62 |
runtime["tokenizer"],
|
| 63 |
runtime["config"],
|
| 64 |
image,
|
| 65 |
request.queries,
|
| 66 |
runtime["device"],
|
| 67 |
+
include_embeddings=request.include_embeddings,
|
| 68 |
+
include_bboxes=request.include_bboxes,
|
| 69 |
+
)
|
| 70 |
+
)
|
| 71 |
+
return result
|
| 72 |
except Exception as error:
|
| 73 |
raise HTTPException(status_code=422, detail=str(error)) from error
|