LPX
commited on
Commit
·
1af0cb5
1
Parent(s):
a986afb
major: prep implementtation of vLLM smart agents
Browse files- app_mcp.py +98 -173
- utils/smart_agents.py +54 -0
app_mcp.py
CHANGED
@@ -19,7 +19,10 @@ from utils.minmax import preprocess as minmax_preprocess
|
|
19 |
from utils.ela import genELA as ELA
|
20 |
from utils.wavelet import wavelet_blocking_noise_estimation
|
21 |
from utils.bitplane import bit_plane_extractor
|
22 |
-
|
|
|
|
|
|
|
23 |
|
24 |
from forensics.registry import register_model, MODEL_REGISTRY, ModelEntry
|
25 |
|
@@ -302,170 +305,15 @@ def get_consensus_label(results):
|
|
302 |
|
303 |
# Update predict_image_with_json to return consensus label
|
304 |
|
305 |
-
class ModelWeightManager:
|
306 |
-
def __init__(self):
|
307 |
-
self.base_weights = {
|
308 |
-
"model_1": 0.15, # SwinV2 Based
|
309 |
-
"model_2": 0.15, # ViT Based
|
310 |
-
"model_3": 0.15, # SDXL Dataset
|
311 |
-
"model_4": 0.15, # SDXL + FLUX
|
312 |
-
"model_5": 0.15, # ViT Based
|
313 |
-
"model_5b": 0.10, # ViT Based, Newer Dataset
|
314 |
-
"model_6": 0.10, # Swin, Midj + SDXL
|
315 |
-
"model_7": 0.05 # ViT
|
316 |
-
}
|
317 |
-
self.situation_weights = {
|
318 |
-
"high_confidence": 1.2, # Boost weights for high confidence predictions
|
319 |
-
"low_confidence": 0.8, # Reduce weights for low confidence
|
320 |
-
"conflict": 0.5, # Reduce weights when models disagree
|
321 |
-
"consensus": 1.5 # Boost weights when models agree
|
322 |
-
}
|
323 |
-
|
324 |
-
def adjust_weights(self, predictions, confidence_scores):
|
325 |
-
"""Dynamically adjust weights based on prediction patterns"""
|
326 |
-
adjusted_weights = self.base_weights.copy()
|
327 |
-
|
328 |
-
# Check for consensus
|
329 |
-
if self._has_consensus(predictions):
|
330 |
-
for model in adjusted_weights:
|
331 |
-
adjusted_weights[model] *= self.situation_weights["consensus"]
|
332 |
-
|
333 |
-
# Check for conflicts
|
334 |
-
if self._has_conflicts(predictions):
|
335 |
-
for model in adjusted_weights:
|
336 |
-
adjusted_weights[model] *= self.situation_weights["conflict"]
|
337 |
-
|
338 |
-
# Adjust based on confidence
|
339 |
-
for model, confidence in confidence_scores.items():
|
340 |
-
if confidence > 0.8:
|
341 |
-
adjusted_weights[model] *= self.situation_weights["high_confidence"]
|
342 |
-
elif confidence < 0.5:
|
343 |
-
adjusted_weights[model] *= self.situation_weights["low_confidence"]
|
344 |
-
|
345 |
-
return self._normalize_weights(adjusted_weights)
|
346 |
-
|
347 |
-
def _has_consensus(self, predictions):
|
348 |
-
"""Check if models agree on prediction"""
|
349 |
-
return len(set(predictions.values())) == 1
|
350 |
-
|
351 |
-
def _has_conflicts(self, predictions):
|
352 |
-
"""Check if models have conflicting predictions"""
|
353 |
-
return len(set(predictions.values())) > 2
|
354 |
-
|
355 |
-
def _normalize_weights(self, weights):
|
356 |
-
"""Normalize weights to sum to 1"""
|
357 |
-
total = sum(weights.values())
|
358 |
-
return {k: v/total for k, v in weights.items()}
|
359 |
-
|
360 |
-
class EnsembleMonitorAgent:
|
361 |
-
def __init__(self):
|
362 |
-
self.performance_metrics = {
|
363 |
-
"model_accuracy": {},
|
364 |
-
"response_times": {},
|
365 |
-
"confidence_distribution": {},
|
366 |
-
"consensus_rate": 0.0
|
367 |
-
}
|
368 |
-
self.alerts = []
|
369 |
-
|
370 |
-
def monitor_prediction(self, model_id, prediction, confidence, response_time):
|
371 |
-
"""Monitor individual model performance"""
|
372 |
-
if model_id not in self.performance_metrics["model_accuracy"]:
|
373 |
-
self.performance_metrics["model_accuracy"][model_id] = []
|
374 |
-
self.performance_metrics["response_times"][model_id] = []
|
375 |
-
self.performance_metrics["confidence_distribution"][model_id] = []
|
376 |
-
|
377 |
-
self.performance_metrics["response_times"][model_id].append(response_time)
|
378 |
-
self.performance_metrics["confidence_distribution"][model_id].append(confidence)
|
379 |
-
|
380 |
-
# Check for performance issues
|
381 |
-
self._check_performance_issues(model_id)
|
382 |
-
|
383 |
-
def _check_performance_issues(self, model_id):
|
384 |
-
"""Check for any performance anomalies"""
|
385 |
-
response_times = self.performance_metrics["response_times"][model_id]
|
386 |
-
if len(response_times) > 10:
|
387 |
-
avg_time = sum(response_times[-10:]) / 10
|
388 |
-
if avg_time > 2.0: # More than 2 seconds
|
389 |
-
self.alerts.append(f"High latency detected for {model_id}: {avg_time:.2f}s")
|
390 |
-
|
391 |
-
class WeightOptimizationAgent:
|
392 |
-
def __init__(self, weight_manager):
|
393 |
-
self.weight_manager = weight_manager
|
394 |
-
self.performance_history = []
|
395 |
-
self.optimization_threshold = 0.1 # 10% performance change triggers optimization
|
396 |
-
|
397 |
-
def analyze_performance(self, predictions, actual_results):
|
398 |
-
"""Analyze model performance and suggest weight adjustments"""
|
399 |
-
# Placeholder for actual_results. In a real scenario, this would come from a validation set.
|
400 |
-
# For now, we'll just track predictions.
|
401 |
-
self.performance_history.append(predictions)
|
402 |
-
|
403 |
-
if self._should_optimize():
|
404 |
-
self._optimize_weights()
|
405 |
-
|
406 |
-
def _should_optimize(self):
|
407 |
-
"""Determine if weights should be optimized"""
|
408 |
-
if len(self.performance_history) < 10:
|
409 |
-
return False
|
410 |
-
|
411 |
-
# Placeholder for actual performance calculation
|
412 |
-
# For demonstration, let's say we optimize every 10 runs
|
413 |
-
return len(self.performance_history) % 10 == 0
|
414 |
-
|
415 |
-
def _optimize_weights(self):
|
416 |
-
"""Optimize model weights based on performance"""
|
417 |
-
logger.info("Optimizing model weights based on recent performance.")
|
418 |
-
# This is where more sophisticated optimization logic would go.
|
419 |
-
# For example, you could slightly adjust weights of models that consistently predict correctly.
|
420 |
-
pass
|
421 |
-
|
422 |
-
class SystemHealthAgent:
|
423 |
-
def __init__(self):
|
424 |
-
self.health_metrics = {
|
425 |
-
"memory_usage": [],
|
426 |
-
"gpu_utilization": [],
|
427 |
-
"model_load_times": {},
|
428 |
-
"error_rates": {}
|
429 |
-
}
|
430 |
-
|
431 |
-
def monitor_system_health(self):
|
432 |
-
"""Monitor overall system health"""
|
433 |
-
self._check_memory_usage()
|
434 |
-
self._check_gpu_utilization()
|
435 |
-
# You might add _check_model_health() here later
|
436 |
-
|
437 |
-
def _check_memory_usage(self):
|
438 |
-
"""Monitor memory usage"""
|
439 |
-
try:
|
440 |
-
import psutil
|
441 |
-
memory = psutil.virtual_memory()
|
442 |
-
self.health_metrics["memory_usage"].append(memory.percent)
|
443 |
-
|
444 |
-
if memory.percent > 90:
|
445 |
-
logger.warning(f"High memory usage detected: {memory.percent}%")
|
446 |
-
except ImportError:
|
447 |
-
logger.warning("psutil not installed. Cannot monitor memory usage.")
|
448 |
-
|
449 |
-
def _check_gpu_utilization(self):
|
450 |
-
"""Monitor GPU utilization if available"""
|
451 |
-
if torch.cuda.is_available():
|
452 |
-
try:
|
453 |
-
gpu_util = torch.cuda.memory_allocated() / torch.cuda.max_memory_allocated()
|
454 |
-
self.health_metrics["gpu_utilization"].append(gpu_util)
|
455 |
-
|
456 |
-
if gpu_util > 0.9:
|
457 |
-
logger.warning(f"High GPU utilization detected: {gpu_util*100:.2f}%")
|
458 |
-
except Exception as e:
|
459 |
-
logger.warning(f"Error monitoring GPU utilization: {e}")
|
460 |
-
else:
|
461 |
-
logger.info("CUDA not available. Skipping GPU utilization monitoring.")
|
462 |
-
|
463 |
def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_degrees, noise_level, sharpen_strength):
|
464 |
# Initialize agents
|
465 |
monitor_agent = EnsembleMonitorAgent()
|
466 |
weight_manager = ModelWeightManager()
|
467 |
optimization_agent = WeightOptimizationAgent(weight_manager)
|
468 |
health_agent = SystemHealthAgent()
|
|
|
|
|
|
|
469 |
|
470 |
# Monitor system health
|
471 |
health_agent.monitor_system_health()
|
@@ -476,8 +324,8 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
|
|
476 |
img_pil = img
|
477 |
img_np_og = np.array(img) # Convert PIL Image to NumPy array
|
478 |
|
479 |
-
# Get predictions
|
480 |
-
|
481 |
confidence_scores = {}
|
482 |
results = [] # To store the results for the DataFrame
|
483 |
|
@@ -494,24 +342,34 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
|
|
494 |
model_end - model_start
|
495 |
)
|
496 |
|
497 |
-
|
498 |
confidence_scores[model_id] = max(result.get("AI Score", 0.0), result.get("Real Score", 0.0))
|
499 |
results.append(result) # Add individual model result to the list
|
500 |
|
501 |
-
#
|
502 |
-
|
503 |
-
|
504 |
-
|
505 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
506 |
|
507 |
-
#
|
|
|
|
|
|
|
508 |
weighted_predictions = {
|
509 |
"AI": 0.0,
|
510 |
"REAL": 0.0,
|
511 |
"UNCERTAIN": 0.0
|
512 |
}
|
513 |
|
514 |
-
for model_id, prediction in
|
515 |
# Ensure the prediction label is valid for weighted_predictions
|
516 |
if prediction in weighted_predictions:
|
517 |
weighted_predictions[prediction] += adjusted_weights[model_id]
|
@@ -524,8 +382,11 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
|
|
524 |
final_prediction_label = "AI"
|
525 |
elif weighted_predictions["REAL"] > weighted_predictions["AI"] and weighted_predictions["REAL"] > weighted_predictions["UNCERTAIN"]:
|
526 |
final_prediction_label = "REAL"
|
|
|
|
|
|
|
527 |
|
528 |
-
#
|
529 |
gradient_image = gradient_processing(img_np_og) # Added gradient processing
|
530 |
minmax_image = minmax_preprocess(img_np_og) # Added MinMax processing
|
531 |
|
@@ -537,7 +398,24 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
|
|
537 |
ela3 = ELA(img_np_og, quality=75, scale=75, contrast=25, linear=False, grayscale=False)
|
538 |
|
539 |
forensics_images = [img_pil, ela1, ela2, ela3, gradient_image, minmax_image]
|
540 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
541 |
# Prepare table rows for Dataframe (exclude model path)
|
542 |
table_rows = [[
|
543 |
r.get("Model", ""),
|
@@ -549,7 +427,54 @@ def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_d
|
|
549 |
|
550 |
# The get_consensus_label function is now replaced by final_prediction_label from weighted consensus
|
551 |
consensus_html = f"<b><span style='color:{'red' if final_prediction_label == 'AI' else ('green' if final_prediction_label == 'REAL' else 'orange')}'>{final_prediction_label}</span></b>"
|
552 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
553 |
return img_pil, forensics_images, table_rows, results, consensus_html
|
554 |
|
555 |
with gr.Blocks(css="#post-gallery { overflow: hidden !important;} .grid-wrap{ overflow-y: hidden !important;} .ms-gr-ant-welcome-icon{ height:unset !important;} .tabs{margin-top:10px;}") as demo:
|
@@ -615,7 +540,7 @@ with gr.Blocks(css="#post-gallery { overflow: hidden !important;} .grid-wrap{ ov
|
|
615 |
outputs=outputs
|
616 |
)
|
617 |
with gr.Tab("🙈 Project Introduction"):
|
618 |
-
gr.Markdown(
|
619 |
|
620 |
with gr.Tab("👑 Community Forensics Preview"):
|
621 |
temp_space = gr.load("aiwithoutborders-xyz/OpenSight-Community-Forensics-Preview", src="spaces")
|
|
|
19 |
from utils.ela import genELA as ELA
|
20 |
from utils.wavelet import wavelet_blocking_noise_estimation
|
21 |
from utils.bitplane import bit_plane_extractor
|
22 |
+
from utils.hf_logger import log_inference_data
|
23 |
+
from utils.weight_management import ContextualWeightOverrideAgent, ModelWeightManager
|
24 |
+
from utils.monitoring_agents import EnsembleMonitorAgent, WeightOptimizationAgent, SystemHealthAgent
|
25 |
+
from utils.smart_agents import ContextualIntelligenceAgent, ForensicAnomalyDetectionAgent
|
26 |
|
27 |
from forensics.registry import register_model, MODEL_REGISTRY, ModelEntry
|
28 |
|
|
|
305 |
|
306 |
# Update predict_image_with_json to return consensus label
|
307 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
308 |
def predict_image_with_json(img, confidence_threshold, augment_methods, rotate_degrees, noise_level, sharpen_strength):
|
309 |
# Initialize agents
|
310 |
monitor_agent = EnsembleMonitorAgent()
|
311 |
weight_manager = ModelWeightManager()
|
312 |
optimization_agent = WeightOptimizationAgent(weight_manager)
|
313 |
health_agent = SystemHealthAgent()
|
314 |
+
# New smart agents
|
315 |
+
context_agent = ContextualIntelligenceAgent()
|
316 |
+
anomaly_agent = ForensicAnomalyDetectionAgent()
|
317 |
|
318 |
# Monitor system health
|
319 |
health_agent.monitor_system_health()
|
|
|
324 |
img_pil = img
|
325 |
img_np_og = np.array(img) # Convert PIL Image to NumPy array
|
326 |
|
327 |
+
# 1. Get initial predictions from all models
|
328 |
+
model_predictions_raw = {}
|
329 |
confidence_scores = {}
|
330 |
results = [] # To store the results for the DataFrame
|
331 |
|
|
|
342 |
model_end - model_start
|
343 |
)
|
344 |
|
345 |
+
model_predictions_raw[model_id] = result["Label"]
|
346 |
confidence_scores[model_id] = max(result.get("AI Score", 0.0), result.get("Real Score", 0.0))
|
347 |
results.append(result) # Add individual model result to the list
|
348 |
|
349 |
+
# 2. Infer context tags using ContextualIntelligenceAgent
|
350 |
+
image_data_for_context = {
|
351 |
+
"width": img.width,
|
352 |
+
"height": img.height,
|
353 |
+
"mode": img.mode,
|
354 |
+
# Add more features like EXIF data if exif_full_dump is used
|
355 |
+
}
|
356 |
+
detected_context_tags = context_agent.infer_context_tags(image_data_for_context, model_predictions_raw)
|
357 |
+
logger.info(f"Detected context tags: {detected_context_tags}")
|
358 |
+
|
359 |
+
# 3. Get adjusted weights, passing context tags
|
360 |
+
adjusted_weights = weight_manager.adjust_weights(model_predictions_raw, confidence_scores, context_tags=detected_context_tags)
|
361 |
|
362 |
+
# 4. Optimize weights if needed
|
363 |
+
# `final_prediction_label` is determined AFTER weighted consensus, so analyze_performance will be called later
|
364 |
+
|
365 |
+
# 5. Calculate weighted consensus
|
366 |
weighted_predictions = {
|
367 |
"AI": 0.0,
|
368 |
"REAL": 0.0,
|
369 |
"UNCERTAIN": 0.0
|
370 |
}
|
371 |
|
372 |
+
for model_id, prediction in model_predictions_raw.items(): # Use raw predictions for weighting
|
373 |
# Ensure the prediction label is valid for weighted_predictions
|
374 |
if prediction in weighted_predictions:
|
375 |
weighted_predictions[prediction] += adjusted_weights[model_id]
|
|
|
382 |
final_prediction_label = "AI"
|
383 |
elif weighted_predictions["REAL"] > weighted_predictions["AI"] and weighted_predictions["REAL"] > weighted_predictions["UNCERTAIN"]:
|
384 |
final_prediction_label = "REAL"
|
385 |
+
|
386 |
+
# Call analyze_performance after final_prediction_label is known
|
387 |
+
optimization_agent.analyze_performance(final_prediction_label, None)
|
388 |
|
389 |
+
# 6. Perform forensic processing
|
390 |
gradient_image = gradient_processing(img_np_og) # Added gradient processing
|
391 |
minmax_image = minmax_preprocess(img_np_og) # Added MinMax processing
|
392 |
|
|
|
398 |
ela3 = ELA(img_np_og, quality=75, scale=75, contrast=25, linear=False, grayscale=False)
|
399 |
|
400 |
forensics_images = [img_pil, ela1, ela2, ela3, gradient_image, minmax_image]
|
401 |
+
|
402 |
+
# 7. Generate boilerplate descriptions for forensic outputs for anomaly agent
|
403 |
+
forensic_output_descriptions = [
|
404 |
+
f"Original augmented image (PIL): {img_pil.width}x{img_pil.height}",
|
405 |
+
"ELA analysis (Pass 1): Grayscale error map, quality 75.",
|
406 |
+
"ELA analysis (Pass 2): Grayscale error map, quality 75, enhanced contrast.",
|
407 |
+
"ELA analysis (Pass 3): Color error map, quality 75, enhanced contrast.",
|
408 |
+
"Gradient processing: Highlights edges and transitions.",
|
409 |
+
"MinMax processing: Deviations in local pixel values."
|
410 |
+
]
|
411 |
+
# You could also add descriptions for Wavelet and Bit Plane if they were dynamic outputs
|
412 |
+
# For instance, if wavelet_blocking_noise_estimation had parameters that changed and you wanted to describe them.
|
413 |
+
|
414 |
+
# 8. Analyze forensic outputs for anomalies using ForensicAnomalyDetectionAgent
|
415 |
+
anomaly_detection_results = anomaly_agent.analyze_forensic_outputs(forensic_output_descriptions)
|
416 |
+
logger.info(f"Forensic anomaly detection: {anomaly_detection_results["summary"]}")
|
417 |
+
|
418 |
+
|
419 |
# Prepare table rows for Dataframe (exclude model path)
|
420 |
table_rows = [[
|
421 |
r.get("Model", ""),
|
|
|
427 |
|
428 |
# The get_consensus_label function is now replaced by final_prediction_label from weighted consensus
|
429 |
consensus_html = f"<b><span style='color:{'red' if final_prediction_label == 'AI' else ('green' if final_prediction_label == 'REAL' else 'orange')}'>{final_prediction_label}</span></b>"
|
430 |
+
|
431 |
+
# Prepare data for logging to Hugging Face dataset
|
432 |
+
inference_params = {
|
433 |
+
"confidence_threshold": confidence_threshold,
|
434 |
+
"augment_methods": augment_methods,
|
435 |
+
"rotate_degrees": rotate_degrees,
|
436 |
+
"noise_level": noise_level,
|
437 |
+
"sharpen_strength": sharpen_strength,
|
438 |
+
"detected_context_tags": detected_context_tags
|
439 |
+
}
|
440 |
+
|
441 |
+
ensemble_output_data = {
|
442 |
+
"final_prediction_label": final_prediction_label,
|
443 |
+
"weighted_predictions": weighted_predictions,
|
444 |
+
"adjusted_weights": adjusted_weights
|
445 |
+
}
|
446 |
+
|
447 |
+
# Collect agent monitoring data
|
448 |
+
agent_monitoring_data_log = {
|
449 |
+
"ensemble_monitor": {
|
450 |
+
"alerts": monitor_agent.alerts,
|
451 |
+
"performance_metrics": monitor_agent.performance_metrics
|
452 |
+
},
|
453 |
+
"weight_optimization": {
|
454 |
+
"prediction_history_length": len(optimization_agent.prediction_history),
|
455 |
+
# You might add a summary of recent accuracy here if _calculate_accuracy is exposed
|
456 |
+
},
|
457 |
+
"system_health": {
|
458 |
+
"memory_usage": health_agent.health_metrics["memory_usage"],
|
459 |
+
"gpu_utilization": health_agent.health_metrics["gpu_utilization"]
|
460 |
+
},
|
461 |
+
"context_intelligence": {
|
462 |
+
"detected_context_tags": detected_context_tags
|
463 |
+
},
|
464 |
+
"forensic_anomaly_detection": anomaly_detection_results
|
465 |
+
}
|
466 |
+
|
467 |
+
# Log the inference data
|
468 |
+
log_inference_data(
|
469 |
+
original_image=img, # Use the original uploaded image
|
470 |
+
inference_params=inference_params,
|
471 |
+
model_predictions=results, # This already contains detailed results for each model
|
472 |
+
ensemble_output=ensemble_output_data,
|
473 |
+
forensic_images=forensics_images, # This is the list of PIL images generated by forensic tools
|
474 |
+
agent_monitoring_data=agent_monitoring_data_log,
|
475 |
+
human_feedback=None # This can be populated later with human review data
|
476 |
+
)
|
477 |
+
|
478 |
return img_pil, forensics_images, table_rows, results, consensus_html
|
479 |
|
480 |
with gr.Blocks(css="#post-gallery { overflow: hidden !important;} .grid-wrap{ overflow-y: hidden !important;} .ms-gr-ant-welcome-icon{ height:unset !important;} .tabs{margin-top:10px;}") as demo:
|
|
|
540 |
outputs=outputs
|
541 |
)
|
542 |
with gr.Tab("🙈 Project Introduction"):
|
543 |
+
gr.Markdown(QUICK_INTRO)
|
544 |
|
545 |
with gr.Tab("👑 Community Forensics Preview"):
|
546 |
temp_space = gr.load("aiwithoutborders-xyz/OpenSight-Community-Forensics-Preview", src="spaces")
|
utils/smart_agents.py
ADDED
@@ -0,0 +1,54 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import logging
|
2 |
+
from PIL import Image
|
3 |
+
|
4 |
+
logger = logging.getLogger(__name__)
|
5 |
+
|
6 |
+
class ContextualIntelligenceAgent:
|
7 |
+
def __init__(self):
|
8 |
+
# In a real scenario, this would involve an LLM call or a sophisticated rule engine
|
9 |
+
pass
|
10 |
+
|
11 |
+
def infer_context_tags(self, image_data: dict, initial_predictions: dict) -> list[str]:
|
12 |
+
"""Simulates an LLM inferring context tags based on image data and predictions."""
|
13 |
+
context_tags = []
|
14 |
+
|
15 |
+
# Boilerplate logic: infer tags based on simple cues
|
16 |
+
if image_data.get("width", 0) > 1000 and image_data.get("height", 0) > 1000:
|
17 |
+
context_tags.append("high_resolution")
|
18 |
+
|
19 |
+
# Example based on initial broad prediction (e.g., if any model strongly predicts 'real')
|
20 |
+
if any(v.get("Real Score", 0) > 0.9 for v in initial_predictions.values()):
|
21 |
+
context_tags.append("potentially_natural_scene")
|
22 |
+
|
23 |
+
# Mock external detection (e.g., from a simpler scene classification model or EXIF data)
|
24 |
+
# For demonstration, we'll hardcode some possible tags here.
|
25 |
+
# In a real system, you'd feed actual image features or metadata to an LLM.
|
26 |
+
mock_tags = ["outdoor", "sunny"] # These could be returned by an actual LLM based on input
|
27 |
+
for tag in mock_tags:
|
28 |
+
if tag not in context_tags:
|
29 |
+
context_tags.append(tag)
|
30 |
+
|
31 |
+
return context_tags
|
32 |
+
|
33 |
+
class ForensicAnomalyDetectionAgent:
|
34 |
+
def __init__(self):
|
35 |
+
# In a real scenario, this would involve an LLM call to analyze textual descriptions
|
36 |
+
pass
|
37 |
+
|
38 |
+
def analyze_forensic_outputs(self, forensic_output_descriptions: list[str]) -> dict:
|
39 |
+
"""Simulates an LLM analyzing descriptions of forensic images for anomalies."""
|
40 |
+
anomalies = {"summary": "No significant anomalies detected.", "details": []}
|
41 |
+
|
42 |
+
# Boilerplate logic: look for keywords in descriptions
|
43 |
+
for desc in forensic_output_descriptions:
|
44 |
+
if "strong edges" in desc.lower() and "ela" in desc.lower():
|
45 |
+
anomalies["summary"] = "Potential manipulation indicated by ELA."
|
46 |
+
anomalies["details"].append("ELA: Unusually strong edges detected, suggesting image compositing.")
|
47 |
+
if "unexpected patterns" in desc.lower() and "bit plane" in desc.lower():
|
48 |
+
anomalies["summary"] = "Anomalies detected in bit plane data."
|
49 |
+
anomalies["details"].append("Bit Plane: Irregular patterns found, possibly indicating hidden data or processing.")
|
50 |
+
|
51 |
+
if len(anomalies["details"]) > 0:
|
52 |
+
anomalies["summary"] = "Multiple anomalies detected across forensic outputs."
|
53 |
+
|
54 |
+
return anomalies
|