diff --git a/.gitattributes b/.gitattributes index 50430c1f5..71665598b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,4 +3,5 @@ demo_movies export-ignore .gitignore export-ignore .readthedocs.yaml export-ignore codecov.yml export-ignore -.github export-ignore \ No newline at end of file +.github export-ignore +demo_movies/*.mkv filter=lfs diff=lfs merge=lfs -text diff --git a/demo_movies/sample1.mkv b/demo_movies/sample1.mkv new file mode 100644 index 000000000..de389f6b9 --- /dev/null +++ b/demo_movies/sample1.mkv @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ebea009f040f900b59732552616805e9a6e9c4ddf173d26a10ac4d30f388ef70 +size 156097649 diff --git a/minian/utilities.py b/minian/utilities.py index a728a0c9e..273a63e30 100755 --- a/minian/utilities.py +++ b/minian/utilities.py @@ -225,7 +225,15 @@ def load_avi_lazy(fname: str) -> darr.array: video_info = next(s for s in probe["streams"] if s["codec_type"] == "video") w = int(video_info["width"]) h = int(video_info["height"]) - f = int(video_info["nb_frames"]) + if "nb_frames" in video_info: + f = int(video_info["nb_frames"]) + else: + r_fps = video_info["r_frame_rate"] + num, denom = r_fps.split("/") + fps = float(num) / float(denom) + f = float(probe["format"]["duration"]) * fps + f = np.round(f).astype(int) + return da.array.from_delayed( da.delayed(load_avi_ffmpeg)(fname, h, w, f), dtype=np.uint8, shape=(f, h, w) ) @@ -257,6 +265,7 @@ def load_avi_ffmpeg(fname: str, h: int, w: int, f: int) -> np.ndarray: out_bytes, err = ( ffmpeg.input(fname) .video.output("pipe:", format="rawvideo", pix_fmt="gray") + .global_args("-hide_banner", "-nostats") .run(capture_stdout=True) ) return np.frombuffer(out_bytes, np.uint8).reshape(f, h, w) diff --git a/minian/visualization.py b/minian/visualization.py index 5c0f70f81..76326d815 100755 --- a/minian/visualization.py +++ b/minian/visualization.py @@ -1217,7 +1217,9 @@ def write_video( vname: Optional[str] = None, vpath: Optional[str] = ".", norm=True, + vcodec="libx264", options={"crf": "18", "preset": "ultrafast"}, + verbose=True, ) -> str: """ Write a video from a movie array using `python-ffmpeg`. @@ -1235,9 +1237,13 @@ def write_video( norm : bool, optional Whether to normalize the values of the input array such that they span the full pixel depth range (0, 255). By default `True`. + vcodec : str, optional + Name of the ffmpeg video encoder to use. By default `"libx264"`. options : dict, optional Optional output arguments passed to `ffmpeg`. By default `{"crf": "18", "preset": "ultrafast"}`. + verbose : bool, optional + Display verbose ffmpeg output. By default `True`. Returns ------- @@ -1265,11 +1271,15 @@ def write_video( arr *= 255 arr = arr.clip(0, 255).astype(np.uint8) w, h = arr.sizes["width"], arr.sizes["height"] + verbosity_args = ["-hide_banner", "-nostats"] + if verbose: + verbosity_args = [] process = ( ffmpeg.input("pipe:", format="rawvideo", pix_fmt="gray", s="{}x{}".format(w, h)) .filter("pad", int(np.ceil(w / 2) * 2), int(np.ceil(h / 2) * 2)) - .output(fname, pix_fmt="yuv420p", vcodec="libx264", r=30, **options) + .output(fname, pix_fmt="yuv420p", vcodec=vcodec, r=30, **options) .overwrite_output() + .global_args(*verbosity_args) .run_async(pipe_stdin=True) ) for blk in arr.data.blocks: @@ -1306,7 +1316,9 @@ def generate_videos( gain=1.5, vpath=".", vname="minian.mp4", + vcodec="libx264", options={"crf": "18", "preset": "ultrafast"}, + verbose=True, ) -> str: """ Generate a video visualizaing the result of minian pipeline. @@ -1351,9 +1363,13 @@ def generate_videos( Desired folder containing the resulting video. By default `"."`. vname : str, optional Desired name of the video. By default `"minian.mp4"`. + vcodec : str, optional + Name of the ffmpeg video encoder to use. By default `"libx264"`. options : dict, optional Output options for `ffmpeg`, passed directly to :func:`write_video`. By default `{"crf": "18", "preset": "ultrafast"}`. + verbose : bool, optional + Display verbose ffmpeg output. By default `True`. Returns ------- @@ -1388,7 +1404,9 @@ def generate_videos( "height", coords="minimal", ) - return write_video(vid, vname, vpath, norm=False, options=options) + return write_video( + vid, vname, vpath, norm=False, vcodec=vcodec, options=options, verbose=verbose + ) def datashade_ndcurve(