Spaces:
Sleeping
Sleeping
File size: 9,888 Bytes
8e99893 d3c5d77 0e286b8 8e99893 f229fcb 8e99893 f229fcb 8e99893 b8db19d 8e99893 b8db19d 8e99893 b8db19d 8e99893 b8db19d 8e99893 ca2efd3 8e99893 ca2efd3 8e99893 ca2efd3 8e99893 b8db19d 8e99893 b8db19d d3c5d77 8e99893 b8db19d 94f2ab2 b8db19d d3c5d77 94f2ab2 d3c5d77 94f2ab2 d3c5d77 94f2ab2 d3c5d77 0e286b8 d3c5d77 0e286b8 d3c5d77 0e286b8 d3c5d77 0e286b8 d3c5d77 0e286b8 94f2ab2 0e286b8 d3c5d77 94f2ab2 d3c5d77 94f2ab2 | 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 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 | import os
import datetime
import tempfile
import torch
import numpy as np
import pandas as pd
import json
from google import genai
from google.genai import types
from PIL import Image
from ultralytics import YOLO
from .utils import batch_iterable
# Define paths
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
ASSETS_DIR = os.path.join(BASE_DIR, 'assets')
MODEL_NAMES = [
"yoloe-11s-seg.pt",
]
for model_name in MODEL_NAMES:
# Construct the absolute path to the model file
MODEL_PATH = os.path.join(ASSETS_DIR, model_name)
# Initialize default model globally (or placeholder)
try:
# Ensure usage of the moved model file
model = YOLO(MODEL_PATH)
except Exception as e:
print(f"Warning: Failed to load model from {MODEL_PATH}. Error: {e}")
model = None
def set_classes_with_descriptions(class_description_dict, neg_class_description_dict=None, model_name='yoloe-11s-seg.pt'):
"""
Sets the class names and aggregated prompt embeddings for the model.
"""
# Load model from assets
model_path = os.path.join(ASSETS_DIR, model_name)
local_model = YOLO(model_path)
class_description_dict = dict(class_description_dict)
if neg_class_description_dict is not None:
neg_class_description_dict = dict(neg_class_description_dict)
else:
neg_class_description_dict = {}
names = tuple(class_description_dict.keys())
all_pe_s_list = []
for name in names:
descriptions = tuple(class_description_dict.get(name, []))
descriptions = [str(i).strip() for i in descriptions
if str(i).strip() != ''
]
neg_descriptions = tuple(neg_class_description_dict.get(name, []))
neg_descriptions = [str(i).strip() for i in neg_descriptions
if str(i).strip() != ''
]
pe_list = []
if descriptions:
pe_pos = local_model.get_text_pe(descriptions)
pe_list.append(pe_pos)
if neg_descriptions:
pe_neg = local_model.get_text_pe(neg_descriptions) * -1.0
pe_list.append(pe_neg)
if pe_list:
pe_all = torch.cat(pe_list, dim=1)
pe_s_aggregated = pe_all.mean(dim=1, keepdim=True)
all_pe_s_list.append(pe_s_aggregated[0])
else:
pe_pos = local_model.get_text_pe([name])
pe_s_aggregated = pe_pos.mean(dim=1, keepdim=True)
all_pe_s_list.append(pe_s_aggregated[0])
pe_s_aggregated = torch.cat(all_pe_s_list, dim=0).unsqueeze(0)
local_model.set_classes(names, pe_s_aggregated)
return local_model
def detect_objects_stream(images, batch_size=3, model_instance=None, conf_threshold=0.1):
"""
Run YOLO on images in batches, stream each batch's results.
"""
# Use global model if none provided, though usually passed from state
if model_instance is None:
model_instance = model
if not images:
yield [], pd.DataFrame([{"Error": "No images uploaded"}]), None
return
if isinstance(model_instance, list) or model_instance is None:
yield [], pd.DataFrame([{"Error": "Model is not Prompted"}]), None
return
all_counts = {}
all_results = []
names = model_instance.names
# Use temp directory for annotations
folder_name = tempfile.mkdtemp()
for batch in batch_iterable(images, batch_size):
# Gradio Gallery with type='filepath' returns list of items.
# Original code assumed list of tuples/list: [img[0] for img in batch]
# We preserve this logic.
batch_paths = [img[0] for img in batch]
np_batch = [
np.array(Image.open(img).convert('RGB'))
for img in batch_paths
]
# Run YOLO prediction
results = model_instance(np_batch, verbose=False, conf=conf_threshold)
for res_id, res in enumerate(results):
# Save annotation to txt
annotation_filename = os.path.basename(batch_paths[res_id])
annotation_filename = os.path.splitext(annotation_filename)[0]
output_path = os.path.join(folder_name, f"{annotation_filename}.txt")
res.save_txt(output_path)
# Plot
plotted_rgb = res.plot()
all_results.append(plotted_rgb)
# Count classes
if res.boxes and res.boxes.cls is not None:
for c in res.boxes.cls.cpu().numpy():
class_name = names[int(c)]
all_counts[class_name] = all_counts.get(class_name, 0) + 1
# Create DataFrame
data = {"number_of_images": [len(all_results)]}
for class_name in names:
data[class_name] = [all_counts.get(class_name, 0)]
df = pd.DataFrame(data)
# Stream partial result
yield all_results, df, folder_name
def save_model(model_instance, model_name='yoloe-11s-seg.pt'):
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
base_name = model_name[:-3] if model_name.endswith('.pt') else model_name
filename = f"{base_name}__{timestamp}__.pt"
# Save the model to a temporary directory
temp_dir = tempfile.gettempdir()
filepath = os.path.join(temp_dir, filename)
model_instance.save(filepath)
return filepath
def set_classes_and_save_model(df, neg_df=None, model_name='yoloe-11s-seg.pt'):
model_instance = set_classes_with_descriptions(df, neg_df, model_name)
status = f'**Status:** <span style="color: #00ffcc;">{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: Model Prompted successfully!</span>'
return model_instance, save_model(model_instance, model_name), status
def refine_prompts_with_gemini(prompts_df, output_images, api_key_input=None):
"""
Refines prompts using Gemini based on previous prompts and detection results.
"""
# Prioritize user input API key, fallback to environment variable
api_key = api_key_input or False #os.environ.get("GEMINI_API_KEY")
if not api_key:
status = f'**Status:** <span style="color: #ff4444;">{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: Error - No Gemini API Key provided.</span>'
return prompts_df, status
client = genai.Client(api_key=api_key)
# Convert prompts_df to dict
prompts_dict = prompts_df.to_dict(orient='list')
cleaned_prompts = {k: [v for v in l if pd.notna(v) and v != ""] for k, l in prompts_dict.items()}
# Create the function declaration for Gemini
properties = {
k: {
"type": "STRING",
"description": f"A better, more descriptive prompt for the class '{k}' to improve detection accuracy."
} for k in cleaned_prompts.keys()
}
declaration = {
"name": "update_prompts",
"description": "Updates the prompts for target classes based on visual feedback from previous detections.",
"parameters": {
"type": "OBJECT",
"properties": properties,
"required": list(cleaned_prompts.keys())
}
}
# Prepare visual context
contents = [
"I am working on an object detection task. Here are the current prompts/descriptions for each class:",
json.dumps(cleaned_prompts, indent=2),
"Based on the provided images (which show the current detection results with bounding boxes), "
"please refine the prompts to be more specific and helpful for the detection model. "
"Use the 'update_prompts' function to return the new prompts."
]
if output_images:
for item in output_images:
img_data = item
if isinstance(item, (list, tuple)):
img_data = item[0]
if isinstance(img_data, np.ndarray):
img = Image.fromarray(img_data.astype('uint8'))
contents.append(img)
elif isinstance(img_data, str) and os.path.exists(img_data):
img = Image.open(img_data)
contents.append(img)
# Call Gemini
try:
response = client.models.generate_content(
model="gemini-3-flash-preview", # Using a stable model name compatible with the new SDK
contents=contents,
config=types.GenerateContentConfig(
tools=[types.Tool(
function_declarations=[declaration]
)],
tool_config=types.ToolConfig(
function_calling_config=types.FunctionCallingConfig(
mode="ANY",
allowed_function_names=["update_prompts"]
)
)
)
)
# Extract the function call
for part in response.candidates[0].content.parts:
if part.function_call:
new_prompts_map = dict(part.function_call.args)
new_df_data = {k: [new_prompts_map.get(k, "")] for k in prompts_df.columns}
new_df = pd.DataFrame(new_df_data)
status = f'**Status:** <span style="color: #00ffcc;">{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: Prompts Refined via AI</span>'
return new_df, status
except Exception as e:
status = f'**Status:** <span style="color: #ff4444;">{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: Error during Gemini refinement: {str(e)}</span>'
return prompts_df, status
status = f'**Status:** <span style="color: #ffbb33;">{datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")}: Gemini completed but no changes were made.</span>'
return prompts_df, status
|