File size: 5,792 Bytes
0611560
c5987cc
58b97f2
ac166dc
b083c09
c5987cc
ac166dc
 
9aeacca
0611560
6e1639c
9aeacca
 
0611560
9aeacca
 
 
 
 
 
 
 
 
 
60e6faa
 
 
 
 
9aeacca
60e6faa
 
9aeacca
60e6faa
 
9aeacca
60e6faa
 
 
 
 
 
 
 
9aeacca
0611560
6e1639c
9aeacca
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
0611560
c5987cc
9aeacca
60e6faa
b083c09
 
9aeacca
 
60e6faa
9aeacca
 
c5987cc
9aeacca
0611560
58b97f2
c5987cc
 
b083c09
 
58b97f2
b083c09
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9aeacca
 
 
 
 
 
 
 
 
 
b083c09
 
 
c5987cc
b083c09
58b97f2
9aeacca
 
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
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
@st.cache_resource
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.")