|
5 | 5 | from os import path as op
|
6 | 6 | import inspect
|
7 | 7 | from functools import wraps
|
| 8 | +import subprocess |
8 | 9 |
|
9 | 10 | import numpy as np
|
10 | 11 | import nibabel as nib
|
@@ -241,7 +242,6 @@ def set_log_level(verbose=None, return_old_level=False):
|
241 | 242 | if verbose not in logging_types:
|
242 | 243 | raise ValueError('verbose must be of a valid type')
|
243 | 244 | verbose = logging_types[verbose]
|
244 |
| - logger = logging.getLogger('surfer') |
245 | 245 | old_verbose = logger.level
|
246 | 246 | logger.setLevel(verbose)
|
247 | 247 | return (old_verbose if return_old_level else None)
|
@@ -273,7 +273,6 @@ def set_log_file(fname=None, output_format='%(message)s', overwrite=None):
|
273 | 273 | but additionally raises a warning to notify the user that log
|
274 | 274 | entries will be appended.
|
275 | 275 | """
|
276 |
| - logger = logging.getLogger('surfer') |
277 | 276 | handlers = logger.handlers
|
278 | 277 | for h in handlers:
|
279 | 278 | if isinstance(h, logging.FileHandler):
|
@@ -634,3 +633,89 @@ def has_fsaverage(subjects_dir=None):
|
634 | 633 |
|
635 | 634 | requires_fsaverage = np.testing.dec.skipif(not has_fsaverage(),
|
636 | 635 | 'Requires fsaverage subject data')
|
| 636 | + |
| 637 | + |
| 638 | +def has_ffmpeg(): |
| 639 | + """Test whether the FFmpeg is available in a subprocess |
| 640 | +
|
| 641 | + Returns |
| 642 | + ------- |
| 643 | + ffmpeg_exists : bool |
| 644 | + True if FFmpeg can be successfully called, False otherwise. |
| 645 | + """ |
| 646 | + try: |
| 647 | + subprocess.call(["ffmpeg"], stdout=subprocess.PIPE, |
| 648 | + stderr=subprocess.PIPE) |
| 649 | + return True |
| 650 | + except OSError: |
| 651 | + return False |
| 652 | + |
| 653 | + |
| 654 | +def assert_ffmpeg_is_available(): |
| 655 | + "Raise a RuntimeError if FFmpeg is not in the PATH" |
| 656 | + if not has_ffmpeg(): |
| 657 | + err = ("FFmpeg is not in the path and is needed for saving " |
| 658 | + "movies. Install FFmpeg and try again. It can be " |
| 659 | + "downlaoded from http://ffmpeg.org/download.html.") |
| 660 | + raise RuntimeError(err) |
| 661 | + |
| 662 | +requires_ffmpeg = np.testing.dec.skipif(not has_ffmpeg(), 'Requires FFmpeg') |
| 663 | + |
| 664 | + |
| 665 | +def ffmpeg(dst, frame_path, framerate=24, codec='mpeg4'): |
| 666 | + """Run FFmpeg in a subprocess to convert an image sequence into a movie |
| 667 | +
|
| 668 | + Parameters |
| 669 | + ---------- |
| 670 | + dst : str |
| 671 | + Destination path. If the extension is not ".mov" or ".avi", ".mov" is |
| 672 | + added. If the file already exists it is overwritten. |
| 673 | + frame_path : str |
| 674 | + Path to the source frames (with a frame number field like '%04d'). |
| 675 | + framerate : float |
| 676 | + Framerate of the movie (frames per second, default 24). |
| 677 | + codec : str |
| 678 | + Codec to use (default 'mpeg4'). |
| 679 | +
|
| 680 | + Notes |
| 681 | + ----- |
| 682 | + Requires FFmpeg to be in the path. FFmpeg can be downlaoded from `here |
| 683 | + <http://ffmpeg.org/download.html>`_. Stdout and stderr are written to the |
| 684 | + logger. If the movie file is not created, a RuntimeError is raised. |
| 685 | + """ |
| 686 | + assert_ffmpeg_is_available() |
| 687 | + |
| 688 | + # find target path |
| 689 | + dst = os.path.expanduser(dst) |
| 690 | + dst = os.path.abspath(dst) |
| 691 | + root, ext = os.path.splitext(dst) |
| 692 | + dirname = os.path.dirname(dst) |
| 693 | + if ext not in ['.mov', '.avi']: |
| 694 | + dst += '.mov' |
| 695 | + |
| 696 | + if os.path.exists(dst): |
| 697 | + os.remove(dst) |
| 698 | + elif not os.path.exists(dirname): |
| 699 | + os.mkdir(dirname) |
| 700 | + |
| 701 | + frame_dir, frame_fmt = os.path.split(frame_path) |
| 702 | + |
| 703 | + # make the movie |
| 704 | + cmd = ['ffmpeg', '-i', frame_fmt, '-r', str(framerate), '-c', codec, dst] |
| 705 | + logger.info("Running FFmpeg with command: %s", ' '.join(cmd)) |
| 706 | + sp = subprocess.Popen(cmd, cwd=frame_dir, stdout=subprocess.PIPE, |
| 707 | + stderr=subprocess.PIPE) |
| 708 | + |
| 709 | + # log stdout and stderr |
| 710 | + stdout, stderr = sp.communicate() |
| 711 | + std_info = os.linesep.join(("FFmpeg stdout", '=' * 25, stdout)) |
| 712 | + logger.info(std_info) |
| 713 | + if stderr.strip(): |
| 714 | + err_info = os.linesep.join(("FFmpeg stderr", '=' * 27, stderr)) |
| 715 | + logger.error(err_info) |
| 716 | + |
| 717 | + # check that movie file is created |
| 718 | + if not os.path.exists(dst): |
| 719 | + err = ("FFmpeg failed, no file created; see log for more more " |
| 720 | + "information.") |
| 721 | + raise RuntimeError(err) |
0 commit comments