Skip to content

Commit ce6a1ca

Browse files
authored
Merge pull request #3 from Techainer/fix_#1
Fix #1: Add support for Landmark
2 parents d4d70ca + aacbd8b commit ce6a1ca

File tree

13 files changed

+147
-120
lines changed

13 files changed

+147
-120
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ jobs:
3434
id: cache-opencv-linux
3535
with:
3636
path: /home/runner/work/sort-node/sort-node/opencv/build
37-
key: cache-opencv-linux
37+
key: cache-opencv-linux-2
3838

3939
- name: Install OpenCV for Linux
4040
if: matrix.os == 'ubuntu-latest'
@@ -95,7 +95,7 @@ jobs:
9595
run: yarn build-prebuild
9696

9797
- name: Build
98-
run: yarn install
98+
run: yarn install --build-from-source --verbose
9999

100100
- name: Run test suite
101101
run: yarn test

.github/workflows/publish-prebuild.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ jobs:
3333
id: cache-opencv-linux
3434
with:
3535
path: /home/runner/work/sort-node/sort-node/opencv/build
36-
key: cache-opencv-linux
36+
key: cache-opencv-linux-2
3737

3838
- name: Install OpenCV for Linux
3939
if: matrix.os == 'ubuntu-latest'

.vscode/c_cpp_properties.json

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,18 @@
44
"name": "Mac",
55
"includePath": [
66
"${workspaceFolder}/**",
7-
"${workspaceFolder}/include/"
7+
"${workspaceFolder}/include/",
8+
"/usr/local/Cellar/opencv/4.5.2/include/opencv4",
9+
"${workspaceFolder}/node_modules/node-addon-api",
10+
"~/anaconda3/envs/sort-cpp/include/node"
811
],
912
"defines": [],
1013
"macFrameworkPath": [
1114
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/System/Library/Frameworks"
1215
],
1316
"compilerPath": "/usr/bin/clang",
1417
"cStandard": "c11",
15-
"cppStandard": "c++98",
18+
"cppStandard": "c++11",
1619
"intelliSenseMode": "macos-clang-x64"
1720
}
1821
],

.vscode/settings.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
"utility": "cpp",
55
"chrono": "cpp",
66
"locale": "cpp",
7-
"string": "cpp"
7+
"string": "cpp",
8+
"ios": "cpp",
9+
"iostream": "cpp"
810
}
911
}

README.md

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This package is maintained by [Techainer](https://techainer.com)
99

1010
## Install
1111
To install this package, make sure you have the following dependencies installed:
12-
- NodeJS 14.x.x
12+
- NodeJS 12+
1313
- cmake 3.9+
1414
- OpenCV 3.x.x (Build from source recommened)
1515
- Eigen 3.x.x (`sudo apt-get install -y libeigen3-dev`)
@@ -19,6 +19,8 @@ Noted that we have provide a `Dockerfile` contain all 3rd dependencies. To use i
1919
docker build -t sort .
2020
./docker_run.sh
2121
```
22+
Or you can reference our github actions [CI](.github/workflows/ci.yml) flow to install dependencies for your own OS.
23+
2224
Then you can install the package from npm:
2325

2426
```bash
@@ -35,9 +37,16 @@ The `SortNode` object can be initialize with 2 arguments:
3537

3638
With each frame, you will need to call `update` method.
3739

38-
This method except a single arguments that had a the format `List[List[float]]`, which means a list of detected object in that frame. Each object will have the format: `[x_top, y_top, width, height, confidence]`.
40+
This method except a single arguments that had a the format `List[List[float]]`, which means a list of detected object in that frame. Each object will have the format: `[x_top, y_top, width, height, confidence]` or `[x_top, y_top, width, height, confidence, landmark_x1, landmark_y1, ...]` for addtional landmark associated with each bounding box
3941

40-
The `update` method will return a list of tracked object in the format `[List[List[int]]`, each object will have the format: `[x_top, y_top, width, height, track_id]`
42+
The `update` method will return a list of tracked object in the format `[List[Object]]`, each object will have the following structure:
43+
```js
44+
{
45+
bbox: List[(int) x_top, y_top, width, height],
46+
track_id: int,
47+
landmarks: List[(float) x1, y1, x2, y2, ..., x_n, y_n],
48+
}
49+
```
4150

4251
Please noted that the number of returned object might not be the same as the number of inputed object.
4352

include/track.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ class Track {
1111
// Destructor
1212
~Track() = default;
1313

14-
void Init(const cv::Rect& bbox);
14+
void Init(const std::pair<cv::Rect, std::vector<float>> &bbox);
1515
void Predict();
16-
void Update(const cv::Rect& bbox);
16+
void Update(const std::pair<cv::Rect, std::vector<float>> &bbox);
1717
cv::Rect GetStateAsBbox() const;
1818
float GetNIS() const;
1919

2020
int coast_cycles_ = 0, hit_streak_ = 0;
21+
std::vector<float> landmarks;
2122

2223
private:
2324
Eigen::VectorXd ConvertBboxToObservation(const cv::Rect& bbox) const;

include/tracker.h

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ class Tracker {
2727
* @param unmatched_det
2828
* @param iou_threshold
2929
*/
30-
static void AssociateDetectionsToTrackers(const std::vector<cv::Rect>& detection,
31-
std::map<int, Track>& tracks,
32-
std::map<int, cv::Rect>& matched,
33-
std::vector<cv::Rect>& unmatched_det,
34-
float iou_threshold = 0.3);
30+
static void AssociateDetectionsToTrackers(const std::vector<std::pair<cv::Rect, std::vector<float>>> &detection,
31+
std::map<int, Track> &tracks,
32+
std::map<int, std::pair<cv::Rect, std::vector<float>>> &matched,
33+
std::vector<std::pair<cv::Rect, std::vector<float>>> &unmatched_det,
34+
float iou_threshold = 0.3);
3535

36-
void Run(const std::vector<cv::Rect>& detections);
36+
void Run(const std::vector<std::pair<cv::Rect, std::vector<float>>> &detections);
3737

3838
std::map<int, Track> GetTracks();
3939

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
},
1919
"name": "@techainer1t/sort-node",
2020
"description": "Node binding of SORT: Simple, online, and real-time tracking of multiple objects in a video sequence.",
21-
"version": "1.0.0",
21+
"version": "1.1.0",
2222
"directories": {
2323
"doc": "docs"
2424
},

src/sort.cpp

Lines changed: 0 additions & 66 deletions
This file was deleted.

src/sort_node.cc

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ namespace sortnode
6969

7070
if (!info[0].IsArray())
7171
{
72-
Napi::TypeError::New(env, "dets must have format: List[List[x1, y1, w, h, conf]]")
72+
Napi::TypeError::New(env, "dets must have format: List[List[x1, y1, w, h, conf]] or List[List[x1, y1, w, h, conf, landmark_x1, landmark_y1, ...]]")
7373
.ThrowAsJavaScriptException();
7474
return env.Null();
7575
}
@@ -82,26 +82,32 @@ namespace sortnode
8282
Napi::Value _each_bbox = jsList[i];
8383
if (!_each_bbox.IsArray())
8484
{
85-
Napi::TypeError::New(env, "dets must have format: List[List[x1, y1, w, h, conf]]")
85+
Napi::TypeError::New(env, "dets must have format: List[List[x1, y1, w, h, conf]] or List[List[x1, y1, w, h, conf, landmark_x1, landmark_y1, ...]]")
8686
.ThrowAsJavaScriptException();
8787
return env.Null();
8888
}
8989
auto each_bbox = _each_bbox.As<Napi::Array>();
9090
auto each_bbox_len = each_bbox.Length();
91-
if (each_bbox_len != 5)
91+
if (each_bbox_len < 5)
9292
{
9393
Napi::TypeError::New(env, "each bbox must have format [x1, y1, w, h, conf]")
9494
.ThrowAsJavaScriptException();
9595
return env.Null();
9696
}
9797

98+
if (each_bbox_len > 5 && (each_bbox_len - 5) % 2 != 0){
99+
Napi::TypeError::New(env, "each bbox with landmark must have format [x1, y1, w, h, conf, landmark_x1, landmark_y1]")
100+
.ThrowAsJavaScriptException();
101+
return env.Null();
102+
}
103+
98104
std::vector<float> current_bbox;
99-
for (uint32_t j = 0; j < 5; j++)
105+
for (uint32_t j = 0; j < each_bbox_len; j++)
100106
{
101107
Napi::Value _val = each_bbox[j];
102108
if (!_val.IsNumber())
103109
{
104-
Napi::TypeError::New(env, "each value x1, y1, w, h, conf must be a number")
110+
Napi::TypeError::New(env, "each value x1, y1, w, h, conf, landmark_xn, landmark_yn, ... must be a float")
105111
.ThrowAsJavaScriptException();
106112
return env.Null();
107113
}
@@ -115,21 +121,27 @@ namespace sortnode
115121
this->frame_index++;
116122

117123
// Convert vector of number to cv::Rect to use
118-
std::vector<cv::Rect> bbox_per_frame;
124+
std::vector<std::pair<cv::Rect, std::vector<float>>> bbox_per_frame;
119125
for (auto &each_bbox : dets)
120126
{
121127
if (each_bbox[4] >= this->kMinConfidence)
122128
{
123-
bbox_per_frame.emplace_back(each_bbox[0], each_bbox[1], each_bbox[2], each_bbox[3]);
129+
cv::Rect rect{int(each_bbox[0]), int(each_bbox[1]), int(each_bbox[2]), int(each_bbox[3])};
130+
std::vector<float> landmarks;
131+
for (auto i = 5; i < each_bbox.size(); i++){
132+
landmarks.push_back(each_bbox[i]);
133+
}
134+
bbox_per_frame.push_back(std::make_pair(rect, landmarks));
124135
}
125136
}
126137

127138
// Run SORT tracker
128139
this->tracker.Run(bbox_per_frame);
129140
const auto tracks = this->tracker.GetTracks();
130141

131-
// Convert results from cv::Rect to normal int vector
142+
// Convert results from cv::Rect to normal float vector
132143
std::vector<std::vector<int> > res;
144+
std::vector<std::vector<float>> res_landmarks;
133145
for (auto &trk : tracks)
134146
{
135147
const auto &bbox = trk.second.GetStateAsBbox();
@@ -142,7 +154,9 @@ namespace sortnode
142154
{
143155
std::vector<int> current_object{bbox.tl().x, bbox.tl().y, bbox.width, bbox.height, trk.first};
144156
// Last value is track id
157+
std::vector<float> landmarks = trk.second.landmarks;
145158
res.push_back(current_object);
159+
res_landmarks.push_back(landmarks);
146160
}
147161
}
148162

@@ -152,11 +166,26 @@ namespace sortnode
152166
{
153167
auto bbox = res[i];
154168
auto jsBbox = Napi::Array::New(env);
155-
for (uint32_t j = 0; j < bbox.size(); j++)
169+
for (uint32_t j = 0; j < bbox.size() - 1; j++)
156170
{
157171
jsBbox[j] = Napi::Number::New(env, bbox[j]);
158172
}
159-
jsOutputList[i] = jsBbox;
173+
// std::cout << "We got the bbox set" << std::endl;
174+
175+
auto landmarks = res_landmarks[i];
176+
auto jsLandmarks = Napi::Array::New(env);
177+
for (uint32_t j = 0; j < landmarks.size(); j++){
178+
jsLandmarks[j] = Napi::Number::New(env, landmarks[j]);
179+
}
180+
// std::cout << "We got the landmarks set" << std::endl;
181+
182+
auto jsDict = Napi::Object::New(env);
183+
jsDict.Set("bbox", jsBbox);
184+
jsDict.Set("track_id", Napi::Number::New(env, bbox[4]));
185+
jsDict.Set("landmarks", jsLandmarks);
186+
187+
188+
jsOutputList[i] = jsDict;
160189
}
161190
return jsOutputList;
162191
}

0 commit comments

Comments
 (0)