Skip to content

Commit 60fc781

Browse files
author
Jonathan Huang
authored
Merge pull request #2825 from tombstone/update_metrics
update offline metrics for Open Images dataset.
2 parents 266c7b8 + 6c522f8 commit 60fc781

File tree

5 files changed

+627
-0
lines changed

5 files changed

+627
-0
lines changed
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Tensorflow Object Detection API: main runnables.
2+
3+
package(
4+
default_visibility = ["//visibility:public"],
5+
)
6+
7+
licenses(["notice"])
8+
9+
# Apache 2.0
10+
11+
py_binary(
12+
name = "offline_eval_map_corloc",
13+
srcs = [
14+
"offline_eval_map_corloc.py",
15+
],
16+
deps = [
17+
":tf_example_parser",
18+
"//tensorflow_models/object_detection:evaluator",
19+
"//tensorflow_models/object_detection/builders:input_reader_builder",
20+
"//tensorflow_models/object_detection/core:standard_fields",
21+
"//tensorflow_models/object_detection/utils:config_util",
22+
"//tensorflow_models/object_detection/utils:label_map_util",
23+
],
24+
)
25+
26+
py_test(
27+
name = "offline_eval_map_corloc_test",
28+
srcs = [
29+
"offline_eval_map_corloc_test.py",
30+
],
31+
deps = [
32+
":offline_eval_map_corloc",
33+
"//tensorflow",
34+
],
35+
)
36+
37+
py_library(
38+
name = "tf_example_parser",
39+
srcs = ["tf_example_parser.py"],
40+
deps = [
41+
"//tensorflow",
42+
"//tensorflow_models/object_detection/core:data_parser",
43+
"//tensorflow_models/object_detection/core:standard_fields",
44+
],
45+
)
46+
47+
py_test(
48+
name = "tf_example_parser_test",
49+
srcs = ["tf_example_parser_test.py"],
50+
deps = [
51+
":tf_example_parser",
52+
"//tensorflow",
53+
"//tensorflow_models/object_detection/core:standard_fields",
54+
],
55+
)
Lines changed: 173 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# ==============================================================================
15+
r"""Evaluation executable for detection data.
16+
17+
This executable evaluates precomputed detections produced by a detection
18+
model and writes the evaluation results into csv file metrics.csv, stored
19+
in the directory, specified by --eval_dir.
20+
21+
The evaluation metrics set is supplied in object_detection.protos.EvalConfig
22+
in metrics_set field.
23+
Currently two set of metrics are supported:
24+
- pascal_voc_metrics: standard PASCAL VOC 2007 metric
25+
- open_images_metrics: Open Image V2 metric
26+
All other field of object_detection.protos.EvalConfig are ignored.
27+
28+
Example usage:
29+
./compute_metrics \
30+
--eval_dir=path/to/eval_dir \
31+
--eval_config_path=path/to/evaluation/configuration/file \
32+
--input_config_path=path/to/input/configuration/file
33+
"""
34+
import csv
35+
import os
36+
import re
37+
import tensorflow as tf
38+
39+
from object_detection import evaluator
40+
from object_detection.core import standard_fields
41+
from object_detection.metrics import tf_example_parser
42+
from object_detection.utils import config_util
43+
from object_detection.utils import label_map_util
44+
45+
flags = tf.app.flags
46+
tf.logging.set_verbosity(tf.logging.INFO)
47+
48+
flags.DEFINE_string('eval_dir', None, 'Directory to write eval summaries to.')
49+
flags.DEFINE_string('eval_config_path', None,
50+
'Path to an eval_pb2.EvalConfig config file.')
51+
flags.DEFINE_string('input_config_path', None,
52+
'Path to an eval_pb2.InputConfig config file.')
53+
54+
FLAGS = flags.FLAGS
55+
56+
57+
def _generate_sharded_filenames(filename):
58+
m = re.search(r'@(\d{1,})', filename)
59+
if m:
60+
num_shards = int(m.group(1))
61+
return [
62+
re.sub(r'@(\d{1,})', '-%.5d-of-%.5d' % (i, num_shards), filename)
63+
for i in range(num_shards)
64+
]
65+
else:
66+
return [filename]
67+
68+
69+
def _generate_filenames(filenames):
70+
result = []
71+
for filename in filenames:
72+
result += _generate_sharded_filenames(filename)
73+
return result
74+
75+
76+
def read_data_and_evaluate(input_config, eval_config):
77+
"""Reads pre-computed object detections and groundtruth from tf_record.
78+
79+
Args:
80+
input_config: input config proto of type
81+
object_detection.protos.InputReader.
82+
eval_config: evaluation config proto of type
83+
object_detection.protos.EvalConfig.
84+
85+
Returns:
86+
Evaluated detections metrics.
87+
88+
Raises:
89+
ValueError: if input_reader type is not supported or metric type is unknown.
90+
"""
91+
if input_config.WhichOneof('input_reader') == 'tf_record_input_reader':
92+
input_paths = input_config.tf_record_input_reader.input_path
93+
94+
label_map = label_map_util.load_labelmap(input_config.label_map_path)
95+
max_num_classes = max([item.id for item in label_map.item])
96+
categories = label_map_util.convert_label_map_to_categories(
97+
label_map, max_num_classes)
98+
99+
object_detection_evaluators = evaluator.get_evaluators(
100+
eval_config, categories)
101+
# Support a single evaluator
102+
object_detection_evaluator = object_detection_evaluators[0]
103+
104+
skipped_images = 0
105+
processed_images = 0
106+
for input_path in _generate_filenames(input_paths):
107+
tf.logging.info('Processing file: {0}'.format(input_path))
108+
109+
record_iterator = tf.python_io.tf_record_iterator(path=input_path)
110+
data_parser = tf_example_parser.TfExampleDetectionAndGTParser()
111+
112+
for string_record in record_iterator:
113+
tf.logging.log_every_n(tf.logging.INFO, 'Processed %d images...', 1000,
114+
processed_images)
115+
processed_images += 1
116+
117+
example = tf.train.Example()
118+
example.ParseFromString(string_record)
119+
decoded_dict = data_parser.parse(example)
120+
121+
if decoded_dict:
122+
object_detection_evaluator.add_single_ground_truth_image_info(
123+
decoded_dict[standard_fields.DetectionResultFields.key],
124+
decoded_dict)
125+
object_detection_evaluator.add_single_detected_image_info(
126+
decoded_dict[standard_fields.DetectionResultFields.key],
127+
decoded_dict)
128+
else:
129+
skipped_images += 1
130+
tf.logging.info('Skipped images: {0}'.format(skipped_images))
131+
132+
return object_detection_evaluator.evaluate()
133+
134+
raise ValueError('Unsupported input_reader_config.')
135+
136+
137+
def write_metrics(metrics, output_dir):
138+
"""Write metrics to the output directory.
139+
140+
Args:
141+
metrics: A dictionary containing metric names and values.
142+
output_dir: Directory to write metrics to.
143+
"""
144+
tf.logging.info('Writing metrics.')
145+
146+
with open(os.path.join(output_dir, 'metrics.csv'), 'w') as csvfile:
147+
metrics_writer = csv.writer(csvfile, delimiter=',')
148+
for metric_name, metric_value in metrics.items():
149+
metrics_writer.writerow([metric_name, str(metric_value)])
150+
151+
152+
def main(argv):
153+
del argv
154+
required_flags = ['input_config_path', 'eval_config_path', 'eval_dir']
155+
for flag_name in required_flags:
156+
if not getattr(FLAGS, flag_name):
157+
raise ValueError('Flag --{} is required'.format(flag_name))
158+
159+
configs = config_util.get_configs_from_multiple_files(
160+
eval_input_config_path=FLAGS.input_config_path,
161+
eval_config_path=FLAGS.eval_config_path)
162+
163+
eval_config = configs['eval_config']
164+
input_config = configs['eval_input_config']
165+
166+
metrics = read_data_and_evaluate(input_config, eval_config)
167+
168+
# Save metrics
169+
write_metrics(metrics, FLAGS.eval_dir)
170+
171+
172+
if __name__ == '__main__':
173+
tf.app.run(main)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
# ==============================================================================
15+
"""Tests for utilities in offline_eval_map_corloc binary."""
16+
17+
import tensorflow as tf
18+
19+
from object_detection.metrics import offline_eval_map_corloc as offline_eval
20+
21+
22+
class OfflineEvalMapCorlocTest(tf.test.TestCase):
23+
24+
def test_generateShardedFilenames(self):
25+
test_filename = '/path/to/file'
26+
result = offline_eval._generate_sharded_filenames(test_filename)
27+
self.assertEqual(result, [test_filename])
28+
29+
test_filename = '/path/to/file-00000-of-00050'
30+
result = offline_eval._generate_sharded_filenames(test_filename)
31+
self.assertEqual(result, [test_filename])
32+
33+
result = offline_eval._generate_sharded_filenames('/path/to/@3.sst')
34+
self.assertEqual(result, [
35+
'/path/to/-00000-of-00003.sst', '/path/to/-00001-of-00003.sst',
36+
'/path/to/-00002-of-00003.sst'
37+
])
38+
39+
result = offline_eval._generate_sharded_filenames('/path/to/abc@3')
40+
self.assertEqual(result, [
41+
'/path/to/abc-00000-of-00003', '/path/to/abc-00001-of-00003',
42+
'/path/to/abc-00002-of-00003'
43+
])
44+
45+
result = offline_eval._generate_sharded_filenames('/path/to/@1')
46+
self.assertEqual(result, ['/path/to/-00000-of-00001'])
47+
48+
def test_generateFilenames(self):
49+
test_filenames = ['/path/to/file', '/path/to/@3.sst']
50+
result = offline_eval._generate_filenames(test_filenames)
51+
self.assertEqual(result, [
52+
'/path/to/file', '/path/to/-00000-of-00003.sst',
53+
'/path/to/-00001-of-00003.sst', '/path/to/-00002-of-00003.sst'
54+
])
55+
56+
57+
if __name__ == '__main__':
58+
tf.test.main()

0 commit comments

Comments
 (0)