Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
846abde
Beginning c++ implementation
JoshPoravanthattil Oct 4, 2022
2ecb075
Added data setup code
JoshPoravanthattil Oct 4, 2022
e8df68e
Progress
JoshPoravanthattil Oct 5, 2022
4f31c15
Progress during LANSCE
JoshPoravanthattil Oct 14, 2022
1cf48a1
Merge branch 'C++' of github.com:JoshPoravanthattil/HATS into C++
JoshPoravanthattil Oct 14, 2022
ffaff23
LANSCE progress pt.2
JoshPoravanthattil Oct 15, 2022
407682a
Moving things around
JoshPoravanthattil Oct 15, 2022
a136a36
Adding comments
JoshPoravanthattil Oct 31, 2022
eb63870
Added function skeletons to the hats.cpp file
Oct 31, 2022
75abb6f
Adding HATS::
JoshPoravanthattil Nov 1, 2022
a3e6a32
Started translating filter memory and included math library
Nov 1, 2022
08da2f7
Added semicolon at end of class in hats.h
Nov 1, 2022
6f3763a
Adding Makefile
JoshPoravanthattil Nov 1, 2022
fd1faca
Finished initial translate of filter memory
Nov 1, 2022
e769f55
Merge branch 'C++' of https://github.com/JoshPoravanthattil/HATS into…
Nov 1, 2022
3ba62e9
Completed get_pixel_cell_partition_matrix conversion
JoshPoravanthattil Nov 1, 2022
a24c1d8
worked on normalize
Nov 1, 2022
ff7e520
Merge branch 'C++' of https://github.com/JoshPoravanthattil/HATS into…
Nov 1, 2022
eb305c0
Finish translating compute_local_memory_time_surface function
JoshPoravanthattil Nov 2, 2022
8d87cc7
Merge branch 'C++' of github.com:JoshPoravanthattil/HATS into C++
JoshPoravanthattil Nov 2, 2022
07f8178
Adding class attributes
JoshPoravanthattil Nov 2, 2022
2bfe26c
Finished converting constructor
JoshPoravanthattil Nov 2, 2022
f3d585a
Finish translating reset function
JoshPoravanthattil Nov 2, 2022
637407c
Finished most functions
JoshPoravanthattil Nov 7, 2022
787e74f
Finished normalize function
Nov 7, 2022
ecc7a13
fixed normalize
Nov 12, 2022
986fd43
adding event_filter prelim
JoshPoravanthattil Nov 30, 2022
e77cce5
filter progress
JoshPoravanthattil Dec 2, 2022
7ea7cdb
Plot Attempt
JoshPoravanthattil Dec 2, 2022
1e8c021
plt show
JoshPoravanthattil Dec 2, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -127,3 +127,13 @@ dmypy.json

# Pyre type checker
.pyre/

#personal files
Test.zip
Train.zip
datasets/Test_binary/
datasets/Train_binary/
datasets/Test/
datasets/Train/
datasets/a.out
HATStest
20 changes: 20 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"configurations": [
{
"name": "Win32",
"includePath": [
"${workspaceFolder}/**"

],
"defines": [
"_DEBUG",
"UNICODE",
"_UNICODE"
],
"cStandard": "c17",
"cppStandard": "c++17",
"intelliSenseMode": "windows-msvc-x64"
}
],
"version": 4
}
189 changes: 189 additions & 0 deletions HATS.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
#include "HATS.h"
#include <cmath>
#include <cassert>
#include <vector>

vector<Event> filter_memory(vector<Event> memory, float event_ts, float temp_window){
// finds all events between [event.ts-temp_window, event.ts)
float limit_ts = event_ts - temp_window;

// Due to the way it is built we only have to find the first extreme
// Find it using binary search
bool found = false;
LRBound bound = {0, (int)(memory.size() - 1)};
int pos = 0;

// Step through memeory until extreme found
// This will be done with a binary search algorithm
while(bound.left<=bound.right && (found == false)){
int midpoint = floor((bound.left + bound.right)/2);
if(memory[midpoint].ts == limit_ts){
pos = midpoint;
found = true;
}else{
if(limit_ts < memory[midpoint].ts){
LRBound bound = {bound.left, midpoint-1};
}else{
LRBound bound = {midpoint+1, bound.right};
}
}
}
//Return all events starting from that index
// To do this in C++, a new vector will be created and initialized
// with all values from pos to end of memeory.
// If returning wrong, specifically missing the first event,
// change pos to pos - 1
vector<Event> returnMem((memory.begin() + pos), memory.end());
return returnMem;
}

vector<vector<int>> get_pixel_cell_partition_matrix(int width, int height, int K){
assert ((width % K == 0) && (height % K == 0));
int cell_width = floor(width/K);
int cell_height = floor(height/K);

vector<vector<int>> matrix(height, vector<int> (width, 0));

for (int i = 0; i < width; i ++) {
for (int j = 0; j < height; j ++) {
int pixel_row = floor(i / K);
int pixel_col = floor(j / K);
matrix[i][j] = pixel_row*cell_width + pixel_col;
}
}

return matrix;
}

vector<vector<vector<vector<float>>>> normalise(vector<vector<vector<vector<float>>>> histograms, vector<vector<int>> event_counter){
/*
A characteristic of event-based sensors is that the amount
of events generated by a moving object is proportional to its
contrast: higher contrast objects generate more events than
low contrast objects. To make the cell descriptor more invariant
to contrast, we therefore normalize h by the number of events |C|
contained in the spatio-temporal window used to compute it.

*/
// To get the right size, set result equal to histograms
vector<vector<vector<vector<float>>>> result = histograms;

// Then set all values to 0
// To do this, the entire matrix must be stepped over
// Due to dimensionality of vector, 4 iterators are needed
for(int i = 0; i < (int)result.size(); i++){
for(int j = 0; j < (int)result[i].size(); j++){
for(int k = 0; k < (int)result[i][j].size(); k++){
for(int h = 0; h < (int)result[i][j][k].size(); h++){
result[i][j][k][h] = 0;
}
}
}
}


// normalise by stepping over each element in histograms
// and dividing by the event count for the corresponding element
// As above, with a 4D vector, multiple iterators are needed
for(int i = 0; i < (int)result.size(); i++){
for(int j = 0; j < (int)result[i].size(); j++){
for(int k = 0; k < (int)result[i][j].size(); k++){
for(int h = 0; h < (int)result[i][j][k].size(); h++){
result[i][j][k][h] = histograms[i][j][k][h]/(event_counter[i][j]+0.1);
}
}
}
}
// Return the normalized result
return result;
}

vector<vector<float>> compute_local_memory_time_surface(Event event_i, vector<Event> filtered_memory, int R, float tau){
// initialize blank time surface
vector<vector<float>> time_surface(2*R+1, vector<float> (2*R+1, 0));

// get the timestamp of the triggering event
int t_i = event_i.ts;

// for every event in the local memory relevant to the event
// (relevean both in spatial and temporal terms), do:
for (int i = 0; i < filtered_memory.size(); i++) {
Event event_j = filtered_memory[i];
// compute the time delta
float delta_t = t_i - event_j.ts;

// compute contribution to time surface
float event_value = exp(-delta_t/tau);

// compute coordinates in the shifted representation
int shifted_y = event_j.y - (event_i.y - R);
int shifted_x = event_j.x - (event_i.x - R);

// sum it to the time surface
time_surface[shifted_y][shifted_x] += event_value;
}

// return the computed time surface
return time_surface;
}

HATS::HATS(float temp_window=0.1, int width=35, int height=35, float delta_t=0.1, float tau=0.5, int R=7, int K=7){
this->temp_window = temp_window;
this->tau = tau;
this->R = R;
this->K = K;

this->cell_width = floor(width/K);
this->cell_height = floor(height/K);
this->n_cells = this->cell_width * this->cell_height;
this->n_polarities = 2;

this->get_cell = get_pixel_cell_partition_matrix(width, height, K);

this->reset();
}

void HATS::reset(){
vector<vector<vector<vector<float>>>> temp(this->n_cells, vector<vector<vector<float>>>(this->n_polarities, vector<vector<float>>(2*this->R+1, vector<float>(2*this->R+1, 0))));
this->histograms = temp;
vector<vector<int>> temp1(this->n_cells, vector<int>(this->n_polarities, 0));
this->event_counter = temp1;
vector<vector<vector<Event>>> temp2(this->n_cells, vector<vector<Event>>(this->n_polarities, vector<Event>(1)));
this->cell_memory = temp2;
}

void HATS::process(Event ev){
// Get the cell corresponding to the event
int cell = this->get_cell[ev.y][ev.x];
int polarity_index = ev.polarity;

// If cell_memory is empty, initialize a list with the event, else add it
if (this->cell_memory[cell][polarity_index].back().ts == 0 && this->cell_memory[cell][polarity_index].size() == 1) {
this->cell_memory[cell][polarity_index][0] = ev;
} else {
this->cell_memory[cell][polarity_index].push_back(ev);
}

// Filter Local Memory to only events in Temporal Window
this-> cell_memory[cell][polarity_index] = filter_memory(this->cell_memory[cell][polarity_index], ev.ts, this->temp_window);

// Get the Local Memory Time Surface
vector<vector<float>> time_surface = compute_local_memory_time_surface(ev, this->cell_memory[cell][polarity_index], this->R, this->tau);

// Add the time surface to the cell histograms
for (int i=0; i<2*this->R+1; i++) {
for (int j=0; j<2*this->R+1; j++) {
this->histograms[cell][polarity_index][i][j] += time_surface[i][j];
}
}

// Increase the event counter for the cell
this->event_counter[cell][polarity_index] += 1;
}

void HATS::process_all(vector<Event> evs){
for (int i=0; i<evs.size(); i++) {
this->process(evs[i]);
}
this->histograms = normalise(this->histograms, this->event_counter);
}
83 changes: 83 additions & 0 deletions HATS.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#ifndef NUM_H
#define NUM_H

#include <vector>
using namespace std;

struct LRBound {
int left = 0;
int right = 0;
};

struct Event {
unsigned int x = 0;
unsigned int y = 0;
unsigned int ts = 0;
int polarity = 0;
};

struct TrainingSample {
vector<float> features;
vector<int> labels;
};

/*
finds all events between [events.ts-temp_window, event.ts)
*/
vector<Event> filter_memory(vector<Event> memory, float event_ts, float temp_window);

/*
function takes as input the width and the height of the image sensor, and the number
K which is the size of the C cells which divide the pixel grid. In order to perform
quick lookup of which is the corresponding cell for each pixl, this function returns
a matrix containing the index of the corresponding cell for each pixel which makes the
lookup O(1). The indices are returned in a row manner, i.e:
0 1 2 3 4
5 6 7 8 9
*/
vector<vector<int>> get_pixel_cell_partition_matrix(int width, int height, int K);

/*
A characteristic of event-based sensors is that the amount
of events generated by a moving object is proportional to its
contrast: higher contrast objects generate more events than
low contrast objects. To make the cell descriptor more invariant
to contrast, we therefore normalize h by the number of events |C|
contained in the spatio-temporal window used to compute it.
*/
vector<vector<float>> normalise(vector<vector<float>> histograms, int event_counter);

/*
The function takes the a filtered memory containing only
events in the neighborhood of the event and belonging to the
temporal window that needs to be considered and outputs a time
surface.
*/
vector<vector<float>> compute_local_memory_time_surface(Event ev, vector<Event> filtered_memory, int R, float tau);

class HATS {
public:
// class attributes
double temp_window;
double tau;
int R;
int K;
int cell_width;
int cell_height;
int n_cells;
int n_polarities;
vector<vector<int>> get_cell;
vector<vector<vector<vector<float>>>> histograms;
vector<vector<int>> event_counter;
vector<vector<vector<Event>>> cell_memory;

// class constructor
HATS(float temp_window, int width, int height, float delta_t, float tau, int R, int K);

// class functions
void reset();
void process(Event ev);
void process_all(vector<Event> evs);
};

#endif
Binary file added HATS.h.gch
Binary file not shown.
Loading