{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# **Create an animated time series**" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "[](https://colab.research.google.com/github/kenoz/SITS_utils/blob/main/docs/source/tutorials/colab_sits_ex02.ipynb)" ] }, { "cell_type": "markdown", "metadata": { "id": "K2FIkbDYrq9l" }, "source": [ "---\n", "The `sits` package enables the creation of time series animations, either as animated **GIFs** or **video files**. Its basic approach treats each successive image as a frame in the animation. However, this can lead to choppy playback that fails to accurately represent the temporal dynamics, especially since satellite time series are often irregularly spaced. To address this, `sits` provides tools to **regularize the time axis** through interpolation and applies blending techniques to **smooth transitions** between frames, resulting in more fluid and temporally faithful animations.\n", "\n", "---\n", "\n", "## 1. Installation of `sits` package and its depedencies\n", "\n", "If needed, install `sits` package with [pip](https://pypi.org/project/SITS/). We also need some other packages for displaying data." ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "id": "xoL6NstiVcp9" }, "outputs": [], "source": [ "# SITS package\n", "!pip install -q --upgrade sits" ] }, { "cell_type": "markdown", "metadata": { "id": "OYAb0l91zqj4" }, "source": [ "Now we can import `sits` and some other libraries." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "executionInfo": { "elapsed": 8932, "status": "ok", "timestamp": 1737993800099, "user": { "displayName": "Kenji Ose", "userId": "04036279333112259084" }, "user_tz": -60 }, "id": "neqafgGHWIkj" }, "outputs": [], "source": [ "import os\n", "# sits lib\n", "from sits import sits, export\n", "# date format\n", "from datetime import datetime\n", "# ignore warnings messages\n", "import warnings\n", "warnings.filterwarnings('ignore')" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "!mkdir -p test_data" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Loading the Satellite Image Time-Series (SITS)\n", "\n", "As explained in the [tutorial 01](colab_sits_ex01.ipynb), we load a Sentinel-2 time series over the Banc d'Arguin." ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "tags": [] }, "outputs": [], "source": [ "bbox_4326 = [-1.283356958716803, 44.54723753300113,\n", " -1.195282436226136, 44.63147049370678]\n", "bbox_3035 = [3426472.0201418595, 2448438.7064564982,\n", " 3434719.22278734, 2458751.114093349]\n", "\n", "# instance of the class sits.StacAttack()\n", "ts_S2 = sits.StacAttack(provider='mpc',\n", " collection='sentinel-2-l2a',\n", " bands=['B03', 'B04', 'B08', 'SCL'])\n", "\n", "# search of items based on bbox coordinates and time interval criteria\n", "ts_S2.searchItems(bbox_4326,\n", " date_start=datetime(2016, 1, 1),\n", " date_end=datetime(2025, 1, 1),\n", " query={\"eo:cloud_cover\": {\"lt\": 10}}\n", " )\n", "# load of the time series in a lazy way\n", "ts_S2.loadCube(bbox_3035, resolution=40, crs_out=3035)\n", "# classes used to mask\n", "SCL_mask = [0, 1, 3, 8, 9, 10]\n", "# creation of the SCL mask\n", "ts_S2.mask(mask_band='SCL', mask_values=[SCL_mask])\n", "ts_S2.mask_apply()\n", "ts_S2.gapfill()" ] }, { "cell_type": "markdown", "metadata": { "id": "_umtFUzpXRyu" }, "source": [ "## 3. Convert time series into animated GIF\n", "\n", "`sits` package allows you to export satellite time series as animated GIF, so you can easily show some phenoma that vary in time and space.\n", "\n", "### 3.1. Creating a nice-looking animation\n", "\n", "In the `xarray.Dataarray`, we choose to keep only three spectral bands in order to display color composites (RGB format). To convert the `xarray.Dataarray` object into an animated GIF, `sits` package uses the `geogif` library. So it is possible to add specific arguments, not presented by default." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "colab": { "base_uri": "https://localhost:8080/" }, "executionInfo": { "elapsed": 859, "status": "ok", "timestamp": 1737993946339, "user": { "displayName": "Kenji Ose", "userId": "04036279333112259084" }, "user_tz": -60 }, "id": "v907yZ0KVxsz", "outputId": "f55f5324-2318-4d73-cb5a-a5fb113d01f3" }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 43.1 s, sys: 10.8 s, total: 53.9 s\n", "Wall time: 22.5 s\n" ] } ], "source": [ "%%time\n", "\n", "test = export.Sits_ds()\n", "test.ds = ts_S2.cube.compute()" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 1.62 s, sys: 317 ms, total: 1.94 s\n", "Wall time: 2.33 s\n" ] } ], "source": [ "%%time\n", "\n", "data_dir = 'test_data'\n", "out_gif = os.path.join(data_dir, 'banc_arguin.gif')\n", "test.export2gif(imgfile=out_gif, fps=8, robust=True,\n", " keep_bands=['B08', 'B04', 'B03'])" ] }, { "cell_type": "markdown", "metadata": { "id": "J-0MPm-Gay1O" }, "source": [ "### 3.2. Improving the animation\n", "\n", "By default, the animated GIF displays only the raw images from the data array. Because the acquisition dates are irregular and the spectral differences between consecutive frames can be large, the animation often appears jerky, reminiscent of early 19th-century films.\n", "\n", "To improve visual continuity, the package provides tools to:\n", "\n", "- Regularize the temporal frequency by interpolating pixel values across time\n", "- Apply a blending effect to smooth transitions between frames\n", "\n", "This results in a more fluid and visually coherent animation, especially useful for presentations or exploratory analysis." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "tags": [] }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "CPU times: user 14.9 s, sys: 3.46 s, total: 18.3 s\n", "Wall time: 18.7 s\n" ] } ], "source": [ "%%time\n", "\n", "test.time_interp()\n", "test.blender()\n", "\n", "out_gif2 = os.path.join(data_dir, 'banc_arguin_v2.gif')\n", "test.export2gif(imgfile=out_gif2, fps=16, robust=True,\n", " keep_bands=['B08', 'B04', 'B03'])" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### 3.3. Comparing Raw vs Smoothed Time Series Animations\n", "\n", "To illustrate the impact of temporal regularization and blending, we compare two animations of the same satellite time series. The first shows the raw, irregular frames, while the second presents a smoothed version with interpolated values and softened transitions." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "tags": [] }, "outputs": [], "source": [ "%%time\n", "\n", "from IPython.display import Image, display\n", "display(Image(filename=out_gif))\n", "display(Image(filename=out_gif2))" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "
\n",
"
\n",
"
\n",
"