miccunifi/Road-Trip-Attack

Code repository for paper "Defending from GeoLocalization through Adversarial Road Trips" (ECCV 2026)

1

stars

1

commits

Python

primary language

Sep 3, 2026

updated

README

Road Trip Attack

Road Trip Attack (RTA) creates targeted adversarial images for GeoCLIP. You provide an image, its real coordinates, and the coordinates where you want GeoCLIP to place it. RTA returns a modified image.

This repository contains the attack used in the paper and a second version that strictly limits the final pixel changes.

Requirements

  • Python 3.12 or newer
  • A CUDA-capable GPU is recommended

RTA can run on a CPU, but it will be much slower. GeoCLIP downloads its model weights the first time you run the attack.

Install

Clone this repository, enter its directory, and run:

python -m pip install .

To install the test tools too:

python -m pip install ".[test]"

The exact dependency versions used for validation are listed in requirements-lock.txt.

Quick start

import rta

adversarial = rta.attack(
    "photo.jpg",
    ground_truth=(32.325436, -64.764404),
    target=(31.249600, 36.788300),
    epsilon=4 / 255,
)
adversarial.save("photo_adversarial.png")

rta.close()

ground_truth and target use (latitude, longitude) order. The input can be a file path or a PIL image. The result is a 224 by 224 RGB PIL image.

CUDA device 0 is used by default. To use a CPU, pass device="cpu":

adversarial = rta.attack(
    "photo.jpg",
    ground_truth=(32.325436, -64.764404),
    target=(31.249600, 36.788300),
    device="cpu",
)

Reuse one model

Loading GeoCLIP takes time. Use an RTA session when attacking several images:

import rta

with rta.RTA(device="cuda:0", seed=131) as attacker:
    adversarial = attacker.attack(
        "photo.jpg",
        ground_truth=(32.325436, -64.764404),
        target=(31.249600, 36.788300),
        epsilon=2 / 255,
    )

The session keeps the random sequence used by the original experiment. When reproducing the paper, do not reset the seed before every image.

Attack versions

RTA provides two variants:

  • legacy is the default. It matches the attack used in the paper. Its epsilon limits each local update, but several updates can add up. The final image is therefore not guaranteed to stay inside the same pixel limit.
  • final_clamp runs the legacy attack and then limits the final image to the requested pixel distance from the original image.

Select a variant with the variant argument:

adversarial = rta.attack(
    "photo.jpg",
    ground_truth=(32.325436, -64.764404),
    target=(31.249600, 36.788300),
    epsilon=4 / 255,
    variant="final_clamp",
)

On the full 2,997-image Im2GPS test set, the largest measured changes for the legacy 2/255 and 4/255 settings were about 3/255 and 6/255. final_clamp kept the largest changes at 2/255 and 4/255, with similar overall attack results.

See the variant notes for details and the validation record for the full results.

Paper settings

The public API uses the original settings:

  • targeted cross-entropy loss
  • five sampled route points per beam state
  • beam width of four
  • five PGD steps per candidate
  • five search iterations
  • radius multiplier of 1.1
  • seed 131
  • step size equal to half of epsilon, unless alpha is provided

GPU calculations can vary slightly across hardware, CUDA versions, and dependency versions. Small differences can change the beam search path, so separate runs may not create identical image files.

Development

Run the tests with:

python -m pytest

Build the source and wheel packages with:

python -m build

License

The code is available under the MIT License. The bundled Natural Earth land data is in the public domain. Its original README and version files are included with the data.

Contributors

miccunifi/Road-Trip-Attack

Code repository for paper "Defending from GeoLocalization through Adversarial Road Trips" (ECCV 2026)

1

stars

1

commits

Python

primary language

Sep 3, 2026

updated

README

Road Trip Attack

Road Trip Attack (RTA) creates targeted adversarial images for GeoCLIP. You provide an image, its real coordinates, and the coordinates where you want GeoCLIP to place it. RTA returns a modified image.

This repository contains the attack used in the paper and a second version that strictly limits the final pixel changes.

Requirements

  • Python 3.12 or newer
  • A CUDA-capable GPU is recommended

RTA can run on a CPU, but it will be much slower. GeoCLIP downloads its model weights the first time you run the attack.

Install

Clone this repository, enter its directory, and run:

python -m pip install .

To install the test tools too:

python -m pip install ".[test]"

The exact dependency versions used for validation are listed in requirements-lock.txt.

Quick start

import rta

adversarial = rta.attack(
    "photo.jpg",
    ground_truth=(32.325436, -64.764404),
    target=(31.249600, 36.788300),
    epsilon=4 / 255,
)
adversarial.save("photo_adversarial.png")

rta.close()

ground_truth and target use (latitude, longitude) order. The input can be a file path or a PIL image. The result is a 224 by 224 RGB PIL image.

CUDA device 0 is used by default. To use a CPU, pass device="cpu":

adversarial = rta.attack(
    "photo.jpg",
    ground_truth=(32.325436, -64.764404),
    target=(31.249600, 36.788300),
    device="cpu",
)

Reuse one model

Loading GeoCLIP takes time. Use an RTA session when attacking several images:

import rta

with rta.RTA(device="cuda:0", seed=131) as attacker:
    adversarial = attacker.attack(
        "photo.jpg",
        ground_truth=(32.325436, -64.764404),
        target=(31.249600, 36.788300),
        epsilon=2 / 255,
    )

The session keeps the random sequence used by the original experiment. When reproducing the paper, do not reset the seed before every image.

Attack versions

RTA provides two variants:

  • legacy is the default. It matches the attack used in the paper. Its epsilon limits each local update, but several updates can add up. The final image is therefore not guaranteed to stay inside the same pixel limit.
  • final_clamp runs the legacy attack and then limits the final image to the requested pixel distance from the original image.

Select a variant with the variant argument:

adversarial = rta.attack(
    "photo.jpg",
    ground_truth=(32.325436, -64.764404),
    target=(31.249600, 36.788300),
    epsilon=4 / 255,
    variant="final_clamp",
)

On the full 2,997-image Im2GPS test set, the largest measured changes for the legacy 2/255 and 4/255 settings were about 3/255 and 6/255. final_clamp kept the largest changes at 2/255 and 4/255, with similar overall attack results.

See the variant notes for details and the validation record for the full results.

Paper settings

The public API uses the original settings:

  • targeted cross-entropy loss
  • five sampled route points per beam state
  • beam width of four
  • five PGD steps per candidate
  • five search iterations
  • radius multiplier of 1.1
  • seed 131
  • step size equal to half of epsilon, unless alpha is provided

GPU calculations can vary slightly across hardware, CUDA versions, and dependency versions. Small differences can change the beam search path, so separate runs may not create identical image files.

Development

Run the tests with:

python -m pytest

Build the source and wheel packages with:

python -m build

License

The code is available under the MIT License. The bundled Natural Earth land data is in the public domain. Its original README and version files are included with the data.

Contributors

Languages

Python

88.7%

HTML

11.3%