Skip to content

Commit 9e76f3b

Browse files
authored
Merge pull request #257 from fmrico/opencv4
Support for Opencv4 in ROS2 Foxy [WIP]
2 parents fec923b + 8afe311 commit 9e76f3b

File tree

5 files changed

+148
-40
lines changed

5 files changed

+148
-40
lines changed

README.md

Lines changed: 13 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
This is a ROS package developed for object detection in camera images. You only look once (YOLO) is a state-of-the-art, real-time object detection system. In the following ROS package you are able to use YOLO (V3) on GPU and CPU. The pre-trained model of the convolutional neural network is able to detect pre-trained classes including the data set from VOC and COCO, or you can also create a network with your own detection objects. For more information about YOLO, Darknet, available training data and training YOLO see the following link: [YOLO: Real-Time Object Detection](http://pjreddie.com/darknet/yolo/).
66

7-
The YOLO packages have been tested under ROS Melodic and Ubuntu 18.04. This is research code, expect that it changes often and any fitness for a particular purpose is disclaimed.
7+
The YOLO packages have been tested under ROS Foxy, Ubuntu 20.04 and OpenCV 4.2.0. This is research code, expect that it changes often and any fitness for a particular purpose is disclaimed.
88

99
**Author: [Marko Bjelonic](https://www.markobjelonic.com), [email protected]**
1010

@@ -52,7 +52,7 @@ URL: https://github.com/leggedrobotics/darknet_ros, 2018.
5252

5353
### Dependencies
5454

55-
This software is built on the Robotic Operating System ([ROS]), which needs to be [installed](http://wiki.ros.org) first. Additionally, YOLO for ROS depends on following software:
55+
This software is built on the Robotic Operating System version([ROS]), which needs to be [installed](http://wiki.ros.org) first. Additionally, YOLO for ROS depends on following software:
5656

5757
- [OpenCV](http://opencv.org/) (computer vision library),
5858
- [boost](http://www.boost.org/) (c++ library),
@@ -63,17 +63,14 @@ This software is built on the Robotic Operating System ([ROS]), which needs to b
6363

6464
In order to install darknet_ros, clone the latest version using SSH (see [how to set up an SSH key](https://confluence.atlassian.com/bitbucket/set-up-an-ssh-key-728138079.html)) from this repository into your catkin workspace and compile the package using ROS.
6565

66-
cd catkin_workspace/src
66+
cd colcon_workspace/src
6767
git clone --recursive [email protected]:leggedrobotics/darknet_ros.git
6868
cd ../
6969

7070
To maximize performance, make sure to build in *Release* mode. You can specify the build type by setting
7171

72-
catkin_make -DCMAKE_BUILD_TYPE=Release
72+
colcon build -DCMAKE_BUILD_TYPE=Release
7373

74-
or using the [Catkin Command Line Tools](http://catkin-tools.readthedocs.io/en/latest/index.html#)
75-
76-
catkin build darknet_ros -DCMAKE_BUILD_TYPE=Release
7774

7875
Darknet on the CPU is fast (approximately 1.5 seconds on an Intel Core i7-6700HQ CPU @ 2.60GHz × 8) but it's like 500 times faster on GPU! You'll have to have an Nvidia GPU and you'll have to install CUDA. The CMakeLists.txt file automatically detects if you have CUDA installed or not. CUDA is a parallel computing platform and application programming interface (API) model created by Nvidia. If you do not have CUDA on your System the build process will switch to the CPU version of YOLO. If you are compiling with CUDA, you might receive the following build error:
7976

@@ -87,7 +84,7 @@ This means that you need to check the compute capability (version) of your GPU.
8784

8885
The yolo-voc.weights and tiny-yolo-voc.weights are downloaded automatically in the CMakeLists.txt file. If you need to download them again, go into the weights folder and download the two pre-trained weights from the COCO data set:
8986

90-
cd catkin_workspace/src/darknet_ros/darknet_ros/yolo_network_config/weights/
87+
cd colcon_workspace/src/darknet_ros/darknet_ros/yolo_network_config/weights/
9188
wget http://pjreddie.com/media/files/yolov2.weights
9289
wget http://pjreddie.com/media/files/yolov2-tiny.weights
9390

@@ -107,24 +104,12 @@ There are more pre-trained weights from different data sets reported [here](http
107104

108105
In order to use your own detection objects you need to provide your weights and your cfg file inside the directories:
109106

110-
catkin_workspace/src/darknet_ros/darknet_ros/yolo_network_config/weights/
111-
catkin_workspace/src/darknet_ros/darknet_ros/yolo_network_config/cfg/
112-
113-
In addition, you need to create your config file for ROS where you define the names of the detection objects. You need to include it inside:
114-
115-
catkin_workspace/src/darknet_ros/darknet_ros/config/
116-
117-
Then in the launch file you have to point to your new config file in the line:
118-
119-
<rosparam command="load" ns="darknet_ros" file="$(find darknet_ros)/config/your_config_file.yaml"/>
107+
colcon_workspace/src/darknet_ros/darknet_ros/yolo_network_config/weights/
108+
colcon_workspace/src/darknet_ros/darknet_ros/yolo_network_config/cfg/
120109

121110
### Unit Tests
122111

123-
Run the unit tests using the [Catkin Command Line Tools](http://catkin-tools.readthedocs.io/en/latest/index.html#)
124-
125-
catkin build darknet_ros --no-deps --verbose --catkin-make-args run_tests
126-
127-
You will see the image above popping up.
112+
** Currently disabled in ROS2 **
128113

129114
## Basic Usage
130115

@@ -142,27 +127,27 @@ You can change the names and other parameters of the publishers, subscribers and
142127

143128
#### Subscribed Topics
144129

145-
* **`/camera_reading`** ([sensor_msgs/Image])
130+
* **`/camera_reading`** ([sensor_msgs/msg/Image])
146131

147132
The camera measurements.
148133

149134
#### Published Topics
150135

151-
* **`object_detector`** ([std_msgs::Int8])
136+
* **`object_detector`** ([std_msgs/msg/Int8])
152137

153138
Publishes the number of detected objects.
154139

155-
* **`bounding_boxes`** ([darknet_ros_msgs::BoundingBoxes])
140+
* **`bounding_boxes`** ([darknet_ros_msgs/msg/BoundingBoxes])
156141

157142
Publishes an array of bounding boxes that gives information of the position and size of the bounding box in pixel coordinates.
158143

159-
* **`detection_image`** ([sensor_msgs::Image])
144+
* **`detection_image`** ([sensor_msgs/msg/Image])
160145

161146
Publishes an image of the detection image including the bounding boxes.
162147

163148
#### Actions
164149

165-
* **`camera_reading`** ([sensor_msgs::Image])
150+
* **`camera_reading`** ([sensor_msgs/msg/Image])
166151

167152
Sends an action with an image and the result is an array of bounding boxes.
168153

darknet_ros/CMakeLists.txt

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@ project(darknet_ros)
33

44
set(CMAKE_CXX_STANDARD 17)
55

6-
76
# Define path of darknet folder here.
87
find_path(DARKNET_PATH
98
NAMES "README.md"
@@ -71,7 +70,7 @@ set(dependencies
7170
)
7271

7372
# Enable OPENCV in darknet
74-
add_definitions(-DOPENCV)
73+
# add_definitions(-DOPENCV)
7574
add_definitions(-O4 -g)
7675

7776
include_directories(
@@ -128,6 +127,8 @@ set(DARKNET_CUDA_FILES
128127
${DARKNET_PATH}/src/convolutional_kernels.cu ${DARKNET_PATH}/src/maxpool_layer_kernels.cu
129128
)
130129

130+
set_source_files_properties(${PROJECT_LIB_FILES} PROPERTIES LANGUAGE CXX)
131+
131132
if (CUDA_FOUND)
132133

133134
link_directories(
@@ -156,10 +157,15 @@ if (CUDA_FOUND)
156157

157158
else()
158159

160+
add_library(${PROJECT_NAME}_core_lib
161+
${DARKNET_CORE_FILES}
162+
)
163+
159164
add_library(${PROJECT_NAME}_lib
160-
${PROJECT_LIB_FILES} ${DARKNET_CORE_FILES}
165+
${PROJECT_LIB_FILES}
161166
)
162167
ament_target_dependencies(${PROJECT_NAME}_lib ${dependencies})
168+
target_compile_definitions(${PROJECT_NAME}_lib PRIVATE -DOPENCV)
163169

164170
add_executable(${PROJECT_NAME}
165171
src/yolo_object_detector_node.cpp
@@ -176,11 +182,13 @@ target_link_libraries(${PROJECT_NAME}_lib
176182
${catkin_LIBRARIES}
177183
${OpenCV_LIBS}
178184
)
185+
target_compile_definitions(${PROJECT_NAME}_lib PRIVATE -DOPENCV)
179186

180187
target_link_libraries(${PROJECT_NAME}
181188
${PROJECT_NAME}_lib
189+
${PROJECT_NAME}_core_lib
182190
)
183-
191+
target_compile_definitions(${PROJECT_NAME} PRIVATE -DOPENCV)
184192

185193
install(TARGETS ${PROJECT_NAME}_lib ${PROJECT_NAME}
186194
ARCHIVE DESTINATION lib

darknet_ros/include/darknet_ros/image_interface.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
#ifndef IMAGE_INTERFACE_H
1010
#define IMAGE_INTERFACE_H
1111

12+
#include "opencv2/highgui/highgui_c.h"
13+
#include "opencv2/imgproc/imgproc_c.h"
14+
#include "opencv2/core/version.hpp"
15+
1216
#include "image.h"
1317

1418
static float get_pixel(image m, int x, int y, int c);

darknet_ros/src/YoloObjectDetector.cpp

Lines changed: 117 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -455,13 +455,32 @@ void *YoloObjectDetector::detectInThread()
455455
return 0;
456456
}
457457

458+
459+
void ipl_into_image_cp(IplImage* src, image im)
460+
{
461+
unsigned char *data = (unsigned char *)src->imageData;
462+
int h = src->height;
463+
int w = src->width;
464+
int c = src->nChannels;
465+
int step = src->widthStep;
466+
int i, j, k;
467+
468+
for(i = 0; i < h; ++i){
469+
for(k= 0; k < c; ++k){
470+
for(j = 0; j < w; ++j){
471+
im.data[k*w*h + i*w + j] = data[i*step + j*c + k]/255.;
472+
}
473+
}
474+
}
475+
}
476+
458477
void *YoloObjectDetector::fetchInThread()
459478
{
460479
{
461480
std::shared_lock<std::shared_mutex> lock(mutexImageCallback_);
462481
IplImageWithHeader_ imageAndHeader = getIplImageWithHeader();
463482
IplImage* ROS_img = imageAndHeader.image;
464-
ipl_into_image(ROS_img, buff_[buffIndex_]);
483+
ipl_into_image_cp(ROS_img, buff_[buffIndex_]);
465484
headerBuff_[buffIndex_] = imageAndHeader.header;
466485
buffId_[buffIndex_] = actionId_;
467486
}
@@ -470,9 +489,54 @@ void *YoloObjectDetector::fetchInThread()
470489
return 0;
471490
}
472491

492+
493+
float get_pixel_cp(image m, int x, int y, int c)
494+
{
495+
assert(x < m.w && y < m.h && c < m.c);
496+
return m.data[c*m.h*m.w + y*m.w + x];
497+
}
498+
499+
int windows = 0;
500+
501+
void show_image_cv_cp(image p, const char *name, IplImage *disp)
502+
{
503+
int x,y,k;
504+
if(p.c == 3) rgbgr_image(p);
505+
//normalize_image(copy);
506+
507+
char buff[256];
508+
//sprintf(buff, "%s (%d)", name, windows);
509+
sprintf(buff, "%s", name);
510+
511+
int step = disp->widthStep;
512+
cvNamedWindow(buff, CV_WINDOW_NORMAL);
513+
//cvMoveWindow(buff, 100*(windows%10) + 200*(windows/10), 100*(windows%10));
514+
++windows;
515+
for(y = 0; y < p.h; ++y){
516+
for(x = 0; x < p.w; ++x){
517+
for(k= 0; k < p.c; ++k){
518+
disp->imageData[y*step + x*p.c + k] = (unsigned char)(get_pixel_cp(p,x,y,k)*255);
519+
}
520+
}
521+
}
522+
if(0){
523+
int w = 448;
524+
int h = w*p.h/p.w;
525+
if(h > 1000){
526+
h = 1000;
527+
w = h*p.w/p.h;
528+
}
529+
IplImage *buffer = disp;
530+
disp = cvCreateImage(cvSize(w, h), buffer->depth, buffer->nChannels);
531+
cvResize(buffer, disp, CV_INTER_LINEAR);
532+
cvReleaseImage(&buffer);
533+
}
534+
cvShowImage(buff, disp);
535+
}
536+
473537
void *YoloObjectDetector::displayInThread(void *ptr)
474538
{
475-
show_image_cv(buff_[(buffIndex_ + 1)%3], "YOLO V3", ipl_);
539+
show_image_cv_cp(buff_[(buffIndex_ + 1)%3], "YOLO V3", ipl_);
476540
int c = cv::waitKey(waitKeyDelay_);
477541
if (c != -1) c = c%256;
478542
if (c == 27) {
@@ -506,6 +570,26 @@ void *YoloObjectDetector::detectLoop(void *ptr)
506570
}
507571
}
508572

573+
574+
image **load_alphabet_with_file_cp(char *datafile) {
575+
int i, j;
576+
const int nsize = 8;
577+
image **alphabets = (image**)calloc(nsize, sizeof(image));
578+
char* labels = "/labels/%d_%d.png";
579+
char * files = (char *) malloc(1 + strlen(datafile)+ strlen(labels) );
580+
strcpy(files, datafile);
581+
strcat(files, labels);
582+
for(j = 0; j < nsize; ++j){
583+
alphabets[j] = (image*)calloc(128, sizeof(image));
584+
for(i = 32; i < 127; ++i){
585+
char buff[256];
586+
sprintf(buff, files, i, j);
587+
alphabets[j][i] = load_image_color(buff, 0, 0);
588+
}
589+
}
590+
return alphabets;
591+
}
592+
509593
void YoloObjectDetector::setupNetwork(char *cfgfile, char *weightfile, char *datafile, float thresh,
510594
char **names, int classes,
511595
int delay, char *prefix, int avg_frames, float hier, int w, int h,
@@ -514,7 +598,7 @@ void YoloObjectDetector::setupNetwork(char *cfgfile, char *weightfile, char *dat
514598
demoPrefix_ = prefix;
515599
demoDelay_ = delay;
516600
demoFrame_ = avg_frames;
517-
image **alphabet = load_alphabet_with_file(datafile);
601+
image **alphabet = load_alphabet_with_file_cp(datafile);
518602
demoNames_ = names;
519603
demoAlphabet_ = alphabet;
520604
demoClasses_ = classes;
@@ -526,6 +610,32 @@ void YoloObjectDetector::setupNetwork(char *cfgfile, char *weightfile, char *dat
526610
set_batch_network(net_, 1);
527611
}
528612

613+
void generate_image_cp(image p, IplImage *disp)
614+
{
615+
int x,y,k;
616+
if(p.c == 3) rgbgr_image(p);
617+
//normalize_image(copy);
618+
619+
int step = disp->widthStep;
620+
for(y = 0; y < p.h; ++y){
621+
for(x = 0; x < p.w; ++x){
622+
for(k= 0; k < p.c; ++k){
623+
disp->imageData[y*step + x*p.c + k] = (unsigned char)(get_pixel_cp(p,x,y,k)*255);
624+
}
625+
}
626+
}
627+
}
628+
629+
image ipl_to_image_cp(IplImage* src)
630+
{
631+
int h = src->height;
632+
int w = src->width;
633+
int c = src->nChannels;
634+
image out = make_image(w, h, c);
635+
ipl_into_image_cp(src, out);
636+
return out;
637+
}
638+
529639
void YoloObjectDetector::yolo()
530640
{
531641
const auto wait_duration = std::chrono::milliseconds(2000);
@@ -557,7 +667,7 @@ void YoloObjectDetector::yolo()
557667
std::shared_lock<std::shared_mutex> lock(mutexImageCallback_);
558668
IplImageWithHeader_ imageAndHeader = getIplImageWithHeader();
559669
IplImage* ROS_img = imageAndHeader.image;
560-
buff_[0] = ipl_to_image(ROS_img);
670+
buff_[0] = ipl_to_image_cp(ROS_img);
561671
headerBuff_[0] = imageAndHeader.header;
562672
}
563673
buff_[1] = copy_image(buff_[0]);
@@ -593,7 +703,7 @@ void YoloObjectDetector::yolo()
593703
if (viewImage_) {
594704
displayInThread(0);
595705
} else {
596-
generate_image(buff_[(buffIndex_ + 1)%3], ipl_);
706+
generate_image_cp(buff_[(buffIndex_ + 1)%3], ipl_);
597707
}
598708
publishInThread();
599709
} else {
@@ -613,7 +723,8 @@ void YoloObjectDetector::yolo()
613723

614724
IplImageWithHeader_ YoloObjectDetector::getIplImageWithHeader()
615725
{
616-
IplImage* ROS_img = new IplImage(camImageCopy_);
726+
IplImage* ROS_img = new IplImage();
727+
*ROS_img = cvIplImage(camImageCopy_);
617728
IplImageWithHeader_ header = {.image = ROS_img, .header = imageHeader_};
618729
return header;
619730
}

darknet_ros/src/image_interface.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,13 @@ static float get_pixel(image m, int x, int y, int c)
1717
image **load_alphabet_with_file(char *datafile) {
1818
int i, j;
1919
const int nsize = 8;
20-
image **alphabets = calloc(nsize, sizeof(image));
20+
image **alphabets = (image**)calloc(nsize, sizeof(image));
2121
char* labels = "/labels/%d_%d.png";
2222
char * files = (char *) malloc(1 + strlen(datafile)+ strlen(labels) );
2323
strcpy(files, datafile);
2424
strcat(files, labels);
2525
for(j = 0; j < nsize; ++j){
26-
alphabets[j] = calloc(128, sizeof(image));
26+
alphabets[j] = (image*)calloc(128, sizeof(image));
2727
for(i = 32; i < 127; ++i){
2828
char buff[256];
2929
sprintf(buff, files, i, j);

0 commit comments

Comments
 (0)