From b345caf40fb64875ec3c3af0d5377a95ed2e6d29 Mon Sep 17 00:00:00 2001 From: Arthur Jinyue Guo Date: Thu, 14 Aug 2025 17:48:06 +0200 Subject: [PATCH 1/3] Add gopro_360 projection conversion --- .gitmodules | 3 + musicalgestures/_360video.py | 69 ++++++++++++++++++++++- musicalgestures/_video.py | 1 + musicalgestures/gopromax-conversion-tools | 1 + 4 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 .gitmodules create mode 160000 musicalgestures/gopromax-conversion-tools diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..0da4228 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "musicalgestures/gopromax-conversion-tools"] + path = musicalgestures/gopromax-conversion-tools + url = https://github.com/alexarje/gopromax-conversion-tools.git diff --git a/musicalgestures/_360video.py b/musicalgestures/_360video.py index 287a244..baca8f8 100644 --- a/musicalgestures/_360video.py +++ b/musicalgestures/_360video.py @@ -1,7 +1,9 @@ import os +import subprocess from enum import Enum -from functools import partial from typing import Dict, Union +from pathlib import Path +from functools import partial from musicalgestures._video import MgVideo from musicalgestures._utils import ffmpeg_cmd, get_length, generate_outfilename @@ -44,6 +46,7 @@ class Projection(Enum): equirectangular = 30 # extra option for equirectangular erp = 31 + gopro_360 = 32 # special gopro .360 format def __str__(self): # collapse all aliases of erp @@ -125,6 +128,7 @@ def convert_projection( target_projection: Union[Projection, str], options: Dict[str, str] = None, print_cmd: bool = False, + test: bool = False, ): """ Convert the video to a different projection. @@ -138,6 +142,48 @@ def convert_projection( if target_projection == self.projection: print(f"{self} is already in target projection {target_projection}.") return + elif self.projection == Projection.gopro_360: + if test: + print( + f"=> Test mode: would convert {self.filename} to {target_projection} with options {options}." + ) + + # use special gopro conversion scripts + assert target_projection in [ + Projection.equirect, + Projection.equirectangular, + Projection.dfisheye, + ], ( + f"Invalid target projection from gopro_360: {target_projection}, only equirect, equirectangular, and dfisheye are supported." + ) + + output_name = generate_outfilename( + f"{self.filename.split('.')[0]}_{target_projection}.mp4" + ) + if target_projection == Projection.dfisheye: + script = "ffmpeg-convert-dual-fisheye.sh" + else: + script = "ffmpeg-convert-v3.sh" + script_path = Path(__file__).parent / "gopromax-conversion-tools/scripts" / script + + cmds = [ + script_path, + "-i", + self.filename, + "-n", + output_name, + ] + if options: + for k, v in options.items(): + cmds.append(k) + cmds.append(v) + + if test: + print(f"=> Command: {' '.join([str(cmd) for cmd in cmds])}") + subprocess.run(cmds) + self.filename = output_name + self.projection = target_projection + else: output_name = generate_outfilename( f"{self.filename.split('.')[0]}_{target_projection}.mp4" @@ -191,3 +237,24 @@ def _parse_projection(self, projection: Union[str, Projection]): return projection else: raise TypeError(f"Unsupported projection type: '{type(projection)}'.") + + +## testing gopro_360 conversion +if __name__ == "__main__": + video = Mg360Video("2023-01-01-GS010008.360", Projection.gopro_360) + video.convert_projection( + Projection.equirect, + options={"-r": "0:180:0", "-s": "00:01:10", "-t": "00:01:20"}, + test=True, + ) + print(f"=> Converted video path: {video.filename}") + print(f"=> Converted video projection: {video.projection}") + + video = Mg360Video("2023-01-01-GS010008.360", Projection.gopro_360) + video.convert_projection( + Projection.dfisheye, + options={"-s": "00:01:10", "-t": "00:01:20"}, + test=True, + ) + print(f"=> Converted video path: {video.filename}") + print(f"=> Converted video projection: {video.projection}") diff --git a/musicalgestures/_video.py b/musicalgestures/_video.py index 4b10571..948dcde 100644 --- a/musicalgestures/_video.py +++ b/musicalgestures/_video.py @@ -230,6 +230,7 @@ def get_video(self): ".ts", ".wmv", ".3gp", + ".360", ] if self.fex not in video_formats: # Check if it is an image file diff --git a/musicalgestures/gopromax-conversion-tools b/musicalgestures/gopromax-conversion-tools new file mode 160000 index 0000000..161c840 --- /dev/null +++ b/musicalgestures/gopromax-conversion-tools @@ -0,0 +1 @@ +Subproject commit 161c84025c26e70ede6caa981ade6534604a6e6b From 9e74843c1628b26daf05ffe3686912e4197fd1e6 Mon Sep 17 00:00:00 2001 From: fisheggg <33652918+fisheggg@users.noreply.github.com> Date: Thu, 14 Aug 2025 15:48:31 +0000 Subject: [PATCH 2/3] Update documentation --- docs/musicalgestures/_360video.md | 7 ++++--- docs/musicalgestures/_video.md | 6 +++--- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/musicalgestures/_360video.md b/docs/musicalgestures/_360video.md index 05ece71..579c30c 100644 --- a/docs/musicalgestures/_360video.md +++ b/docs/musicalgestures/_360video.md @@ -9,7 +9,7 @@ ## Mg360Video -[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_360video.py#L91) +[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_360video.py#L94) ```python class Mg360Video(MgVideo): @@ -30,13 +30,14 @@ Class for 360 videos. ### Mg360Video().convert_projection -[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_360video.py#L123) +[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_360video.py#L126) ```python def convert_projection( target_projection: Union[Projection, str], options: Dict[str, str] = None, print_cmd: bool = False, + test: bool = False, ): ``` @@ -54,7 +55,7 @@ options (Dict[str, str], optional): Options for the conversion. Defaults to None ## Projection -[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_360video.py#L9) +[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_360video.py#L11) ```python class Projection(Enum): diff --git a/docs/musicalgestures/_video.md b/docs/musicalgestures/_video.md index 4834907..3e93df6 100644 --- a/docs/musicalgestures/_video.md +++ b/docs/musicalgestures/_video.md @@ -79,7 +79,7 @@ Creates an average image of all frames in the video. ### MgVideo().extract_frame -[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_video.py#L330) +[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_video.py#L331) ```python def extract_frame(**kwargs): @@ -101,7 +101,7 @@ see _utils.extract_frame for details. ### MgVideo().from_numpy -[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_video.py#L290) +[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_video.py#L291) ```python def from_numpy(array, fps, target_name=None): @@ -119,7 +119,7 @@ Creates a video attribute to the Musical Gestures object with the given correct ### MgVideo().numpy -[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_video.py#L277) +[[find in source code]](https://github.com/fourMs/MGT-python/blob/master/musicalgestures/_video.py#L278) ```python def numpy(): From 6fca90abfd7dfc68a55a9eebbc4a0e631a7411ee Mon Sep 17 00:00:00 2001 From: Arthur Jinyue Guo Date: Fri, 15 Aug 2025 14:57:28 +0200 Subject: [PATCH 3/3] Increase version number --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index a2d762e..fa0eddc 100644 --- a/setup.py +++ b/setup.py @@ -12,7 +12,7 @@ setup( name='musicalgestures', packages=['musicalgestures'], - version='v1.3.2', + version='v1.3.3', license='GNU General Public License v3 (GPLv3)', description='Musical Gestures Toolbox for Python', long_description=README,