Compare commits
	
		
			20 Commits 
		
	
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								
								 | 
						0778d1134a | 1 year ago | 
| 
							
							
								
								 | 
						ac632ad4a4 | 1 year ago | 
| 
							
							
								
								 | 
						abbc55cded | 1 year ago | 
| 
							
							
								
								 | 
						67be65c338 | 1 year ago | 
| 
							
							
								
								 | 
						dc1a0048e3 | 1 year ago | 
| 
							
							
								
								 | 
						2fe1fc6a69 | 1 year ago | 
| 
							
							
								
								 | 
						9003780371 | 1 year ago | 
| 
							
							
								
								 | 
						3ce5ca0c14 | 1 year ago | 
| 
							
							
								
								 | 
						acf3174228 | 1 year ago | 
| 
							
							
								
								 | 
						1d53af5b0f | 1 year ago | 
| 
							
							
								
								 | 
						b78446bcf7 | 1 year ago | 
| 
							
							
								
								 | 
						99df9a29fb | 1 year ago | 
| 
							
							
								
								 | 
						d1c91b2ba8 | 1 year ago | 
| 
							
							
								
								 | 
						589734cce9 | 1 year ago | 
| 
							
							
								
								 | 
						5788a20a20 | 1 year ago | 
| 
							
							
								
								 | 
						ac3b55d497 | 1 year ago | 
| 
							
							
								
								 | 
						a84c188f0e | 1 year ago | 
| 
							
							
								
								 | 
						d859084ee3 | 1 year ago | 
| 
							
							
								
								 | 
						5104e7af27 | 1 year ago | 
| 
							
							
								
								 | 
						4eb21c1fa7 | 1 year ago | 
				 21 changed files with 1204 additions and 123 deletions
			
			
		@ -0,0 +1,3 @@ | 
				
			|||
{ | 
				
			|||
    "C_Cpp.errorSquiggles": "enabled" | 
				
			|||
} | 
				
			|||
@ -1,2 +1,12 @@ | 
				
			|||
ofxOpenCv | 
				
			|||
ofxOpenCv | 
				
			|||
ofxOsc | 
				
			|||
ofxOsc | 
				
			|||
ofxNetwork | 
				
			|||
ofxNetwork | 
				
			|||
ofxGui | 
				
			|||
ofxGui | 
				
			|||
ofxTSNE | 
				
			|||
ofxTSNE | 
				
			|||
ofxPiMapper | 
				
			|||
ofxPiMapper | 
				
			|||
@ -0,0 +1,282 @@ | 
				
			|||
#include "Map.h" | 
				
			|||
#include "algorithm" | 
				
			|||
 | 
				
			|||
Map::Map(){ | 
				
			|||
     | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void Map::Setup(){ | 
				
			|||
    scale = 10000; | 
				
			|||
    isDebug = true; | 
				
			|||
 | 
				
			|||
    json_embeddings = ofLoadJson(jsonPath); | 
				
			|||
    if(!json_embeddings.is_array()){ | 
				
			|||
        ofLogError() << "JSON is not an array"; | 
				
			|||
        return; | 
				
			|||
    } else { | 
				
			|||
        std::cout << "JSON LOADED" << std::endl; | 
				
			|||
        SetupTSNE(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    mapFbo.allocate(ofGetWindowWidth() / 2, ofGetWindowHeight(), GL_RGB); | 
				
			|||
 | 
				
			|||
    fboImage.allocate(ofGetWindowWidth() / 2, ofGetWindowHeight(), OF_IMAGE_COLOR); | 
				
			|||
 | 
				
			|||
    Setup3D(); | 
				
			|||
 | 
				
			|||
    SetupNodes(); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void Map::Update(std::string& vp_img, bool& is_active){ | 
				
			|||
    time = ofGetElapsedTimef(); | 
				
			|||
    for(auto& n : nodes){ | 
				
			|||
        glm::vec2 offset = n.amplitude * sin(n.speed * time); | 
				
			|||
        n.offset = glm::vec3(offset.x, offset.y, 0); | 
				
			|||
    } | 
				
			|||
    std::cout << cam.getPosition() << std::endl; | 
				
			|||
    if(is_active){ | 
				
			|||
        has_reached = false; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    if(!is_active) | 
				
			|||
        MoveCamera(vp_img); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void Map::Draw(){ | 
				
			|||
    mapFbo.begin(); | 
				
			|||
 | 
				
			|||
    cam.begin(); | 
				
			|||
 | 
				
			|||
    ofClear(0, 0, 0, 1); | 
				
			|||
 | 
				
			|||
    ofMatrix4x4 projectionMatrix = cam.getProjectionMatrix(); | 
				
			|||
    ofMatrix4x4 viewMatrix = cam.getModelViewMatrix(); | 
				
			|||
    ofMatrix4x4 mvpMatrix = projectionMatrix * viewMatrix; | 
				
			|||
 | 
				
			|||
    for (auto& n :sortedNodes){ | 
				
			|||
        glm::vec3 node_position = n->position + n->offset; | 
				
			|||
 | 
				
			|||
 | 
				
			|||
        if(isFrustum(node_position, 50)){ | 
				
			|||
            n->texture.getTexture().bind(); | 
				
			|||
            ofPushMatrix(); | 
				
			|||
                ofFill(); | 
				
			|||
                n->geom.setPosition(node_position); | 
				
			|||
                n->geom.draw(); | 
				
			|||
            ofPopMatrix(); | 
				
			|||
            n->texture.getTexture().unbind(); | 
				
			|||
        } | 
				
			|||
         | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    cam.end(); | 
				
			|||
 | 
				
			|||
    mapFbo.end(); | 
				
			|||
 | 
				
			|||
    mapFbo.readToPixels(fboPixels); | 
				
			|||
 | 
				
			|||
    fboImage.setFromPixels(fboPixels); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/*
 | 
				
			|||
    Setup texture for each node | 
				
			|||
*/ | 
				
			|||
void Map::SetupNodes(){ | 
				
			|||
    std::cout << "Setting up nodes.." << std::endl; | 
				
			|||
    for (auto& node : nodes){ | 
				
			|||
        ofImage img; | 
				
			|||
        ofPlanePrimitive plane; | 
				
			|||
 | 
				
			|||
        img.load("data/" + node.image_path); | 
				
			|||
        int imageW = img.getWidth() * 0.1; | 
				
			|||
        int imageH = img.getHeight() * 0.1; | 
				
			|||
        int maxWidth = 1; | 
				
			|||
        int maxHeight = 1; | 
				
			|||
        float aspectRatio = (float)imageW / (float)imageH; | 
				
			|||
 | 
				
			|||
        // Determine plane dimensions based on aspect ratio and constraints
 | 
				
			|||
        float planeW, planeH; | 
				
			|||
        if (aspectRatio > (float)maxWidth / maxHeight) { | 
				
			|||
            // Image is wider relative to the max constraints
 | 
				
			|||
            planeW = maxWidth; | 
				
			|||
            planeH = maxWidth / aspectRatio; | 
				
			|||
        } else { | 
				
			|||
            // Image is taller relative to the max constraints
 | 
				
			|||
            planeH = maxHeight; | 
				
			|||
            planeW = maxHeight * aspectRatio; | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        // Ensure that dimensions do not exceed constraints
 | 
				
			|||
        if (planeH > maxHeight) { | 
				
			|||
            planeH = maxHeight; | 
				
			|||
            planeW = maxHeight * aspectRatio; | 
				
			|||
        } | 
				
			|||
        if (planeW > maxWidth) { | 
				
			|||
            planeW = maxWidth; | 
				
			|||
            planeH = maxWidth / aspectRatio; | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        plane.set(imageW, imageH); | 
				
			|||
        plane.setPosition(node.position); | 
				
			|||
        plane.setResolution(10, 10); | 
				
			|||
        plane.mapTexCoordsFromTexture(img.getTexture()); | 
				
			|||
        img.getTexture().setTextureWrap(GL_REPEAT, GL_REPEAT); | 
				
			|||
        plane.setScale(1); | 
				
			|||
 | 
				
			|||
        node.geom = plane; | 
				
			|||
        node.texture = img; | 
				
			|||
        node.speed = glm::vec2(ofRandom(0.1, 0.5), ofRandom(0.1, 0.5)); | 
				
			|||
        node.amplitude = glm::vec2(ofRandom(1, 5), ofRandom(1, 5)); | 
				
			|||
        node.phase = glm::vec2(ofRandom(0, TWO_PI), ofRandom(0, TWO_PI)); | 
				
			|||
    } | 
				
			|||
    std::cout << "Finished setting up nodes!" << std::endl; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/*
 | 
				
			|||
    Setup TSNE - reads JSON, converts to points, creates nodes & hashmap | 
				
			|||
*/ | 
				
			|||
void Map::SetupTSNE(){ | 
				
			|||
    for (const auto& entry: json_embeddings) { | 
				
			|||
        if (entry.contains("vector") && entry["vector"].is_array()) { | 
				
			|||
            Node n; | 
				
			|||
            n.foldername = entry["folder"]; | 
				
			|||
            n.image_path =  entry["image"]; | 
				
			|||
            n.isLost = entry["lost"].get<int>(); | 
				
			|||
 | 
				
			|||
            std::vector<float> emb; | 
				
			|||
 | 
				
			|||
            for (const auto& value: entry["vector"]){ | 
				
			|||
                if(value.is_number()){ | 
				
			|||
                    emb.push_back(value.get<float>()); | 
				
			|||
                } else { | 
				
			|||
                    ofLogError() << "Vector value is not a number"; | 
				
			|||
                } | 
				
			|||
            } | 
				
			|||
 | 
				
			|||
            n.emotion_vector = emb; | 
				
			|||
 | 
				
			|||
            nodes.push_back(n); | 
				
			|||
            embeddings.push_back(emb); | 
				
			|||
        } | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    std::cout << nodes.size() << std::endl; | 
				
			|||
 | 
				
			|||
    points = tsne.run(embeddings, dims, perplexity, theta, normalise, runManually); | 
				
			|||
 | 
				
			|||
    for (size_t i = 0; i < points.size(); i++){ | 
				
			|||
        const auto& vec = points[i]; | 
				
			|||
        auto& n = nodes[i]; | 
				
			|||
        n.position = (glm::vec3(vec[0] * scale, vec[1] * scale, vec[2] * scale)); | 
				
			|||
        node_hash_map[n.image_path] = &n; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    // Instead of sorting nodes, just create a sorted reference list
 | 
				
			|||
    for (auto& node : nodes) { | 
				
			|||
        sortedNodes.push_back(&node);  // Pointers to the original nodes
 | 
				
			|||
    } | 
				
			|||
     | 
				
			|||
    SortNodes();  // Sort the references for rendering
 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/*
 | 
				
			|||
    Setup 3D environment | 
				
			|||
*/ | 
				
			|||
void Map::Setup3D(){ | 
				
			|||
    cam.setFov(20); | 
				
			|||
    cam.removeAllInteractions(); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/*
 | 
				
			|||
    Query hashmap | 
				
			|||
*/ | 
				
			|||
bool Map::SearchMap(std::string& search_string){ | 
				
			|||
    std::string t = search_string; | 
				
			|||
    if(node_hash_map.find(t) != node_hash_map.end()){ | 
				
			|||
        Node* n = node_hash_map[t]; | 
				
			|||
        std::cout << n->foldername << std::endl; | 
				
			|||
        return true; | 
				
			|||
    } else { | 
				
			|||
        return false; | 
				
			|||
    } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/* search if image is in the hashmap -> if true, move to its position */ | 
				
			|||
void Map::MoveCamera(std::string& vp_img){ | 
				
			|||
    if(SearchMap(vp_img)){ | 
				
			|||
        Node* n = node_hash_map[vp_img]; | 
				
			|||
        glm::vec3 cur_pos(cam.getPosition().x, cam.getPosition().y, cam.getPosition().z); | 
				
			|||
        glm::vec3 target_pos((n->position.x), (n->position.y), (n->position.z) + 100); | 
				
			|||
        glm::vec3 dir = target_pos - cur_pos; | 
				
			|||
        float dist = glm::length(dir); | 
				
			|||
        dir = glm::normalize(dir); | 
				
			|||
        accel = dir * 0.02f; | 
				
			|||
        vel += accel; | 
				
			|||
 | 
				
			|||
        // Define minimum and maximum velocities based on distance
 | 
				
			|||
        const float minVelocity = 0.01f; // Minimum velocity when close to the target
 | 
				
			|||
        const float maxVelocityDistance = 1000.0f; // Distance at which max velocity is applied
 | 
				
			|||
        const float maxVelocity = 50.0f; // Maximum velocity
 | 
				
			|||
 | 
				
			|||
        // Scale max velocity based on distance
 | 
				
			|||
        float dynamicMaxVelocity = glm::clamp(dist / maxVelocityDistance * maxVelocity, minVelocity, maxVelocity); | 
				
			|||
 | 
				
			|||
        // Clamp the velocity to the range [-dynamicMaxVelocity, dynamicMaxVelocity]
 | 
				
			|||
        float velocityLength = glm::length(vel); | 
				
			|||
        if (velocityLength > dynamicMaxVelocity) { | 
				
			|||
            vel = glm::normalize(vel) * dynamicMaxVelocity; | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        // Update position
 | 
				
			|||
        glm::vec3 newPosition = cur_pos + vel; | 
				
			|||
         | 
				
			|||
 | 
				
			|||
        if(cam.getPosition() == target_pos){ | 
				
			|||
            has_reached = true; | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        if(!has_reached){ | 
				
			|||
            cam.setPosition(target_pos); | 
				
			|||
        } | 
				
			|||
 | 
				
			|||
        std::cout << "velocity " << vel << std::endl; | 
				
			|||
        std::cout << "target " << target_pos << std::endl; | 
				
			|||
        std::cout << "new position " << cam.getPosition() << std::endl; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void Map::buttonPressed(int key){ | 
				
			|||
    if(key==OF_KEY_LEFT){ | 
				
			|||
        z_adj -= 1; | 
				
			|||
    } else { | 
				
			|||
        z_adj += 1; | 
				
			|||
    } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/*
 | 
				
			|||
    Sort nodes by z position, for draw calls (lowest -> highest) | 
				
			|||
*/ | 
				
			|||
void Map::SortNodes(){ | 
				
			|||
    std::sort(sortedNodes.begin(), sortedNodes.end(), [](const Node* a, const Node* b){ | 
				
			|||
        return a->position.z < b->position.z;  // Sorting by z-position
 | 
				
			|||
    }); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
bool Map::isFrustum(const glm::vec3& position, float radius){ | 
				
			|||
    float box_w = 2000; | 
				
			|||
    float box_h = 2000; | 
				
			|||
    glm::vec3 cam_position = cam.getPosition(); | 
				
			|||
 | 
				
			|||
    float left = cam_position.x - box_w / 2.0f; | 
				
			|||
    float right = cam_position.x + box_w / 2.0f; | 
				
			|||
    float top = cam_position.y + box_h / 2.0f; | 
				
			|||
    float bottom = cam_position.y - box_h / 2.0f; | 
				
			|||
 | 
				
			|||
    // Check if the object (with radius) is within the bounding box
 | 
				
			|||
    if (position.x + radius < left || position.x - radius > right) return false; | 
				
			|||
    if (position.y + radius < bottom || position.y - radius > top) return false; | 
				
			|||
 | 
				
			|||
    // Z-depth doesn't matter in this approach, so we're ignoring it
 | 
				
			|||
    return true; | 
				
			|||
} | 
				
			|||
@ -0,0 +1,87 @@ | 
				
			|||
#ifndef _MAP | 
				
			|||
#define _MAP | 
				
			|||
 | 
				
			|||
#include "ofMain.h" | 
				
			|||
#include "ofxOsc.h" | 
				
			|||
#include "ofxTSNE.h" | 
				
			|||
 | 
				
			|||
struct Node { | 
				
			|||
    glm::vec2 speed; | 
				
			|||
    glm::vec2 amplitude; | 
				
			|||
    glm::vec2 phase; | 
				
			|||
    glm::vec3 offset; | 
				
			|||
    ofImage texture; | 
				
			|||
    ofPlanePrimitive geom; | 
				
			|||
    glm::vec3 position; | 
				
			|||
    std::string image_path; | 
				
			|||
    std::string foldername; | 
				
			|||
    std::vector<float> emotion_vector; | 
				
			|||
    int isLost; | 
				
			|||
}; | 
				
			|||
 | 
				
			|||
class Map { | 
				
			|||
    public: | 
				
			|||
 | 
				
			|||
    /*
 | 
				
			|||
        Methods | 
				
			|||
    */ | 
				
			|||
 | 
				
			|||
    Map(); | 
				
			|||
    void Setup(); | 
				
			|||
    void Update(std::string& vp_img, bool& is_active); | 
				
			|||
    void Draw(); | 
				
			|||
    void SetupNodes(); | 
				
			|||
    void SetupTSNE(); | 
				
			|||
    void Setup3D(); | 
				
			|||
 | 
				
			|||
    bool SearchMap(std::string& search_string); | 
				
			|||
    void SortNodes(); | 
				
			|||
 | 
				
			|||
    void MoveCamera(std::string& vp_img); | 
				
			|||
 | 
				
			|||
    bool isFrustum(const glm::vec3& position, float radius); | 
				
			|||
 | 
				
			|||
    void buttonPressed(int key); | 
				
			|||
 | 
				
			|||
    /*
 | 
				
			|||
        Variables | 
				
			|||
    */ | 
				
			|||
    bool isDebug; | 
				
			|||
    float time; | 
				
			|||
 | 
				
			|||
    ofFbo mapFbo; | 
				
			|||
    std::vector<Node> nodes; | 
				
			|||
    std::vector<Node*> sortedNodes; | 
				
			|||
    float scale; | 
				
			|||
 | 
				
			|||
    ofxTSNE tsne; | 
				
			|||
    vector<vector<double>> points; | 
				
			|||
    vector<vector<float>> embeddings; | 
				
			|||
    int dims = 3; | 
				
			|||
    float perplexity = 20; | 
				
			|||
    float theta = 0.2; | 
				
			|||
    bool normalise = true; | 
				
			|||
    bool runManually = false; | 
				
			|||
    ofJson json_embeddings; | 
				
			|||
    std::string jsonPath = "data/json/embeddings.json"; | 
				
			|||
 | 
				
			|||
    std::unordered_map<std::string, Node*> node_hash_map; | 
				
			|||
 | 
				
			|||
    /*
 | 
				
			|||
        3D Variables | 
				
			|||
    */ | 
				
			|||
    ofLight light; | 
				
			|||
    ofEasyCam cam; | 
				
			|||
    float zoom; | 
				
			|||
    glm::vec3 accel; | 
				
			|||
    glm::vec3 vel; | 
				
			|||
    float z_adj = 0; | 
				
			|||
    bool has_reached = false; | 
				
			|||
 | 
				
			|||
    ofImage fboImage; | 
				
			|||
    ofPixels fboPixels; | 
				
			|||
 | 
				
			|||
    private: | 
				
			|||
}; | 
				
			|||
 | 
				
			|||
#endif | 
				
			|||
@ -0,0 +1,99 @@ | 
				
			|||
#include "ofMain.h" | 
				
			|||
#include "Onnx.h" | 
				
			|||
#include "Yolo.h" | 
				
			|||
 | 
				
			|||
class ModelThread : public ofThread | 
				
			|||
{ | 
				
			|||
    public: | 
				
			|||
    ofImage* img; | 
				
			|||
    ofFbo* fbo; | 
				
			|||
    Onnx* model; | 
				
			|||
    Yolo* yolo; | 
				
			|||
    std::vector<types::BoxfWithLandmarks>* detected_faces; | 
				
			|||
    std::string model_type; | 
				
			|||
 | 
				
			|||
    // emotional recognition model
 | 
				
			|||
    std::vector<ofImage>* croppedFaces; | 
				
			|||
    float* emotional_data; | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    ~ModelThread(){ | 
				
			|||
        stop(); | 
				
			|||
        waitForThread(false); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    void setup(ofImage* _img, ofFbo* _fbo, Onnx* _model){ | 
				
			|||
        std::lock_guard<std::mutex> lock(mutex); | 
				
			|||
        this->img = _img; | 
				
			|||
        this->fbo = _fbo; | 
				
			|||
        this->model = _model; | 
				
			|||
        this->model_type = "depth"; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    void setupYolo(ofImage* _img, std::vector<types::BoxfWithLandmarks>* _detected_faces, Onnx* _model, Yolo* _yolo){ | 
				
			|||
        std::lock_guard<std::mutex> lock(mutex); | 
				
			|||
        this->img = _img; | 
				
			|||
        this->detected_faces = _detected_faces; | 
				
			|||
        this->model_type = "yolo"; | 
				
			|||
        this->model = _model; | 
				
			|||
        this->yolo = _yolo; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    void start(){ | 
				
			|||
        startThread(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    void stop(){ | 
				
			|||
        stopThread(); | 
				
			|||
        condition.notify_all(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    void threadedFunction(){ | 
				
			|||
        while(isThreadRunning()){ | 
				
			|||
            if(model_type == "depth"){ | 
				
			|||
                std::unique_lock<std::mutex> lock(mutex); | 
				
			|||
                inferDepthImage(fbo, img, model); | 
				
			|||
                condition.wait(lock); | 
				
			|||
            } else if(model_type == "yolo") { | 
				
			|||
                std::unique_lock<std::mutex> lock(mutex); | 
				
			|||
                inferYolo(); | 
				
			|||
                condition.wait(lock); | 
				
			|||
            } | 
				
			|||
             | 
				
			|||
        } | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    void update(){ | 
				
			|||
        std::lock_guard<std::mutex> lock(mutex); | 
				
			|||
        condition.notify_one(); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    void inferYolo(){ | 
				
			|||
        auto output_tensors_face = model->Run(*img); | 
				
			|||
 | 
				
			|||
        auto output_faces = output_tensors_face.front().GetTensorTypeAndShapeInfo().GetShape(); | 
				
			|||
 | 
				
			|||
        unsigned int num_anchors = output_faces[1];  // Number of anchors
 | 
				
			|||
 | 
				
			|||
        float* output_face_ptr = output_tensors_face.front().GetTensorMutableData<float>(); | 
				
			|||
 | 
				
			|||
        yolo->ParseOutput(output_face_ptr, *detected_faces, num_anchors); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    void inferDepthImage(ofFbo* fbo, ofImage* img, Onnx* model){ | 
				
			|||
        auto output_tensors = model->Run(*img); | 
				
			|||
        float* output_ptr = output_tensors.front().GetTensorMutableData<float>(); | 
				
			|||
        size_t num_elements = output_tensors.front().GetTensorTypeAndShapeInfo().GetElementCount(); | 
				
			|||
 | 
				
			|||
        float min_value = model->ReduceMin(output_ptr, num_elements); | 
				
			|||
        float max_value = model->ReduceMax(output_ptr, num_elements); | 
				
			|||
 | 
				
			|||
        model->Normalize(output_ptr, num_elements, min_value, max_value); | 
				
			|||
 | 
				
			|||
        model->DataToFbo(output_ptr, 518, 518, *fbo); | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
 | 
				
			|||
    protected: | 
				
			|||
    std::condition_variable condition; | 
				
			|||
}; | 
				
			|||
@ -0,0 +1,21 @@ | 
				
			|||
#include "QuadSource.h" | 
				
			|||
 | 
				
			|||
void QuadSource::setup(){ | 
				
			|||
    // name
 | 
				
			|||
    name = "test"; | 
				
			|||
 | 
				
			|||
    // allocate size
 | 
				
			|||
    allocate(1512, 1080); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
 | 
				
			|||
void QuadSource::update(){ | 
				
			|||
	 | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void QuadSource::draw(){ | 
				
			|||
	// Fill FBO with our quads
 | 
				
			|||
    ofClear(0); | 
				
			|||
	//ofBackground(255, 120, 10);
 | 
				
			|||
	fbo_in->draw(0, 0); | 
				
			|||
} | 
				
			|||
@ -0,0 +1,17 @@ | 
				
			|||
#ifndef _QUADSOURCE | 
				
			|||
#define _QUADSOURCE | 
				
			|||
 | 
				
			|||
#include "ofMain.h" | 
				
			|||
#include "FboSource.h" | 
				
			|||
 | 
				
			|||
class QuadSource : public ofx::piMapper::FboSource{ | 
				
			|||
 | 
				
			|||
    public: | 
				
			|||
    void setup(); | 
				
			|||
    void update(); | 
				
			|||
    void draw(); | 
				
			|||
 | 
				
			|||
    std::vector<ofRectangle> quads; | 
				
			|||
    std::vector<float> quad_speeds; | 
				
			|||
}; | 
				
			|||
#endif | 
				
			|||
@ -0,0 +1,40 @@ | 
				
			|||
#include "Request.h" | 
				
			|||
 | 
				
			|||
/* setup http server */ | 
				
			|||
void Request::setup(std::string ip, int port, std::string page){ | 
				
			|||
    std::cout << "Initialising HTTP Server" << std::endl; | 
				
			|||
 | 
				
			|||
    req.method = ofHttpRequest::POST; | 
				
			|||
    req.url = "http://" + ip + ":" + ofToString(port) + "/" + page; | 
				
			|||
    req.headers["Content-Type"] = "application/json"; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/* send a request to vp_server & return frame/video/folder */ | 
				
			|||
VPResp Request::query(Vector7D& in){ | 
				
			|||
    VPResp vp_resp; | 
				
			|||
    try { | 
				
			|||
        req.body = "{\"vector\": [" + | 
				
			|||
                ofToString(in.angry) + "," + | 
				
			|||
                ofToString(in.disgust) + "," + | 
				
			|||
                ofToString(in.fear) + "," + | 
				
			|||
                ofToString(in.happy) + "," + | 
				
			|||
                ofToString(in.sad) + "," + | 
				
			|||
                ofToString(in.surprise) + "," + | 
				
			|||
                ofToString(in.neutral) + "]}"; | 
				
			|||
 | 
				
			|||
        auto resp = http.handleRequest(req); | 
				
			|||
        json_resp = ofJson::parse(resp.data.getText()); | 
				
			|||
        vp_resp.folder = json_resp["folder"]; | 
				
			|||
        vp_resp.image = json_resp["image"]; | 
				
			|||
        vp_resp.video = json_resp["video"]; | 
				
			|||
        vp_resp.frame = json_resp["frame"]; | 
				
			|||
        vp_resp.lost = json_resp["lost"]; | 
				
			|||
 | 
				
			|||
        past_vp_resp = vp_resp; | 
				
			|||
 | 
				
			|||
        return vp_resp; | 
				
			|||
    } catch (exception e) { | 
				
			|||
        // Some issue happening here when plugging in controllers, or when they initially connect
 | 
				
			|||
        return past_vp_resp; | 
				
			|||
    } | 
				
			|||
} | 
				
			|||
@ -0,0 +1,46 @@ | 
				
			|||
#ifndef _REQUEST | 
				
			|||
#define _REQUEST | 
				
			|||
 | 
				
			|||
#include "ofMain.h" | 
				
			|||
 | 
				
			|||
/* emotional embedding */ | 
				
			|||
struct Vector7D { | 
				
			|||
    float angry; | 
				
			|||
    float disgust; | 
				
			|||
    float fear; | 
				
			|||
    float happy; | 
				
			|||
    float sad; | 
				
			|||
    float surprise; | 
				
			|||
    float neutral; | 
				
			|||
     | 
				
			|||
    bool operator!=(const Vector7D &other) const { | 
				
			|||
        return angry != other.angry || | 
				
			|||
               disgust != other.disgust || | 
				
			|||
               fear != other.fear || | 
				
			|||
               happy != other.happy || | 
				
			|||
               sad != other.sad || | 
				
			|||
               surprise != other.surprise || | 
				
			|||
               neutral != other.neutral; | 
				
			|||
    } | 
				
			|||
}; | 
				
			|||
 | 
				
			|||
/* Vantage point query structure */ | 
				
			|||
struct VPResp{ | 
				
			|||
    std::string folder; | 
				
			|||
    std::string video; | 
				
			|||
    std::string image; | 
				
			|||
    std::string frame; | 
				
			|||
    int lost; | 
				
			|||
}; | 
				
			|||
 | 
				
			|||
class Request{ | 
				
			|||
    public: | 
				
			|||
    void setup(std::string ip, int port, std::string page); | 
				
			|||
    VPResp query(Vector7D& in); | 
				
			|||
 | 
				
			|||
    ofHttpRequest req; | 
				
			|||
    ofURLFileLoader http; | 
				
			|||
    ofJson json_resp; | 
				
			|||
    VPResp past_vp_resp; | 
				
			|||
}; | 
				
			|||
#endif | 
				
			|||
@ -0,0 +1,159 @@ | 
				
			|||
#include "Server.h" | 
				
			|||
 | 
				
			|||
void Server::start(){ | 
				
			|||
    std::cout << "Initialising TCP sever" << std::endl; | 
				
			|||
    server.setup(port); | 
				
			|||
    osc_sender.setup(OSC_HOST, OSC_PORT); | 
				
			|||
    http.setup(http_ip, http_port, http_page); | 
				
			|||
    is_active = true; | 
				
			|||
    previous_embedding = embedding; | 
				
			|||
    last_change_time = std::chrono::steady_clock::now(); | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void Server::update(){ | 
				
			|||
 | 
				
			|||
    for ( int i = 0; i < server.getLastID(); i++){ | 
				
			|||
        if (server.isClientConnected(i)) { | 
				
			|||
            const int buffer_size = 8; | 
				
			|||
            char buffer[buffer_size]; | 
				
			|||
            int bytes_recieved = server.receiveRawBytes(i, buffer, buffer_size); | 
				
			|||
 | 
				
			|||
            if (bytes_recieved == buffer_size){ | 
				
			|||
                float value; | 
				
			|||
                int id; | 
				
			|||
                memcpy(&value, buffer, sizeof(float)); | 
				
			|||
                memcpy(&id, buffer + sizeof(float), sizeof(int)); | 
				
			|||
 | 
				
			|||
                std::string ip_address = server.getClientIP(i); | 
				
			|||
 | 
				
			|||
                addOrUpdateClient(id, value, ip_address); | 
				
			|||
            } | 
				
			|||
        } | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    updateEmbedding(); | 
				
			|||
    checkActivity(); | 
				
			|||
    sendOSCMessage(); | 
				
			|||
 | 
				
			|||
    if(debug){ | 
				
			|||
        printClients(); | 
				
			|||
    } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void Server::addOrUpdateClient(int client_id, float value, const std::string& ip_address){ | 
				
			|||
    ClientInfo client; | 
				
			|||
 | 
				
			|||
    client.ip_address = ip_address; | 
				
			|||
    client.value = value; | 
				
			|||
 | 
				
			|||
    clients[client_id] = client; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void Server::updateEmbedding(){ | 
				
			|||
    for(const auto& c : clients){ | 
				
			|||
        const ClientInfo& info = c.second; | 
				
			|||
        float val = std::round(info.value * 100.0f) / 100.0f; | 
				
			|||
        switch(c.first){ | 
				
			|||
            case 0:  | 
				
			|||
                embedding.angry = val; | 
				
			|||
                break; | 
				
			|||
            case 1:  | 
				
			|||
                embedding.disgust = val; | 
				
			|||
                break; | 
				
			|||
            case 2:  | 
				
			|||
                embedding.fear = val; | 
				
			|||
                break; | 
				
			|||
            case 3:  | 
				
			|||
                embedding.happy = val; | 
				
			|||
                break; | 
				
			|||
            case 4:  | 
				
			|||
                embedding.sad = val; | 
				
			|||
                break; | 
				
			|||
            case 5:  | 
				
			|||
                embedding.surprise = val; | 
				
			|||
                break; | 
				
			|||
            case 6:  | 
				
			|||
                embedding.neutral = val; | 
				
			|||
                embedding.fear = ofRandom(0.1, 0.6); | 
				
			|||
                embedding.angry = ofRandom(0.01, 0.99); | 
				
			|||
                embedding.happy = ofRandom(0.01, 0.99); | 
				
			|||
                break; | 
				
			|||
        } | 
				
			|||
    } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
void Server::printClients(){ | 
				
			|||
    for( const auto& c : clients){ | 
				
			|||
        int id = c.first; | 
				
			|||
        const ClientInfo& info = c.second; | 
				
			|||
        std::cout << "id: " << id | 
				
			|||
            << ", value: " << info.value | 
				
			|||
            << ", IP: " << info.ip_address << std::endl; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    std::cout << is_active << std::endl; | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/* check if the controllers are in use */ | 
				
			|||
void Server::checkActivity(){ | 
				
			|||
    if (previous_embedding.neutral != embedding.neutral) {  // Check if embedding has changed
 | 
				
			|||
        last_change_time = std::chrono::steady_clock::now();  // Reset the timer if there is a change
 | 
				
			|||
        previous_embedding = embedding;  // Update the previous embedding to the current one
 | 
				
			|||
        is_active = true; | 
				
			|||
        sendHttpRequest(); | 
				
			|||
    } else { | 
				
			|||
        // Calculate the time since the last change
 | 
				
			|||
        auto now = std::chrono::steady_clock::now(); | 
				
			|||
        auto duration = std::chrono::duration_cast<std::chrono::seconds>(now - last_change_time).count(); | 
				
			|||
 | 
				
			|||
        if (duration >= 2) { | 
				
			|||
            is_active = false; | 
				
			|||
        } | 
				
			|||
    } | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/* send osc msg, check if audio file exists and it is different to the past audiofile */ | 
				
			|||
void Server::sendOSCMessage(){ | 
				
			|||
    std::vector<ofxOscMessage> messages; | 
				
			|||
 | 
				
			|||
    ofxOscMessage me_0; | 
				
			|||
    ofxOscMessage me_1; | 
				
			|||
    ofxOscMessage me_2; | 
				
			|||
    ofxOscMessage me_3; | 
				
			|||
    ofxOscMessage me_file; | 
				
			|||
 | 
				
			|||
    std::string audio_file = vp_resp.folder; | 
				
			|||
 | 
				
			|||
    // Check if file exists in a given dir
 | 
				
			|||
    ofFile file("/home/cailean/Desktop/rave/all_wav_files/" +  audio_file + ".wav"); | 
				
			|||
 | 
				
			|||
    if(!is_active && (audio_file != past_audio_file) && file.exists()){ | 
				
			|||
        me_file.setAddress("/emote/filename"); | 
				
			|||
        me_file.addStringArg(audio_file + ".wav"); | 
				
			|||
        messages.push_back(me_file); | 
				
			|||
        past_audio_file = audio_file; | 
				
			|||
    } | 
				
			|||
 | 
				
			|||
    me_0.setAddress("/emote/0"); | 
				
			|||
    me_0.addFloatArg(embedding.neutral); | 
				
			|||
    messages.push_back(me_0); | 
				
			|||
    me_1.setAddress("/emote/1"); | 
				
			|||
    me_1.addFloatArg(embedding.neutral); | 
				
			|||
    messages.push_back(me_1); | 
				
			|||
    me_2.setAddress("/emote/2"); | 
				
			|||
    me_2.addFloatArg(embedding.neutral); | 
				
			|||
    messages.push_back(me_2); | 
				
			|||
    me_3.setAddress("/emote/3"); | 
				
			|||
    me_3.addFloatArg(embedding.neutral); | 
				
			|||
    messages.push_back(me_3); | 
				
			|||
 | 
				
			|||
    for (auto& msg : messages){ | 
				
			|||
         osc_sender.sendMessage(msg, false); | 
				
			|||
    } | 
				
			|||
    | 
				
			|||
} | 
				
			|||
 | 
				
			|||
/* sends request to http server if is_active = true */ | 
				
			|||
void Server::sendHttpRequest(){ | 
				
			|||
    vp_resp = http.query(embedding); | 
				
			|||
} | 
				
			|||
@ -0,0 +1,54 @@ | 
				
			|||
#ifndef _SERVER | 
				
			|||
#define _SERVER | 
				
			|||
 | 
				
			|||
#include "ofMain.h" | 
				
			|||
#include "ofxNetwork.h" | 
				
			|||
#include "Request.h" | 
				
			|||
#include <unordered_map> | 
				
			|||
#include <chrono> | 
				
			|||
#include "ofxOsc.h" | 
				
			|||
 | 
				
			|||
#define OSC_HOST "127.0.0.1" | 
				
			|||
#define OSC_PORT 9002 | 
				
			|||
 | 
				
			|||
struct ClientInfo { | 
				
			|||
    float value; | 
				
			|||
    std::string ip_address; | 
				
			|||
}; | 
				
			|||
 | 
				
			|||
class Server{ | 
				
			|||
    public: | 
				
			|||
    Server(int port, Vector7D& _embedding, bool debug, std::string _http_ip, int _http_port, std::string _http_page)  | 
				
			|||
        : port(port), embedding(_embedding), debug(debug), http_ip(_http_ip), http_port(_http_port), http_page(_http_page) {} | 
				
			|||
 | 
				
			|||
    void start(); | 
				
			|||
    void update(); | 
				
			|||
    void addOrUpdateClient(int client_id, float value, const std::string& ip_address); | 
				
			|||
    void printClients(); | 
				
			|||
    void updateEmbedding(); | 
				
			|||
    void checkActivity(); | 
				
			|||
    void sendHttpRequest(); | 
				
			|||
    void sendOSCMessage(); | 
				
			|||
 | 
				
			|||
    int port; | 
				
			|||
    ofxTCPServer server; | 
				
			|||
    std::unordered_map<int, ClientInfo> clients; | 
				
			|||
    bool debug; | 
				
			|||
    bool is_active; | 
				
			|||
    Vector7D& embedding; | 
				
			|||
 | 
				
			|||
    Request http; | 
				
			|||
    std::string http_ip; | 
				
			|||
    int http_port; | 
				
			|||
    std::string http_page; | 
				
			|||
    VPResp vp_resp; | 
				
			|||
 | 
				
			|||
    private: | 
				
			|||
    Vector7D previous_embedding; | 
				
			|||
    std::chrono::time_point<std::chrono::steady_clock> last_change_time; | 
				
			|||
 | 
				
			|||
    ofxOscSender osc_sender; | 
				
			|||
    std::string past_audio_file; | 
				
			|||
}; | 
				
			|||
 | 
				
			|||
#endif | 
				
			|||
					Loading…
					
					
				
		Reference in new issue