Skip to content

deeplabcut.create_project.new

Functions:

Name Description
create_new_project

Create the necessary folders and files for a new project.

create_new_project

create_new_project(
    project: str,
    experimenter: str,
    videos: list[str],
    working_directory: str | None = None,
    copy_videos: bool = False,
    video_extensions: str | Sequence[str] | None = None,
    multianimal: bool = False,
    individuals: list[str] | None = None,
)

Create the necessary folders and files for a new project.

Creating a new project involves creating the project directory, sub-directories and a basic configuration file. The configuration file is loaded with the default values. Change its parameters to your projects need.

Parameters

project : string The name of the project.

string

The name of the experimenter.

list[str]

A list of strings representing the full paths of the videos or video-directories to include in the project.

video_extensions (str | Sequence[str] | None, default=None): Controls how videos are filtered, based on file extension. File paths and directory contents are treated differently: - None (default): file paths are accepted as-is; directories are scanned for files with a recognized video extension. - str or Sequence[str] (e.g. "mp4" or ["mp4", "avi"]): both file paths and directory contents are filtered by the given extension(s).

string, optional

The directory where the project will be created. The default is the current working directory.

bool, optional, Default: False.

If True, the videos are copied to the videos directory. If False, symlinks of the videos will be created in the project/videos directory; in the event of a failure to create symbolic links, videos will be moved instead.

bool, optional. Default: False.

For creating a multi-animal project (introduced in DLC 2.2)

list[str]|None = None,

Relevant only if multianimal is True. list of individuals to be used in the project configuration. If None - defaults to ['individual1', 'individual2', 'individual3']

Returns

str Path to the new project configuration file.

Raises

FileNotFoundError If a non-existent path is passed to videos.

Examples

Linux/MacOS:

deeplabcut.create_new_project( project='reaching-task', experimenter='Linus', videos=[ '/data/videos/mouse1.avi', '/data/videos/mouse2.avi', '/data/videos/mouse3.avi' ], working_directory='/analysis/project/', ) deeplabcut.create_new_project( project='reaching-task', experimenter='Linus', videos=['/data/videos'], video_extensions='.mp4', )

Windows:

deeplabcut.create_new_project( 'reaching-task', 'Bill', [r'C:\yourusername\rig-95\Videos\reachingvideo1.avi'], copy_videos=True, )

Users must format paths with either: r'C:\ OR 'C:\ <- i.e. a double backslash \ \ )

Source code in deeplabcut/create_project/new.py
@renamed_parameter(old="videotype", new="video_extensions", since="3.0.0")
def create_new_project(
    project: str,
    experimenter: str,
    videos: list[str],
    working_directory: str | None = None,
    copy_videos: bool = False,
    video_extensions: str | Sequence[str] | None = None,
    multianimal: bool = False,
    individuals: list[str] | None = None,
):
    r"""Create the necessary folders and files for a new project.

    Creating a new project involves creating the project directory, sub-directories and
    a basic configuration file. The configuration file is loaded with the default
    values. Change its parameters to your projects need.

    Parameters
    ----------
    project : string
        The name of the project.

    experimenter : string
        The name of the experimenter.

    videos : list[str]
        A list of strings representing the full paths of the videos or video-directories
        to include in the project.

    video_extensions (str | Sequence[str] | None, default=None):
        Controls how ``videos`` are filtered, based on file extension.
        File paths and directory contents are treated differently:
        - ``None`` (default): file paths are accepted as-is; directories are
          scanned for files with a recognized video extension.
        - ``str`` or ``Sequence[str]`` (e.g. ``"mp4"`` or ``["mp4", "avi"]``):
          both file paths and directory contents are filtered by the given
          extension(s).

    working_directory : string, optional
        The directory where the project will be created. The default is the
        ``current working directory``.

    copy_videos : bool, optional, Default: False.
        If True, the videos are copied to the ``videos`` directory. If False, symlinks
        of the videos will be created in the ``project/videos`` directory; in the event
        of a failure to create symbolic links, videos will be moved instead.

    multianimal: bool, optional. Default: False.
        For creating a multi-animal project (introduced in DLC 2.2)

    individuals: list[str]|None = None,
        Relevant only if multianimal is True.
        list of individuals to be used in the project configuration.
        If None - defaults to ['individual1', 'individual2', 'individual3']

    Returns
    -------
    str
        Path to the new project configuration file.

    Raises
    ------
    FileNotFoundError
        If a non-existent path is passed to ``videos``.

    Examples
    --------

    Linux/MacOS:

    >>> deeplabcut.create_new_project(
            project='reaching-task',
            experimenter='Linus',
            videos=[
                '/data/videos/mouse1.avi',
                '/data/videos/mouse2.avi',
                '/data/videos/mouse3.avi'
            ],
            working_directory='/analysis/project/',
        )
    >>> deeplabcut.create_new_project(
            project='reaching-task',
            experimenter='Linus',
            videos=['/data/videos'],
            video_extensions='.mp4',
        )

    Windows:

    >>> deeplabcut.create_new_project(
            'reaching-task',
            'Bill',
            [r'C:\yourusername\rig-95\Videos\reachingvideo1.avi'],
            copy_videos=True,
        )

    Users must format paths with either:
    r'C:\ OR 'C:\\ <- i.e. a double backslash \ \ )
    """
    from datetime import datetime as dt

    from deeplabcut.utils import auxiliaryfunctions

    months_3letter = {
        1: "Jan",
        2: "Feb",
        3: "Mar",
        4: "Apr",
        5: "May",
        6: "Jun",
        7: "Jul",
        8: "Aug",
        9: "Sep",
        10: "Oct",
        11: "Nov",
        12: "Dec",
    }

    date = dt.today()
    month = months_3letter[date.month]
    day = date.day
    d = str(month[0:3] + str(day))
    date = dt.today().strftime("%Y-%m-%d")
    if working_directory is None:
        working_directory = "."
    wd = Path(working_directory).resolve()
    project_name = f"{project}-{experimenter}-{date}"
    project_path = wd / project_name

    # Create project and sub-directories
    if not DEBUG and project_path.exists():
        print(f'Project "{project_path}" already exists!')
        return os.path.join(str(project_path), "config.yaml")
    video_path = project_path / "videos"
    data_path = project_path / "labeled-data"
    shuffles_path = project_path / "training-datasets"
    results_path = project_path / "dlc-models"
    for p in [video_path, data_path, shuffles_path, results_path]:
        p.mkdir(parents=True, exist_ok=DEBUG)
        print(f'Created "{p}"')

    # Add all videos in the folder. Multiple folders can be passed in a list,
    # similar to the video files. Folders and video files can also be passed!
    collected_videos: list[Path] = collect_video_paths(videos, extensions=video_extensions)

    # TODO @deruyter92 2026-05-20: Move this verbosity block to `collect_video_paths` instead
    files_per_dir: dict[Path, int] = {}
    for f in collected_videos:
        files_per_dir[f.parent] = files_per_dir.get(f.parent, 0) + 1
    for dir, count in files_per_dir.items():
        print(f"Found {count} videos in {dir}")
    for p in (Path(v) for v in videos if Path(v).is_dir()):
        if p.resolve() not in {d.resolve() for d in files_per_dir}:
            print(f"No videos found in {p}")
            print(f"Perhaps change the video_extensions, which is currently set to: {video_extensions}")

    videos = collected_videos
    dirs = [data_path / i.stem for i in videos]
    for p in dirs:
        """Creates directory under data."""
        p.mkdir(parents=True, exist_ok=True)

    destinations = [video_path.joinpath(vp.name) for vp in videos]
    if copy_videos:
        print("Copying the videos")
        for src, dst in zip(videos, destinations, strict=False):
            shutil.copy(os.fspath(src), os.fspath(dst))  # https://www.python.org/dev/peps/pep-0519/
    else:
        # creates the symlinks of the video and puts it in the videos directory.
        print("Attempting to create a symbolic link of the video ...")
        for src, dst in zip(videos, destinations, strict=False):
            if dst.exists() and not DEBUG:
                raise FileExistsError(f"Video {dst} exists already!")
            try:
                src = str(src)
                dst = str(dst)
                os.symlink(src, dst)
                print(f"Created the symlink of {src} to {dst}")
            except OSError:
                try:
                    import subprocess

                    subprocess.check_call(f"mklink {dst} {src}", shell=True)
                except (OSError, subprocess.CalledProcessError):
                    print("Symlink creation impossible (exFat architecture?): copying the video instead.")
                    shutil.copy(os.fspath(src), os.fspath(dst))
                    print(f"{src} copied to {dst}")
            videos = destinations

    if copy_videos:
        videos = destinations  # in this case the *new* location should be added to the config file

    # adds the video list to the config.yaml file
    video_sets = {}
    for video in videos:
        print(video)
        try:
            # For windows os.path.realpath does not work and does not link to the real
            # video. [old: rel_video_path = os.path.realpath(video)]
            rel_video_path = str(Path.resolve(Path(video)))
        except Exception:
            rel_video_path = os.readlink(str(video))

        try:
            vid = VideoReader(rel_video_path)
            video_sets[rel_video_path] = {"crop": ", ".join(map(str, vid.get_bbox()))}
        except OSError:
            warnings.warn("Cannot open the video file! Skipping to the next one...", stacklevel=2)
            os.remove(video)  # Removing the video or link from the project

    if not len(video_sets):
        # Silently sweep the files that were already written.
        shutil.rmtree(project_path, ignore_errors=True)
        warnings.warn(
            "No valid videos were found. The project was not created... "
            "Verify the video files and re-create the project.",
            stacklevel=2,
        )
        return "nothingcreated"

    # Set values to config file:
    if multianimal:  # parameters specific to multianimal project
        cfg_file, ruamelFile = auxiliaryfunctions.create_config_template(multianimal)
        cfg_file["multianimalproject"] = multianimal
        cfg_file["identity"] = False
        cfg_file["individuals"] = individuals if individuals else ["individual1", "individual2", "individual3"]
        cfg_file["multianimalbodyparts"] = ["bodypart1", "bodypart2", "bodypart3"]
        cfg_file["uniquebodyparts"] = []
        cfg_file["bodyparts"] = "MULTI!"
        cfg_file["skeleton"] = [
            ["bodypart1", "bodypart2"],
            ["bodypart2", "bodypart3"],
            ["bodypart1", "bodypart3"],
        ]
        engine = cfg_file.get("engine")
        if engine in Engine.PYTORCH.aliases:
            cfg_file["default_augmenter"] = "albumentations"
            cfg_file["default_net_type"] = "resnet_50"
        elif engine in Engine.TF.aliases:
            cfg_file["default_augmenter"] = "multi-animal-imgaug"
            cfg_file["default_net_type"] = "dlcrnet_ms5"
        else:
            raise ValueError(f"Unknown or undefined engine {engine}")
        cfg_file["default_track_method"] = "ellipse"
    else:
        cfg_file, ruamelFile = auxiliaryfunctions.create_config_template()
        cfg_file["multianimalproject"] = False
        cfg_file["bodyparts"] = ["bodypart1", "bodypart2", "bodypart3", "objectA"]
        cfg_file["skeleton"] = [["bodypart1", "bodypart2"], ["objectA", "bodypart3"]]
        cfg_file["default_augmenter"] = "default"
        cfg_file["default_net_type"] = "resnet_50"

    # common parameters:
    cfg_file["Task"] = project
    cfg_file["scorer"] = experimenter
    cfg_file["video_sets"] = video_sets
    cfg_file["project_path"] = str(project_path)
    cfg_file["date"] = d
    cfg_file["cropping"] = False
    cfg_file["start"] = 0
    cfg_file["stop"] = 1
    cfg_file["numframes2pick"] = 20
    cfg_file["TrainingFraction"] = [0.95]
    cfg_file["iteration"] = 0
    cfg_file["snapshotindex"] = -1
    cfg_file["detector_snapshotindex"] = -1
    cfg_file["x1"] = 0
    cfg_file["x2"] = 640
    cfg_file["y1"] = 277
    cfg_file["y2"] = 624
    cfg_file["batch_size"] = (
        8  # batch size during inference (video - analysis); see https://www.biorxiv.org/content/early/2018/10/30/457242
    )
    cfg_file["detector_batch_size"] = 1
    cfg_file["corner2move2"] = (50, 50)
    cfg_file["move2corner"] = True
    cfg_file["skeleton_color"] = "black"
    cfg_file["pcutoff"] = 0.6
    cfg_file["dotsize"] = 12  # for plots size of dots
    cfg_file["alphavalue"] = 0.7  # for plots transparency of markers
    cfg_file["colormap"] = "rainbow"  # for plots type of colormap

    projconfigfile = os.path.join(str(project_path), "config.yaml")
    # Write dictionary to yaml  config file
    auxiliaryfunctions.write_config(projconfigfile, cfg_file)

    print('Generated "{}"'.format(project_path / "config.yaml"))
    print(
        f"\nA new project with name {project_name} is created at {str(wd)} "
        "and a configurable file (config.yaml) is stored there. "
        "Change the parameters in this file to adapt to your project's needs.\n "
        "Once you have changed the configuration file, "
        "use the function 'extract_frames' to select frames for labeling.\n. "
        "[OPTIONAL] Use the function 'add_new_videos' to add new videos to your project (at any stage)."
    )
    return projconfigfile