CoolFace
Datasetpublic

ps3020/ATLAS-KITTI

ATLAS-KITTI Adversarial LiDAR point clouds derived from the KITTI 3D object detection validation split (3769 frames). Anonymous release supporting a submission under review; author and affiliation details are withheld for the review period. Part of ATLAS: ps3020/ATLAS-KITTI · ps3020/ATLAS-nuScenes Configs Injection — a phantom vehicle that does not exist is added: inject_easy, inject_medium, inject_hard These are decreasing phantom point densities. KITTI 3D object… See the full description on the dataset page: https://huggingface.co/datasets/ps3020/ATLAS-KITTI.

sourceHugging Facecc-by-nc-sa-3.0updated 2mo agoView on Hugging Face
0likes196downloads
Dataset Card

ATLAS-KITTI

Adversarial LiDAR point clouds derived from the KITTI 3D object detection validation split (3769 frames).

Anonymous release supporting a submission under review; author and affiliation details are withheld for the review period.

Part of ATLAS: ps3020/ATLAS-KITTI · ps3020/ATLAS-nuScenes

Configs

Injection — a phantom vehicle that does not exist is added:

inject_easy, inject_medium, inject_hard

These are decreasing phantom point densities. KITTI 3D object has no sequences, so world-fixed and ego-relative placement are equivalent and there is a single injection family (unlike ATLAS-nuScenes, which has both).

Removal — points in an azimuth wedge are deleted to hide a real vehicle:

removal_az10, removal_az20, removal_az30, removal_az40, removal_az50, removal_az60 (suffix = wedge width in degrees)

Every row is an attacked frame; there are no clean frames.

Setup

1. Install

bash
pip install datasets numpy

2. Load a config

python
from datasets import load_dataset
ds = load_dataset("ps3020/ATLAS-KITTI", "removal_az20", split="train")

Add streaming=True to avoid downloading a whole config (each is 0.35–0.89 GB).

That is all that is required. Each row carries the complete attacked point cloud, the attacked object's box, clean KITTI ground truth, and calibration, so this dataset is fully self-contained — no KITTI download is needed.

Quick check

python
from datasets import load_dataset
import numpy as np

ds = load_dataset("ps3020/ATLAS-KITTI", "removal_az20",
                  split="train", streaming=True)
ex = next(iter(ds))
pts = np.asarray(ex["points"], np.float32).reshape(ex["num_points"], ex["point_dim"])
print(ex["frame_id"], pts.shape)
# 000001 (15889, 4)

Usage

python
from datasets import load_dataset
import numpy as np

ds = load_dataset("ps3020/ATLAS-KITTI", "removal_az20", split="train")
ex = ds[0]

# points are stored FLAT -- reshape to recover the cloud
pts = np.asarray(ex["points"], np.float32).reshape(ex["num_points"], ex["point_dim"])
# (N, 4) = x, y, z, intensity

spoof_gt  = np.asarray(ex["spoof_gt"], np.float32)                      # (7,) attacked object
gt_boxes  = np.asarray(ex["gt_boxes"], np.float32).reshape(ex["num_gt"], 7)
gt_names  = ex["gt_names"]

Or use the bundled helper, which returns arrays directly:

python
from load_atlas_kitti import load_atlas, to_arrays, removal_rate

ds = load_atlas("removal_az20")                 # add streaming=True to avoid download
pts, spoof_gt, gt_boxes, gt_names = to_arrays(ds[0])

Attack success rate

spoof_gt is the attacked object. Score each frame by whether a prediction overlaps it at IoU >= 0.3:

  • injection — success = a detection appears (false positive created)
  • removal — success = the detection is missing (true positive destroyed)
python
asr = n_success / len(ds)

Use `len(ds)`, not 3769. Frames that could not be attacked were never written, so configs differ in length:

configframes
inject_easy3769
inject_medium3767
inject_hard3649
removal_az10removal_az603384

Fields

fielddescription
frame_idKITTI frame id, e.g. "000001"
points, num_points, point_dimattacked cloud, flattened; reshape to (N, 4)
spoof_gt(7,) attacked object [x, y, z, dx, dy, dz, heading], LiDAR frame
gt_boxes, gt_names, num_gtclean KITTI ground truth (DontCare excluded)
gt_difficulty, gt_num_pointsKITTI difficulty, points per box
calib_P2, calib_R0_rect, calib_Tr_velo_to_camcalibration, flattened 4×4 row-major
image_shape[height, width] — varies across frames
atk_*attack parameters — 6 fields for injection, 20 for removal

For removal, the realised removal rate is atk_n_points_removed / atk_n_points_in_sector.

Calibration

ASR needs only spoof_gt (LiDAR frame). Calibration is included because the official KITTI 3D AP is computed in camera coordinates, so reproducing standard KITTI evaluation requires projecting predictions with these matrices:

python
P2  = np.asarray(ex["calib_P2"], np.float32).reshape(4, 4)
R0  = np.asarray(ex["calib_R0_rect"], np.float32).reshape(4, 4)
V2C = np.asarray(ex["calib_Tr_velo_to_cam"], np.float32).reshape(4, 4)
H, W = ex["image_shape"]

# LiDAR point/box centre -> image pixel
uv = P2 @ (R0 @ (V2C @ np.append(xyz, 1.0)))
uv = uv[:2] / uv[2]

Notes

  • Clouds are camera-FOV cropped (roughly |azimuth| <= 40°, x > 5 m), not full 360°.
  • Attacks target the Car class.
  • KITTI 3D object has no sequences, so frames are attacked independently.

License

cc-by-nc-sa-3.0, inherited from KITTI. Please cite KITTI alongside this dataset.