Skip to content

Commit 28d0bec

Browse files
committed
image annotation export ++
1 parent 1bb4872 commit 28d0bec

File tree

8 files changed

+511
-229
lines changed

8 files changed

+511
-229
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,7 @@ add_library(${LIBRARY_NAME}
9393
src/core/event.cpp
9494
src/core/handle.cpp
9595
src/core/history.cpp
96+
src/core/image.cpp
9697
src/core/interaction.cpp
9798
src/core/loader.cpp
9899
src/core/log.cpp

src/annotations/image.cpp

Lines changed: 31 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,23 +7,39 @@
77

88
#include <QPainterPath>
99
#include <QTransform>
10+
#include <QPainter>
1011

11-
struct PointImageAnnotation : ImageAnnotationBase {
12-
virtual void render() override {
13-
if (controlPoints().empty()) {
14-
shape = nullptr;
15-
complete = false;
16-
} else {
17-
shape = std::make_shared<QPainterPath>();
18-
shape->addPolygon(QPolygonF(QRectF(-10, -2, 20, 4))
19-
.united(QPolygonF(QRectF(-2, -10, 4, 20))));
20-
shape->translate(controlPoints().front().x(),
21-
controlPoints().front().y());
22-
complete = true;
23-
}
12+
void ImageAnnotationBase::heatmap(QPainter &painter, const QColor &color) {
13+
render();
14+
if (shape) {
15+
painter.setPen(Qt::NoPen);
16+
painter.setBrush(color);
17+
painter.drawPath(*shape);
2418
}
25-
};
26-
DECLARE_TYPE(PointImageAnnotation, ImageAnnotationBase);
19+
}
20+
21+
void PointImageAnnotation::render() {
22+
if (controlPoints().empty()) {
23+
shape = nullptr;
24+
complete = false;
25+
} else {
26+
shape = std::make_shared<QPainterPath>();
27+
shape->addPolygon(QPolygonF(QRectF(-10, -2, 20, 4))
28+
.united(QPolygonF(QRectF(-2, -10, 4, 20))));
29+
shape->translate(controlPoints().front().x(), controlPoints().front().y());
30+
complete = true;
31+
}
32+
}
33+
34+
void PointImageAnnotation::heatmap(QPainter &painter, const QColor &color) {
35+
if (!controlPoints().empty()) {
36+
auto p = controlPoints().at(0);
37+
painter.setPen(Qt::NoPen);
38+
painter.setBrush(color);
39+
// painter.drawPoint(QPointF(p[0], p[1]));
40+
painter.drawEllipse(QPointF(p[0], p[1]), 3.0, 3.0);
41+
}
42+
}
2743

2844
struct RectangleImageAnnotation : ImageAnnotationBase {
2945
virtual void render() override {

src/annotations/image.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,26 @@
66
#include "../core/document.h"
77

88
class QPainterPath;
9+
class QPainter;
10+
class QColor;
911

1012
struct ImageAnnotationBase : AnnotationBase {
11-
protected:
13+
protected:
1214
ImageAnnotationBase() {}
1315

14-
public:
16+
public:
1517
PROPERTY(std::vector<Eigen::Vector2d>, controlPoints);
1618
PROPERTY(std::string, topic);
1719
bool complete = false;
1820
std::shared_ptr<QPainterPath> shape;
1921
virtual void render() = 0;
22+
virtual void heatmap(QPainter& painter, const QColor& color);
2023
virtual void constrain() {}
2124
};
2225
DECLARE_TYPE(ImageAnnotationBase, AnnotationBase);
26+
27+
struct PointImageAnnotation : ImageAnnotationBase {
28+
virtual void render() override;
29+
virtual void heatmap(QPainter& painter, const QColor& color) override;
30+
};
31+
DECLARE_TYPE(PointImageAnnotation, ImageAnnotationBase);

src/core/image.cpp

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
// TAMSVIZ
2+
// (c) 2020-2024 Philipp Ruppel
3+
4+
#include "image.h"
5+
6+
#include "log.h"
7+
8+
#include <cv_bridge/cv_bridge.h>
9+
10+
#include <sensor_msgs/CompressedImage.h>
11+
#include <sensor_msgs/Image.h>
12+
#include <sensor_msgs/image_encodings.h>
13+
14+
#include <QtWidgets>
15+
16+
bool isImageMessageTypeName(const std::string &message_type_name) {
17+
return message_type_name == "sensor_msgs/Image" ||
18+
message_type_name == "sensor_msgs/CompressedImage";
19+
}
20+
21+
bool isImageMessage(const Message &image) {
22+
return isImageMessageTypeName(image.type()->name());
23+
}
24+
25+
bool tryConvertImageMessageToMat(const Message &image, cv::Mat &mat,
26+
std::string &encoding,
27+
std_msgs::Header &header) {
28+
if (!isImageMessage(image)) {
29+
LOG_WARN_THROTTLE(1, "not an image message " << image.type()->name());
30+
return false;
31+
}
32+
if (auto img = image.instantiate<sensor_msgs::Image>()) {
33+
header = img->header;
34+
if (sensor_msgs::image_encodings::isBayer(img->encoding) ||
35+
img->encoding == sensor_msgs::image_encodings::YUV422) {
36+
try {
37+
cv_bridge::CvImageConstPtr cv_ptr =
38+
cv_bridge::toCvCopy(*img, sensor_msgs::image_encodings::RGB8);
39+
mat = cv_ptr->image;
40+
encoding = cv_ptr->encoding;
41+
return true;
42+
} catch (cv_bridge::Exception &e) {
43+
LOG_ERROR("cv_bridge exception: " << e.what());
44+
}
45+
}
46+
try {
47+
cv_bridge::CvImageConstPtr cv_ptr = cv_bridge::toCvCopy(*img);
48+
mat = cv_ptr->image;
49+
encoding = cv_ptr->encoding;
50+
return true;
51+
} catch (cv_bridge::Exception &e) {
52+
LOG_ERROR("cv_bridge exception: " << e.what());
53+
}
54+
} else if (auto img = image.instantiate<sensor_msgs::CompressedImage>()) {
55+
header = img->header;
56+
try {
57+
cv_bridge::CvImageConstPtr cv_ptr =
58+
cv_bridge::toCvCopy(*img, sensor_msgs::image_encodings::RGB8);
59+
mat = cv_ptr->image;
60+
encoding = cv_ptr->encoding;
61+
return true;
62+
} catch (cv_bridge::Exception &e) {
63+
LOG_ERROR("cv_bridge exception: " << e.what());
64+
}
65+
} else {
66+
LOG_WARN("unsupported image message type" << image.type()->name());
67+
}
68+
if (mat.empty()) {
69+
LOG_WARN("failed to decode image");
70+
}
71+
header = std_msgs::Header();
72+
return false;
73+
}
74+
75+
bool tryConvertMatToU8(const cv::Mat &in, cv::Mat &out) {
76+
switch (in.type()) {
77+
case CV_16UC1:
78+
in.convertTo(out, CV_8U, 1.0 / 256);
79+
break;
80+
case CV_32FC1:
81+
in.convertTo(out, CV_8U, 255.0);
82+
break;
83+
case CV_64FC1:
84+
in.convertTo(out, CV_8U, 255.0);
85+
break;
86+
case CV_16UC3:
87+
in.convertTo(out, CV_8UC3, 1.0 / 256.0);
88+
break;
89+
case CV_32FC3:
90+
in.convertTo(out, CV_8UC3, 255.0);
91+
break;
92+
case CV_64FC3:
93+
in.convertTo(out, CV_8UC3, 255.0);
94+
break;
95+
case CV_16UC4:
96+
in.convertTo(out, CV_8UC4, 1.0 / 256.0);
97+
break;
98+
case CV_32FC4:
99+
in.convertTo(out, CV_8UC4, 255.0);
100+
break;
101+
case CV_64FC4:
102+
in.convertTo(out, CV_8UC4, 255.0);
103+
break;
104+
default:
105+
out = in;
106+
break;
107+
}
108+
return CV_MAT_DEPTH(out.type()) == CV_8U;
109+
}
110+
111+
bool tryConvertMatToImage(const cv::Mat &in_mat,
112+
const std::string &img_encoding, QImage &image) {
113+
{
114+
cv::Mat mat;
115+
if (tryConvertMatToU8(in_mat, mat)) {
116+
switch (mat.type()) {
117+
case CV_8UC1:
118+
image = QImage((uchar *)mat.data, mat.cols, mat.rows, mat.step,
119+
QImage::Format_Grayscale8);
120+
return true;
121+
case CV_8UC3:
122+
if (img_encoding == "bgr8") {
123+
cv::cvtColor(mat, mat, cv::COLOR_BGR2RGB);
124+
}
125+
image = QImage((uchar *)mat.data, mat.cols, mat.rows, mat.step,
126+
QImage::Format_RGB888);
127+
return true;
128+
case CV_8UC4:
129+
if (img_encoding == "bgra8") {
130+
cv::cvtColor(mat, mat, cv::COLOR_BGRA2RGBA);
131+
}
132+
image = QImage((uchar *)mat.data, mat.cols, mat.rows, mat.step,
133+
QImage::Format_RGBA8888);
134+
return true;
135+
}
136+
}
137+
}
138+
LOG_WARN("cv to qt image format not yet supported " << in_mat.type());
139+
return false;
140+
}

src/core/image.h

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// TAMSVIZ
2+
// (c) 2020-2024 Philipp Ruppel
3+
4+
#pragma once
5+
6+
#include "message.h"
7+
8+
#include <opencv2/imgproc/imgproc.hpp>
9+
10+
class QImage;
11+
12+
bool isImageMessageTypeName(const std::string &message_type_name);
13+
14+
bool isImageMessage(const Message &image);
15+
16+
bool tryConvertImageMessageToMat(const Message &image, cv::Mat &mat,
17+
std::string &encoding,
18+
std_msgs::Header &header);
19+
20+
bool tryConvertMatToU8(cv::Mat &in, cv::Mat &out);
21+
22+
bool tryConvertMatToImage(const cv::Mat &mat, const std::string &img_encoding,
23+
QImage &image);

0 commit comments

Comments
 (0)