Powered by OpenAIRE graph
Found an issue? Give us feedback
ZENODOarrow_drop_down
ZENODO
Dataset . 2025
License: CC BY
Data sources: Datacite
ZENODO
Dataset . 2025
License: CC BY
Data sources: Datacite
ZENODO
Dataset . 2025
License: CC BY
Data sources: Datacite
ZENODO
Dataset . 2025
License: CC BY
Data sources: Datacite
versions View all 4 versions
addClaim

This Research product is the result of merged Research products in OpenAIRE.

You have already added 0 works in your ORCID record related to the merged Research product.

GVCCS : Ground Visible Camera Contrail Sequences

Authors: Jarry, Gabriel; Very, Philippe; Ballerini, Franck; Dalmau, Ramon;

GVCCS : Ground Visible Camera Contrail Sequences

Abstract

The GVCCS dataset provides the first open-access, instance-level annotated video dataset for contrail detection, segmentation, and tracking from Réuniwatt CamVision visible ground-based camera. Designed to support research into aviation’s non-CO₂ climate impacts, it contains 122 high-resolution video sequences (24,228 images) captured at EUROCONTROL’s Innovation Hub in Brétigny-sur-Orge, France. Each sequence has been carefully labelled with instance-level multi polygon annotations, including temporally resolved contrail masks and consistent instance identifiers. A subset of contrails is also linked to unique flight identifiers (when attribution is possible) based on known aircraft trajectories. The dataset is aimed at researchers in environmental science, aviation, remote sensing, and computer vision. It supports both semantic and panoptic segmentation tasks and is intended to foster development of models for: Contrail detection and instance segmentation Temporal tracking and lifecycle analysis Contrail-to-flight attribution and validation of physical models Key Features: 24,228 annotated images across 122 video sequences (30sec frame rate) with a total of 176,194 individual polygons. 4651 instance-level multi-polygon contrails with temporal consistency (contrails are tracked across frames). 3354 contrails are associated to a flight with unique identifier. Image resolution: 1024×1024 (geometrically projected from fisheye camera) covering an area of 75km x 75km. Folder organisation : GVCCS/├── azimuth_zenith_grid.npy├── train/│ ├── annotations.json│ ├── parquet/│ └── images/├── test/│ ├── annotations.json│ ├── parquet/│ └── images/ azimuth_zenith_grid.npyis the grid of pixel to azitmuth zenith mapping Both train and test folders have a similar structure, with images, annotations, csv files. Images are high-resolution (1024×1024 pixels JPG files) stored in the images subfolders. Each projected image has been enhanced for visual clarity using brightness adjustment, local contrast amplification, and color rebalancing to improve contrail visibility (see paper description for more details). The parquetfolders include flight data for each sequence saved in parquet format. Each parquet file contains tabular data for the flight passing over the camera filtered with altitude higher than 15 000ft with the following columns: Column Description FLIGHT_ID Unique identifier for each flight CALL_SIGN Airline call sign (flight number) REGISTRATION Aircraft registration code ICAO_TYPE Aircraft type code according to ICAO classification TIMESTAMP_S Timestamp of the data point in seconds since epoch (warning timestamps are UTC-2 please see v1.1 for UTC) LONGITUDE Longitude of the aircraft at the timestamp LATITUDE Latitude of the aircraft at the timestamp ALTI_FT Standard Pressure Altitude in feet GRND_SPD Ground speed of the aircraft in knots AZIMUTH Aircraft heading (azimuth) in radian ZENITH Zenith angle (vertical angle) in raduan PIXEL_X Projected X pixel coordinate corresponding to the aircraft position in the image PIXEL_Y Projected Y pixel coordinate corresponding to the aircraft position in the image The annotations.json files are provided in COCO format, including per-frame and per-instance polygon annotations, object tracking (consistent instance IDs across frames), flight attribution (when available), and video-level metadata. It contains four main sections with the following fields: annotations area: Area of the annotated polygon (float) bbox: Bounding box of the instance [x, y, width, height] category_id: ID referring to the object category contrail_id: Unique ID of the contrail instance (consistent across frames) flight_id: ID linking the annotation to the flight data (if available) id: Unique annotation ID image_id: ID of the associated image iscrowd: Boolean flag for crowd annotations segmentation: Polygon coordinates describing the shape type: Type of annotation (e.g., polygon) categories (only one category: contrail) color: Color associated with the category (for visualization) id: Category ID isthing: Boolean flag indicating whether the category is a "thing" (vs. stuff) name: Category name images file_name: Image file name height: Image height (pixels) id: Unique image ID time: Timestamp of the image capture video_id: ID of the associated video (if applicable) width: Image width (pixels) videos height: Video frame height (pixels) id: Unique video ID length: Number of frames in the video start: Start timestamp of the video stop: End timestamp of the video width: Video frame width (pixels) Code example to retrieve coordinates from pixels import os import numpy as np import matplotlib.pyplot as plt from scipy.interpolate import RegularGridInterpolator from geographiclib import geodesic from scipy.interpolate import LinearNDInterpolator, RegularGridInterpolator, NearestNDInterpolator from geographiclib import geodesic from typing import Tuple, Union import numpy as np RESOLUTION=1024 wgs84 = geodesic.Geodesic.WGS84 class Projector: def __init__(self, azimuth_zenith_grid: np.typing.NDArray, resolution: int = RESOLUTION, azimuth: float = 270., height_above_ground_m: float = 90., longitude_deg: float = 2.3467954996250784, latitude_deg: float = 48.600518087374105): self.resolution = resolution self.azimuth = azimuth grid_size = azimuth_zenith_grid.shape[0] if grid_size == self.resolution: extended_grid = False elif grid_size == (self.resolution + 2): extended_grid = True else: raise ValueError("Grid size must be equal to resolution for regular grid, or resolution + 2 for extended grid") self.height_above_ground_m = height_above_ground_m self.longitude_deg = longitude_deg self.latitude_deg = latitude_deg # Get azimuth and zenith azimuth_grid = azimuth_zenith_grid[..., 0] zenith_grid = azimuth_zenith_grid[..., 1] # Compute components cos_grid = np.cos(azimuth_grid) sin_grid = np.sin(azimuth_grid) # Generate interpolator values_grid = np.stack([cos_grid, sin_grid, zenith_grid], axis=-1) # Define the regular grid coordinates for the interpolator origin = - 0.5 if extended_grid else 0.5 x = np.arange(grid_size) + origin # Create the interpolator (maps PIXEL in ENCORD (x, y) -> (cos(az), sin(az), zenith)) self.pixel_to_azzen_regular = RegularGridInterpolator( (x, x), values_grid, bounds_error=False, fill_value=None # Extrapolate! ) # Flatten pixel coordinates pixels_flat = np.stack(np.meshgrid(x, x, indexing='ij'), axis=-1).reshape(-1, 2) # Flatten unwrapped azimuth and zenith azimuth_flat = azimuth_grid.flatten() zenith_flat = zenith_grid.flatten() # Combine as input for the interpolator values_flat = np.stack([azimuth_flat, zenith_flat], axis=-1) # Create reverse interpolator: (azimuth (unwarapped), zenith) → (pixel_x, pixel_y) self.azzen_to_pixel_linear = LinearNDInterpolator(values_flat, pixels_flat) def pixel_to_azzen(self, x: np.typing.NDArray, y: np.typing.NDArray) -> Tuple[np.typing.NDArray, np.typing.NDArray]: # Clip pixels to image resolution x = self.resolution - np.asarray(x) y = np.asarray(y) points = np.column_stack((x, y)) # Clip pixels to image resolution points = np.clip(points, a_min=0, a_max=self.resolution) result = self.pixel_to_azzen_regular(points) azimuth_rad = np.arctan2(result[:, 1], result[:, 0]) zenith_rad = result[:, 2] return azimuth_rad, zenith_rad return azimuth_rad, zenith_rad def azzen_to_lonlat(self, azimuth_rad: np.typing.NDArray, zenith_rad: np.typing.NDArray, altitude_m: np.typing.NDArray) -> Tuple[np.typing.NDArray, np.typing.NDArray]: azimuth_rad = np.asarray(azimuth_rad) zenith_rad = np.asarray(zenith_rad) altitude_m = np.asarray(altitude_m) # Convert to elevation angle in radians elevation_angle_rad = (np.pi / 2.) - zenith_rad # Altitude difference delta_altitude_m = altitude_m - self.height_above_ground_m # Distance on surface distance_on_surface_m = delta_altitude_m / np.tan(elevation_angle_rad) # Convert azimuth to degrees azimuth_deg = np.degrees(azimuth_rad) + self.azimuth def compute_direct(azimuth_deg, distance_on_surface_m): result = wgs84.Direct(self.latitude_deg, self.longitude_deg, azimuth_deg, distance_on_surface_m) return result['lon2'], result['lat2'] compute_vec = np.vectorize(compute_direct, otypes=[float, float]) lon2, lat2 = compute_vec(azimuth_deg, distance_on_surface_m) return lon2, lat2 # Load azimuth-zenith grid folder_path = "YOUR_PATH/GVCCS" grid_path = os.path.join(folder_path, "azimuth_zenith_grid.npy") azimuth_zenith_grid = np.load(grid_path) projector = Projector(azimuth_zenith_grid) # Define polygon vertices in pixel coordinates (corners of the image here) pixel_polygon = np.array([ [0, 0], [0, RESOLUTION-1], [RESOLUTION-1, RESOLUTION-1], [RESOLUTION-1, 0] ]) # Interpolate azimuth and zenith for polygon corners azimuths, zeniths = projector.pixel_to_azzen(pixel_polygon[:,0], pixel_polygon[:,1]) # Altitude hypothesis target_altitude_m = 10000 # Convert polygon corners from azimuth/zenith to lat/lon longitudes, latitudes = projector.azzen_to_lonlat(azimuths, zeniths, target_altitude_m) # Close polygon for plotting latitudes = np.append(latitudes, latitudes[0]) longitudes = np.append(longitudes, longitudes[0]) # Plot polygon in lat/lon plt.figure(figsize=(8,6)) plt.plot(longitudes, latitudes, marker='o') plt.plot(longitudes[:1], latitudes[:1], marker='o',color='r') plt.title(f'Polygon projected from pixel coordinates to lat/lon at {target_altitude_m} m altitude') plt.xlabel('Longitude') plt.ylabel('Latitude') plt.grid(True) plt.show() If you use GVCCS in your work and want to compare with our baselines, please cite the following references: Dataset : @dataset{jarry_2025_16419651, author = {Jarry, Gabriel and Very, Philippe and Ballerini, Franck and Dalmau, Ramon}, title = {GVCCS : Ground Visible Camera Contrail Sequences}, month = jul, year = 2025, publisher = {Zenodo}, version = {v1.0}, doi = {10.5281/zenodo.16419651}, url = {https://doi.org/10.5281/zenodo.16419651},} Paper baselines : @misc{jarry2025gvccsdatasetcontrailidentification, title={GVCCS: A Dataset for Contrail Identification and Tracking on Visible Whole Sky Camera Sequences}, author={Gabriel Jarry and Ramon Dalmau and Philippe Very and Franck Ballerini and Stephania-Denisa Bocu}, year={2025}, eprint={2507.18330}, archivePrefix={arXiv}, primaryClass={cs.CV}, url={https://arxiv.org/abs/2507.18330}, } License: CC BY 4.0

Keywords

Environmental impact, Atmospheric sciences, Aerospace engineering, Machine learning, Computer vision, Aviation

  • BIP!
    Impact byBIP!
    citations
    This is an alternative to the "Influence" indicator, which also reflects the overall/total impact of an article in the research community at large, based on the underlying citation network (diachronically).
    0
    popularity
    This indicator reflects the "current" impact/attention (the "hype") of an article in the research community at large, based on the underlying citation network.
    Average
    influence
    This indicator reflects the overall/total impact of an article in the research community at large, based on the underlying citation network (diachronically).
    Average
    impulse
    This indicator reflects the initial momentum of an article directly after its publication, based on the underlying citation network.
    Average
Powered by OpenAIRE graph
Found an issue? Give us feedback
citations
This is an alternative to the "Influence" indicator, which also reflects the overall/total impact of an article in the research community at large, based on the underlying citation network (diachronically).
BIP!Citations provided by BIP!
popularity
This indicator reflects the "current" impact/attention (the "hype") of an article in the research community at large, based on the underlying citation network.
BIP!Popularity provided by BIP!
influence
This indicator reflects the overall/total impact of an article in the research community at large, based on the underlying citation network (diachronically).
BIP!Influence provided by BIP!
impulse
This indicator reflects the initial momentum of an article directly after its publication, based on the underlying citation network.
BIP!Impulse provided by BIP!
0
Average
Average
Average