DeepFaceRecognition.cpp
Go to the documentation of this file.
1/*
2 * This file is part of ArmarX.
3 *
4 * ArmarX is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * ArmarX is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program. If not, see <http://www.gnu.org/licenses/>.
15 *
16 * @package VisionX::ArmarXObjects::DeepFaceRecognition
17 * @author Mirko Waechter (waechter at kit dot edu)
18 * @date 2017
19 * @copyright http://www.gnu.org/licenses/gpl-2.0.txt
20 * GNU General Public License
21 */
22
23#include "DeepFaceRecognition.h"
24
25#include <Eigen/Geometry>
26
27#include <SimoxUtility/algorithm/string/string_tools.h>
28
32
34
36
37#include <Calibration/Calibration.h>
38#include <Image/ImageProcessor.h>
39#include <Image/IplImageAdaptor.h>
40#include <Image/PrimitivesDrawer.h>
41#include <Image/PrimitivesDrawerCV.h>
45
46using namespace armarx;
47using namespace visionx;
48
49//void visionx::DeepFaceRecognition::onInitObjectLocalizerProcessor()
50void
52{
53 usingProxy("FaceRecognition");
54 usingProxy("LongtermMemory");
55 usingProxy(getProperty<std::string>("ImageProviderName").getValue());
56
57
58 offeringTopic("TextToSpeech");
59}
60
61//void visionx::DeepFaceRecognition::onConnectObjectLocalizerProcessor()
62void
64{
65 getProxy(faceReg, "FaceRecognition");
66 getProxy(ltm, "LongtermMemory");
67 getProxy(imageProvider, getProperty<std::string>("ImageProviderName").getValue());
68 // auto calibration = imageProvider->getMonocularCalibration();
69
70 // this->calibration.reset(visionx::tools::convert(imageProvider->getMonocularCalibration()));
71 this->calibration.reset(new CCalibration());
72 calibration->SetCameraParameters(
73 987.99, 956.82, 639.32, 415.64, 0, 0, 0, 0, Math3d::unit_mat, Math3d::zero_vec, 1280, 720);
74
76 if (!ltm->hasSegment(faceSegmentName))
77 {
78 faceSegmentPrx = memoryx::EntityMemorySegmentInterfacePrx::checkedCast(
79 ltm->addGenericSegment(faceSegmentName));
80 }
81 else
82 {
83 faceSegmentPrx =
84 memoryx::EntityMemorySegmentInterfacePrx::checkedCast(ltm->getSegment(faceSegmentName));
85 }
86}
87
94
95memoryx::ObjectLocalizationResultList
96visionx::DeepFaceRecognition::localizeObjectClasses(const memoryx::ObjectClassNameList& classes,
97 const Ice::Current&)
98{
99 ARMARX_VERBOSE << deactivateSpam(10) << "Localizing: " << classes;
100 memoryx::ObjectLocalizationResultList result;
102 ARMARX_CHECK_EXPRESSION(faceSegmentPrx);
103
104 auto faceLocations = faceReg->calculateFaceLocations();
105 ARMARX_VERBOSE << deactivateSpam(10, std::to_string(faceLocations.size())) << "Found "
106 << faceLocations.size() << " faces";
107 auto agentName = getProperty<std::string>("AgentName").getValue();
108 auto cameraFrame = getProperty<std::string>("CameraFrameName").getValue();
109 for (visionx::FaceLocation faceLocation : faceLocations)
110 {
111 memoryx::EntityPtr oldFace =
112 memoryx::EntityPtr::dynamicCast(faceSegmentPrx->getEntityByName(faceLocation.label));
113 bool updateFaceMemory = true;
114 ARMARX_DEBUG << deactivateSpam(1) << "Rect: " << faceLocation.topLeft.e0 << ", "
115 << faceLocation.topLeft.e1 << ", " << faceLocation.bottomRight.e0 << ", "
116 << faceLocation.bottomRight.e1;
117 float centerX = (faceLocation.bottomRight.e0 + faceLocation.topLeft.e0) * 0.5f;
118 float centerY = (faceLocation.bottomRight.e1 + faceLocation.topLeft.e1) * 0.5f;
119 memoryx::ObjectLocalizationResult r;
120 r.objectClassName = "face";
121 r.instanceName = faceLocation.label;
122 Eigen::Matrix3f ori = Eigen::Matrix3f::Identity();
123 r.orientation = new FramedOrientation(ori, cameraFrame, agentName);
124 Vec3d cameraCoords;
125 float faceHeightPixel = faceLocation.bottomRight.e1 - faceLocation.topLeft.e1;
126 float distance =
127 calibration->GetCameraParameters().focalLength.y * 160.f / (faceHeightPixel);
128 this->calibration->ImageToCameraCoordinates(
129 Vec2d{centerX, centerY}, cameraCoords, distance, false);
130 ARMARX_VERBOSE << deactivateSpam(1) << "Camera coordinates for " << faceLocation.label
131 << ": " << cameraCoords.x << ", " << cameraCoords.y
132 << " distance: " << distance << ", " << VAROUT(faceHeightPixel)
133 << VAROUT(centerX) << VAROUT(centerY);
134 r.position = new FramedPosition(
135 Eigen::Vector3f(cameraCoords.x, cameraCoords.y, distance), cameraFrame, agentName);
136 Eigen::Matrix3f id = Eigen::Matrix3f::Identity();
137 r.orientation = new FramedOrientation(id, cameraFrame, agentName);
138 r.recognitionCertainty = 0.7f;
139 Eigen::Vector3f cov(10, 10, 10000);
140 r.positionNoise =
141 new memoryx::MultivariateNormalDistribution(Eigen::Vector3f::Zero(), cov.asDiagonal());
142
143 r.timeStamp = new armarx::TimestampVariant(faceLocation.timestamp);
144 result.push_back(r);
145 if (faceLocation.label == "Unknown")
146 {
147 continue;
148 }
149 if (oldFace && oldFace->hasAttribute("LastGreetingTime"))
150 {
151 VariantPtr updateTimeVariant =
152 VariantPtr::dynamicCast(oldFace->getAttribute("LastGreetingTime")->getValue());
153 ARMARX_CHECK_EXPRESSION(updateTimeVariant);
154 ARMARX_CHECK_EXPRESSION(updateTimeVariant->get<TimestampVariant>());
155 updateFaceMemory &=
156 updateTimeVariant->get<TimestampVariant>()->toTime() +
157 IceUtil::Time::secondsDouble(getProperty<float>("GreetAgainDelay").getValue()) <
159 }
160 auto face = oldFace ? oldFace : memoryx::Entity::CreateGenericEntity();
162 // int matchCounter = 0;
163 if (updateFaceMemory)
164 {
165 // if (oldFace && oldFace->hasAttribute("MatchCounter"))
166 // {
167 // bool lastLocalizationTooOld = false;
168 // if (oldFace->hasAttribute("UpdateTime"))
169 // {
170 // VariantPtr updateTimeVariant = VariantPtr::dynamicCast(oldFace->getAttribute("UpdateTime")->getValue());
171 // lastLocalizationTooOld &= updateTimeVariant->get<TimestampVariant>()->toTime() + IceUtil::Time::secondsDouble(10) > TimeUtil::GetTime();
172
173 // }
174 // if (!lastLocalizationTooOld)
175 // {
176 // matchCounter = oldFace->getAttribute("MatchCounter")->getValue()->getInt();
177 // }
178 // }
179 // matchCounter++;
180 face->setName(faceLocation.label);
181
182 face->putAttribute("ImagePosition", new armarx::Vector2(centerX, centerY));
183 face->putAttribute("UpdateTime", TimestampVariant::nowPtr());
184 // if (matchCounter >= getProperty<int>("FaceMatchCounter").getValue())
185 float prob = updateAndCheckFaceExistenceProbability(faceLocation);
186 ARMARX_INFO << deactivateSpam(1) << VAROUT(prob);
187 if (prob > getProperty<float>("GreetThreshold").getValue())
188
189 {
190 auto tts_label = simox::alg::replace_all(faceLocation.label, "mirko", "mir ko");
191 face->putAttribute("LastGreetingTime", TimestampVariant::nowPtr());
192 tts->reportTextWithParams("Hello %1%.", {tts_label});
193 ARMARX_IMPORTANT << "Hello " << faceLocation.label;
194 // matchCounter = 0; // reset back to 0 so
195 }
196 }
197 // face->putAttribute("MatchCounter", matchCounter); //always update matchCounter, though matchCounter is only increased if LastGreetingTime is old (see GreetAgainDelay)
198 faceSegmentPrx->upsertEntityByName(faceLocation.label, face);
199 }
200 return result;
201}
202
203float
204visionx::DeepFaceRecognition::updateAndCheckFaceExistenceProbability(
205 const visionx::FaceLocation& faceLocation)
206{
207 auto& timeConfList = faceConfidenceHistory[faceLocation.label];
208
209 double filterRadius = getProperty<float>("FilterRadius").getValue(); // kind of seconds
210 double sigma = filterRadius / 2.5;
211 double sigmaSquare = sigma * sigma;
212 auto gaussianBellCurve = [&](double x) { return exp(-double((x) * (x)) / (sigmaSquare)); };
213
214 auto now = IceUtil::Time::now();
215 // remove old entries
216 for (auto it = timeConfList.begin(); it != timeConfList.end();)
217 {
218 IceUtil::Time t = it->first;
219 // double confidence = it->second;
220 double tDiff = (t - now).toMilliSecondsDouble() * 0.001;
221 double weight = gaussianBellCurve(tDiff);
222 // ARMARX_INFO << VAROUT(tDiff) << VAROUT(weight) << t.toDateTime() + " " << now.toDateTime();
223 if (weight < 0.001)
224 {
225 it = timeConfList.erase(it);
226 }
227 else
228 {
229 it++;
230 }
231 }
232
233 timeConfList.push_back(std::make_pair(now, faceLocation.confidence));
234 float sum = 0;
235 for (auto& timeConfPair : timeConfList)
236 {
237 IceUtil::Time t = timeConfPair.first;
238 double confidence = timeConfPair.second;
239 double tDiff = (t - now).toMilliSecondsDouble() * 0.001;
240 double weight = gaussianBellCurve(tDiff);
241 // ARMARX_INFO << VAROUT(confidence) << VAROUT(weight) << VAROUT(tDiff);
242 sum += confidence * weight;
243 }
244 ARMARX_INFO << VAROUT(sum);
245 return sum;
246}
SpamFilterDataPtr deactivateSpam(SpamFilterDataPtr const &spamFilter, float deactivationDurationSec, const std::string &identifier, bool deactivate)
Definition Logging.cpp:75
#define VAROUT(x)
std::string getConfigIdentifier()
Retrieve config identifier for this component as set in constructor.
Definition Component.cpp:90
Property< PropertyType > getProperty(const std::string &name)
The FramedOrientation class.
Definition FramedPose.h:216
The FramedPosition class.
Definition FramedPose.h:158
void offeringTopic(const std::string &name)
Registers a topic for retrival after initialization.
TopicProxyType getTopic(const std::string &name)
Returns a proxy of the specified topic.
bool usingProxy(const std::string &name, const std::string &endpoints="")
Registers a proxy for retrieval after initialization and adds it to the dependency list.
Ice::ObjectPrx getProxy(long timeoutMs=0, bool waitForScheduler=true) const
Returns the proxy of this object (optionally it waits for the proxy)
static IceUtil::Time GetTime(TimeMode timeMode=TimeMode::VirtualTime)
Get the current time.
Definition TimeUtil.cpp:42
Implements a Variant type for timestamps.
static TimestampVariantPtr nowPtr()
The Vector2 class.
Definition Pose.h:53
static EntityPtr CreateGenericEntity()
Creates an entity without any convenience getter/setter functions.
Definition Entity.cpp:41
The MultivariateNormalDistribution class.
void onInitComponent() override
Pure virtual hook for the subclass.
armarx::PropertyDefinitionsPtr createPropertyDefinitions() override
Add a memory entity representing the hand marker in order to set its properties.
memoryx::ObjectLocalizationResultList localizeObjectClasses(const memoryx::ObjectClassNameList &classes, const Ice::Current &) override
void onConnectComponent() override
Pure virtual hook for the subclass.
#define ARMARX_CHECK_EXPRESSION(expression)
This macro evaluates the expression and if it turns out to be false it will throw an ExpressionExcept...
#define ARMARX_INFO
The normal logging level.
Definition Logging.h:181
#define ARMARX_IMPORTANT
The logging level for always important information, but expected behaviour (in contrast to ARMARX_WAR...
Definition Logging.h:190
#define ARMARX_DEBUG
The logging level for output that is only interesting while debugging.
Definition Logging.h:184
#define ARMARX_VERBOSE
The logging level for verbose information.
Definition Logging.h:187
This file offers overloads of toIce() and fromIce() functions for STL container types.
IceInternal::Handle< Variant > VariantPtr
Definition Variant.h:41
IceUtil::Handle< class PropertyDefinitionContainer > PropertyDefinitionsPtr
PropertyDefinitions smart pointer type.
IceInternal::Handle< Entity > EntityPtr
Typedef of EntityPtr as IceInternal::Handle<Entity> for convenience.
Definition Entity.h:45
ArmarX headers.
double distance(const Point &a, const Point &b)
Definition point.hpp:95