/*
Structures for Kafka streams containing Ultinous video analysis results.
Copyright (C) 2014,2018 Ultinous Inc.
*/
////////////////////////////////////////////////////////////////////////////////////////////////////////
//
// This proto describes the Kafka messages created as a result of running video analysis components.
// They are all outputs of one or more components. Some of them are also input to complex components.
//
// Each record below has a time stamp and a key, in addition to the payload described by the proto message.
// The time and key are documented above each record.
//
// Some of the records below are linked by sharing the same key in order to be able to identify one with another.
// For example, AgeRecord has the same key as ObjectDetectionRecord so that each age can be assigned to
// the correseponding person when multiple persons are detected in a single frame.
//
////////////////////////////////////////////////////////////////////////////////////////////////////////
syntax = "proto3";
import "ultinous/proto/common/skeleton.proto";
package ultinous.proto.kafka;
option java_package = "com.ultinous.proto.kafka";
option java_multiple_files = true;
import "ultinous/proto/common/kafka_common.proto";
/** Output of MGR Object Detection and the Detection Filter.
One instance of this record is generated for each detected head/face on each frame.
For each analyzed video frame, a series of detection records are created, corresponding to the number of
people detected in that frame. The detection records belonging to a single video frame are indexed sequentially
thus these indices are only unique within a single frame. Therefore a combined key is generated from the
the timestamp of the source video frame and the detection index to make it unique for the entire video stream.
The end_of_frame field indicates that no more records for the given input video frame will be inserted into the stream.
When this flag is true, all other fields of the record are invalid.
time: timestamp of the input video frame
key: time + "_" + sequential index within frame
*/
message ObjectDetectionRecord
{
ObjectType type = 1;
Rect bounding_box = 2;
float detection_confidence = 3;
bool end_of_frame = 4;
}
/** 3D rotation coordinates for head pose.
See e.g. https://howthingsfly.si.edu/flight-dynamics/roll-pitch-and-yaw
Coordinates can be positive or negative as well.
(0, 0, 0) degrees means the head directly facing the camera.
unit: degrees
*/
message Orientation3D {
float yaw = 1;
float roll = 2;
float pitch = 3;
}
/** Output of MGR Head Pose Detection.
One instance of this record is generated for a detection if its head pose can be determined.
time: timestamp of the input video frame
key: same as the key of the corresponding ObjectDetectionRecord
*/
message HeadPose3DRecord
{
Orientation3D pose = 1;
bool end_of_frame = 2;
}
/** Feature vector
*/
message FeatureVector {
enum FeatureType
{
PERSON_FACE = 0;
PERSON_FULL_BODY = 1;
}
string model_id = 1;
repeated float feature = 2;
FeatureType type = 3;
}
/** Output of MGR Feature Vector Detection.
Feature vectors are an internal representation of the characteristics of a specific person's face or full body.
This record type is only to be used by other Ultinous software components, e.g. for face recognition.
One instance of this record is generated for a detection if its feature vector can be determined.
time: timestamp of the input video frame
key: same as the key of the corresponding ObjectDetectionRecord
*/
message FeatureVectorRecord
{
FeatureVector features = 1;
bool end_of_frame = 2;
}
/** Output of MGR Gender Detection.
One instance of this record is generated for a detection if its gender can be determined.
time: timestamp of the input video frame
key: same as the key of the corresponding ObjectDetectionRecord
*/
message GenderRecord
{
enum Gender {
MALE = 0;
FEMALE = 1;
}
Gender gender = 1;
float confidence = 2;
bool end_of_frame = 3;
}
/** Output of MGR Face Mask Detection.
One instance of this record is generated for a detection if it is suitable for face mask detection.
time: timestamp of the input video frame
key: same as the key of the corresponding ObjectDetectionRecord
*/
message FaceMaskRecord
{
bool has_mask = 1;
float confidence = 2;
bool end_of_frame = 3;
}
/** Output of MGR Age Detection.
One instance of this record is generated for a detection if its age can be determined.
time: timestamp of the input video frame
key: same as the key of the corresponding ObjectDetectionRecord
*/
message AgeRecord
{
uint32 age = 1;
float confidence = 2;
bool end_of_frame = 3;
}
/** Output of MGR Skeleton Detection.
One instance of this record is generated for each detected person on each frame.
A skeleton is a set of points, labelled with a body part label.
Points are not connected, a possible way of connecting them is described in skeleton.proto.
time: timestamp of the input video frame
key: time + "_" + sequential index within frame
*/
message SkeletonRecord
{
/** A single point in the skeleton.
*/
message SkeletonPoint
{
float x = 1;
float y = 2;
common.SkeletonPointType type = 3;
float confidence = 4;
}
repeated SkeletonPoint points = 1;
bool end_of_frame = 2;
}
/** Output of MGR containing frame information.
One instance of this record is generated for each input video frame.
time: timestamp of the input video frame
key: empty
*/
message FrameInfoRecord
{
uint32 columns = 1;
uint32 rows = 2;
}
/** Output of kafka_tracker.
One instance of this record is generated for each new point on each track.
For each track, a new point is created for each new frame, until the track ends.
New points of the track are predicted by the tracking algorithm.
The predicted points may or may not be confirmed by an actual detection.
Even when confirmed, predicted points produce smoother tracks than bounding box centers.
time: time of the frame
key: trackStartTime_trackId
trackId is a serial number starting from 0 when the application starts.
Note that the key is the same for all TrackChangeRecords of a single track.
*/
message TrackChangeRecord
{
bool end_of_track = 1;
string detection_key = 2;
Point point = 3;
}
/** Identifier structure for a PassEvent
*/
message PassEventId
{
string track_key = 1;
uint32 serial = 2;
}
/** Details of a passage event.
Used by PassDetectionRecord and PassCounterRecord.
*/
message PassEvent
{
/** Direction of line crossing.
The pass line itself is considered to have a direction, from its first point to the last one.
The terms 'left' and 'right' are defined by an observer moving along the direction of the pass line.
Analogy: left vs. right bank of a river.
*/
enum CrossDirection
{
LR = 0;
RL = 1;
}
PassEventId id = 1;
string pass_line_id = 2;
CrossDirection cross_dir = 3;
uint32 section_idx = 4;
Point cross_point = 5;
}
/** Output of kafka_passdet.
One instance of this record is generated when any of the following events happens:
E1. Passage event (type = PASS_CANDIDATE, pass_candidate given)
A track crosses a pass line. That is, the previous and current TrackChangeRecord points are
on opposite sides of the pass line. The last point of the track can be either detected or
predicted, this is represented by the pass_candidate.is_extrapolated being true. For non-predicted
events there will be no passage realization (type = PASS_REALIZED). For predicted events there
is a possibility for a realization event until the end of the track (type = END_OF_TRACK).
Key is pass_line_id.
Time is the time of the frame containing the last point of the track, i.e. the first frame after the passage.
E2. Passage Realization event (type = PASS_REALIZED, pass_realized given):
A prediction based PassCandidate (is_extrapolated = true) can be confirmed by a true detection.
Such a PassCandidate can become realized until the track ends. A single track change input may
trigger several Passage Realization Events when the track contained several passages based
on prediction.
Key is empty.
Time is the time of the frame containing the last point of the track, i.e. the frame containing the true detection.
E3. End of Track event (type = END_OF_TRACK, end_of_track given):
A track has ended. It is sent out even if the track didn't generate passage event.
Key is empty.
Time is the time of the TrackChangeRecord with end_of_track=true.
E4. Heartbeat event (type = HEARTBEAT, no details):
There is input but no message was sent in the last second.
Key is empty.
Time is the time of the last input.
*/
message PassDetectionRecord
{
enum Type
{
HEARTBEAT = 0;
PASS_CANDIDATE = 1;
PASS_REALIZED = 2;
END_OF_TRACK= 3;
}
message PassCandidate
{
PassEvent pass = 1;
bool is_extrapolated = 2;
}
message PassRealized
{
PassEventId pass_event_ref = 1;
}
message EndOfTrack
{
string track_key = 1;
}
Type type = 1;
oneof details
{
PassCandidate pass_candidate = 2;
PassRealized pass_realized = 3;
EndOfTrack end_of_track = 4;
}
}
/** Key to feature vector clusters.*/
message FVClusterId
{
int64 first_detection_time = 1;
string first_detection_key = 2;
string first_detection_stream_id = 3;
}
/** Feature vector cluster representation */
message FVCluster
{
FeatureVector representative_fv = 1;
uint32 num_observations = 2;
/** Shows if cluster has been realized.
If cluster_realization in the configuration is null, this value will always be true.
Clusters are not meant to be used until is_realized is true.
While false, outputs are only saved to keep track of input keys and their clusters.
*/
bool is_realized = 3;
}
/** Event of adding a new feature vector to a cluster system.
Happens for each feature vector input of kafka_feature_vector_clustering.
Happens for each feature vector input from a registration stream of kafka_reid.
The feature vector input is either registered as a new cluster or updates the feature a stored cluster.
*/
message FVRegistrationEvent
{
FVClusterId cluster_id = 1;
FVCluster cluster = 2;
string input_stream_id = 3;
}
/** Event of merging some clusters into one. */
message FVClusterMergingEvent
{
FVClusterId cluster_id = 1;
FVCluster cluster = 2;
repeated FVClusterId merged_clusters = 3;
}
/** Event of deleting a cluster when its retention period expires.
Provides the reason of deleting:
1. EXPIRED: Deleted because the expiration period has reached.
2. REMOVED: Deleted by deletion message.
3. REALIZED: Deleted because the cluster has been successfully realized.
*/
message FVClusterDeletedEvent
{
enum DeletionReason
{
EXPIRED = 0;
REMOVED = 1;
REALIZED = 2;
}
FVClusterId deleted_cluster = 1;
DeletionReason reason = 2;
}
/** Output of kafka_feature_vector_clustering.
One instance of this record is generated when any of the following happens:
1. reg_event: The feature vector input is either registered as a new cluster or updates a stored cluster.
2. merge_event: Some of the clusters are merged into one cluster.
3. delete_event: A cluster is deleted because its retention period expired.
4. end_of_input_record: There will be no more events for this input. This is denoted by an empty message.
time: Time of the current input record.
key: Key of the current input record.
*/
message FVClusterUpdateRecord
{
enum Type
{
END_OF_INPUT_RECORD = 0;
REG_EVENT = 1;
MERGE_EVENT = 2;
DELETE_EVENT = 3;
}
Type type = 1;
oneof event
{
FVRegistrationEvent reg_event = 2;
FVClusterMergingEvent merge_event = 3;
FVClusterDeletedEvent delete_event = 4;
}
}
/** Output of kafka_reid.
kafka_reid has an internal database of feature vector clusters. Each cluster corresponds to a separate identity (person).
One instance of this record is generated when any of the following happens:
1. reid_event: The input from a reidentification stream is matched by a list of stored clusters.
2. reg_event: The input from a registration stream is either registered as a new cluster or updates a stored cluster.
3. merge_event: Some of the clusters are merged into one cluster.
4. delete_event: A cluster is deleted because its retention period expired.
5. end_of_input_record: There will be no more events for this input. This is denoted by an empty message.
time: Time of the current input record.
key: Key of the current input record.
*/
message ReidRecord
{
enum Type
{
END_OF_INPUT_RECORD = 0;
REID_EVENT = 1;
REG_EVENT = 2;
MERGE_EVENT = 3;
DELETE_EVENT = 4;
}
message ScoredFVClusterId
{
FVClusterId id = 1;
float score = 2;
}
message ReidEvent
{
repeated ScoredFVClusterId match_list = 1;
string input_stream_id = 2;
}
Type type = 1;
oneof event
{
ReidEvent reid_event = 2;
FVRegistrationEvent reg_event = 3;
FVClusterMergingEvent merge_event = 4;
FVClusterDeletedEvent delete_event = 5;
}
}