Spaces:
Sleeping
Sleeping
File size: 3,751 Bytes
3d46076 | 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 | import os
import numpy as np
from PIL import Image
from typing import Dict, Any, Optional
from src.tools.base import ToolConnector
class ObserveVisualConnector(ToolConnector):
def __init__(self):
super().__init__(
name="observe_visual",
description="Observes and extracts 64-dimensional biophysical visual features from an image file or synthetic visual field.",
timeout_sec=10.0
)
@property
def input_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"image_path": {"type": "string"},
"synthetic_target": {"type": "string"}
}
}
@property
def output_schema(self) -> Dict[str, Any]:
return {
"type": "object",
"properties": {
"features_vector": {"type": "array", "items": {"type": "number"}},
"mean_brightness": {"type": "number"},
"contrast": {"type": "number"},
"dominant_channel": {"type": "string"}
},
"required": ["features_vector", "mean_brightness", "contrast", "dominant_channel"]
}
def _execute(self, params: Dict[str, Any], execution_id: str) -> Dict[str, Any]:
img_path = params.get("image_path")
if img_path and os.path.exists(img_path):
img = Image.open(img_path).convert("RGB").resize((64, 64))
arr = np.array(img, dtype=np.float32) / 255.0
else:
# Generate synthetic sensory visual field based on synthetic_target
target = params.get("synthetic_target", "neutral")
arr = np.zeros((64, 64, 3), dtype=np.float32)
if "red" in target:
arr[:, :, 0] = 0.8
elif "green" in target:
arr[:, :, 1] = 0.8
elif "blue" in target:
arr[:, :, 2] = 0.8
elif "bright" in target:
arr[:] = 0.9
else:
# Gradient field
for y in range(64):
for x in range(64):
arr[y, x, 0] = x / 64.0
arr[y, x, 1] = y / 64.0
arr[y, x, 2] = 0.5
# Extract 64-d feature vector
# 16 values: 4x4 spatial grid of mean luminance
grid = arr.mean(axis=2).reshape(4, 16, 4, 16).mean(axis=(1, 3)).flatten()
# 16 values: color distribution histograms
hist_r, _ = np.histogram(arr[:, :, 0], bins=8, range=(0, 1))
hist_g, _ = np.histogram(arr[:, :, 1], bins=8, range=(0, 1))
# 16 values: horizontal gradients
grad_x = np.abs(np.diff(arr.mean(axis=2), axis=1))
grad_pool_x = grad_x.reshape(4, 16, 63).mean(axis=(1, 2))
grad_pool_pad = np.pad(grad_pool_x, (0, 12), mode="edge")
# 16 values: vertical gradients
grad_y = np.abs(np.diff(arr.mean(axis=2), axis=0))
grad_pool_y = grad_y.reshape(63, 4, 16).mean(axis=(0, 2))
grad_pool_y_pad = np.pad(grad_pool_y, (0, 12), mode="edge")
feat = np.concatenate([grid, hist_r / 4096.0, hist_g / 4096.0, grad_pool_pad[:16], grad_pool_y_pad[:16]])
feat = feat[:64].astype(np.float32)
mean_b = float(arr.mean())
contrast = float(arr.std())
channel_means = [arr[:, :, 0].mean(), arr[:, :, 1].mean(), arr[:, :, 2].mean()]
dom_idx = int(np.argmax(channel_means))
dominant_channel = ["red", "green", "blue"][dom_idx]
return {
"features_vector": feat.tolist(),
"mean_brightness": round(mean_b, 4),
"contrast": round(contrast, 4),
"dominant_channel": dominant_channel
}
|