Skip to content

Commit daeb57f

Browse files
authored
Merge pull request #62 from tue-robotics/robocup
Robocup RWC2018
2 parents 4edb699 + be9aea2 commit daeb57f

File tree

20 files changed

+527
-84
lines changed

20 files changed

+527
-84
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ Package | Build status Xenial Kinetic x64 | Description
1515
[openface_ros](https://github.com/tue-robotics/image_recognition/tree/master/openface_ros) | [![Build Status](http://build.ros.org/job/Ksrc_uX__openface_ros__ubuntu_xenial__source/1//badge/icon)](http://build.ros.org/job/Ksrc_uX__openface_ros__ubuntu_xenial__source/1/) | ROS wrapper for Openface (https://github.com/cmusatyalab/openface) to detect and recognize faces in images.
1616
[skybiometry_ros](https://github.com/tue-robotics/image_recognition/tree/master/skybiometry_ros) | [![Build Status](http://build.ros.org/job/Ksrc_uX__skybiometry_ros__ubuntu_xenial__source/1//badge/icon)](http://build.ros.org/job/Ksrc_uX__skybiometry_ros_ubuntu_xenial__source/1/) | ROS wrapper for Skybiometry (https://skybiometry.com/) for getting face properties of a detected face, e.g. age estimation, gender estimation etc.
1717
[openpose_ros](https://github.com/tue-robotics/image_recognition/tree/master/openpose_ros) | [![Build Status](http://build.ros.org/job/Ksrc_uX__openpose_ros__ubuntu_xenial__source/1//badge/icon)](http://build.ros.org/job/Ksrc_uX__openpose_ros_ubuntu_xenial__source/1/) | ROS wrapper for Openpose (https://github.com/CMU-Perceptual-Computing-Lab/) for getting poses of 2D images.
18+
[keras_ros](https://github.com/tue-robotics/image_recognition/tree/master/keras_ros) | [![Build Status](http://build.ros.org/job/Ksrc_uX__keras_ros__ubuntu_xenial__source/1//badge/icon)](http://build.ros.org/job/Ksrc_uX__keras_ros_ubuntu_xenial__source/1/) | ROS wrappers for Keras neural nets.
1819

1920
## Travis CI Build Status
2021

image_recognition/package.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
<run_depend>image_recognition_msgs</run_depend>
1313

1414
<run_depend>tensorflow_ros</run_depend>
15+
<run_depend>keras_ros</run_depend>
1516
<run_depend>openface_ros</run_depend>
1617
<run_depend>skybiometry_ros</run_depend>
1718
<run_depend>openpose_ros</run_depend>

image_recognition_util/src/image_recognition_util/image_writer.py

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,69 @@ def bitget(byteval, idx):
3232
return cmap
3333

3434

35+
def write_estimation(dir_path, image, label, annotated_original_image=None, suffix=""):
36+
"""
37+
Write estimation to a directory, for the estimation, a directory of the run will be created
38+
"""
39+
if dir_path is None:
40+
return False
41+
42+
# Check if path exists, otherwise created it
43+
if not os.path.exists(dir_path):
44+
os.makedirs(dir_path)
45+
46+
# Check if path exists, otherwise created it
47+
estimations_dir = dir_path + "/estimations"
48+
if not os.path.exists(estimations_dir):
49+
os.makedirs(estimations_dir)
50+
51+
# Make a directory of the estimation with current time
52+
estimation_dir = "%s/%s%s" % (estimations_dir, datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S_%f"), suffix)
53+
os.makedirs(estimation_dir)
54+
55+
filename = "%s/%s.jpg" % (estimation_dir, label)
56+
cv2.imwrite(filename, image)
57+
58+
if annotated_original_image is not None:
59+
filename = "%s/annotated_original_image.jpg" % estimation_dir
60+
cv2.imwrite(filename, annotated_original_image)
61+
62+
return True
63+
64+
65+
def write_estimations(dir_path, images, labels, annotated_original_image=None, suffix=""):
66+
"""
67+
Write estimations to a directory, for each estimation cycle, a directory of the run will be created
68+
"""
69+
assert len(images) == len(labels)
70+
71+
if dir_path is None:
72+
return False
73+
74+
# Check if path exists, otherwise created it
75+
if not os.path.exists(dir_path):
76+
os.makedirs(dir_path)
77+
78+
# Check if path exists, otherwise created it
79+
estimations_dir = dir_path + "/estimations"
80+
if not os.path.exists(estimations_dir):
81+
os.makedirs(estimations_dir)
82+
83+
# Make a directory of the estimation with current time
84+
estimation_dir = "%s/%s%s" % (estimations_dir, datetime.datetime.now().strftime("%Y-%m-%d-%H-%M-%S_%f"), suffix)
85+
os.makedirs(estimation_dir)
86+
87+
for i, (image, label) in enumerate(zip(images, labels)):
88+
filename = "%s/%s_%d.jpg" % (estimation_dir, label, i)
89+
cv2.imwrite(filename, image)
90+
91+
if annotated_original_image is not None:
92+
filename = "%s/annotated_original_image.jpg" % estimation_dir
93+
cv2.imwrite(filename, annotated_original_image)
94+
95+
return True
96+
97+
3598
def write_annotated(dir_path, image, label, verified=False):
3699
"""
37100
Write an image with an annotation to a folder

keras_ros/CMakeLists.txt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
cmake_minimum_required(VERSION 2.8.3)
2+
project(keras_ros)
3+
4+
find_package(catkin REQUIRED COMPONENTS
5+
rospy
6+
image_recognition_msgs
7+
image_recognition_util
8+
)
9+
10+
catkin_python_setup()
11+
12+
catkin_package()
13+
14+
install(PROGRAMS
15+
scripts/face_properties_node
16+
scripts/get_face_properties
17+
DESTINATION ${CATKIN_PACKAGE_BIN_DESTINATION}
18+
)
19+

keras_ros/README.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# keras_ros
2+
3+
Image recognition with use of Keras.
4+
5+
## Installation
6+
7+
See https://github.com/tue-robotics/image_recognition for installation instructions.
8+
9+
## ROS Node (face_properties_node)
10+
11+
Age and gender estimation with use of WideResNet from https://github.com/yu4u/age-gender-estimation. You can download the pre-trained model here: https://github.com/yu4u/age-gender-estimation/releases/download/v0.5/weights.18-4.06.hdf5
12+
13+
```
14+
rosrun keras_ros face_properties_node _weights_file_path:=[path_to_model]
15+
```
16+
17+
Run the image_recognition_rqt test gui (https://github.com/tue-robotics/image_recognition_rqt)
18+
19+
rosrun image_recognition_rqt test_gui
20+
21+
Configure the service you want to call with the gear-wheel in the top-right corner of the screen. If everything is set-up, draw a rectangle in the image around a face:
22+
23+
![Wide ResNet](doc/wide_resnet_test.png)
24+
25+
## Scripts
26+
27+
### Get face properties (get_face_properties)
28+
29+
Get the classification result of an input image:
30+
31+
```
32+
rosrun keras_ros get_face_properties --image doc/face.png --weights-path [path_to_model]
33+
```
34+
35+
![Example](doc/face.png)
36+
37+
Output:
38+
39+
[(50.5418073660112, array([0.5845756 , 0.41542447], dtype=float32))]
40+
41+
## Troubleshooting
42+
43+
```
44+
An error occurred: softmax() got an unexpected keyword argument 'axis'
45+
```
46+
47+
Make sure you have tensorflow version >= 1.5.

keras_ros/doc/face.png

25.1 KB
Loading

keras_ros/doc/wide_resnet_test.png

343 KB
Loading

keras_ros/package.xml

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?xml version="1.0"?>
2+
<package>
3+
<name>keras_ros</name>
4+
<version>0.0.1</version>
5+
<description>The keras_ros package</description>
6+
7+
<maintainer email="[email protected]">Rein Appeldoorn</maintainer>
8+
9+
<license>TODO</license>
10+
11+
<buildtool_depend>catkin</buildtool_depend>
12+
<build_depend>rospy</build_depend>
13+
<build_depend>image_recognition_msgs</build_depend>
14+
<build_depend>image_recognition_util</build_depend>
15+
16+
<run_depend>rospy</run_depend>
17+
<run_depend>image_recognition_msgs</run_depend>
18+
<run_depend>python-keras-pip</run_depend>
19+
<run_depend>python-numpy</run_depend>
20+
<run_depend>python-opencv</run_depend>
21+
<run_depend>python-tensorflow-pip</run_depend>
22+
</package>
Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,95 @@
1+
#!/usr/bin/env python
2+
import datetime
3+
import os
4+
import sys
5+
6+
import cv2
7+
import rospy
8+
from cv_bridge import CvBridge, CvBridgeError
9+
from image_recognition_msgs.msg import FaceProperties
10+
from image_recognition_msgs.srv import GetFaceProperties
11+
from image_recognition_util import image_writer
12+
from keras_ros.age_gender_estimator import AgeGenderEstimator
13+
14+
15+
class WideResnetFaceProperties:
16+
def __init__(self, weights_file_path, img_size, depth, width, save_images_folder):
17+
"""
18+
ROS node that wraps the Wide Resnet age gender estimator
19+
"""
20+
self._bridge = CvBridge()
21+
self._properties_srv = rospy.Service('get_face_properties', GetFaceProperties, self._get_face_properties_srv)
22+
self._estimator = AgeGenderEstimator(weights_file_path, img_size, depth, width)
23+
24+
if save_images_folder:
25+
self._save_images_folder = os.path.expanduser(save_images_folder)
26+
if not os.path.exists(self._save_images_folder):
27+
os.makedirs(self._save_images_folder)
28+
else:
29+
self._save_images_folder = None
30+
31+
rospy.loginfo("WideResnetFaceProperties node initialized:")
32+
rospy.loginfo(" - weights_file_path=%s", weights_file_path)
33+
rospy.loginfo(" - img_size=%s", img_size)
34+
rospy.loginfo(" - depth=%s", depth)
35+
rospy.loginfo(" - width=%s", width)
36+
rospy.loginfo(" - save_images_folder=%s", save_images_folder)
37+
38+
def _get_face_properties_srv(self, req):
39+
"""
40+
Callback when the GetFaceProperties service is called
41+
:param req: Input images
42+
:return: properties
43+
"""
44+
# Convert to opencv images
45+
try:
46+
bgr_images = [self._bridge.imgmsg_to_cv2(image, "bgr8") for image in req.face_image_array]
47+
except CvBridgeError as e:
48+
raise Exception("Could not convert image to opencv image: %s" % str(e))
49+
50+
rospy.loginfo("Estimating the age and gender of %d incoming images ...", len(bgr_images))
51+
estimations = self._estimator.estimate(bgr_images)
52+
rospy.loginfo("Done")
53+
54+
face_properties_array = []
55+
for (age, gender) in estimations:
56+
face_properties_array.append(FaceProperties(
57+
age=int(age),
58+
gender=FaceProperties.FEMALE if gender[0] > 0.5 else FaceProperties.MALE,
59+
))
60+
61+
# Store images if specified
62+
if self._save_images_folder:
63+
def _get_label(p):
64+
return "age_%d_gender_%s" % (p.age, "male" if p.gender == FaceProperties.MALE else "female")
65+
66+
image_writer.write_estimations(self._save_images_folder, bgr_images,
67+
[_get_label(p) for p in face_properties_array],
68+
suffix="_face_properties")
69+
70+
# Service response
71+
return {"properties_array": face_properties_array}
72+
73+
if __name__ == '__main__':
74+
rospy.init_node("face_properties")
75+
76+
try:
77+
weights_file_path = rospy.get_param("~weights_file_path")
78+
img_size = rospy.get_param("~image_size", 64)
79+
depth = rospy.get_param("~depth", 16)
80+
width = rospy.get_param("~width", 8)
81+
save_images = rospy.get_param("~save_images", True)
82+
83+
save_images_folder = None
84+
if save_images:
85+
save_images_folder = rospy.get_param("~save_images_folder", "/tmp/keras_ros")
86+
except KeyError as e:
87+
rospy.logerr("Parameter %s not found" % e)
88+
sys.exit(1)
89+
90+
try:
91+
WideResnetFaceProperties(weights_file_path, img_size, depth, width, save_images_folder)
92+
rospy.spin()
93+
except Exception as e:
94+
rospy.logfatal(e)
95+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#!/usr/bin/env python
2+
import argparse
3+
from keras_ros.age_gender_estimator import AgeGenderEstimator
4+
import cv2
5+
6+
# Assign description to the help doc
7+
parser = argparse.ArgumentParser(description='Get face properties using WideResnet')
8+
9+
# Add arguments
10+
parser.add_argument('--image', type=str, help='Image', required=True)
11+
parser.add_argument('--weights-path', type=str, help='Path to the weights of the WideResnet model', required=True)
12+
parser.add_argument('--image-size', type=int, help='Size of the input image', default=64)
13+
parser.add_argument('--depth', type=int, help='Depth of the network', default=16)
14+
parser.add_argument('--width', type=int, help='Width of the network', default=8)
15+
16+
args = parser.parse_args()
17+
18+
# Read the image
19+
img = cv2.imread(args.image)
20+
21+
# Create Skybiometry iface
22+
estimator = AgeGenderEstimator(args.weights_path, args.image_size, args.depth, args.width)
23+
24+
# Pretty print the output
25+
try:
26+
print estimator.estimate([img])
27+
except Exception as e:
28+
print "An error occurred: %s" % e

0 commit comments

Comments
 (0)