Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

tfdsv4 fix carla configs #1788

Merged
merged 14 commits into from
Dec 23, 2022
3 changes: 3 additions & 0 deletions armory/datasets/adversarial/carla_obj_det_dev/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
"""carla_obj_det_dev dataset."""

from .carla_obj_det_dev import CarlaObjDetDev
225 changes: 225 additions & 0 deletions armory/datasets/adversarial/carla_obj_det_dev/carla_obj_det_dev.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,225 @@
"""carla_obj_det_dev dataset."""

import collections
import json
import os
from copy import deepcopy
import numpy as np

import tensorflow as tf
import tensorflow_datasets as tfds

_DESCRIPTION = """
Synthetic multimodality (RGB, depth) dataset generated using CARLA (https://carla.org).
"""

_CITATION = """
@inproceedings{Dosovitskiy17,
title = { {CARLA}: {An} Open Urban Driving Simulator},
author = {Alexey Dosovitskiy and German Ros and Felipe Codevilla and Antonio Lopez and Vladlen Koltun},
booktitle = {Proceedings of the 1st Annual Conference on Robot Learning},
pages = {1--16},
year = {2017}
}
"""

# fmt: off
_URLS = "https://armory-public-data.s3.us-east-2.amazonaws.com/carla/carla_od_dev_2.0.0.tar.gz"
lcadalzo marked this conversation as resolved.
Show resolved Hide resolved
# fmt: on


class CarlaObjDetDev(tfds.core.GeneratorBasedBuilder):
"""DatasetBuilder for carla_obj_det_dev dataset."""

VERSION = tfds.core.Version("2.0.0")
RELEASE_NOTES = {
"1.0.0": "Initial release.",
"1.0.1": "Correcting error to RGB and depth image pairing",
"2.0.0": "Eval5 update with higher resolution, HD textures, accurate annotations, and objects overlapping patch",
}

def _info(self) -> tfds.core.DatasetInfo:
"""Returns the dataset metadata."""
features = {
# sequence of [RGB, depth] images
"image": tfds.features.Sequence(
tfds.features.Image(shape=(960, 1280, 3)),
length=2,
),
# sequence of image features for [RGB, depth]
"images": tfds.features.Sequence(
tfds.features.FeaturesDict(
{
"file_name": tfds.features.Text(),
"height": tf.int64,
"width": tf.int64,
"id": tf.int64,
},
),
length=2,
),
# both modalities share the same categories
"categories": tfds.features.Sequence(
tfds.features.FeaturesDict(
{
"id": tf.int64, # {'pedstrian':1, 'vehicles':2, 'trafficlight':3}
"name": tfds.features.Text(),
"supercategory": tfds.features.Text(),
}
)
),
# both modalities share the same objects
"objects": tfds.features.Sequence(
{
"id": tf.int64,
"image_id": tf.int64,
"area": tf.int64, # un-normalized area
"boxes": tfds.features.BBoxFeature(), # normalized bounding box [ymin, xmin, ymax, xmax]
"labels": tfds.features.ClassLabel(num_classes=5),
"is_crowd": tf.bool,
}
),
# these data only apply to the "green screen patch" objects, which both modalities share
"patch_metadata": tfds.features.FeaturesDict(
{
# green screen vertices in (x,y) starting from top-left moving clockwise
"gs_coords": tfds.features.Tensor(shape=[4, 2], dtype=tf.int32),
# binarized segmentation mask of patch.
# mask[x,y] == 1 indicates patch pixel; 0 otherwise
"mask": tfds.features.Image(shape=(960, 1280, 3)),
"avg_patch_depth": tfds.features.Tensor(shape=(), dtype=tf.float64),
}
),
}

return tfds.core.DatasetInfo(
builder=self,
description=_DESCRIPTION,
features=tfds.features.FeaturesDict(features),
citation=_CITATION,
)

def _split_generators(self, dl_manager: tfds.download.DownloadManager):
"""Returns SplitGenerators."""
path = dl_manager.download_and_extract(_URLS)
lcadalzo marked this conversation as resolved.
Show resolved Hide resolved
return {"dev": self._generate_examples(os.path.join(path, "dev"))}

def _generate_examples(self, path):
"""yield examples"""

# For each image, gets its annotations and yield relevant data
depth_folder = "_out/sensor.camera.depth.2"
foreground_mask_folder = "_out/foreground_mask"
patch_metadata_folder = "_out/patch_metadata"

annotation_path = os.path.join(
path, "_out", "kwcoco_annotations_without_patch_and_sans_tiny_objects.json"
)

cocoanno = COCOAnnotation(annotation_path)

images_rgb = (
cocoanno.images()
) # list of dictionaries of RGB image id, height, width, file_name

# sort images alphabetically
images_rgb = sorted(images_rgb, key=lambda x: x["file_name"].lower())

for idx, image_rgb in enumerate(images_rgb):

# Discard irrelevant fields
image_rgb.pop("date_captured")
image_rgb.pop("license")
image_rgb.pop("coco_url")
image_rgb.pop("flickr_url")
image_rgb.pop("video_id")
image_rgb.pop("frame_index")

# Pairing RGB and depth
fpath_rgb = image_rgb["file_name"] # rgb image path
fname = fpath_rgb.split("/")[-1]
fname_no_ext = fname.split(".")[0]
fpath_depth = os.path.join(depth_folder, fname) # depth image path
image_depth = deepcopy(image_rgb)
image_depth["file_name"] = fpath_depth

# get object annotations for each image
annotations = cocoanno.get_annotations(image_rgb["id"])

# For unknown reasons, when kwcoco is saved after removing tiny objects,
# bbox format changes from [x,y,w,h] to [x1,y1,x2,y1]
def build_bbox(x1, y1, x2, y2):
return tfds.features.BBox(
ymin=y1 / image_rgb["height"],
xmin=x1 / image_rgb["width"],
ymax=y2 / image_rgb["height"],
xmax=x2 / image_rgb["width"],
)

example = {
"image": [
os.path.join(
path,
modality,
)
for modality in [fpath_rgb, fpath_depth]
],
"images": [image_rgb, image_depth],
"categories": cocoanno.categories(),
"objects": [
{
"id": anno["id"],
"image_id": anno["image_id"],
"area": anno["area"],
"boxes": build_bbox(*anno["bbox"]),
"labels": anno["category_id"],
"is_crowd": bool(anno["iscrowd"]),
}
for anno in annotations
],
"patch_metadata": {
"gs_coords": np.load(
os.path.join(
path, patch_metadata_folder, fname_no_ext + "_coords.npy"
)
),
"avg_patch_depth": np.load(
os.path.join(
path, patch_metadata_folder, fname_no_ext + "_avg_depth.npy"
)
),
"mask": os.path.join(path, foreground_mask_folder, fname),
},
}

yield idx, example


class COCOAnnotation(object):
"""COCO annotation helper class."""

def __init__(self, annotation_path):
with tf.io.gfile.GFile(annotation_path) as f:
data = json.load(f)
self._data = data

# for each images["id"], find all annotations such that annotations["image_id"] == images["id"]
img_id2annotations = collections.defaultdict(list)
for a in self._data["annotations"]:
img_id2annotations[a["image_id"]].append(a)
self._img_id2annotations = {
k: list(sorted(v, key=lambda a: a["id"]))
for k, v in img_id2annotations.items()
}

def categories(self):
"""Return the category dicts, as sorted in the file."""
return self._data["categories"]

def images(self):
"""Return the image dicts, as sorted in the file."""
return self._data["images"]

def get_annotations(self, img_id):
"""Return all annotations associated with the image id string."""
return self._img_id2annotations.get(img_id, [])
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://armory-public-data.s3.us-east-2.amazonaws.com/carla/carla_od_dev_2.0.0.tar.gz 67507963 30c7593817867eb97b3c7e1358451c576805bb4423599b09ad99f15a2ebdd5c9 carla_od_dev_2.0.0.tar.gz
7 changes: 7 additions & 0 deletions armory/datasets/cached_datasets.json
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
{
"carla_obj_det_dev": {
"sha256": "a7adc2400d1fafb03f6d49d10b61ac6405382bc19df446ee25e3d4afce2775a4",
"size": 64152715,
"subdir": "carla_obj_det_dev/2.0.0",
"url": null,
"version": "2.0.0"
},
"carla_over_obj_det_dev": {
"sha256": "77761f1d5c6eca40984aa40f38fab0568b9bb4a4dca696e876fccaa2dd9be56d",
"size": 59760259,
Expand Down
34 changes: 17 additions & 17 deletions armory/datasets/preprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,19 +39,22 @@ def supervised_image_classification(element):
return (image_to_canon(element["image"]), element["label"])


mnist = register(supervised_image_classification, "mnist")
cifar10 = register(supervised_image_classification, "cifar10")
resisc45 = register(supervised_image_classification, "resisc45")


@register
def digit(element):
return (audio_to_canon(element["audio"]), element["label"])


@register
def carla_obj_det_dev(element, modality="rgb"):
return carla_multimodal_obj_det(element["image"], modality=modality), (
convert_tf_obj_det_label_to_pytorch(element["image"], element["objects"]),
element["patch_metadata"],
)


@register
def carla_over_obj_det_dev(element, modality="rgb"):
return carla_over_obj_det_image(element["image"], modality=modality), (
return carla_multimodal_obj_det(element["image"], modality=modality), (
convert_tf_obj_det_label_to_pytorch(element["image"], element["objects"]),
element["patch_metadata"],
)
Expand All @@ -64,6 +67,11 @@ def xview(element):
)


mnist = register(supervised_image_classification, "mnist")
cifar10 = register(supervised_image_classification, "cifar10")
resisc45 = register(supervised_image_classification, "resisc45")


def image_to_canon(image, resize=None, target_dtype=tf.float32, input_type="uint8"):
"""
TFDS Image feature uses (height, width, channels)
Expand Down Expand Up @@ -98,14 +106,6 @@ def audio_to_canon(audio, resample=None, target_dtype=tf.float32, input_type="in
return audio


# config = {
# "preprocessor": "mnist(max_frames=1)"
# "preprocessor_kwargs": {
# "max_frames": null,
# }
# }


def video_to_canon(
video,
resize=None,
Expand All @@ -132,7 +132,7 @@ def video_to_canon(
return video


def carla_over_obj_det_image(x, modality="rgb"):
def carla_multimodal_obj_det(x, modality="rgb"):
if modality == "rgb":
return image_to_canon(x[0])
elif modality == "depth":
Expand All @@ -150,8 +150,8 @@ def convert_tf_boxes_to_pytorch(x, box_array):
Converts object detection boxes from TF format of [y1/height, x1/width, y2/height, x2/width]
to PyTorch format of [x1, y1, x2, y2]

:param x: TF tensor of shape (nb, H, W, C)
:param y: TF tensor of shape (num_boxes, 4)
:param x: TF tensor of shape (nb, H, W, C) or (H, W, C)
:param box_array: TF tensor of shape (num_boxes, 4)
:return: TF tensor of shape (num_boxes, 4)
"""
x_shape = tf.shape(x)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
"use_label": true
},
"dataset": {
"batch_size": 1,
"eval_split": "dev",
"framework": "numpy",
"modality": "rgb",
"module": "armory.data.adversarial_datasets",
"name": "carla_obj_det_dev"
"test": {
"batch_size": 1,
"name": "carla_obj_det_dev",
"preprocessor_kwargs": {
"modality": "rgb"
},
"split": "dev"
}
},
"defense": null,
"metric": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
"use_label": false
},
"dataset": {
"batch_size": 1,
"eval_split": "dev",
"framework": "numpy",
"modality": "rgb",
"module": "armory.data.adversarial_datasets",
"name": "carla_obj_det_dev"
"test": {
"batch_size": 1,
"name": "carla_obj_det_dev",
"preprocessor_kwargs": {
"modality": "rgb"
},
"split": "dev"
}
},
"defense": {
"kwargs": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,14 @@
"use_label": false
},
"dataset": {
"batch_size": 1,
"eval_split": "dev",
"framework": "numpy",
"modality": "rgb",
"module": "armory.data.adversarial_datasets",
"name": "carla_obj_det_dev"
"test": {
"batch_size": 1,
"name": "carla_obj_det_dev",
"preprocessor_kwargs": {
"modality": "rgb"
},
"split": "dev"
}
},
"defense": null,
"metric": {
Expand Down
Loading