-
Notifications
You must be signed in to change notification settings - Fork 106
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #672 from ufal/img_feat
Extracting image features
- Loading branch information
Showing
6 changed files
with
196 additions
and
110 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,42 @@ | ||
from typing import List | ||
from typing import List, Callable, Iterable | ||
import os | ||
|
||
from typeguard import check_argument_types | ||
import numpy as np | ||
|
||
|
||
def numpy_reader(files: List[str]): | ||
def single_tensor(files: List[str]) -> np.ndarray: | ||
"""Load a single tensor from a numpy file.""" | ||
check_argument_types() | ||
if len(files) == 1: | ||
return np.load(files[0]) | ||
|
||
return np.concatenate([np.load(f) for f in files], axis=0) | ||
|
||
|
||
def from_file_list(prefix: str, | ||
default_tensor_name: str = "arr_0") -> Callable: | ||
"""Load a list of numpy arrays from a list of .npz numpy files. | ||
Args: | ||
prefix: A common prefix for the files in the list. | ||
default_tensor_name: Key of the tensors to load from the npz files. | ||
Returns: | ||
A generator function that yields the loaded arryas. | ||
""" | ||
check_argument_types() | ||
|
||
def load(files: List[str]) -> Iterable[np.ndarray]: | ||
for list_file in files: | ||
with open(list_file, encoding="utf-8") as f_list: | ||
for line in f_list: | ||
path = os.path.join(prefix, line.rstrip()) | ||
with np.load(path) as npz: | ||
yield npz[default_tensor_name] | ||
|
||
return load | ||
|
||
|
||
# pylint: disable=invalid-name | ||
numpy_file_list_reader = from_file_list(prefix="") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
#!/usr/bin/env python3 | ||
"""Extract imagenet features from given images. | ||
The script reads a list of pahts to images (specified by path prefix and list | ||
of relative paths), process the images using an imagenet network and extract a | ||
given convolutional map from the image. The maps are saved as numpy tensors in | ||
files with a different prefix and the same relative path from this prefix | ||
ending with .npz. | ||
""" | ||
|
||
import argparse | ||
import os | ||
import sys | ||
|
||
import numpy as np | ||
import tensorflow as tf | ||
|
||
from neuralmonkey.dataset import Dataset | ||
from neuralmonkey.encoders.imagenet_encoder import ImageNet | ||
from neuralmonkey.logging import log | ||
from neuralmonkey.readers.image_reader import single_image_for_imagenet | ||
|
||
|
||
SUPPORTED_NETWORKS = [ | ||
"vgg_16", "vgg_19", "resnet_v2_50", "resnet_v2_101", "resnet_v2_152"] | ||
|
||
|
||
def main(): | ||
parser = argparse.ArgumentParser(description=__doc__) | ||
parser.add_argument("--net", type=str, choices=SUPPORTED_NETWORKS, | ||
help="Type of imagenet network.") | ||
parser.add_argument("--input-prefix", type=str, default="", | ||
help="Prefix of the image path.") | ||
parser.add_argument("--output-prefix", type=str, default="", | ||
help="Prefix of the path to the output numpy files.") | ||
parser.add_argument("--slim-models", type=str, required=True, | ||
help="Path to SLIM models in cloned tensorflow/models " | ||
"repository") | ||
parser.add_argument("--model-checkpoint", type=str, required=True, | ||
help="Path to the ImageNet model checkpoint.") | ||
parser.add_argument("--conv-map", type=str, required=True, | ||
help="Name of the convolutional map that is.") | ||
parser.add_argument("--images", type=str, | ||
help="File with paths to images or stdin by default.") | ||
parser.add_argument("--batch-size", type=int, default=128) | ||
args = parser.parse_args() | ||
|
||
if not os.path.exists(args.input_prefix): | ||
raise ValueError("Directory {} does not exist.".format( | ||
args.input_prefix)) | ||
if not os.path.exists(args.output_prefix): | ||
raise ValueError("Directory {} does not exist.".format( | ||
args.output_prefix)) | ||
|
||
if args.net.startswith("vgg_"): | ||
img_size = 224 | ||
vgg_normalization = True | ||
zero_one_normalization = False | ||
elif args.net.startswith("resnet_v2"): | ||
img_size = 229 | ||
vgg_normalization = False | ||
zero_one_normalization = True | ||
else: | ||
raise ValueError("Unspported network: {}.".format(args._net)) | ||
|
||
log("Creating graph for the ImageNet network.") | ||
imagenet = ImageNet( | ||
name="imagenet", data_id="images", network_type=args.net, | ||
slim_models_path=args.slim_models, load_checkpoint=args.model_checkpoint, | ||
spatial_layer=args.conv_map) | ||
|
||
log("Creating TensorFlow session.") | ||
session = tf.Session() | ||
session.run(tf.global_variables_initializer()) | ||
log("Loading ImageNet model variables.") | ||
imagenet.load(session) | ||
|
||
if args.images is None: | ||
log("No input file provided, reading paths from stdin.") | ||
source = sys.stdin | ||
else: | ||
source = open(args.images) | ||
|
||
images = [] | ||
image_paths = [] | ||
|
||
def process_images(): | ||
dataset = Dataset("dataset", {"images": np.array(images)}, {}) | ||
feed_dict = imagenet.feed_dict(dataset) | ||
feature_maps = session.run(imagenet.spatial_states, feed_dict=feed_dict) | ||
|
||
for features, rel_path in zip(feature_maps, image_paths): | ||
npz_path = os.path.join(args.output_prefix, rel_path + ".npz") | ||
os.makedirs(os.path.dirname(npz_path), exist_ok=True) | ||
np.savez(npz_path, features) | ||
print(npz_path) | ||
|
||
|
||
for img in source: | ||
img_path = os.path.join(args.input_prefix, img.rstrip()) | ||
images.append(single_image_for_imagenet( | ||
img_path, img_size, img_size, vgg_normalization, | ||
zero_one_normalization)) | ||
image_paths.append(img.rstrip()) | ||
|
||
if len(images) >= args.batch_size: | ||
process_images() | ||
images = [] | ||
image_paths = [] | ||
process_images() | ||
|
||
if args.images is not None: | ||
source.close() | ||
|
||
|
||
if __name__ == "__main__": | ||
main() |