Spaces:
Running
Running
import streamlit as st | |
import numpy as np | |
import matplotlib.pyplot as plt | |
from matplotlib.backends.backend_agg import FigureCanvasAgg | |
from streamlit_drawable_canvas import st_canvas | |
import time | |
from PIL import Image | |
import io | |
from transformers import AutoModelForCausalLM, AutoTokenizer | |
# Constants | |
WIDTH, HEIGHT = 800, 400 | |
AVATAR_WIDTH, AVATAR_HEIGHT = 300, 400 | |
# Set up DialoGPT model | |
def load_model(): | |
tokenizer = AutoTokenizer.from_pretrained("microsoft/DialoGPT-small") | |
model = AutoModelForCausalLM.from_pretrained("microsoft/DialoGPT-small") | |
return tokenizer, model | |
tokenizer, model = load_model() | |
# Create sensation map for the avatar | |
def create_sensation_map(width, height): | |
sensation_map = np.zeros((height, width, 3)) # RGB channels for pain, pleasure, and neutral | |
for y in range(height): | |
for x in range(width): | |
# Base sensation | |
base = np.sin(x/15) * np.cos(y/15) * 0.5 + np.random.normal(0, 0.1) | |
# Pain regions (red channel) | |
pain = np.exp(-((x-75)**2 + (y-100)**2) / 2000) + np.exp(-((x-225)**2 + (y-300)**2) / 2000) | |
# Pleasure regions (green channel) | |
pleasure = np.exp(-((x-150)**2 + (y-200)**2) / 2000) + np.exp(-((x-75)**2 + (y-300)**2) / 2000) | |
# Neutral sensation (blue channel) | |
neutral = 1 - (pain + pleasure) | |
sensation_map[y, x] = [pain, pleasure, neutral] | |
return sensation_map | |
avatar_sensation_map = create_sensation_map(AVATAR_WIDTH, AVATAR_HEIGHT) | |
# Streamlit app | |
st.title("Advanced Humanoid Touch Simulation") | |
# Create two columns | |
col1, col2 = st.columns(2) | |
# Avatar column | |
with col1: | |
st.subheader("Humanoid Avatar") | |
avatar_fig, avatar_ax = plt.subplots(figsize=(4, 6)) | |
avatar_ax.imshow(avatar_sensation_map) | |
avatar_ax.axis('off') | |
st.pyplot(avatar_fig) | |
# Touch interface column | |
with col2: | |
st.subheader("Touch Interface") | |
touch_fig, touch_ax = plt.subplots(figsize=(4, 6)) | |
touch_ax.add_patch(plt.Rectangle((0, 0), AVATAR_WIDTH, AVATAR_HEIGHT, fill=False)) | |
touch_ax.set_xlim(0, AVATAR_WIDTH) | |
touch_ax.set_ylim(0, AVATAR_HEIGHT) | |
touch_ax.axis('off') | |
# Convert matplotlib figure to Image | |
canvas = FigureCanvasAgg(touch_fig) | |
canvas.draw() | |
buf = io.BytesIO() | |
plt.savefig(buf, format='png') | |
buf.seek(0) | |
img = Image.open(buf) | |
# Use streamlit-drawable-canvas for interaction | |
canvas_result = st_canvas( | |
fill_color="rgba(255, 165, 0, 0.3)", | |
stroke_width=3, | |
stroke_color="#e00", | |
background_color="#eee", | |
background_image=img, | |
update_streamlit=True, | |
height=AVATAR_HEIGHT, | |
width=AVATAR_WIDTH, | |
drawing_mode="point", | |
point_display_radius=5, | |
key="canvas", | |
) | |
def calculate_sensation(x, y, pressure, duration): | |
sensation = avatar_sensation_map[int(y), int(x)] | |
modified_sensation = sensation * pressure * (1 + np.log(duration + 1)) | |
return modified_sensation | |
def generate_description(x, y, pressure, duration, pain, pleasure, neutral): | |
prompt = f"Human: Describe the sensation when touched at ({x:.1f}, {y:.1f}) with pressure {pressure:.2f} for {duration:.2f} seconds. Pain: {pain:.2f}, Pleasure: {pleasure:.2f}, Neutral: {neutral:.2f}.\nAvatar:" | |
input_ids = tokenizer.encode(prompt, return_tensors="pt") | |
output = model.generate(input_ids, max_length=150, num_return_sequences=1, no_repeat_ngram_size=2, top_k=50, top_p=0.95, temperature=0.7) | |
return tokenizer.decode(output[0], skip_special_tokens=True).split("Avatar: ")[-1].strip() | |
# Initialize session state | |
if 'touch_start_time' not in st.session_state: | |
st.session_state.touch_start_time = None | |
if 'last_touch_position' not in st.session_state: | |
st.session_state.last_touch_position = None | |
# Handle touch events | |
if canvas_result.json_data is not None: | |
objects = canvas_result.json_data["objects"] | |
if len(objects) > 0: | |
last_object = objects[-1] | |
current_position = (last_object["left"], last_object["top"]) | |
if st.session_state.touch_start_time is None: | |
st.session_state.touch_start_time = time.time() | |
st.session_state.last_touch_position = current_position | |
else: | |
# Calculate pressure based on movement | |
if st.session_state.last_touch_position is not None: | |
dx = current_position[0] - st.session_state.last_touch_position[0] | |
dy = current_position[1] - st.session_state.last_touch_position[1] | |
distance = np.sqrt(dx**2 + dy**2) | |
pressure = 1 + distance / 10 # Adjust this formula as needed | |
else: | |
pressure = 1.0 | |
duration = time.time() - st.session_state.touch_start_time | |
x, y = current_position | |
sensation = calculate_sensation(x, y, pressure, duration) | |
pain, pleasure, neutral = sensation | |
description = generate_description(x, y, pressure, duration, pain, pleasure, neutral) | |
st.write(f"Touch at ({x:.1f}, {y:.1f}) with pressure {pressure:.2f} for {duration:.2f} seconds") | |
st.write(f"Pain: {pain:.2f}, Pleasure: {pleasure:.2f}, Neutral: {neutral:.2f}") | |
st.write("Avatar's response:") | |
st.write(description) | |
st.session_state.last_touch_position = current_position | |
else: | |
st.session_state.touch_start_time = None | |
st.session_state.last_touch_position = None | |
st.write("Click and drag on the touch interface to simulate touching the avatar.") | |
st.write("The avatar's sensation map shows pain (red), pleasure (green), and neutral (blue) areas.") |