Skip to content

Commit b4d7c44

Browse files
committed
FINALLY GOT IT WORKING WITH FRAMES WITHOUT FACES
1 parent 08d7cad commit b4d7c44

4 files changed

Lines changed: 66 additions & 47 deletions

File tree

mediapipe/examples/ios/facemeshioslib/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ objc_library(
123123
deps = [
124124
"//mediapipe/objc:mediapipe_framework_ios",
125125
"//mediapipe/objc:mediapipe_input_sources_ios",
126+
"//mediapipe/calculators/core:packet_presence_calculator",
126127
# "//mediapipe/objc:mediapipe_layer_renderer", # no need for layer renderer since I don't render
127128
] + select({
128129
# "//mediapipe:ios_i386": [],

mediapipe/examples/ios/facemeshioslib/FaceMeshIOSLib.h

Lines changed: 4 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,20 +19,14 @@
1919

2020
@protocol FaceMeshIOSLibDelegate <NSObject>
2121
@optional
22-
/** Array of faces, with faces represented by arrays of face landmarks
23-
* This does not always get called. If there are no faces detected by the Face Detector (Short Range) model,
24-
* then this does not get called
22+
/**
23+
* Array of faces, with faces represented by arrays of face landmarks
2524
*/
2625
- (void)didReceiveFaces:(NSArray <NSArray<FaceMeshIOSLibFaceLandmarkPoint *>*>*)faces;
27-
/** Array of faces, with faces represented by arrays of face landmarks
28-
* This does not always get called. If there are no faces detected by the Face Detector (Short Range) model,
29-
* then this does not get called
26+
/**
27+
* Array of faces, with faces represented by arrays of face landmarks
3028
*/
3129
- (void)didReceiveFaceBoxes:(NSArray <FaceMeshIOSLibNormalizedRect *>*)faces;
32-
/** Array of faces, with faces represented by arrays of face landmarks
33-
* This is the result called by the Face Detection (Short Range) model (a.k.a. BlazeFace)
34-
*/
35-
- (void)didReceiveFaceDetections:(NSArray <FaceMeshIOSLibNormalizedRect *>*)faces;
3630
@end
3731

3832
@interface FaceMeshIOSLib : NSObject

mediapipe/examples/ios/facemeshioslib/FaceMeshIOSLib.mm

Lines changed: 43 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
// NormalizedLandmarkList won't be defined unless we import this header (following FaceMeshGpuViewController's imports)
88
#include "mediapipe/framework/formats/landmark.pb.h"
99
#include "mediapipe/framework/formats/rect.pb.h"
10+
#include "mediapipe/framework/formats/detection.pb.h"
1011

1112
//#import "mediapipe/objc/MPPLayerRenderer.h"
1213

@@ -20,7 +21,9 @@
2021
static const char* kNumFacesInputSidePacket = "num_faces";
2122
static const char* kLandmarksOutputStream = "multi_face_landmarks";
2223
static const char* kFaceRectsOutputStream = "face_rects_from_landmarks";
23-
static const char* kFaceDetectionRectsOutputStream = "face_rects_from_detections";
24+
static const char* kLandmarkPresenceOutputStream = "landmark_presence";
25+
// static const char* kFaceDetectionRectsOutputStream = "face_rects_from_detections";
26+
// static const char* kFaceDetectionsRawDetectionsOutputStream = "face_detections";
2427

2528
// Max number of faces to detect/process.
2629
static const int kNumFaces = 1;
@@ -78,8 +81,14 @@ + (MPPGraph*)loadGraphFromResource:(NSString*)resource {
7881
outputPacketType:MPPPacketTypeRaw];
7982
// The face detections rect output stream
8083
// This is kind of almost direct from blazeface I think, so it's likely out every frame.
84+
// Turns out this doesn't come out at all... what the heck
8185
// [newGraph addFrameOutputStream:kFaceDetectionRectsOutputStream
8286
// outputPacketType:MPPPacketTypeRaw];
87+
88+
// The Presence Detection stream
89+
// This is with much much many many thanks to @homuler here: https://github.com/google/mediapipe/issues/850#issuecomment-683268033
90+
[newGraph addFrameOutputStream:kLandmarkPresenceOutputStream
91+
outputPacketType:MPPPacketTypeRaw];
8392
return newGraph;
8493
}
8594

@@ -149,12 +158,12 @@ - (void)mediapipeGraph:(MPPGraph*)graph
149158
didOutputPacket:(const ::mediapipe::Packet&)packet
150159
fromStream:(const std::string&)streamName {
151160
if (streamName == kLandmarksOutputStream) {
152-
if (packet.IsEmpty()) {
161+
if (packet.IsEmpty()) { // This condition never gets called because FaceLandmarkFrontGpu does not process when there are no detections
153162
return;
154163
}
155164
const auto& multi_face_landmarks = packet.Get<std::vector<::mediapipe::NormalizedLandmarkList>>();
156-
NSLog(@"[TS:%lld] Number of face instances with landmarks: %lu", packet.Timestamp().Value(),
157-
multi_face_landmarks.size());
165+
// NSLog(@"[TS:%lld] Number of face instances with landmarks: %lu", packet.Timestamp().Value(),
166+
// multi_face_landmarks.size());
158167
NSMutableArray <NSArray <FaceMeshIOSLibFaceLandmarkPoint *>*>*faceLandmarks = [NSMutableArray new];
159168

160169
for (int face_index = 0; face_index < multi_face_landmarks.size(); ++face_index) {
@@ -176,10 +185,11 @@ - (void)mediapipeGraph:(MPPGraph*)graph
176185
[self.delegate didReceiveFaces:faceLandmarks];
177186
}
178187
}
179-
else if (streamName == kFaceDetectionRectsOutputStream) {
180-
if (packet.IsEmpty()) {
181-
NSLog(@"[TS:%lld] No face detections", packet.Timestamp().Value());
182-
if([self.delegate respondsToSelector:@selector(didReceiveFaces:)]) {
188+
189+
else if (streamName == kFaceRectsOutputStream) {
190+
if (packet.IsEmpty()) { // This condition never gets called because FaceLandmarkFrontGpu does not process when there are no detections
191+
// NSLog(@"[TS:%lld] No face rects", packet.Timestamp().Value());
192+
if([self.delegate respondsToSelector:@selector(didReceiveFaceBoxes:)]) {
183193
[self.delegate didReceiveFaceBoxes:@[]];
184194
}
185195
return;
@@ -197,35 +207,36 @@ - (void)mediapipeGraph:(MPPGraph*)graph
197207
rect.centerX = centerX; rect.centerY = centerY; rect.height = height; rect.width = width; rect.rotation = rotation;
198208
[outRects addObject:rect];
199209
}
200-
if([self.delegate respondsToSelector:@selector(didReceiveFaceDetections:)]) {
201-
[self.delegate didReceiveFaceDetections:outRects];
210+
if([self.delegate respondsToSelector:@selector(didReceiveFaceBoxes:)]) {
211+
[self.delegate didReceiveFaceBoxes:outRects];
202212
}
203213
}
204-
else if (streamName == kFaceRectsOutputStream) {
214+
else if (streamName == kLandmarkPresenceOutputStream) {
215+
bool is_landmark_present = true;
205216
if (packet.IsEmpty()) {
206-
NSLog(@"[TS:%lld] No face rects", packet.Timestamp().Value());
207-
if([self.delegate respondsToSelector:@selector(didReceiveFaces:)]) {
208-
[self.delegate didReceiveFaceBoxes:@[]];
209-
}
210-
return;
217+
is_landmark_present = false;
211218
}
212-
const auto& face_rects_from_landmarks = packet.Get<std::vector<::mediapipe::NormalizedRect>>();
213-
NSMutableArray <FaceMeshIOSLibNormalizedRect *>*outRects = [NSMutableArray new];
214-
for (int face_index = 0; face_index < face_rects_from_landmarks.size(); ++face_index) {
215-
const auto& face = face_rects_from_landmarks[face_index];
216-
float centerX = face.x_center();
217-
float centerY = face.y_center();
218-
float height = face.height();
219-
float width = face.width();
220-
float rotation = face.rotation();
221-
FaceMeshIOSLibNormalizedRect *rect = [FaceMeshIOSLibNormalizedRect new];
222-
rect.centerX = centerX; rect.centerY = centerY; rect.height = height; rect.width = width; rect.rotation = rotation;
223-
[outRects addObject:rect];
219+
else {
220+
is_landmark_present = packet.Get<bool>();
224221
}
225-
if([self.delegate respondsToSelector:@selector(didReceiveFaceBoxes:)]) {
226-
[self.delegate didReceiveFaceBoxes:outRects];
222+
if (is_landmark_present) {
223+
// NSLog(@"Landmarks present");
224+
// Landmarks are present; no need to do anything (the rest of the callbacks will get called on their own)
225+
}
226+
else {
227+
// NSLog(@"Landmarks not present");
228+
// No landmarks are present, we call our delegate with empty faces to make our protocol consistent with number of frames
229+
if([self.delegate respondsToSelector:@selector(didReceiveFaceBoxes:)]) {
230+
[self.delegate didReceiveFaceBoxes:@[]];
231+
}
232+
if([self.delegate respondsToSelector:@selector(didReceiveFaces:)]) {
233+
[self.delegate didReceiveFaces:@[]];
234+
}
227235
}
228236
}
237+
else {
238+
NSLog(@"Unknown %@ packet with stream name %s", packet.IsEmpty() ? @"EMPTY" : @"NON-EMPTY",streamName.c_str());
239+
}
229240
}
230241

231242

@@ -235,14 +246,14 @@ - (void)processVideoFrame:(CVPixelBufferRef)imageBuffer {
235246
const auto ts =
236247
mediapipe::Timestamp(self.timestamp++ * mediapipe::Timestamp::kTimestampUnitsPerSecond);
237248
NSError* err = nil;
238-
NSLog(@"sending imageBuffer @%@ to %s", @(ts.DebugString().c_str()), kInputStream);
249+
// NSLog(@"sending imageBuffer @%@ to %s", @(ts.DebugString().c_str()), kInputStream);
239250
auto sent = [self.mediapipeGraph sendPixelBuffer:imageBuffer
240251
intoStream:kInputStream
241252
packetType:MPPPacketTypePixelBuffer
242253
timestamp:ts
243254
allowOverwrite:NO
244255
error:&err];
245-
NSLog(@"imageBuffer %s", sent ? "sent!" : "not sent.");
256+
// NSLog(@"imageBuffer %s", sent ? "sent!" : "not sent.");
246257
if (err) {
247258
NSLog(@"sendPixelBuffer error: %@", err);
248259
}

mediapipe/graphs/face_mesh/pure_face_mesh_mobile.pbtxt

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,15 @@ output_stream: "face_rects_from_landmarks"
2525
# see detection.proto
2626
# Regions of interest calculated based on face detections.
2727
# (std::vector<NormalizedRect>)
28-
output_stream: "face_rects_from_detections"
28+
# output_stream: "face_rects_from_detections"
2929

30-
# There's also face_rects_from_detections, but that likely isn't as accurate (it's from BlazeFace)
30+
# Extra outputs (for debugging, for instance).
31+
# Detected faces. (std::vector<Detection>)
32+
# (std::vector<Detections>)
33+
# output_stream: "face_detections"
3134

35+
# Landmark presence (needed because whole graph won't emit anything if no faces are detected)
36+
output_stream: "landmark_presence"
3237

3338
# screw the throttling, we do that ourselves.
3439
# *throttling node code was deleted from here*
@@ -44,9 +49,17 @@ node {
4449
# face_detections is the stream that comes out from face_detection_short_range_common
4550
# output_stream: "DETECTIONS:face_detections"
4651
47-
# we need to output this too because otherwise the graph outputs nothing
48-
# for frames without faces, and that makes it pretty hard to use
49-
output_stream: "ROIS_FROM_DETECTIONS:face_rects_from_detections"
52+
# output_stream: "ROIS_FROM_DETECTIONS:face_rects_from_detections"
53+
}
54+
55+
# See this thread here https://github.com/google/mediapipe/issues/850#issuecomment-683268033
56+
# "if there are no packets in the corresponding output stream, it is designed to wait until the packet comes in"
57+
# That means that we'd get absolutely nothing to work with and won't know if our frame had anythin!
58+
# So we add PacketPresenceCalculator
59+
node {
60+
calculator: "PacketPresenceCalculator"
61+
input_stream: "PACKET:multi_face_landmarks"
62+
output_stream: "PRESENCE:landmark_presence"
5063
}
5164

5265
# nope not rendering.

0 commit comments

Comments
 (0)