Skip to content

Commit 5c74564

Browse files
committed
Combining Camera and Lidar exercises
1 parent ae23df6 commit 5c74564

38 files changed

+1101
-0
lines changed
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cd /home/workspace/cluster_with_roi
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
2+
3+
add_definitions(-std=c++11)
4+
5+
set(CXX_FLAGS "-Wall" "-pedantic")
6+
set(CMAKE_CXX_FLAGS, "${CXX_FLAGS}")
7+
8+
project(camera_fusion)
9+
10+
find_package(OpenCV 4.1 REQUIRED)
11+
12+
include_directories(${OpenCV_INCLUDE_DIRS})
13+
link_directories(${OpenCV_LIBRARY_DIRS})
14+
add_definitions(${OpenCV_DEFINITIONS})
15+
16+
# Executables for exercises
17+
add_executable (cluster_with_roi src/cluster_with_roi.cpp src/structIO.cpp)
18+
target_link_libraries (cluster_with_roi ${OpenCV_LIBRARIES})
Binary file not shown.
Binary file not shown.
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#include <iostream>
2+
#include <numeric>
3+
#include <opencv2/core.hpp>
4+
#include <opencv2/highgui.hpp>
5+
#include <opencv2/imgproc.hpp>
6+
7+
#include "structIO.hpp"
8+
#include "dataStructures.h"
9+
10+
using namespace std;
11+
12+
void loadCalibrationData(cv::Mat &P_rect_00, cv::Mat &R_rect_00, cv::Mat &RT)
13+
{
14+
RT.at<double>(0,0) = 7.533745e-03; RT.at<double>(0,1) = -9.999714e-01; RT.at<double>(0,2) = -6.166020e-04; RT.at<double>(0,3) = -4.069766e-03;
15+
RT.at<double>(1,0) = 1.480249e-02; RT.at<double>(1,1) = 7.280733e-04; RT.at<double>(1,2) = -9.998902e-01; RT.at<double>(1,3) = -7.631618e-02;
16+
RT.at<double>(2,0) = 9.998621e-01; RT.at<double>(2,1) = 7.523790e-03; RT.at<double>(2,2) = 1.480755e-02; RT.at<double>(2,3) = -2.717806e-01;
17+
RT.at<double>(3,0) = 0.0; RT.at<double>(3,1) = 0.0; RT.at<double>(3,2) = 0.0; RT.at<double>(3,3) = 1.0;
18+
19+
R_rect_00.at<double>(0,0) = 9.999239e-01; R_rect_00.at<double>(0,1) = 9.837760e-03; R_rect_00.at<double>(0,2) = -7.445048e-03; R_rect_00.at<double>(0,3) = 0.0;
20+
R_rect_00.at<double>(1,0) = -9.869795e-03; R_rect_00.at<double>(1,1) = 9.999421e-01; R_rect_00.at<double>(1,2) = -4.278459e-03; R_rect_00.at<double>(1,3) = 0.0;
21+
R_rect_00.at<double>(2,0) = 7.402527e-03; R_rect_00.at<double>(2,1) = 4.351614e-03; R_rect_00.at<double>(2,2) = 9.999631e-01; R_rect_00.at<double>(2,3) = 0.0;
22+
R_rect_00.at<double>(3,0) = 0; R_rect_00.at<double>(3,1) = 0; R_rect_00.at<double>(3,2) = 0; R_rect_00.at<double>(3,3) = 1;
23+
24+
P_rect_00.at<double>(0,0) = 7.215377e+02; P_rect_00.at<double>(0,1) = 0.000000e+00; P_rect_00.at<double>(0,2) = 6.095593e+02; P_rect_00.at<double>(0,3) = 0.000000e+00;
25+
P_rect_00.at<double>(1,0) = 0.000000e+00; P_rect_00.at<double>(1,1) = 7.215377e+02; P_rect_00.at<double>(1,2) = 1.728540e+02; P_rect_00.at<double>(1,3) = 0.000000e+00;
26+
P_rect_00.at<double>(2,0) = 0.000000e+00; P_rect_00.at<double>(2,1) = 0.000000e+00; P_rect_00.at<double>(2,2) = 1.000000e+00; P_rect_00.at<double>(2,3) = 0.000000e+00;
27+
28+
}
29+
30+
void showLidarTopview(std::vector<LidarPoint> &lidarPoints, cv::Size worldSize, cv::Size imageSize)
31+
{
32+
// create topview image
33+
cv::Mat topviewImg(imageSize, CV_8UC3, cv::Scalar(0, 0, 0));
34+
35+
// plot Lidar points into image
36+
for (auto it = lidarPoints.begin(); it != lidarPoints.end(); ++it)
37+
{
38+
float xw = (*it).x; // world position in m with x facing forward from sensor
39+
float yw = (*it).y; // world position in m with y facing left from sensor
40+
41+
int y = (-xw * imageSize.height / worldSize.height) + imageSize.height;
42+
int x = (-yw * imageSize.height / worldSize.height) + imageSize.width / 2;
43+
44+
float zw = (*it).z; // world position in m with y facing left from sensor
45+
if(zw > -1.40){
46+
47+
float val = it->x;
48+
float maxVal = worldSize.height;
49+
int red = min(255, (int)(255 * abs((val - maxVal) / maxVal)));
50+
int green = min(255, (int)(255 * (1 - abs((val - maxVal) / maxVal))));
51+
cv::circle(topviewImg, cv::Point(x, y), 5, cv::Scalar(0, green, red), -1);
52+
}
53+
}
54+
55+
// plot distance markers
56+
float lineSpacing = 2.0; // gap between distance markers
57+
int nMarkers = floor(worldSize.height / lineSpacing);
58+
for (size_t i = 0; i < nMarkers; ++i)
59+
{
60+
int y = (-(i * lineSpacing) * imageSize.height / worldSize.height) + imageSize.height;
61+
cv::line(topviewImg, cv::Point(0, y), cv::Point(imageSize.width, y), cv::Scalar(255, 0, 0));
62+
}
63+
64+
// display image
65+
string windowName = "Top-View Perspective of LiDAR data";
66+
cv::namedWindow(windowName, 2);
67+
cv::imshow(windowName, topviewImg);
68+
cv::waitKey(0); // wait for key to be pressed
69+
}
70+
71+
72+
void clusterLidarWithROI(std::vector<BoundingBox> &boundingBoxes, std::vector<LidarPoint> &lidarPoints)
73+
{
74+
// store calibration data in OpenCV matrices
75+
cv::Mat P_rect_xx(3,4,cv::DataType<double>::type); // 3x4 projection matrix after rectification
76+
cv::Mat R_rect_xx(4,4,cv::DataType<double>::type); // 3x3 rectifying rotation to make image planes co-planar
77+
cv::Mat RT(4,4,cv::DataType<double>::type); // rotation matrix and translation vector
78+
loadCalibrationData(P_rect_xx, R_rect_xx, RT);
79+
80+
// loop over all Lidar points and associate them to a 2D bounding box
81+
cv::Mat X(4, 1, cv::DataType<double>::type);
82+
cv::Mat Y(3, 1, cv::DataType<double>::type);
83+
84+
for (auto it1 = lidarPoints.begin(); it1 != lidarPoints.end(); ++it1)
85+
{
86+
// assemble vector for matrix-vector-multiplication
87+
X.at<double>(0, 0) = it1->x;
88+
X.at<double>(1, 0) = it1->y;
89+
X.at<double>(2, 0) = it1->z;
90+
X.at<double>(3, 0) = 1;
91+
92+
// project Lidar point into camera
93+
Y = P_rect_xx * R_rect_xx * RT * X;
94+
cv::Point pt;
95+
pt.x = Y.at<double>(0, 0) / Y.at<double>(0, 2); // pixel coordinates
96+
pt.y = Y.at<double>(1, 0) / Y.at<double>(0, 2);
97+
98+
double shrinkFactor = 0.10;
99+
vector<vector<BoundingBox>::iterator> enclosingBoxes; // pointers to all bounding boxes which enclose the current Lidar point
100+
for (vector<BoundingBox>::iterator it2 = boundingBoxes.begin(); it2 != boundingBoxes.end(); ++it2)
101+
{
102+
// shrink current bounding box slightly to avoid having too many outlier points around the edges
103+
cv::Rect smallerBox;
104+
smallerBox.x = (*it2).roi.x + shrinkFactor * (*it2).roi.width / 2.0;
105+
smallerBox.y = (*it2).roi.y + shrinkFactor * (*it2).roi.height / 2.0;
106+
smallerBox.width = (*it2).roi.width * (1 - shrinkFactor);
107+
smallerBox.height = (*it2).roi.height * (1 - shrinkFactor);
108+
109+
// check wether point is within current bounding box
110+
if (smallerBox.contains(pt))
111+
{
112+
it2->lidarPoints.push_back(*it1);
113+
lidarPoints.erase(it1);
114+
it1--;
115+
break;
116+
}
117+
} // eof loop over all bounding boxes
118+
119+
} // eof loop over all Lidar points
120+
}
121+
122+
int main()
123+
{
124+
std::vector<LidarPoint> lidarPoints;
125+
readLidarPts("../dat/C53A3_currLidarPts.dat", lidarPoints);
126+
127+
std::vector<BoundingBox> boundingBoxes;
128+
readBoundingBoxes("../dat/C53A3_currBoundingBoxes.dat", boundingBoxes);
129+
130+
clusterLidarWithROI(boundingBoxes, lidarPoints);
131+
for (auto it = boundingBoxes.begin(); it != boundingBoxes.end(); ++it)
132+
{
133+
if (it->lidarPoints.size() > 0)
134+
{
135+
showLidarTopview(it->lidarPoints, cv::Size(10.0, 25.0), cv::Size(1000, 2000));
136+
}
137+
}
138+
139+
return 0;
140+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
#ifndef dataStructures_h
2+
#define dataStructures_h
3+
4+
#include <vector>
5+
#include <opencv2/core.hpp>
6+
7+
struct LidarPoint { // single lidar point in space
8+
double x,y,z,r; // x,y,z in [m], r is point reflectivity
9+
};
10+
11+
struct BoundingBox { // bounding box around a classified object (contains both 2D and 3D data)
12+
13+
int boxID; // unique identifier for this bounding box
14+
int trackID; // unique identifier for the track to which this bounding box belongs
15+
16+
cv::Rect roi; // 2D region-of-interest in image coordinates
17+
int classID; // ID based on class file provided to YOLO framework
18+
double confidence; // classification trust
19+
20+
std::vector<LidarPoint> lidarPoints; // Lidar 3D points which project into 2D image roi
21+
std::vector<cv::KeyPoint> keypoints; // keypoints enclosed by 2D roi
22+
std::vector<cv::DMatch> kptMatches; // keypoint matches enclosed by 2D roi
23+
};
24+
25+
#endif /* dataStructures_h */
Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
#include <iostream>
2+
#include <fstream>
3+
#include <algorithm>
4+
#include <opencv2/highgui/highgui.hpp>
5+
#include "structIO.hpp"
6+
7+
using namespace std;
8+
9+
10+
/* TEMPLATES */
11+
template<typename T> void write_pod(std::ofstream& out, T& t)
12+
{
13+
out.write(reinterpret_cast<char*>(&t), sizeof(T));
14+
}
15+
16+
17+
template<typename T> void read_pod(std::ifstream& in, T& t)
18+
{
19+
in.read(reinterpret_cast<char*>(&t), sizeof(T));
20+
}
21+
22+
23+
template<typename T> void read_pod_vector(std::ifstream& in, std::vector<T>& vect)
24+
{
25+
long size;
26+
27+
read_pod(in, size);
28+
for(int i = 0;i < size;++i)
29+
{
30+
T t;
31+
read_pod(in, t);
32+
vect.push_back(t);
33+
}
34+
}
35+
36+
template<typename T> void write_pod_vector(std::ofstream& out, std::vector<T>& vect)
37+
{
38+
long size = vect.size();
39+
write_pod<long>(out, size);
40+
for(auto it=vect.begin(); it!=vect.end(); ++it)
41+
{
42+
write_pod<T>(out,*it);
43+
}
44+
}
45+
46+
47+
48+
49+
/* DATATYPE WRAPPERS */
50+
51+
void writeBoundingBoxes(std::vector<BoundingBox> &input, const char* fileName)
52+
{
53+
std::ofstream out(fileName);
54+
write_pod_vector(out, input);
55+
out.close();
56+
}
57+
58+
void readBoundingBoxes(const char* fileName, std::vector<BoundingBox> &output)
59+
{
60+
std::ifstream in(fileName);
61+
read_pod_vector(in, output);
62+
}
63+
64+
void writeLidarPts(std::vector<LidarPoint> &input, const char* fileName)
65+
{
66+
std::ofstream out(fileName);
67+
write_pod_vector(out, input);
68+
out.close();
69+
}
70+
71+
72+
void readLidarPts(const char* fileName, std::vector<LidarPoint> &output)
73+
{
74+
std::ifstream in(fileName);
75+
read_pod_vector(in, output);
76+
}
77+
78+
79+
void writeKeypoints(std::vector<cv::KeyPoint> &input, const char* fileName)
80+
{
81+
std::ofstream out(fileName);
82+
write_pod_vector(out, input);
83+
out.close();
84+
}
85+
86+
87+
void readKeypoints(const char* fileName, std::vector<cv::KeyPoint> &output)
88+
{
89+
std::ifstream in(fileName);
90+
read_pod_vector(in, output);
91+
}
92+
93+
94+
void writeKptMatches(std::vector<cv::DMatch> &input, const char* fileName)
95+
{
96+
std::ofstream out(fileName);
97+
write_pod_vector(out, input);
98+
out.close();
99+
}
100+
101+
102+
void readKptMatches(const char* fileName, std::vector<cv::DMatch> &output)
103+
{
104+
std::ifstream in(fileName);
105+
read_pod_vector(in, output);
106+
}
107+
108+
109+
110+
void writeDescriptors(cv::Mat &input, const char* fileName)
111+
{
112+
cv::FileStorage opencv_file(fileName, cv::FileStorage::WRITE);
113+
opencv_file << "desc_matrix" << input;
114+
opencv_file.release();
115+
}
116+
117+
118+
void readDescriptors(const char* fileName, cv::Mat &output)
119+
{
120+
cv::FileStorage opencv_file(fileName, cv::FileStorage::READ);
121+
opencv_file["desc_matrix"] >> output;
122+
opencv_file.release();
123+
}
124+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#ifndef structIO_hpp
2+
#define structIO_hpp
3+
4+
#include <stdio.h>
5+
#include "dataStructures.h"
6+
7+
void writeLidarPts(std::vector<LidarPoint> &input, const char* fileName);
8+
void readLidarPts(const char* fileName, std::vector<LidarPoint> &output);
9+
10+
void writeKeypoints(std::vector<cv::KeyPoint> &input, const char* fileName);
11+
void readKeypoints(const char* fileName, std::vector<cv::KeyPoint> &output);
12+
13+
void writeKptMatches(std::vector<cv::DMatch> &input, const char* fileName);
14+
void readKptMatches(const char* fileName, std::vector<cv::DMatch> &output);
15+
16+
void writeDescriptors(cv::Mat &input, const char* fileName);
17+
void readDescriptors(const char* fileName, cv::Mat &output);
18+
19+
void writeBoundingBoxes(std::vector<BoundingBox> &input, const char* fileName);
20+
void readBoundingBoxes(const char* fileName, std::vector<BoundingBox> &output);
21+
22+
23+
template<typename T> void write_pod(std::ofstream& out, T& t);
24+
template<typename T> void read_pod(std::ifstream& in, T& t);
25+
template<typename T> void read_pod_vector(std::ifstream& in, std::vector<T>& vect);
26+
template<typename T> void write_pod_vector(std::ofstream& out, std::vector<T>& vect);
27+
28+
#endif /* structIO_hpp */
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
cd /home/workspace/lidar_to_camera
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
cmake_minimum_required(VERSION 2.8 FATAL_ERROR)
2+
3+
add_definitions(-std=c++11)
4+
5+
set(CXX_FLAGS "-Wall" "-pedantic")
6+
set(CMAKE_CXX_FLAGS, "${CXX_FLAGS}")
7+
8+
project(camera_fusion)
9+
10+
find_package(OpenCV 4.1 REQUIRED)
11+
12+
include_directories(${OpenCV_INCLUDE_DIRS})
13+
link_directories(${OpenCV_LIBRARY_DIRS})
14+
add_definitions(${OpenCV_DEFINITIONS})
15+
16+
# Executables for exercises
17+
add_executable (show_lidar_top_view src/show_lidar_top_view.cpp src/structIO.cpp)
18+
target_link_libraries (show_lidar_top_view ${OpenCV_LIBRARIES})
19+
20+
add_executable (project_lidar_to_camera src/project_lidar_to_camera.cpp src/structIO.cpp)
21+
target_link_libraries (project_lidar_to_camera ${OpenCV_LIBRARIES})

0 commit comments

Comments
 (0)