Skip to content

Commit 1cc9e31

Browse files
authored
Merge pull request #304 from tritonuas/feat/data-aggregation
Add data aggregation structure to retain data on the obc and overwrite it with new data from the gcs
2 parents 1fc6cef + a6243d9 commit 1cc9e31

File tree

8 files changed

+142
-15
lines changed

8 files changed

+142
-15
lines changed

include/cv/aggregator.hpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <thread>
1212
#include <unordered_map>
1313
#include <vector>
14+
#include <map>
1415

1516
#include "cv/pipeline.hpp"
1617
#include "cv/utilities.hpp"
@@ -50,7 +51,16 @@ class CVAggregator {
5051
// For the endpoint to reset the current list of structs
5152
std::vector<AggregatedRun> popAllRuns();
5253

54+
// gets the record of all cv results
55+
LockPtr<std::map<int, IdentifiedTarget>> getCVRecord();
56+
57+
void updateRecords(std::vector<IdentifiedTarget>& new_values);
58+
5359
private:
60+
std::mutex cv_record_mut;
61+
62+
std::shared_ptr<std::map<int, IdentifiedTarget>> cv_record;
63+
5464
void worker(ImageData image, int thread_num);
5565

5666
Pipeline pipeline;

include/cv/pipeline.hpp

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
#include <cmath>
55
#include <memory>
6+
#include <optional>
67
#include <string>
78
#include <utility>
89
#include <vector>
@@ -29,14 +30,21 @@ struct PipelineResults {
2930
};
3031

3132
struct PipelineParams {
32-
// Added outputPath parameter with default empty string
33-
explicit PipelineParams(std::string yoloModelPath, std::string outputPath = "",
33+
// yoloModelPath is optional; when absent, no CV models will be loaded.
34+
explicit PipelineParams(std::optional<std::string> yoloModelPath = std::nullopt,
35+
std::string outputPath = "",
3436
bool do_preprocess = true)
3537
: yoloModelPath{std::move(yoloModelPath)},
3638
outputPath(std::move(outputPath)),
3739
do_preprocess(do_preprocess) {}
3840

39-
std::string yoloModelPath;
41+
explicit PipelineParams(const std::string& yoloModelPath,
42+
std::string outputPath = "",
43+
bool do_preprocess = true)
44+
: PipelineParams(std::optional<std::string>(yoloModelPath), std::move(outputPath),
45+
do_preprocess) {}
46+
47+
std::optional<std::string> yoloModelPath;
4048
bool do_preprocess;
4149
std::string outputPath;
4250
};

src/core/mission_state.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,3 +128,5 @@ void MissionState::setCamera(std::shared_ptr<CameraInterface> camera) { this->ca
128128
bool MissionState::getMappingIsDone() { return this->mappingIsDone; }
129129

130130
void MissionState::setMappingIsDone(bool isDone) { this->mappingIsDone = isDone; }
131+
132+

src/cv/aggregator.cpp

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ CVAggregator::CVAggregator(Pipeline&& p) : pipeline(std::move(p)) {
99
this->num_worker_threads = 0;
1010
this->results = std::make_shared<CVResults>();
1111
this->matched_results = std::make_shared<MatchedResults>();
12-
12+
this->cv_record = std::make_shared<std::map<int, IdentifiedTarget>>();
1313
AirdropTarget dummy; // Create one dummy template
1414

1515
// Configure the coordinate part of the dummy
@@ -36,6 +36,19 @@ LockPtr<MatchedResults> CVAggregator::getMatchedResults() {
3636
return LockPtr<MatchedResults>(this->matched_results, &this->mut);
3737
}
3838

39+
LockPtr<std::map<int, IdentifiedTarget>> CVAggregator::getCVRecord() {
40+
return LockPtr<std::map<int, IdentifiedTarget>>(this->cv_record, &this->cv_record_mut);
41+
}
42+
void CVAggregator::updateRecords(std::vector<IdentifiedTarget>& new_values) {
43+
LockPtr<std::map<int, IdentifiedTarget>> records = this->getCVRecord();
44+
for (IdentifiedTarget id : new_values) {
45+
if (records.data->contains(id.run_id())) {
46+
records.data->at(id.run_id()).CopyFrom(id);
47+
} else {
48+
LOG_F(WARNING, "Tried to modify with an ID not inside CVRecord. ID: (%d)", id.run_id());
49+
}
50+
}
51+
}
3952
void CVAggregator::runPipeline(const ImageData& image) {
4053
Lock lock(this->mut);
4154

src/cv/pipeline.cpp

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,18 @@
77

88
// Pipeline constructor: initialize YOLO detector and the preprocess flag.
99
Pipeline::Pipeline(const PipelineParams& p)
10-
: yoloDetector(std::make_unique<YOLO>(p.yoloModelPath, 0.05f, 640, 640)),
11-
outputPath(p.outputPath),
12-
do_preprocess(p.do_preprocess) {}
10+
: outputPath(p.outputPath),
11+
do_preprocess(p.do_preprocess) {
12+
if (p.yoloModelPath.has_value() && !p.yoloModelPath->empty()) {
13+
yoloDetector = std::make_unique<YOLO>(*p.yoloModelPath, 0.05f, 640, 640);
14+
LOG_F(INFO, "YOLO model loaded from path: %s", p.yoloModelPath->c_str());
15+
} else {
16+
yoloDetector.reset();
17+
LOG_F(WARNING, "No CV models are loaded (no YOLO model path provided).");
18+
LOG_F(WARNING, "CVAGGREGATOR WILL NOT BE WORKING AS INTENDED. USE AT YOUR OWN RISK.");
19+
LOG_F(WARNING, "Provide a YOLO model path to enable detections.");
20+
}
21+
}
1322

1423
PipelineResults Pipeline::run(const ImageData& imageData) {
1524
LOG_F(INFO, "Running pipeline on an image");
@@ -88,7 +97,9 @@ PipelineResults Pipeline::run(const ImageData& imageData) {
8897

8998
// 3) DRAW DETECTIONS ON THE IMAGE
9099
// (this modifies processedImage in-place)
91-
this->yoloDetector->drawAndPrintDetections(processedImage, yoloResults);
100+
if (this->yoloDetector) {
101+
this->yoloDetector->drawAndPrintDetections(processedImage, yoloResults);
102+
}
92103

93104
// Save the annotated image if an output path is specified
94105
if (!outputPath.empty()) {

src/network/gcs_routes.cpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -351,7 +351,6 @@ DEF_GCS_HANDLE(Post, takeoff, autonomous) {
351351

352352
DEF_GCS_HANDLE(Get, targets, all) {
353353
LOG_REQUEST("GET", "/targets/all");
354-
355354
auto aggregator = state->getCV();
356355
if (!aggregator) {
357356
LOG_RESPONSE(ERROR, "CV not connected yet", BAD_REQUEST);
@@ -370,13 +369,15 @@ DEF_GCS_HANDLE(Get, targets, all) {
370369
std::vector<IdentifiedTarget> out_data;
371370
out_data.reserve(new_runs.size()); // Reserve space for efficiency
372371

372+
// get aggregate to store a record of these results
373+
LockPtr<std::map<int, IdentifiedTarget>> records = aggregator->getCVRecord();
374+
std::shared_ptr<std::map<int, IdentifiedTarget>> records_ptr = records.data;
375+
373376
for (const auto& run : new_runs) {
374377
// Create ONE IdentifiedTarget message per AggregatedRun
375378
IdentifiedTarget target;
376-
377379
// Set the run ID
378380
target.set_run_id(run.run_id);
379-
380381
// START COMPRESSION
381382

382383
// Compress the annotated image before converting to base64
@@ -409,10 +410,8 @@ DEF_GCS_HANDLE(Get, targets, all) {
409410

410411
// Convert the compressed image to base64 and set it (once per run)
411412
std::string b64 = cvMatToBase64(compressed_image);
412-
413-
// END COMPRESSION
414-
415413
target.set_picture(b64);
414+
// END COMPRESSION
416415

417416
// Ensure coords and bboxes vectors are the same size (should be guaranteed by Aggregator
418417
// logic)
@@ -440,10 +439,15 @@ DEF_GCS_HANDLE(Get, targets, all) {
440439
proto_bbox->set_y2(run.bboxes[i].y2);
441440
}
442441

442+
// copy the target to a record object to store
443+
IdentifiedTarget record;
444+
record.CopyFrom(target);
445+
// remove image
446+
record.set_picture("");
447+
records_ptr->insert_or_assign(run.run_id, target);
443448
// Add the completed IdentifiedTarget (representing the whole run) to the output list
444449
out_data.push_back(std::move(target));
445450
} // End loop over AggregatedRuns
446-
447451
// 3) Serialize the vector of IdentifiedTarget messages to JSON
448452
// Ensure messagesToJson can handle a vector or use iterators correctly
449453
std::string out_data_json = messagesToJson(out_data.begin(), out_data.end());

src/utilities/datatypes.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,3 +111,4 @@ std::string AirdropTypeObjectsToString(const AirdropType& color) {
111111
default: return "IDFK";
112112
}
113113
}
114+

tests/unit/modify_runs.cpp

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#include <gtest/gtest.h>
2+
#include "cv/aggregator.hpp"
3+
TEST(ModifyRuns, OverWriteData)
4+
{
5+
PipelineParams params(std::nullopt,
6+
"",
7+
false);
8+
Pipeline pipeline(params);
9+
CVAggregator aggregator(std::move(pipeline));
10+
11+
std::vector<IdentifiedTarget> updated;
12+
{
13+
auto lock = aggregator.getCVRecord();
14+
auto ptr = lock.data;
15+
// Add some mock runs
16+
for (int i = 1; i < 11; i++)
17+
{
18+
IdentifiedTarget target;
19+
target.set_run_id(i);
20+
// set mock bounding box
21+
GPSCoord *proto_coord = target.add_coordinates();
22+
proto_coord->set_latitude(0);
23+
proto_coord->set_longitude(0);
24+
proto_coord->set_altitude(0);
25+
26+
BboxProto *proto_bbox = target.add_bboxes();
27+
proto_bbox->set_x1(0);
28+
proto_bbox->set_y1(0);
29+
proto_bbox->set_x2(0);
30+
proto_bbox->set_y2(0);
31+
32+
ptr->insert_or_assign(i, target);
33+
}
34+
35+
for (int i = 1; i < 11; i++)
36+
{
37+
IdentifiedTarget target;
38+
target.set_run_id(i);
39+
40+
GPSCoord *proto_coord = target.add_coordinates(); // Use the plural field name
41+
proto_coord->set_latitude(i);
42+
proto_coord->set_longitude(i);
43+
proto_coord->set_altitude(i);
44+
45+
// Add bounding box
46+
BboxProto *proto_bbox = target.add_bboxes(); // Use the plural field name
47+
proto_bbox->set_x1(i);
48+
proto_bbox->set_y1(i);
49+
proto_bbox->set_x2(i);
50+
proto_bbox->set_y2(i);
51+
ptr->insert_or_assign(i, target);
52+
53+
updated.push_back(target);
54+
}
55+
// deallocate to unlock the mutex
56+
}
57+
aggregator.updateRecords(updated);
58+
59+
for (auto value : updated)
60+
{
61+
auto lock = aggregator.getCVRecord();
62+
auto ptr = lock.data;
63+
ASSERT_TRUE(ptr->contains(value.run_id()));
64+
for (int i = 0; i < value.bboxes_size(); i++)
65+
{
66+
ASSERT_EQ(value.bboxes().at(i).x1(), ptr->at(value.run_id()).bboxes().at(i).x1());
67+
ASSERT_EQ(value.bboxes().at(i).y1(), ptr->at(value.run_id()).bboxes().at(i).y1());
68+
ASSERT_EQ(value.bboxes().at(i).x2(), ptr->at(value.run_id()).bboxes().at(i).x2());
69+
ASSERT_EQ(value.bboxes().at(i).y2(), ptr->at(value.run_id()).bboxes().at(i).y2());
70+
}
71+
for (int i = 0; i < value.coordinates_size(); i++)
72+
{
73+
ASSERT_EQ(value.coordinates().at(i).altitude(), ptr->at(value.run_id()).coordinates().at(i).altitude());
74+
ASSERT_EQ(value.coordinates().at(i).longitude(), ptr->at(value.run_id()).coordinates().at(i).longitude());
75+
ASSERT_EQ(value.coordinates().at(i).latitude(), ptr->at(value.run_id()).coordinates().at(i).latitude());
76+
}
77+
}
78+
}

0 commit comments

Comments
 (0)