Project import generated by Copybara.

PiperOrigin-RevId: 253489161
This commit is contained in:
MediaPipe Team
2019-06-16 16:06:57 -07:00
committed by jqtang
commit d68f5e4169
844 changed files with 134997 additions and 0 deletions
+31
View File
@@ -0,0 +1,31 @@
# Copyright 2019 The MediaPipe Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
cc_library(
name = "simple_run_graph_main",
srcs = ["simple_run_graph_main.cc"],
deps = [
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/port:commandlineflags",
"//mediapipe/framework/port:file_helpers",
"//mediapipe/framework/port:map_util",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status",
"@com_google_absl//absl/strings",
],
)
+48
View File
@@ -0,0 +1,48 @@
**Hello World**
To build the "Hello World" example, use:
```
bazel build -c opt mediapipe/examples/desktop/hello_world:hello_world
```
and then run it using:
```
bazel-bin/mediapipe/examples/desktop/hello_world/hello_world --logtostderr
```
**TFlite Object Detection**
To build the objet detection demo using a TFLite model on desktop, use:
```
bazel build -c opt mediapipe/examples/desktop/object_detection:object_detection_tflite --define 'MEDIAPIPE_DISABLE_GPU=1'
```
and run it using:
```
bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tflite \
--calculator_graph_config_file=mediapipe/graphs/object_detection/object_detection_desktop_tflite_graph.pbtxt \
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file \
--alsologtostderr
```
**TensorFlow Object Detection**
To build the object detection demo using a TensorFlow model on desktop, use:
```
bazel build -c opt mediapipe/examples/desktop/object_detection:object_detection_tensorflow \
--define 'MEDIAPIPE_DISABLE_GPU=1'
```
and run it using:
```
bazel-bin/mediapipe/examples/desktop/object_detection/object_detection_tensorflow \
--calculator_graph_config_file=mediapipe/graphs/object_detection/object_detection_desktop_tensorflow_graph.pbtxt \
--input_side_packets=input_video_path=/path/to/input/file,output_video_path=/path/to/output/file
--alsologtostderr
```
+14
View File
@@ -0,0 +1,14 @@
"""Copyright 2019 The MediaPipe Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
@@ -0,0 +1,30 @@
# Copyright 2019 The MediaPipe Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
cc_binary(
name = "hello_world",
srcs = ["hello_world.cc"],
visibility = ["//visibility:public"],
deps = [
"//mediapipe/calculators/core:pass_through_calculator",
"//mediapipe/framework:calculator_graph",
"//mediapipe/framework/port:logging",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status",
],
)
@@ -0,0 +1,65 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// A simple example to print out "Hello World!" from a MediaPipe graph.
#include "mediapipe/framework/calculator_graph.h"
#include "mediapipe/framework/port/logging.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/status.h"
namespace mediapipe {
::mediapipe::Status PrintHelloWorld() {
// Configures a simple graph, which concatenates 2 PassThroughCalculators.
CalculatorGraphConfig config = ParseTextProtoOrDie<CalculatorGraphConfig>(R"(
input_stream: "in"
output_stream: "out"
node {
calculator: "PassThroughCalculator"
input_stream: "in"
output_stream: "out1"
}
node {
calculator: "PassThroughCalculator"
input_stream: "out1"
output_stream: "out"
}
)");
CalculatorGraph graph;
RETURN_IF_ERROR(graph.Initialize(config));
ASSIGN_OR_RETURN(OutputStreamPoller poller,
graph.AddOutputStreamPoller("out"));
RETURN_IF_ERROR(graph.StartRun({}));
// Give 10 input packets that contains the same std::string "Hello World!".
for (int i = 0; i < 10; ++i) {
RETURN_IF_ERROR(graph.AddPacketToInputStream(
"in", MakePacket<std::string>("Hello World!").At(Timestamp(i))));
}
// Close the input stream "in".
RETURN_IF_ERROR(graph.CloseInputStream("in"));
mediapipe::Packet packet;
// Get the output packets std::string.
while (poller.Next(&packet)) {
LOG(INFO) << packet.Get<std::string>();
}
return graph.WaitUntilDone();
}
} // namespace mediapipe
int main(int argc, char** argv) {
CHECK(mediapipe::PrintHelloWorld().ok());
return 0;
}
@@ -0,0 +1,39 @@
# Copyright 2019 The MediaPipe Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
cc_library(
name = "run_graph_file_io_main",
srcs = ["run_graph_file_io_main.cc"],
deps = [
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/port:commandlineflags",
"//mediapipe/framework/port:file_helpers",
"//mediapipe/framework/port:map_util",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status",
"@com_google_absl//absl/strings",
],
)
cc_binary(
name = "media_sequence_demo",
deps = [
":run_graph_file_io_main",
"//mediapipe/graphs/media_sequence:clipped_images_from_file_at_24fps_calculators",
],
)
@@ -0,0 +1,51 @@
# Preparing data sets for machine learning with MediaPipe
We include two pipelines to prepare data sets for training TensorFlow models.
Using these data sets is split into two parts. First, the data set is
constructed in with a Python script and MediaPipe C++ binary. The C++ binary
should be compiled by the end user because the preparation for different data
sets requires different MediaPipe calculator dependencies. The result of running
the script is a data set of TFRecord files on disk. The second stage is reading
the data from TensorFlow into a tf.data.Dataset. Both pipelines can be imported
and support a simple call to as_dataset() to make the data available.
### Demo data set
To generate the demo dataset you must have Tensorflow [version >= 1.19]
installed. Then the media_sequence_demo binary must be built from the top
directory in the mediapipe repo and the command to build the data set must be
run from the same directory.
```
bazel -c opt mediapipe/examples/desktop/media_sequence:media_sequence_demo \
--define=MEDIAPIPE_DISABLE_GPU=1
python -m mediapipe.examples.desktop.media_sequence.demo_dataset \
--alsologtostderr \
--path_to_demo_data=/tmp/demo_data/ \
--path_to_mediapipe_binary=bazel-bin/mediapipe/examples/desktop/\
media_sequence/media_sequence_demo \
--path_to_graph_directory=mediapipe/graphs/media_sequence/
```
### Charades data set
The Charades data set is ready for training and/or evaluating action recognition
models in TensorFlow. You may only use this script in ways that comply with the
Allen Institute for Artificial Intelligence's [license for the Charades data
set.](https://allenai.org/plato/charades/license.txt)
To generate the Charades dataset you must have Tensorflow [version >= 1.19]
installed. Then the media_sequence_demo binary must be built from the top
directory in the mediapipe repo and the command to build the data set must be
run from the same directory.
```
bazel -c opt mediapipe/examples/desktop/media_sequence:media_sequence_demo \
--define=MEDIAPIPE_DISABLE_GPU=1
python -m mediapipe.examples.desktop.media_sequence.charades_dataset \
--alsologtostderr \
--path_to_charades_data=/tmp/charades_data/ \
--path_to_mediapipe_binary=bazel-bin/mediapipe/examples/desktop/\
media_sequence/media_sequence_demo \
--path_to_graph_directory=mediapipe/graphs/media_sequence/
```
@@ -0,0 +1,14 @@
"""Copyright 2019 The MediaPipe Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
"""
@@ -0,0 +1,517 @@
r"""Copyright 2019 The MediaPipe Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
Code to download and parse the Charades dataset for TensorFlow models.
The [Charades data set](https://allenai.org/plato/charades/) is a data set of
human action recognition collected with and maintained by the Allen Institute
for Artificial Intelligence. This script downloads and prepares the data set for
training a TensorFlow model. To use this script, you must abide by the
[lincense](https://allenai.org/plato/charades/license.txt) for the Charades data
set provided by the Allen Institute. The license for this script only covers
this code and not the data set.
Running this code as a module generates the data set on disk. First, the
required files are downloaded (_download_data). Then, for each split in the
data set (generate_examples), the metadata is generated from the annotations for
each example (_generate_metadata), and MediaPipe is used to fill in the video
frames (_run_mediapipe). The data set is written to disk as a set of numbered
TFRecord files. If the download is disrupted, the incomplete files will need to
be removed before running the script again. This pattern can be reproduced and
modified to generate most video data sets.
Generating the data on disk will probably take 4-8 hours and requires 150 GB of
disk space. (Image compression quality is the primary determiner of disk usage.)
After generating the data, the 30 GB of compressed video data can be deleted.
Once the data is on disk, reading the data as a tf.data.Dataset is accomplished
with the following lines:
charades = CharadesDataset("charades_data_path")
dataset = charades.as_dataset("test")
# implement additional processing and batching here
images_and_labels = dataset.make_one_shot_iterator().get_next()
images = images_and_labels["images"]
labels = image_and_labels["classification_target"]
label_weights = image_and_labels["indicator_matrix"]
This data is structured for per-frame action classification where images is
the sequence of images, labels are the sequence of classification targets and,
label_weights is 1 for valid frames and 0 for padded frames (if any). See
as_dataset() for more details.
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import contextlib
import csv
import os
import random
import subprocess
import sys
import tempfile
import urllib
import zipfile
from absl import app
from absl import flags
from absl import logging
import tensorflow as tf
from mediapipe.util.sequence import media_sequence as ms
DATA_URL_ANNOTATIONS = "http://ai2-website.s3.amazonaws.com/data/Charades.zip"
DATA_URL_VIDEOS = "http://ai2-website.s3.amazonaws.com/data/Charades_v1_480.zip"
DATA_URL_LICENSE = "https://allenai.org/plato/charades/license.txt"
CITATION = r"""@article{sigurdsson2016hollywood,
author = {Gunnar A. Sigurdsson and G{\"u}l Varol and Xiaolong Wang and Ivan Laptev and Ali Farhadi and Abhinav Gupta},
title = {Hollywood in Homes: Crowdsourcing Data Collection for Activity Understanding},
journal = {ArXiv e-prints},
eprint = {1604.01753},
year = {2016},
url = {http://arxiv.org/abs/1604.01753},
}"""
SECONDS_TO_MICROSECONDS = 1000000
GRAPHS = ["clipped_images_from_file_at_24fps.pbtxt"]
SPLITS = {
"train": ("charades_v1_train_records", # base name for sharded files
"Charades_v1_train.csv", # path to csv of annotations
1000, # number of shards
7986), # number of examples
"test": ("charades_v1_test_records",
"Charades_v1_test.csv",
100,
1864),
}
NUM_CLASSES = 157
CLASS_LABEL_OFFSET = 1
class Charades(object):
"""Generates and loads the Charades data set."""
def __init__(self, path_to_data):
if not path_to_data:
raise ValueError("You must supply the path to the data directory.")
self.path_to_data = path_to_data
def as_dataset(self, split, shuffle=False, repeat=False,
serialized_prefetch_size=32, decoded_prefetch_size=32):
"""Returns Charades as a tf.data.Dataset.
After running this function, calling padded_batch() on the Dataset object
will produce batches of data, but additional preprocessing may be desired.
If using padded_batch, the indicator_matrix output distinguishes valid
from padded frames.
Args:
split: either "train" or "test"
shuffle: if true, shuffles both files and examples.
repeat: if true, repeats the data set forever.
serialized_prefetch_size: the buffer size for reading from disk.
decoded_prefetch_size: the buffer size after decoding.
Returns:
A tf.data.Dataset object with the following structure: {
"images": uint8 tensor, shape [time, height, width, channels]
"segment_matrix": binary tensor of segments, shape [time, num_segments].
See one_hot_segments() for details.
"indicator_matrix": binary tensor indicating valid frames,
shape [time, 1]. If padded with zeros to align sizes, the indicator
marks where segments is valid.
"classification_target": binary tensor of classification targets,
shape [time, 158 classes]. More than one value in a row can be 1.0 if
segments overlap.
"example_id": a unique string id for each example, shape [].
"sampling_rate": the frame rate for each sequence, shape [].
"gt_segment_seconds": the start and end time of each segment,
shape [num_segments, 2].
"gt_segment_classes": the class labels for each segment,
shape [num_segments].
"num_segments": the number of segments in the example, shape [].
"num_timesteps": the number of timesteps in the example, shape [].
"images": the [time, height, width, channels] tensor of images.
"""
def parse_fn(sequence_example):
"""Parses a Charades example."""
context_features = {
ms.get_example_id_key(): ms.get_example_id_default_parser(),
ms.get_segment_start_index_key(): (
ms.get_segment_start_index_default_parser()),
ms.get_segment_end_index_key(): (
ms.get_segment_end_index_default_parser()),
ms.get_segment_label_index_key(): (
ms.get_segment_label_index_default_parser()),
ms.get_segment_label_string_key(): (
ms.get_segment_label_string_default_parser()),
ms.get_segment_start_timestamp_key(): (
ms.get_segment_start_timestamp_default_parser()),
ms.get_segment_end_timestamp_key(): (
ms.get_segment_end_timestamp_default_parser()),
ms.get_image_frame_rate_key(): (
ms.get_image_frame_rate_default_parser()),
}
sequence_features = {
ms.get_image_encoded_key(): ms.get_image_encoded_default_parser()
}
parsed_context, parsed_sequence = tf.io.parse_single_sequence_example(
sequence_example, context_features, sequence_features)
sequence_length = tf.shape(parsed_sequence[ms.get_image_encoded_key()])[0]
num_segments = tf.shape(
parsed_context[ms.get_segment_label_index_key()])[0]
# segments matrix and targets for training.
segments_matrix, indicator = one_hot_segments(
tf.sparse_tensor_to_dense(
parsed_context[ms.get_segment_start_index_key()]),
tf.sparse_tensor_to_dense(
parsed_context[ms.get_segment_end_index_key()]),
sequence_length)
classification_target = timepoint_classification_target(
segments_matrix,
tf.sparse_tensor_to_dense(
parsed_context[ms.get_segment_label_index_key()]
) + CLASS_LABEL_OFFSET,
NUM_CLASSES + CLASS_LABEL_OFFSET)
# [segments, 2] start and end time in seconds.
gt_segment_seconds = tf.to_float(tf.concat(
[tf.expand_dims(tf.sparse_tensor_to_dense(parsed_context[
ms.get_segment_start_timestamp_key()]), 1),
tf.expand_dims(tf.sparse_tensor_to_dense(parsed_context[
ms.get_segment_end_timestamp_key()]), 1)],
1)) / float(SECONDS_TO_MICROSECONDS)
gt_segment_classes = tf.sparse_tensor_to_dense(parsed_context[
ms.get_segment_label_index_key()]) + CLASS_LABEL_OFFSET
example_id = parsed_context[ms.get_example_id_key()]
sampling_rate = parsed_context[ms.get_image_frame_rate_key()]
images = tf.map_fn(tf.image.decode_jpeg,
parsed_sequence[ms.get_image_encoded_key()],
back_prop=False,
dtype=tf.uint8)
output_dict = {
"segment_matrix": segments_matrix,
"indicator_matrix": indicator,
"classification_target": classification_target,
"example_id": example_id,
"sampling_rate": sampling_rate,
"gt_segment_seconds": gt_segment_seconds,
"gt_segment_classes": gt_segment_classes,
"num_segments": num_segments,
"num_timesteps": sequence_length,
"images": images,
}
return output_dict
if split not in SPLITS:
raise ValueError("Split %s not in %s" % split, str(SPLITS.keys()))
all_shards = tf.io.gfile.glob(
os.path.join(self.path_to_data, SPLITS[split][0] + "-*-of-*"))
random.shuffle(all_shards)
all_shards_dataset = tf.data.Dataset.from_tensor_slices(all_shards)
cycle_length = min(16, len(all_shards))
dataset = all_shards_dataset.apply(
tf.contrib.data.parallel_interleave(
tf.data.TFRecordDataset,
cycle_length=cycle_length,
block_length=1, sloppy=True,
buffer_output_elements=serialized_prefetch_size))
dataset = dataset.prefetch(serialized_prefetch_size)
if shuffle:
dataset = dataset.shuffle(serialized_prefetch_size)
if repeat:
dataset = dataset.repeat()
dataset = dataset.map(parse_fn)
dataset = dataset.prefetch(decoded_prefetch_size)
return dataset
def generate_examples(self,
path_to_mediapipe_binary, path_to_graph_directory):
"""Downloads data and generates sharded TFRecords.
Downloads the data files, generates metadata, and processes the metadata
with MediaPipe to produce tf.SequenceExamples for training. The resulting
files can be read with as_dataset(). After running this function the
original data files can be deleted.
Args:
path_to_mediapipe_binary: Path to the compiled binary for the BUILD target
mediapipe/examples/desktop/demo:media_sequence_demo.
path_to_graph_directory: Path to the directory with MediaPipe graphs in
mediapipe/graphs/media_sequence/.
"""
if not path_to_mediapipe_binary:
raise ValueError(
"You must supply the path to the MediaPipe binary for "
"mediapipe/examples/desktop/demo:media_sequence_demo.")
if not path_to_graph_directory:
raise ValueError(
"You must supply the path to the directory with MediaPipe graphs in "
"mediapipe/graphs/media_sequence/.")
logging.info("Downloading data.")
annotation_dir, video_dir = self._download_data()
for name, annotations, shards, _ in SPLITS.values():
annotation_file = os.path.join(
annotation_dir, annotations)
logging.info("Generating metadata for split: %s", name)
all_metadata = list(self._generate_metadata(annotation_file, video_dir))
random.seed(47)
random.shuffle(all_metadata)
shard_names = [os.path.join(self.path_to_data, name + "-%05d-of-%05d" % (
i, shards)) for i in range(shards)]
writers = [tf.io.TFRecordWriter(shard_name) for shard_name in shard_names]
with _close_on_exit(writers) as writers:
for i, seq_ex in enumerate(all_metadata):
print("Processing example %d of %d (%d%%) \r" % (
i, len(all_metadata), i * 100 / len(all_metadata)), end="")
for graph in GRAPHS:
graph_path = os.path.join(path_to_graph_directory, graph)
seq_ex = self._run_mediapipe(
path_to_mediapipe_binary, seq_ex, graph_path)
writers[i % len(writers)].write(seq_ex.SerializeToString())
logging.info("Data extraction complete.")
def _generate_metadata(self, annotations_file, video_dir):
"""For each row in the annotation CSV, generates the corresponding metadata.
Args:
annotations_file: path to the file of Charades CSV annotations.
video_dir: path to the directory of video files referenced by the
annotations.
Yields:
Each tf.SequenceExample of metadata, ready to pass to MediaPipe.
"""
with open(annotations_file, "r") as annotations:
reader = csv.DictReader(annotations)
for row in reader:
metadata = tf.train.SequenceExample()
filepath = os.path.join(video_dir, "%s.mp4" % row["id"])
actions = row["actions"].split(";")
action_indices = []
action_strings = []
action_start_times = []
action_end_times = []
for action in actions:
if not action:
continue
string, start, end = action.split(" ")
action_indices.append(int(string[1:]))
action_strings.append(bytes23(string))
action_start_times.append(int(float(start) * SECONDS_TO_MICROSECONDS))
action_end_times.append(int(float(end) * SECONDS_TO_MICROSECONDS))
ms.set_example_id(bytes23(row["id"]), metadata)
ms.set_clip_data_path(bytes23(filepath), metadata)
ms.set_clip_start_timestamp(0, metadata)
ms.set_clip_end_timestamp(
int(float(row["length"]) * SECONDS_TO_MICROSECONDS), metadata)
ms.set_segment_start_timestamp(action_start_times, metadata)
ms.set_segment_end_timestamp(action_end_times, metadata)
ms.set_segment_label_string(action_strings, metadata)
ms.set_segment_label_index(action_indices, metadata)
yield metadata
def _download_data(self):
"""Downloads and extracts data if not already available."""
if sys.version_info >= (3, 0):
urlretrieve = urllib.request.urlretrieve
else:
urlretrieve = urllib.urlretrieve
logging.info("Creating data directory.")
tf.io.gfile.makedirs(self.path_to_data)
logging.info("Downloading license.")
local_license_path = os.path.join(
self.path_to_data, DATA_URL_LICENSE.split("/")[-1])
if not tf.io.gfile.exists(local_license_path):
urlretrieve(DATA_URL_LICENSE, local_license_path)
logging.info("Downloading annotations.")
local_annotations_path = os.path.join(
self.path_to_data, DATA_URL_ANNOTATIONS.split("/")[-1])
if not tf.io.gfile.exists(local_annotations_path):
urlretrieve(DATA_URL_ANNOTATIONS, local_annotations_path)
logging.info("Downloading videos.")
local_videos_path = os.path.join(
self.path_to_data, DATA_URL_VIDEOS.split("/")[-1])
if not tf.io.gfile.exists(local_videos_path):
urlretrieve(DATA_URL_VIDEOS, local_videos_path, progress_hook)
logging.info("Extracting annotations.")
# return video dir and annotation_dir by removing .zip from the path.
annotations_dir = local_annotations_path[:-4]
if not tf.io.gfile.exists(annotations_dir):
with zipfile.ZipFile(local_annotations_path) as annotations_zip:
annotations_zip.extractall(self.path_to_data)
logging.info("Extracting videos.")
video_dir = local_videos_path[:-4]
if not tf.io.gfile.exists(video_dir):
with zipfile.ZipFile(local_videos_path) as videos_zip:
videos_zip.extractall(self.path_to_data)
return annotations_dir, video_dir
def _run_mediapipe(self, path_to_mediapipe_binary, sequence_example, graph):
"""Runs MediaPipe over MediaSequence tf.train.SequenceExamples.
Args:
path_to_mediapipe_binary: Path to the compiled binary for the BUILD target
mediapipe/examples/desktop/demo:media_sequence_demo.
sequence_example: The SequenceExample with metadata or partial data file.
graph: The path to the graph that extracts data to add to the
SequenceExample.
Returns:
A copy of the input SequenceExample with additional data fields added
by the MediaPipe graph.
Raises:
RuntimeError: if MediaPipe returns an error or fails to run the graph.
"""
if not path_to_mediapipe_binary:
raise ValueError("--path_to_mediapipe_binary must be specified.")
input_fd, input_filename = tempfile.mkstemp()
output_fd, output_filename = tempfile.mkstemp()
cmd = [path_to_mediapipe_binary,
"--calculator_graph_config_file=%s" % graph,
"--input_side_packets=input_sequence_example=%s" % input_filename,
"--output_side_packets=output_sequence_example=%s" % output_filename]
with open(input_filename, "wb") as input_file:
input_file.write(sequence_example.SerializeToString())
mediapipe_output = subprocess.check_output(cmd)
if b"Failed to run the graph" in mediapipe_output:
raise RuntimeError(mediapipe_output)
with open(output_filename, "rb") as output_file:
output_example = tf.train.SequenceExample()
output_example.ParseFromString(output_file.read())
os.close(input_fd)
os.remove(input_filename)
os.close(output_fd)
os.remove(output_filename)
return output_example
def one_hot_segments(start_indices, end_indices, num_samples):
"""Returns a one-hot, float matrix of segments at each timestep.
All integers in the inclusive range of start_indices and end_indices are used.
This allows start and end timestamps to be mapped to the same index and the
segment will not be omitted.
Args:
start_indices: a 1d tensor of integer indices for the start of each
segement.
end_indices: a tensor of integer indices for the end of each segment.
Must be the same shape as start_indices. Values should be >= start_indices
but not strictly enforced.
num_samples: the number of rows in the output. Indices should be <
num_samples, but this is not strictly enforced.
Returns:
(segments, indicator)
segments: A [num_samples, num_elements(start_indices)] tensor where in each
column the rows with indices >= start_indices[column] and
<= end_indices[column] are 1.0 and all other values are 0.0.
indicator: a tensor of 1.0 values with shape [num_samples, 1]. If padded
with zeros to align sizes, the indicator marks where segments is valid.
"""
start_indices = tf.convert_to_tensor(start_indices)
end_indices = tf.convert_to_tensor(end_indices)
start_indices.shape.assert_is_compatible_with(end_indices.shape)
start_indices.shape.assert_has_rank(1)
end_indices.shape.assert_has_rank(1)
# create a matrix of the index at each row with a column per segment.
indices = tf.to_int64(
tf.tile(
tf.transpose(tf.expand_dims(tf.range(num_samples), 0)),
[1, tf.shape(start_indices)[0]]))
# switch to one hot encoding of segments (includes start and end indices)
segments = tf.to_float(
tf.logical_and(
tf.greater_equal(indices, start_indices),
tf.less_equal(indices, end_indices)))
# create a tensors of ones everywhere there's an annotation. If padded with
# zeros later, element-wise multiplication of the loss will mask out the
# padding.
indicator = tf.ones(shape=[num_samples, 1], dtype=tf.float32)
return segments, indicator
def timepoint_classification_target(segments, segment_classes, num_classes):
"""Produces a classification target at each timepoint.
If no segments are present at a time point, the first class is set to 1.0.
This should be used as a background class unless segments are always present.
Args:
segments: a [time, num_segments] tensor that is 1.0 at indices within
each segment and 0.0 elsewhere.
segment_classes: a [num_segments] tensor with the class index of each
segment.
num_classes: the number of classes (must be >= max(segment_classes) + 1)
Returns:
a [time, num_classes] tensor. In the final output, more than one
value in a row can be 1.0 if segments overlap.
"""
num_segments = tf.shape(segments)[1]
matrix_of_class_indices = tf.to_int32(
segments * tf.to_float(tf.expand_dims(segment_classes, 0)))
# First column will have one count per zero segment. Correct this to be 0
# unless no segments are present.
one_hot = tf.reduce_sum(tf.one_hot(matrix_of_class_indices, num_classes), 1)
normalizer = tf.concat([
tf.ones(shape=[1, 1], dtype=tf.float32) / tf.to_float(num_segments),
tf.ones(shape=[1, num_classes - 1], dtype=tf.float32)
], 1)
corrected_one_hot = tf.floor(one_hot * normalizer)
return corrected_one_hot
def progress_hook(blocks, block_size, total_size):
print("Downloaded %d%% of %d bytes (%d blocks)\r" % (
blocks * block_size / total_size * 100, total_size, blocks), end="")
def bytes23(string):
"""Creates a bytes string in either Python 2 or 3."""
if sys.version_info >= (3, 0):
return bytes(string, "utf8")
else:
return bytes(string)
@contextlib.contextmanager
def _close_on_exit(writers):
"""Call close on all writers on exit."""
try:
yield writers
finally:
for writer in writers:
writer.close()
def main(argv):
if len(argv) > 1:
raise app.UsageError("Too many command-line arguments.")
Charades(flags.FLAGS.path_to_charades_data).generate_examples(
flags.FLAGS.path_to_mediapipe_binary,
flags.FLAGS.path_to_graph_directory)
if __name__ == "__main__":
flags.DEFINE_string("path_to_charades_data",
"",
"Path to directory to write data to.")
flags.DEFINE_string("path_to_mediapipe_binary",
"",
"Path to the MediaPipe run_graph_file_io_main binary.")
flags.DEFINE_string("path_to_graph_directory",
"",
"Path to directory containing the graph files.")
app.run(main)
@@ -0,0 +1,317 @@
r"""Copyright 2019 The MediaPipe Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
A demo data set constructed with MediaSequence and MediaPipe.
This code demonstrates the steps for constructing a data set with MediaSequence.
This code has two functions. First, it can be run as a module to download and
prepare a toy dataset. Second, it can be imported and used to provide a
tf.data.Dataset reading that data from disk via as_dataset().
Running as a module prepares the data in three stages via generate_examples().
First, the actual data files are downloaded. If the download is disrupted, the
incomplete files will need to be removed before running the script again.
Second, the annotations are parsed and reformated into metadata as described in
the MediaSequence documentation. Third, MediaPipe is run to extract subsequences
of frames for subsequent training via _run_mediapipe().
The toy data set is classifying a clip as a panning shot of galaxy or nebula
from videos releasued under the [Creative Commons Attribution 4.0 International
license](http://creativecommons.org/licenses/by/4.0/) on the ESA/Hubble site.
(The use of these ESA/Hubble materials does not imply the endorsement by
ESA/Hubble or any ESA/Hubble employee of a commercial product or service.) Each
video is split into 5 or 6 ten-second clips with a label of "galaxy" or "nebula"
and downsampled to 10 frames per second. (The last clip for each test example is
only 6 seconds.) There is one video of each class in each of the training and
testing splits.
Reading the data as a tf.data.Dataset is accomplished with the following lines:
demo = DemoDataset("demo_data_path")
dataset = demo.as_dataset("test")
# implement additional processing and batching here
images_and_labels = dataset.make_one_shot_iterator().get_next()
images = images_and_labels["images"]
labels = image_and_labels["labels"]
"""
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
import contextlib
import csv
import os
import random
import subprocess
import sys
import tempfile
import urllib
from absl import app
from absl import flags
from absl import logging
import tensorflow as tf
from mediapipe.util.sequence import media_sequence as ms
SPLITS = {
"train":
"""url,label index,label string,duration,credits
https://cdn.spacetelescope.org/archives/videos/medium_podcast/heic1608c.mp4,0,nebula,50,"ESA/Hubble; Music: Johan B. Monell"
https://cdn.spacetelescope.org/archives/videos/medium_podcast/heic1712b.mp4,1,galaxy,50,"ESA/Hubble, Digitized Sky Survey, Nick Risinger (skysurvey.org) Music: Johan B Monell"
""",
"test":
"""url,label index,label string,duration,credits
https://cdn.spacetelescope.org/archives/videos/medium_podcast/heic1301b.m4v,0,nebula,56,"NASA, ESA. Acknowledgement: Josh Lake"
https://cdn.spacetelescope.org/archives/videos/medium_podcast/heic1305b.m4v,1,galaxy,56,"NASA, ESA, Digitized Sky Survey 2. Acknowledgement: A. van der Hoeven"
"""
}
NUM_CLASSES = 2
NUM_SHARDS = 2
SECONDS_PER_EXAMPLE = 10
MICROSECONDS_PER_SECOND = 1000000
TF_RECORD_PATTERN = "demo_space_dataset_%s_tfrecord"
GRAPHS = ["clipped_images_from_file_at_24fps.pbtxt"]
class DemoDataset(object):
"""Generates and loads a demo data set."""
def __init__(self, path_to_data):
if not path_to_data:
raise ValueError("You must supply the path to the data directory.")
self.path_to_data = path_to_data
def as_dataset(self,
split,
shuffle=False,
repeat=False,
serialized_prefetch_size=32,
decoded_prefetch_size=32):
"""Returns the dataset as a tf.data.Dataset.
Args:
split: either "train" or "test"
shuffle: if true, shuffles both files and examples.
repeat: if true, repeats the data set forever.
serialized_prefetch_size: the buffer size for reading from disk.
decoded_prefetch_size: the buffer size after decoding.
Returns:
A tf.data.Dataset object with the following structure: {
"images": uint8 tensor, shape [time, height, width, channels]
"labels": one hot encoded label tensor, shape [2]
"id": a unique string id for each example, shape []
}
"""
def parse_fn(sequence_example):
"""Parses a clip classification example."""
context_features = {
ms.get_example_id_key():
ms.get_example_id_default_parser(),
ms.get_clip_label_index_key():
ms.get_clip_label_index_default_parser(),
ms.get_clip_label_string_key():
ms.get_clip_label_string_default_parser()
}
sequence_features = {
ms.get_image_encoded_key(): ms.get_image_encoded_default_parser(),
}
parsed_context, parsed_sequence = tf.io.parse_single_sequence_example(
sequence_example, context_features, sequence_features)
example_id = parsed_context[ms.get_example_id_key()]
classification_target = tf.one_hot(
tf.sparse_tensor_to_dense(
parsed_context[ms.get_clip_label_index_key()]), NUM_CLASSES)
images = tf.map_fn(
tf.image.decode_jpeg,
parsed_sequence[ms.get_image_encoded_key()],
back_prop=False,
dtype=tf.uint8)
return {
"id": example_id,
"labels": classification_target,
"images": images,
}
if split not in SPLITS:
raise ValueError("split '%s' is unknown." % split)
all_shards = tf.io.gfile.glob(
os.path.join(self.path_to_data, TF_RECORD_PATTERN % split + "-*-of-*"))
if shuffle:
random.shuffle(all_shards)
all_shards_dataset = tf.data.Dataset.from_tensor_slices(all_shards)
cycle_length = min(16, len(all_shards))
dataset = all_shards_dataset.apply(
tf.contrib.data.parallel_interleave(
tf.data.TFRecordDataset,
cycle_length=cycle_length,
block_length=1,
sloppy=True,
buffer_output_elements=serialized_prefetch_size))
dataset = dataset.prefetch(serialized_prefetch_size)
if shuffle:
dataset = dataset.shuffle(serialized_prefetch_size)
if repeat:
dataset = dataset.repeat()
dataset = dataset.map(parse_fn)
dataset = dataset.prefetch(decoded_prefetch_size)
return dataset
def generate_examples(self, path_to_mediapipe_binary,
path_to_graph_directory):
"""Downloads data and generates sharded TFRecords.
Downloads the data files, generates metadata, and processes the metadata
with MediaPipe to produce tf.SequenceExamples for training. The resulting
files can be read with as_dataset(). After running this function the
original data files can be deleted.
Args:
path_to_mediapipe_binary: Path to the compiled binary for the BUILD target
mediapipe/examples/desktop/demo:media_sequence_demo.
path_to_graph_directory: Path to the directory with MediaPipe graphs in
mediapipe/graphs/media_sequence/.
"""
if not path_to_mediapipe_binary:
raise ValueError("You must supply the path to the MediaPipe binary for "
"mediapipe/examples/desktop/demo:media_sequence_demo.")
if not path_to_graph_directory:
raise ValueError(
"You must supply the path to the directory with MediaPipe graphs in "
"mediapipe/graphs/media_sequence/.")
logging.info("Downloading data.")
tf.io.gfile.makedirs(self.path_to_data)
if sys.version_info >= (3, 0):
urlretrieve = urllib.request.urlretrieve
else:
urlretrieve = urllib.urlretrieve
for split in SPLITS:
reader = csv.DictReader(SPLITS[split].split("\n"))
all_metadata = []
for row in reader:
url = row["url"]
basename = url.split("/")[-1]
local_path = os.path.join(self.path_to_data, basename)
if not tf.io.gfile.exists(local_path):
urlretrieve(url, local_path)
for start_time in range(0, int(row["duration"]), SECONDS_PER_EXAMPLE):
metadata = tf.train.SequenceExample()
ms.set_example_id(bytes23(basename + "_" + str(start_time)),
metadata)
ms.set_clip_data_path(bytes23(local_path), metadata)
ms.set_clip_start_timestamp(start_time * MICROSECONDS_PER_SECOND,
metadata)
ms.set_clip_end_timestamp(
(start_time + SECONDS_PER_EXAMPLE) * MICROSECONDS_PER_SECOND,
metadata)
ms.set_clip_label_index((int(row["label index"]),), metadata)
ms.set_clip_label_string((bytes23(row["label string"]),),
metadata)
all_metadata.append(metadata)
random.seed(47)
random.shuffle(all_metadata)
shard_names = [self._indexed_shard(split, i) for i in range(NUM_SHARDS)]
writers = [tf.io.TFRecordWriter(shard_name) for shard_name in shard_names]
with _close_on_exit(writers) as writers:
for i, seq_ex in enumerate(all_metadata):
for graph in GRAPHS:
graph_path = os.path.join(path_to_graph_directory, graph)
seq_ex = self._run_mediapipe(path_to_mediapipe_binary, seq_ex,
graph_path)
writers[i % len(writers)].write(seq_ex.SerializeToString())
def _indexed_shard(self, split, index):
"""Constructs a sharded filename."""
return os.path.join(
self.path_to_data,
TF_RECORD_PATTERN % split + "-%05d-of-%05d" % (index, NUM_SHARDS))
def _run_mediapipe(self, path_to_mediapipe_binary, sequence_example, graph):
"""Runs MediaPipe over MediaSequence tf.train.SequenceExamples.
Args:
path_to_mediapipe_binary: Path to the compiled binary for the BUILD target
mediapipe/examples/desktop/demo:media_sequence_demo.
sequence_example: The SequenceExample with metadata or partial data file.
graph: The path to the graph that extracts data to add to the
SequenceExample.
Returns:
A copy of the input SequenceExample with additional data fields added
by the MediaPipe graph.
Raises:
RuntimeError: if MediaPipe returns an error or fails to run the graph.
"""
if not path_to_mediapipe_binary:
raise ValueError("--path_to_mediapipe_binary must be specified.")
input_fd, input_filename = tempfile.mkstemp()
output_fd, output_filename = tempfile.mkstemp()
cmd = [
path_to_mediapipe_binary,
"--calculator_graph_config_file=%s" % graph,
"--input_side_packets=input_sequence_example=%s" % input_filename,
"--output_side_packets=output_sequence_example=%s" % output_filename
]
with open(input_filename, "wb") as input_file:
input_file.write(sequence_example.SerializeToString())
mediapipe_output = subprocess.check_output(cmd)
if b"Failed to run the graph" in mediapipe_output:
raise RuntimeError(mediapipe_output)
with open(output_filename, "rb") as output_file:
output_example = tf.train.SequenceExample()
output_example.ParseFromString(output_file.read())
os.close(input_fd)
os.remove(input_filename)
os.close(output_fd)
os.remove(output_filename)
return output_example
def bytes23(string):
"""Creates a bytes string in either Python 2 or 3."""
if sys.version_info >= (3, 0):
return bytes(string, "utf8")
else:
return bytes(string)
@contextlib.contextmanager
def _close_on_exit(writers):
"""Call close on all writers on exit."""
try:
yield writers
finally:
for writer in writers:
writer.close()
def main(argv):
if len(argv) > 1:
raise app.UsageError("Too many command-line arguments.")
DemoDataset(flags.FLAGS.path_to_demo_data).generate_examples(
flags.FLAGS.path_to_mediapipe_binary, flags.FLAGS.path_to_graph_directory)
if __name__ == "__main__":
flags.DEFINE_string("path_to_demo_data", "",
"Path to directory to write data to.")
flags.DEFINE_string("path_to_mediapipe_binary", "",
"Path to the MediaPipe run_graph_file_io_main binary.")
flags.DEFINE_string("path_to_graph_directory", "",
"Path to directory containing the graph files.")
app.run(main)
@@ -0,0 +1,93 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// A simple main function to run a MediaPipe graph. Input side packets are read
// from files provided via the command line and output side packets are written
// to disk.
#include "absl/strings/str_split.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/commandlineflags.h"
#include "mediapipe/framework/port/file_helpers.h"
#include "mediapipe/framework/port/map_util.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/status.h"
DEFINE_string(
calculator_graph_config_file, "",
"Name of file containing text format CalculatorGraphConfig proto.");
DEFINE_string(input_side_packets, "",
"Comma-separated list of key=value pairs specifying side packets "
"and corresponding file paths for the CalculatorGraph. The side "
"packets are read from the files and fed to the graph as strings "
"even if they represent doubles, floats, etc.");
DEFINE_string(output_side_packets, "",
"Comma-separated list of key=value pairs specifying the output "
"side packets and paths to write to disk for the "
"CalculatorGraph.");
::mediapipe::Status RunMediaPipeGraph() {
std::string calculator_graph_config_contents;
RETURN_IF_ERROR(mediapipe::file::GetContents(
FLAGS_calculator_graph_config_file, &calculator_graph_config_contents));
LOG(INFO) << "Get calculator graph config contents: "
<< calculator_graph_config_contents;
mediapipe::CalculatorGraphConfig config =
mediapipe::ParseTextProtoOrDie<mediapipe::CalculatorGraphConfig>(
calculator_graph_config_contents);
std::map<std::string, ::mediapipe::Packet> input_side_packets;
std::vector<std::string> kv_pairs =
absl::StrSplit(FLAGS_input_side_packets, ',');
for (const std::string& kv_pair : kv_pairs) {
std::vector<std::string> name_and_value = absl::StrSplit(kv_pair, '=');
RET_CHECK(name_and_value.size() == 2);
RET_CHECK(!::mediapipe::ContainsKey(input_side_packets, name_and_value[0]));
std::string input_side_packet_contents;
RETURN_IF_ERROR(mediapipe::file::GetContents(name_and_value[1],
&input_side_packet_contents));
input_side_packets[name_and_value[0]] =
::mediapipe::MakePacket<std::string>(input_side_packet_contents);
}
LOG(INFO) << "Initialize the calculator graph.";
mediapipe::CalculatorGraph graph;
RETURN_IF_ERROR(graph.Initialize(config, input_side_packets));
LOG(INFO) << "Start running the calculator graph.";
RETURN_IF_ERROR(graph.Run());
LOG(INFO) << "Gathering output side packets.";
kv_pairs = absl::StrSplit(FLAGS_output_side_packets, ',');
for (const std::string& kv_pair : kv_pairs) {
std::vector<std::string> name_and_value = absl::StrSplit(kv_pair, '=');
RET_CHECK(name_and_value.size() == 2);
::mediapipe::StatusOr<::mediapipe::Packet> output_packet =
graph.GetOutputSidePacket(name_and_value[0]);
RET_CHECK(output_packet.ok())
<< "Packet " << name_and_value[0] << " was not available.";
const std::string& serialized_string =
output_packet.ValueOrDie().Get<std::string>();
RETURN_IF_ERROR(
mediapipe::file::SetContents(name_and_value[1], serialized_string));
}
return ::mediapipe::OkStatus();
}
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
::mediapipe::Status run_status = RunMediaPipeGraph();
if (!run_status.ok()) {
LOG(ERROR) << "Failed to run the graph: " << run_status.message();
} else {
LOG(INFO) << "Success!";
}
return 0;
}
@@ -0,0 +1,70 @@
# Copyright 2019 The MediaPipe Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
licenses(["notice"]) # Apache 2.0
package(default_visibility = ["//mediapipe/examples:__subpackages__"])
cc_library(
name = "object_detection_tensorflow_deps",
deps = [
"@org_tensorflow//tensorflow/c/kernels:bitcast_op",
"@org_tensorflow//tensorflow/core:direct_session",
"@org_tensorflow//tensorflow/core/kernels:argmax_op",
"@org_tensorflow//tensorflow/core/kernels:bias_op",
"@org_tensorflow//tensorflow/core/kernels:cast_op",
"@org_tensorflow//tensorflow/core/kernels:concat_op",
"@org_tensorflow//tensorflow/core/kernels:constant_op",
"@org_tensorflow//tensorflow/core/kernels:control_flow_ops",
"@org_tensorflow//tensorflow/core/kernels:conv_ops",
"@org_tensorflow//tensorflow/core/kernels:cwise_op",
"@org_tensorflow//tensorflow/core/kernels:depthwise_conv_op",
"@org_tensorflow//tensorflow/core/kernels:fused_batch_norm_op",
"@org_tensorflow//tensorflow/core/kernels:gather_op",
"@org_tensorflow//tensorflow/core/kernels:identity_op",
"@org_tensorflow//tensorflow/core/kernels:matmul_op",
"@org_tensorflow//tensorflow/core/kernels:non_max_suppression_op",
"@org_tensorflow//tensorflow/core/kernels:pack_op",
"@org_tensorflow//tensorflow/core/kernels:reduction_ops",
"@org_tensorflow//tensorflow/core/kernels:relu_op",
"@org_tensorflow//tensorflow/core/kernels:reshape_op",
"@org_tensorflow//tensorflow/core/kernels:resize_bilinear_op",
"@org_tensorflow//tensorflow/core/kernels:sequence_ops",
"@org_tensorflow//tensorflow/core/kernels:shape_ops",
"@org_tensorflow//tensorflow/core/kernels:slice_op",
"@org_tensorflow//tensorflow/core/kernels:split_op",
"@org_tensorflow//tensorflow/core/kernels:tensor_array_ops",
"@org_tensorflow//tensorflow/core/kernels:tile_ops",
"@org_tensorflow//tensorflow/core/kernels:topk_op",
"@org_tensorflow//tensorflow/core/kernels:transpose_op",
"@org_tensorflow//tensorflow/core/kernels:unpack_op",
],
)
cc_binary(
name = "object_detection_tensorflow",
deps = [
":object_detection_tensorflow_deps",
"//mediapipe/examples/desktop:simple_run_graph_main",
"//mediapipe/graphs/object_detection:desktop_tensorflow_calculators",
],
)
cc_binary(
name = "object_detection_tflite",
deps = [
"//mediapipe/examples/desktop:simple_run_graph_main",
"//mediapipe/graphs/object_detection:desktop_tflite_calculators",
],
)
@@ -0,0 +1,69 @@
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// A simple main function to run a MediaPipe graph.
#include "absl/strings/str_split.h"
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/port/commandlineflags.h"
#include "mediapipe/framework/port/file_helpers.h"
#include "mediapipe/framework/port/map_util.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/status.h"
DEFINE_string(
calculator_graph_config_file, "",
"Name of file containing text format CalculatorGraphConfig proto.");
DEFINE_string(input_side_packets, "",
"Comma-separated list of key=value pairs specifying side packets "
"for the CalculatorGraph. All values will be treated as the "
"string type even if they represent doubles, floats, etc.");
::mediapipe::Status RunMediaPipeGraph() {
std::string calculator_graph_config_contents;
RETURN_IF_ERROR(mediapipe::file::GetContents(
FLAGS_calculator_graph_config_file, &calculator_graph_config_contents));
LOG(INFO) << "Get calculator graph config contents: "
<< calculator_graph_config_contents;
mediapipe::CalculatorGraphConfig config =
mediapipe::ParseTextProtoOrDie<mediapipe::CalculatorGraphConfig>(
calculator_graph_config_contents);
std::map<std::string, ::mediapipe::Packet> input_side_packets;
std::vector<std::string> kv_pairs =
absl::StrSplit(FLAGS_input_side_packets, ',');
for (const std::string& kv_pair : kv_pairs) {
std::vector<std::string> name_and_value = absl::StrSplit(kv_pair, '=');
RET_CHECK(name_and_value.size() == 2);
RET_CHECK(!::mediapipe::ContainsKey(input_side_packets, name_and_value[0]));
input_side_packets[name_and_value[0]] =
::mediapipe::MakePacket<std::string>(name_and_value[1]);
}
LOG(INFO) << "Initialize the calculator graph.";
mediapipe::CalculatorGraph graph;
RETURN_IF_ERROR(graph.Initialize(config, input_side_packets));
LOG(INFO) << "Start running the calculator graph.";
return graph.Run();
}
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
::mediapipe::Status run_status = RunMediaPipeGraph();
if (!run_status.ok()) {
LOG(ERROR) << "Failed to run the graph: " << run_status.message();
} else {
LOG(INFO) << "Success!";
}
return 0;
}