Skip to content

Commit

Permalink
Adding automation for video creation (#3)
Browse files Browse the repository at this point in the history
* Update overlay script to generate a map-overlaid video

* Exclude .outfile work directory from git

* Remove .outfile directory

* Fix a constant fps to variable fps

* Print ffmpeg command to be used for combining.

* Fix gitignore to ignore .outfile directory

* Retain original streams

* Retain original streams

* Remove .outfile

* Remove .outfile

* preparing for automation

* Update README.md

* Update automation

Adjust overlay video and width based on main video width height, update cli arguments of main.py, overlay offset based on HERO or 360 (defined in settings)

* Add exiftool to detect 360 video and add variables for overlay dimension and offset

* Update requirements.tx and variables.txt

* Freeze requirements.txt

* Delete msilib

* Add tk package to requirements.txt

* Remove tk dependency

* Fix error round on str

* Use avi container and xvid codec for creating overlay video

* restore main.py

* Ignore no 'codec_name' streams

* Copy global metadata using exiftool

* Update cli command

* Fix multiline.geojson and map overlay video location

* Fix equirectangular detection

* Fix multiline.geojson not found

* Fix error

* Fix individual custom value of w/h offset l/b

* Revert main.py

* updating readme

Co-authored-by: swdmnd <[email protected]>
  • Loading branch information
himynamesdave and swdmnd authored May 5, 2022
1 parent 1294ca8 commit dc92eee
Show file tree
Hide file tree
Showing 22 changed files with 404 additions and 406 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -160,4 +160,6 @@ cython_debug/
# End of https://www.toptal.com/developers/gitignore/api/python
/mapbox-images
.DS_Store
*.geojson
.outfile/
*.geojson
exiftool.exe
160 changes: 78 additions & 82 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,118 +2,114 @@

Use GoPro telemetry to generate a picture-in-picture map.

## Prerequisites

1. First extract the telemetry file (as json) from your GoPro video using [gopro-telemetry](https://github.com/JuanIrache/gopro-telemetry/). [Detailed instructions about how to do this can be found in this post](https://www.trekview.org/blog/2022/gopro-telemetry-exporter-getting-started/).
2. Install required packages `pip3 install -r requirements.txt`
3. Fill in the `variables.txt` with your Mapbox API Key and username, and other variables if needed (see `variables.txt` section on this page.

## Usage

0. First extract the telemetry file (as json) from your GoPro video using [gopro-telemetry](https://github.com/JuanIrache/gopro-telemetry/). [Detailed instructions about how to do this can be found in this post](https://www.trekview.org/blog/2022/gopro-telemetry-exporter-getting-started/).
1. Install required packages `pip3 install -r requirements.txt`
2. Fill in the `variables.txt` with your Mapbox API Key and username, and other variables if needed.
3. Run `python3 main.py -f TELEMETRY.json`, replacing `TELEMETRY.json` with the file created at step 0. This will generate a `multiline.geojson` file, upload it to a new style in your Mapbox accountg and generate .jpg map images in the directory `mapbox-images/`. This process will happen at `services/geojson_service/generate_images()`.
`python3 main.py -f TELEMETRY.json -i INPUT.mp4 -o OUT_DIRECTORY`

* `-f`: input telemetry json file (see prerequisites)
* `-i`: input video file you generated telemetry json from (and map should be overlaid on)
* `-o`: output directory for final video file with overlay

### Example usage

```
python3 main.py -f GX010044-gps-only.json -i GX010044.MP4 -o video-overlay/
```

### `variables.txt`

To run the script you need to set the required variables (and optional variables if you don't want to use defaults):

* `mapbox_key` (required): your MapBox API key. [You can get a MapBox API key here](https://account.mapbox.com/) that will allow you [50,000 free static image lookups each month](https://www.mapbox.com/pricing/#glstatic). Ensure your key has the following permissions when you create it; `Styles:Tiles`, `Styles:Read`, `Styles:Write`, `Styles:List`, `Styles:Download`, `Styles:Protect`, `Fonts:Read`, `Datasets:Read`, `Vision:Read`, `Uploads:Read`, `Uploads:List`, `Uploads:Write`, `Tilesets:Read`, `Tilesets:List` and `Tilesets:Write`
* Default: null
* Default: ''
* `mapbox_username` (required): you MapBox username/account name. [You can see this under your account settings in MapBox](https://account.mapbox.com/)
* Default: null
* Default: ''
* `mapbox_base_style`: [Set your basemap style](https://docs.mapbox.com/api/maps/styles/). Options include (but not limited to): `mapbox/streets-v11`, `mapbox/outdoors-v11`, `mapbox/satellite-v9`, and `mapbox/satellite-streets-v11`
* Default: `mapbox/outdoors-v11`
* `mapbox_user_style`: If you want to reuse a previous style you can pass it here like so `trekview/cl20cn42p009i15o97k316e8u` (this variable is not needed 99% of the time).
* Default: null
* Default: ''
* `mapbox_marker_label`: You can add a label inside the marker. It can be a digit (0-9), a letter (a-z) or a Maki icon (without svg extension, i.e: circle). [List of available icons](https://labs.mapbox.com/maki-icons/).
* Default: ''
* `mapbox_img_w`: defines the width of the image for overlay. Recommended is 20% of video input width. Image height will be generated automatically based of 4:3 resolution.
* Default: `500`
* `mapbox_img_w`: defines the width of the image for overlay as a ratio of input video.
* `0.2` (20%) of input video width (for HERO)
* `0.1` (10%) of input video width (for equirectangular)
* `mapbox_img_h`: defines the height of the image for overlay as a ratio of input video.
* `0.2` (20%) of input video width (for HERO)
* `0.1` (10%) of input video width (for equirectangular)
* `mapbox_zoom_level`: the zoom level for the map (recommended between 8-10). [See MapBox docs for more](https://docs.mapbox.com/help/glossary/zoom-level/). Between 0 and 22. In short, the higher the zoom number, the closer to the ground the zoom is.
* Default: 10
* Default: `10`
* `mapbox_line_colour_hex`: the colour you want for the line passed as a 6 digit hex code
* Default: `E48241`
* Default: #`E48241`
* `mapbox_marker_colour_hex`: the colour you want for the map point, passed as a 6 digit hex code (e.g. `000000` for black)
* Default: `000000`
* Default: #`000000`
* `mapbox_line_width`: the width in pixels for the linestring.
* Default: `1`
* `video_overlay_l_offset`: Left pixel padding
* Default: 40% of video width
* `video_overlay_t_offset`: Top pixel padding
* Default: 60% of video width
* Default: `1`px
* `video_overlay_b_offset`: defines the bottom offset of the image for overlay as a ratio of input video. Could be set manually to ratio value or px value (20px means 20 pixels off from bottom).
* Default:
* `0.02` (2%) video height (for HERO)
* `0.3` (30%) video height (for equirectangular)
* `video_overlay_l_offset`: defines the left offset of the image for overlay as a ratio of input video. Could be set manually to ratio value or px value (20px means 20 pixels off from left).
* Default:
* `0.02` (2%) video width (for HERO)
* `0.3` (30%) video height (for equirectangular)

#### Sample varible for common GoPro video sizes

## Sample varible for common GoPro video sizes
##### HERO video

### HERO video @ 5.2k
An example `variables.txt` with some custom values;

```
mapbox_key: YOUR_KEY
mapbox_username: YOUR_USER
mapbox_base_style: outdoors
mapbox_zoom_level: 10
mapbox_img_w: 500
mapbox_line_colour_hex: E48241
mapbox_marker_colour_hex: 000000
video_overlay_l_offset_pc:
video_overlay_t_offset_pc:
mapbox_base_style: mapbox/outdoors-v11
mapbox_user_style:
mapbox_marker_label: circle
mapbox_zoom_level: 15
mapbox_line_colour_hex: ffffff
mapbox_marker_colour_hex: 3bb2d0
mapbox_line_width: 1
mapbox_img_w: 0.2
mapbox_img_h: 0.2
video_overlay_b_offset: 0.02
video_overlay_l_offset: 0.02
```

`python3 main.py -f GS018422-gps-only.json`

### 360 video @ 5.6k


`python3 main.py -f GS018422.json`


## Todo

1. Automate step 3 by creating a mapbox style programatically
2. Merge step 2 and 3 into a single command
3. Add variables to MapBox Scripts
4. Add the final logic to overlay mapbox images on video (to not be done yet)
5. Bundle everything into a single script, taking `variables.txt` and an input json via the command line.

## Overlay Scripts
##### 360 video

There are 2 scripts: overlay.py (ffmpeg) and overlay_cv2.py (opencv)
An example `variables.txt` with some custom values;

### overlay.py

This script requires ffmpeg installed in the system and ffmpeg-python from pip. For example, in Ubuntu:

# install ffmpeg
sudo apt install ffmpeg

# install ffmpeg-python (assuming it is python 3, or use pip3)
pip install ffmpeg-python

To run this script:

python overlay.py [json] [image_directory] [main_video] [output_video_path]

not supplying any of the arguments will result in default value for all arguments.

Default values:

json = "data.json"
image_directory = './images/'
main_video = 'sample.mp4'
output_video_path = 'out.mp4'

### overlay_cv2.py

This script requires opencv. To install it, run:

pip install opencv-python

To run this script:

python overlay_cv2.py [json] [image_directory] [main_video]

not supplying any of the arguments will result in default value for all arguments.
```
mapbox_key: YOUR_KEY
mapbox_username: YOUR_USER
mapbox_base_style: mapbox/outdoors-v11
mapbox_user_style:
mapbox_marker_label: circle
mapbox_zoom_level: 15
mapbox_line_colour_hex: ffffff
mapbox_marker_colour_hex: 3bb2d0
mapbox_line_width: 1
mapbox_img_w: 0.1
mapbox_img_h: 0.1
video_overlay_b_offset: 0.3
video_overlay_l_offset: 0.3
```

Default values:
## How it works

json = "data.json"
image_directory = './images/'
main_video = 'sample.mp4'
1. Script detects if video is equirectangular or normal hero video using exiftool
2. a `multiline.geojson` file from all .json telemetry points
3. the `multiline.geojson` file is uploaded as a new style to the specified MapBox account
4. the script used the new style which is used to generate a .jpg map images for each point in the `mapbox-images/` directory.
5. The resulting images are wrapped up into a video containing map images at frame spacing rate defined in the telemetry json.
6. The map video is overlaid using ffmpeg to the original video (based on default values or `variables.txt` values) and all streams copied from original video (e.g. video, sound, and telemetry) to the new final video
7. exiftool is used to copy global metadata to the new final video

## License

Expand Down
31 changes: 25 additions & 6 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,38 @@
import argparse
from time import sleep

import os
from services.geojson_service import generate_images, get_data
from services.mapbox_service import create_base_style
from settings import MAPBOX_USER_STYLE
from settings import MAPBOX_USER_STYLE, set_working_directory
import overlay

parser = argparse.ArgumentParser()
files_dir = "geojson-files"

parser.add_argument("-f", "--file", help="Telemetry Filename Including .json")
parser.add_argument("-i", "--input", help="Input video file name.")
parser.add_argument("-o", "--output", help="Output directory, if it doesn't exist, it will be created.")
args = parser.parse_args()

if not args.file:
print(
"Please provide the telemetry filename (in base directory) using the -f flag, e.g. python3 main.py -f telemetry.json"
)
if not args.file or not args.input or not args.output:
parser.print_help()
exit()

# check if input video exists
if not os.path.isfile(args.input):
raise Exception("Input video file not found!")

# check output directory, if it is exist as a file, throw an error
if os.path.exists(args.output):
if os.path.isfile(args.output):
raise Exception("There is a file with the same name as the specified output directory!")
else:
os.mkdir(args.output)

# set working directory
set_working_directory(args.output)

overlay.set_overlay_dimensions(args.input)

images_data = get_data(args.file)
# override style recreation (to reuse one created previously, for example)
Expand All @@ -26,3 +42,6 @@
sleep(5)
# call the image generator method
generate_images(MAPBOX_USER_STYLE, images_data)

# start creating overlay
overlay.create(args.file, args.input)
Loading

0 comments on commit dc92eee

Please sign in to comment.