-
Notifications
You must be signed in to change notification settings - Fork 9.8k
Conversation
afe1ac6
to
6633563
Compare
6633563
to
0138a18
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM
/// The options for the face detector. | ||
final FaceDetectorOptions options; | ||
|
||
/// Closes the face detector and release its model resources. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
release => releases
'enableLandmarks': options.enableLandmarks, | ||
'enableTracking': options.enableTracking, | ||
'minFaceSize': options.minFaceSize, | ||
'mode': options.mode == FaceDetectorMode.fast ? 'fast' : 'accurate', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code silently does the wrong thing if you add a new FaceDetectorMode
in the future. We could add something like
String modeToString(FaceDetectorMode mode) {
final String full = mode.toString();
return full.substring(full.indexOf('.') + 1);
}
This method could alternatively be implemented using a switch
.
} | ||
} | ||
|
||
class FaceDetectorOptions {} | ||
/// Options for [FaceDetector]. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That doesn't give any new information :-)
You might want to add that instances are immutable.
class FaceDetectorOptions {} | ||
/// Options for [FaceDetector]. | ||
class FaceDetectorOptions { | ||
FaceDetectorOptions({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Dartdoc missing on constructor. The preconditions enforced by your assert
s should be documented.
faces.add(new Face._(data)); | ||
}); | ||
|
||
return faces; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using forEach
might be considered clunkier than using List.map
return reply.map<Face>((dynamic data) => Face._(data)).toList();
and is likely slower than using a plain for
loop:
final List<Face> faces = <Face>[];
for (dynamic data in reply) {
faces.add(Face._(data));
}
return faces;
/// The axis-aligned bounding rectangle of the detected face. | ||
final Rectangle<int> boundingBox; | ||
|
||
/// The rotation of the face about the vertical axis of the image. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In degrees or radians? Is the rotation normalized to some interval like 0..360?
Same below.
|
||
final Map<FaceLandmarkType, FaceLandmark> _landmarks; | ||
|
||
/// The axis-aligned bounding rectangle of the detected face. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the coordinate system here? Image pixels with (0, 0) the upper left corner?
const Point<num>(19, 20), | ||
expect(block.cornerPoints, <Point<int>>[ | ||
const Point<int>(17, 18), | ||
const Point<int>(19, 20), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[nit] You could move the const
out to the list literal to avoid repeating it:
const <Point<int>>[
Point<int>(17, 18),
Point<int>(19, 20),
]
expect( | ||
face.landmark(FaceLandmarkType.rightMouth).type, | ||
FaceLandmarkType.rightMouth, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Half of the above code could be reduced to
for (FaceLandmarkType type in FaceLandmarkType.values) {
expect(face.landmark(type).type, type)
}
|
||
data.forEach((dynamic key, dynamic point) { | ||
FaceLandmarkType landmarkType; | ||
switch (key) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This switch
could be replaced by a lookup into a Map<String, FaceLandmarkType>
:
enum FaceLandmarkType { ... }
Map<String, FaceLandmarkType> _stringToTypeMap;
String _typeToString(FaceLandmarkType type) {
final String full = type.toString();
return full.substring(full.indexOf('.') + 1);
}
FaceLandmarkType _stringToType(String string) {
_stringToTypeMap ??= Map<String, FaceLandmarkType>.fromIterables(
FaceLandmarkType.values.map<String>(_typeToString),
FaceLandmarkType.values,
);
FaceLandmarkType type = _stringToTypeMap[string];
if (type == null) {
throw ArgumentError.value(string, 'Landmark name', 'Not valid landmark.');
}
return type;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure how important readability is, but i also got this to work. haha
_landmarks = Map<FaceLandmarkType, FaceLandmark>.fromIterables(
FaceLandmarkType.values,
List<FaceLandmark>.generate(
FaceLandmarkType.values.length,
(index) {
final FaceLandmarkType type = FaceLandmarkType.values[index];
return FaceLandmark._(
type,
Point<int>(
data['landmarks'][_enumToString(type)][0],
data['landmarks'][_enumToString(type)][1],
),
);
},
),
);
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would fail if missing one though. I'll probably have to add a check.
2285f17
to
104ada7
Compare
), | ||
); | ||
}, | ||
), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have a list
of type List<T>
and an expression expr(t)
that has type U
when t
has type T
, then you can replace
List<U>.generate(list.length, (int index) => expr(list[index]))
by the simpler
list.map<U>((T t) => expr(t)).toList()
provided expr
does not use index
. Since you only need an Iterable<U>
, you can dispense with the .toList()
and simplify your code to
_landmarks = Map<FaceLandmarkType, FaceLandmark>.fromIterables(
FaceLandmarkType.values,
FaceLandmarkType.values.map<FaceLandmark>((FaceLandmarkType type) {
final dynamic landmarkMap = data['landmarks'];
final String typeString = _enumToString(type);
if (landmarkMap[typeString] == null) return null;
return FaceLandmark._(
type,
Point<int>(
landmarkMap[typeString][0],
landmarkMap[typeString][1],
),
);
}
);
Next, you can avoid repeated lookup of data['landmarks']
and landmarkMap[typeString]
by a bit of hoisting:
final Map<dynamic, dynamic> landmarkMap = data['landmarks'];
_landmarks = Map<FaceLandmarkType, FaceLandmark>.fromIterables(
FaceLandmarkType.values,
FaceLandmarkType.values.map<FaceLandmark>((FaceLandmarkType type) {
final List<dynamic> position = landmarkMap[_enumToString(type)];
return (position == null) ? null : FaceLandmark._(type, Point<int>(position[0], position[1]));
}
);
No description provided.