Skip to content

Commit 6f38db0

Browse files
authored
Merge pull request #322 from tritonuas/feat/compression
Compression Algorithm with denoising.
2 parents b262d85 + 28b3b70 commit 6f38db0

File tree

3 files changed

+44
-62
lines changed

3 files changed

+44
-62
lines changed

include/cv/utilities.hpp

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
#ifndef INCLUDE_CV_UTILITIES_HPP_
22
#define INCLUDE_CV_UTILITIES_HPP_
33

4+
#include <optional>
5+
#include <vector>
46
#include <opencv2/opencv.hpp>
57

68
#include "protos/obc.pb.h"
@@ -27,4 +29,16 @@ struct DetectedTarget {
2729
// Helper function to crop out a bounding box from an image if you still need it
2830
cv::Mat crop(const cv::Mat& original, const Bbox& bbox);
2931

32+
/**
33+
* Denoises and compresses a JPEG image to a desired quality.
34+
*
35+
* @param img : a const reference to the image you want to compress
36+
* @param quality : the quality you want to compress to. A quality of about 60
37+
* compresses an image about half size.
38+
*
39+
* @return : std::nullopt if the compression fails.
40+
* cv::Mat otherwise
41+
*/
42+
std::optional<cv::Mat> compressImg(const cv::Mat& img, int quality = 60);
43+
3044
#endif // INCLUDE_CV_UTILITIES_HPP_

src/cv/utilities.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
#include "cv/utilities.hpp"
2+
#include "utilities/logging.hpp"
23

34
int Bbox::width() const { return x2 - x1; }
45

@@ -10,3 +11,25 @@ cv::Mat crop(const cv::Mat& original, const Bbox& bbox) {
1011
auto x = cv::Mat(original.clone(), cv::Rect(bbox.x1, bbox.y1, bbox.width(), bbox.height()));
1112
return x;
1213
}
14+
15+
std::optional<cv::Mat> compressImg(const cv::Mat& img, int quality) {
16+
std::vector<int> compressionParams {cv::IMWRITE_JPEG_QUALITY, quality};
17+
std::vector<uchar> buffer;
18+
cv::Mat denoisedImg;
19+
20+
cv::fastNlMeansDenoisingColored(img, denoisedImg);
21+
22+
if (!cv::imencode(".jpg", denoisedImg, buffer, compressionParams)) {
23+
LOG_F(WARNING, "Unable to encode the image into a buffer. Using the orignal instead");
24+
return std::nullopt;
25+
}
26+
27+
cv::Mat decodedImg = cv::imdecode(buffer, cv::IMREAD_COLOR);
28+
29+
if (decodedImg.empty()) {
30+
LOG_F(WARNING, "Unable to decoode the image from a buffer. Using the orignal instead");
31+
return std::nullopt;
32+
}
33+
34+
return std::make_optional(decodedImg);
35+
}

src/network/gcs_routes.cpp

Lines changed: 7 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include "utilities/http.hpp"
2525
#include "utilities/logging.hpp"
2626
#include "utilities/serialize.hpp"
27+
#include "cv/utilities.hpp"
2728

2829
extern "C" {
2930
#include "udp_squared/protocol.h"
@@ -251,39 +252,11 @@ DEF_GCS_HANDLE(Get, camera, capture) {
251252
}
252253

253254
std::optional<ImageTelemetry> telemetry = image->TELEMETRY;
254-
255-
// START COMPRESSION
256-
// Compress the image before converting to base64
257-
std::vector<uchar> compressed_data;
258-
std::vector<int> compression_params;
259-
compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
260-
compression_params.push_back(85); // Quality: 0-100, 85 is a good balance for transmission
261-
262-
cv::Mat compressed_image;
263-
if (cv::imencode(".jpg", image->DATA, compressed_data, compression_params)) {
264-
// Create compressed Mat from encoded data
265-
compressed_image = cv::imdecode(compressed_data, cv::IMREAD_COLOR);
266-
267-
if (!compressed_image.empty()) {
268-
LOG_F(INFO,
269-
"Compressed manual capture image from %zu bytes to %zu bytes "
270-
"(%.1f%% compression)",
271-
image->DATA.total() * image->DATA.elemSize(), compressed_data.size(),
272-
(1.0 - static_cast<double>(compressed_data.size()) /
273-
(image->DATA.total() * image->DATA.elemSize())) *
274-
100.0);
275-
} else {
276-
LOG_F(WARNING, "Failed to decode compressed manual capture image, using original");
277-
compressed_image = image->DATA;
278-
}
279-
} else {
280-
LOG_F(WARNING, "Failed to compress manual capture image, using original");
281-
compressed_image = image->DATA;
282-
}
283-
255+
std::optional<cv::Mat> optCompressedImg = compressImg(image->DATA);
256+
cv::Mat compressed_image = optCompressedImg.has_value() ?
257+
optCompressedImg->clone() : image->DATA;
284258
ManualImage manual_image;
285259
manual_image.set_img_b64(cvMatToBase64(compressed_image));
286-
// END COMPRESSION
287260

288261
manual_image.set_timestamp(image->TIMESTAMP);
289262
if (telemetry.has_value()) {
@@ -379,40 +352,12 @@ DEF_GCS_HANDLE(Get, targets, all) {
379352
IdentifiedTarget target;
380353
// Set the run ID
381354
target.set_run_id(run.run_id);
382-
// START COMPRESSION
383-
384-
// Compress the annotated image before converting to base64
385-
std::vector<uchar> compressed_data;
386-
std::vector<int> compression_params;
387-
compression_params.push_back(cv::IMWRITE_JPEG_QUALITY);
388-
compression_params.push_back(85);
389-
// Quality: 0-100, 85 is a good balance for transmission
390-
391-
cv::Mat compressed_image;
392-
if (cv::imencode(".jpg", run.annotatedImage, compressed_data, compression_params)) {
393-
// Create compressed Mat from encoded data
394-
compressed_image = cv::imdecode(compressed_data, cv::IMREAD_COLOR);
395-
396-
if (!compressed_image.empty()) {
397-
LOG_F(INFO, "Compressed image from %zu bytes to %zu bytes (%.1f%% compression)",
398-
run.annotatedImage.total() * run.annotatedImage.elemSize(),
399-
compressed_data.size(),
400-
(1.0 - static_cast<double>(compressed_data.size()) /
401-
(run.annotatedImage.total() * run.annotatedImage.elemSize())) *
402-
100.0);
403-
} else {
404-
LOG_F(WARNING, "Failed to decode compressed image, using original");
405-
compressed_image = run.annotatedImage;
406-
}
407-
} else {
408-
LOG_F(WARNING, "Failed to compress image, using original");
409-
compressed_image = run.annotatedImage;
410-
}
411355

412-
// Convert the compressed image to base64 and set it (once per run)
356+
std::optional<cv::Mat> optCompressedImg = compressImg(run.annotatedImage);
357+
cv::Mat compressed_image = optCompressedImg.has_value() ?
358+
optCompressedImg->clone() : run.annotatedImage;
413359
std::string b64 = cvMatToBase64(compressed_image);
414360
target.set_picture(b64);
415-
// END COMPRESSION
416361

417362
// Ensure coords and bboxes vectors are the same size (should be guaranteed by Aggregator
418363
// logic)

0 commit comments

Comments
 (0)