Beispiel #1
0
def save_scale(obj_input, overwrite=True, dirpath=None):
    """
    Save a created or detected scale to attributes.yaml
    
    Parameters
    ----------
    obj_input : DataFrame or container
        input object
    overwrite: bool optional
        overwrite csv if it already exists
    dirpath: str, optional
        location to save df
    """

    ## kwargs
    flag_overwrite = overwrite

    ## load df
    if obj_input.__class__.__name__ in ["int", "float"]:
        scale_current_px_mm_ratio = obj_input
    elif obj_input.__class__.__name__ == "container":
        if hasattr(obj_input, "scale_current_px_mm_ratio"):
            scale_current_px_mm_ratio = obj_input.scale_current_px_mm_ratio
        if (dirpath.__class__.__name__ == "NoneType"
                and not obj_input.dirpath.__class__.__name__ == "NoneType"):
            dirpath = obj_input.dirpath
    else:
        print("No scale supplied - cannot export results.")
        return

    ## dirpath
    if dirpath.__class__.__name__ == "NoneType":
        print('No save directory ("dirpath") specified - cannot save result.')
        return
    else:
        if not os.path.isdir(dirpath):
            q = input(
                "Save folder {} does not exist - create?.".format(dirpath))
            if q in ["True", "true", "y", "yes"]:
                os.makedirs(dirpath)
            else:
                print("Directory not created - aborting")
                return

    ## load attributes file
    attr_path = os.path.join(dirpath, "attributes.yaml")
    if os.path.isfile(attr_path):
        attr = _load_yaml(attr_path)
    else:
        attr = {}
    if not "scale" in attr:
        attr["scale"] = {}

    ## check if file exists
    while True:
        if "current_px_mm_ratio" in attr["scale"] and flag_overwrite == False:
            print("- scale not saved (overwrite=False)")
            break
        elif "current_px_mm_ratio" in attr["scale"] and flag_overwrite == True:
            print("- save scale to attributes (overwriting)")
            pass
        else:
            print("- save scale to attributes")
            pass
        attr["scale"]["current_px_mm_ratio"] = scale_current_px_mm_ratio
        break
    _save_yaml(attr, attr_path)
Beispiel #2
0
def save_drawing(obj_input, overwrite=True, dirpath=None):
    """
    Save drawing coordinates to attributes.yaml 
    
    Parameters
    ----------
    obj_input : DataFrame or container
        input object
    overwrite: bool optional
        overwrite if drawing already exists
    dirpath: str, optional
        location to save df

    """
    ## kwargs
    flag_overwrite = overwrite

    ## load df
    if obj_input.__class__.__name__ == "DataFrame":
        df = obj_input
    elif obj_input.__class__.__name__ == "container":
        df = obj_input.df_draw
        if (dirpath.__class__.__name__ == "NoneType"
                and not obj_input.dirpath.__class__.__name__ == "NoneType"):
            dirpath = obj_input.dirpath
    else:
        print("No df supplied - cannot export results.")
        return

    ## dirpath
    if dirpath.__class__.__name__ == "NoneType":
        print('No save directory ("dirpath") specified - cannot save result.')
        return
    else:
        if not os.path.isdir(dirpath):
            q = input(
                "Save folder {} does not exist - create?.".format(dirpath))
            if q in ["True", "true", "y", "yes"]:
                os.makedirs(dirpath)
            else:
                print("Directory not created - aborting")
                return

    ## load attributes file
    attr_path = os.path.join(dirpath, "attributes.yaml")
    if os.path.isfile(attr_path):
        attr = _load_yaml(attr_path)
    else:
        attr = {}
    if not "drawing" in attr:
        attr["drawing"] = {}

    ## check if file exists
    while True:
        if "drawing" in attr and flag_overwrite == False:
            print("- drawing not saved (overwrite=False)")
            break
        elif "drawing" in attr and flag_overwrite == True:
            print("- drawing saved (overwriting)")
            pass
        elif not "drawing" in attr:
            attr["drawing"] = {}
            print("- drawing saved")
            pass
        for idx, row in df.iterrows():
            # if not row["coords"] == attr["drawing"]["coords"]:
            attr["drawing"] = dict(row)
        _save_yaml(attr, attr_path)
        break
Beispiel #3
0
def save_data_entry(obj_input, overwrite=True, dirpath=None):
    """
    Save data entry to attributes.yaml 

    Parameters
    ----------
    obj_input : DataFrame or container
        input object
    overwrite: bool optional
        overwrite if entry already exists
    dirpath: str, optional
        location to save df
    """

    ## kwargs
    flag_overwrite = overwrite

    ## load df
    if obj_input.__class__.__name__ == "DataFrame":
        df = obj_input
    elif obj_input.__class__.__name__ == "container":
        df = obj_input.df_other_data
        if (dirpath.__class__.__name__ == "NoneType"
                and not obj_input.dirpath.__class__.__name__ == "NoneType"):
            dirpath = obj_input.dirpath
    else:
        print("No df supplied - cannot export results.")
        return

    ## dirpath
    if dirpath.__class__.__name__ == "NoneType":
        print('No save directory ("dirpath") specified - cannot save result.')
        return
    else:
        if not os.path.isdir(dirpath):
            q = input(
                "Save folder {} does not exist - create?.".format(dirpath))
            if q in ["True", "true", "y", "yes"]:
                os.makedirs(dirpath)
            else:
                print("Directory not created - aborting")
                return

    ## load attributes file
    attr_path = os.path.join(dirpath, "attributes.yaml")
    if os.path.isfile(attr_path):
        attr = _load_yaml(attr_path)
    else:
        attr = {}
    if not "other" in attr:
        attr["other"] = {}

    ## check if entry exists
    while True:
        for col in list(df):
            if col in attr["other"] and flag_overwrite == False:
                print("- column " + col + " not saved (overwrite=False)")
                continue
            elif col in attr["other"] and flag_overwrite == True:
                print("- add column " + col + " (overwriting)")
                pass
            else:
                print("- add column " + col)
                pass
            attr["other"][col] = df[col][0]
        break
    _save_yaml(attr, attr_path)
Beispiel #4
0
    def __init__(self, root_dir, overwrite=False):

        ## kwargs
        flag_overwrite = overwrite

        ## path conversion
        root_dir = root_dir.replace(os.sep, "/")
        root_dir = os.path.abspath(root_dir)

        ## feedback
        print("--------------------------------------------")
        print("Phenopype will create a new project at\n" + root_dir + "\n")

        ## decision tree if directory exists
        while True:
            create = input("Proceed? (y/n)\n")
            if create == "y" or create == "yes":
                if os.path.isdir(root_dir):
                    if flag_overwrite == True:
                        rmtree(root_dir, onerror=_del_rw)
                        print('\n"' + root_dir + '" created (overwritten)')
                        pass
                    else:
                        overwrite = input(
                            "Warning - project root_dir already exists - overwrite? (y/n)"
                        )
                        if overwrite == "y" or overwrite == "yes":
                            rmtree(root_dir, onerror=_del_rw)
                            print('\n"' + root_dir + '" created (overwritten)')
                            pass
                        else:
                            print('\n"' + root_dir + '" not created!')
                            print("--------------------------------------------")
                            break
                else:
                    pass
            else:
                print('\n"' + root_dir + '" not created!')
                break

            ## make directories
            self.root_dir = root_dir
            os.makedirs(self.root_dir)
            self.data_dir = os.path.join(self.root_dir, "data")
            os.makedirs(self.data_dir)

            # ##  set working directory
            # if not os.path.abspath(root_dir) == os.getcwd():
            #     os.chdir(root_dir)
            #     print("Current working directory changed to " + os.path.abspath(root_dir))
            # else:
            #     print("Already in " + os.path.abspath(root_dir))

            ## generate empty lists
            for lst in [
                "dirnames",
                "dirpaths_rel",
                "dirpaths",
                "filenames",
                "filepaths_rel",
                "filepaths",
            ]:
                setattr(self, lst, [])

            ## global project attributes
            project_data = {
                "date_created": datetime.today().strftime("%Y%m%d_%H%M%S"),
                "date_changed": datetime.today().strftime("%Y%m%d_%H%M%S"),
                "root_dir": self.root_dir,
                "data_dir": self.data_dir,
            }

            _save_yaml(project_data, os.path.join(self.root_dir, "attributes.yaml"))

            print(
                "\nproject attributes written to "
                + os.path.join(self.root_dir, "attributes.yaml")
            )
            print("--------------------------------------------")
            break
Beispiel #5
0
    def edit_config(
        self,
        name,
        step, 
        function,
        **kwargs
    ):
        """
        [new/experimental] Add or edit functions in all configuration files of a project.

        Parameters
        ----------

        name: str
            name of config-file. this gets appended to all files and serves as and
            identifier of a specific analysis pipeline
        step: str
            name of the step the function is in 
        function: str
            name of the function
        """

        ## kwargs
        flag_checked = False
        
        ## go through project directories
        for directory in self.dirpaths:
            dirname = os.path.basename(directory)

            ## save config
            preset_path = os.path.join(
                self.root_dir, directory, "pype_config_" + name + ".yaml"
            )
            
            if os.path.isfile(preset_path):
                config = _load_yaml(preset_path)
                
            ordered_steps = ["preprocessing",
                          "segmentation",
                          "measurement",
                          "visualization",
                          "export"
                          ]

            if not step in config.keys():
                new_config = ordereddict([("image", ordereddict(config["image"]))])
                new_config.update(ordereddict([("pype", ordereddict(config["pype"]))]))
                for ordered_step in ordered_steps:
                    if ordered_step in config:
                        new_config.update(ordereddict([(ordered_step, config[ordered_step])]))
                    elif not ordered_step in config and ordered_step == step:
                        new_config.update(ordereddict([(ordered_step, [function] )]))
            else:
                new_config = copy.deepcopy(config)
                if not function in new_config[step]:
                    new_config[step].append(function)
                    
            
            if flag_checked == False:
                _show_yaml(new_config)
                check = input("This is what the new config may look like (can differ beteeen files) - proceed?")
            
            if check in ["True", "true", "y", "yes"]:
                flag_checked = True
                _save_yaml(new_config, preset_path)
                print("New config saved for " + dirname)
            else:
                print("User check failed - aborting.")
                return 
Beispiel #6
0
    def add_scale(self, reference_image, overwrite=False, **kwargs):
        """
        Add pype configuration presets to all project directories. 

        Parameters
        ----------

        reference_image: str
            name of template image, either project directory or file link. template 
            image gets stored in root directory, and information appended to all 
            attributes files in the project directories
        overwrite: bool, optional
            overwrite option, if a given pype config-file already exist
        template: bool, optional
            should a template for scale detection be created. with an existing 
            template, phenopype can try to find a reference card in a given image,
            measure its dimensions, and adjust pixel-to-mm-ratio and colour space
        """

        ## kwargs
        flag_overwrite = overwrite
        test_params = kwargs.get("test_params", {})

        ## load template image
        if reference_image.__class__.__name__ == "str":
            if os.path.isfile(reference_image):
                reference_image = cv2.imread(reference_image)
            elif os.path.isdir(os.path.join(self.data_dir, reference_image)):
                attr = _load_yaml(
                    os.path.join(self.data_dir, reference_image, "attributes.yaml")
                )
                reference_image = cv2.imread(
                    os.path.join(self.root_dir, attr["project"]["raw_path"])
                )
            elif reference_image in self.dirnames:
                attr = _load_yaml(
                    os.path.join(self.data_dir, reference_image, "attributes.yaml")
                )
                reference_image = cv2.imread(attr["project"]["raw_path"])
            else:
                print("wrong path - cannot load reference image")
                return
        elif reference_image.__class__.__name__ == "ndarray":
            pass
        elif reference_image.__class__.__name__ == "int":
            reference_image = cv2.imread(self.filepaths[reference_image])
        else:
            print("wrong type - cannot load reference image")
            return

        ## save template
        template_path = os.path.join(self.root_dir, "scale_template.jpg")
        while True:
            if os.path.isfile(template_path) and flag_overwrite == False:
                print(
                    "- scale template not saved - file already exists (overwrite=False)."
                )
                break
            elif os.path.isfile(template_path) and flag_overwrite == True:
                print("- scale template saved under " + template_path + " (overwritten).")
                pass
            elif not os.path.isfile(template_path):
                print("- scale template saved under " + template_path + ".")
                pass

            ## measure scale
            px_mm_ratio, df_masks, template = preprocessing.create_scale(
                reference_image, template=True, test_params=test_params
            )
            cv2.imwrite(template_path, template)
            break

        ## save scale information
        for directory in self.dirpaths:
            attr = _load_yaml(os.path.join(self.root_dir, directory, "attributes.yaml"))
            if not "scale" in attr:
                print("added scale information to " + attr["project"]["dirname"])
                pass
            elif "scale" in attr and flag_overwrite:
                print(
                    "added scale information to "
                    + attr["project"]["dirname"]
                    + " (overwritten)"
                )
                pass
            elif "scale" in attr and not flag_overwrite:
                print(
                    "could not add scale information to "
                    + attr["project"]["dirname"]
                    + " (overwrite=False)"
                )
                continue
            attr["scale"] = {
                "template_px_mm_ratio": px_mm_ratio,
                "template_path": template_path,
            }
            _save_yaml(attr, os.path.join(self.root_dir, directory, "attributes.yaml"))
Beispiel #7
0
    def add_config(
        self,
        name,
        config_preset=None,
        interactive=False,
        overwrite=False,
        idx=0,
        **kwargs
    ):
        """
        Add pype configuration presets to all image folders in the project, either by using
        the templates included in the presets folder, or by adding your own templates
        by providing a path to a yaml file. Can be tested and modified using the 
        interactive flag before distributing the config files.

        Parameters
        ----------

        name: str
            name of config-file. this gets appended to all files and serves as and
            identifier of a specific analysis pipeline
        preset: str, optional
            can be either a string denoting a template name (e.g. preset1, preset2, 
            landamarking1, ... - in "phenopype/settings/presets.py") or a path to a 
            compatible yaml file
        interactive: bool, optional
            start a pype and modify preset before saving it to phenopype directories
        overwrite: bool, optional
            overwrite option, if a given pype config-file already exist
        kwargs: 
            developer options
        """

        ## kwargs
        flag_interactive = interactive
        flag_overwrite = overwrite

        ## legacy
        preset = kwargs.get("preset")
        if (
            config_preset.__class__.__name__ == "NoneType"
            and not preset.__class__.__name__ == "NoneType"
        ):
            config_preset = preset

        ## load config
        if not config_preset.__class__.__name__ == "NoneType" and hasattr(
            presets, config_preset
        ):
            config = _create_generic_pype_config(preset=config_preset, config_name=name)
        elif not config_preset.__class__.__name__ == "NoneType" and os.path.isfile(
            config_preset
        ):
            config = {
                "pype": {
                    "name": name,
                    "preset": config_preset,
                    "date_created": datetime.today().strftime("%Y%m%d_%H%M%S"),
                }
            }
            config.update(_load_yaml(config_preset))
            print(config)
        elif not config_preset.__class__.__name__ == "NoneType" and not hasattr(
            presets, config_preset
        ):
            print("Provided preset NOT found - terminating")
            return
        elif config_preset.__class__.__name__ == "NoneType":
            print("No preset provided - defaulting to preset " + default_pype_config)
            config = _load_yaml(eval("presets." + default_pype_config))

        ## modify
        if flag_interactive:
            image_location = os.path.join(
                self.root_dir,
                "pype_template_image" + os.path.splitext(self.filenames[idx])[1],
            )
            copyfile(self.filepaths[idx], image_location)
            config_location = os.path.join(
                self.root_dir, "pype_config_template-" + name + ".yaml"
            )
            _save_yaml(config, config_location)
            p = pype(
                image_location,
                name="template-" + name,
                config_location=config_location,
                presetting=True,
            )
            config = p.config

        ## go through project directories
        for directory in self.dirpaths:
            attr = _load_yaml(os.path.join(self.root_dir, directory, "attributes.yaml"))
            pype_preset = {"image": attr["image"]}
            pype_preset.update(config)

            ## save config
            preset_path = os.path.join(
                self.root_dir, directory, "pype_config_" + name + ".yaml"
            )
            dirname = attr["project"]["dirname"]
            if os.path.isfile(preset_path) and flag_overwrite == False:
                print(
                    "pype_"
                    + name
                    + ".yaml already exists in "
                    + dirname
                    + " (overwrite=False)"
                )
                continue
            elif os.path.isfile(preset_path) and flag_overwrite == True:
                print("pype_" + name + ".yaml created for " + dirname + " (overwritten)")
                _save_yaml(pype_preset, preset_path)
            else:
                print("pype_" + name + ".yaml created for " + dirname)
                _save_yaml(pype_preset, preset_path)
Beispiel #8
0
    def add_files(
        self,
        image_dir,
        filetypes=default_filetypes,
        include=[],
        exclude=[],
        raw_mode="copy",
        search_mode="dir",
        unique_mode="path",
        overwrite=False,
        resize=1,
        **kwargs
    ):
        """
        Add files to your project from a directory, can look recursively. 
        Specify in- or exclude arguments, filetypes, duplicate-action and copy 
        or link raw files to save memory on the harddrive. For each found image,
        a folder will be created in the "data" folder within the projects root
        directory. If found images are in subfolders and search_mode is 
        recursive, the respective phenopype directories will be created with 
        flattened path as prefix. 
        
        E.g., with "raw_files" as folder with the original image files 
        and "phenopype_proj" as rootfolder:
        
        - raw_files/file.jpg ==> phenopype_proj/data/file.jpg
        - raw_files/subdir1/file.jpg ==> phenopype_proj/data/1__subdir1__file.jpg
        - raw_files/subdir1/subdir2/file.jpg ==> phenopype_proj/data/2__subdir1__subdir2__file.jpg
    
        Parameters
        ----------
        image_dir: str 
            path to directory with images
        filetypes: list or str, optional
            single or multiple string patterns to target files with certain endings.
            "default_filetypes" are configured in settings.py
        include: list or str, optional
            single or multiple string patterns to target certain files to include
        exclude: list or str, optional
            single or multiple string patterns to target certain files to exclude - 
            can overrule "include"
        raw_mode: {"copy", "link"} str, optional
            how should the raw files be passed on to the phenopype directory tree: 
            "copy" will make a copy of the original file, "link" will only send the 
            link to the original raw file to attributes, but not copy the actual 
            file (useful for big files)
        search_mode: {"dir", "recursive"}, str, optional
            "dir" searches current directory for valid files; "recursive" walks 
            through all subdirectories
        unique_mode: {"filepath", "filename"}, str, optional:
            how to deal with image duplicates - "filepath" is useful if identically 
            named files exist in different subfolders (folder structure will be 
            collapsed and goes into the filename), whereas filename will ignore 
            all similar named files after their first occurrence.
        kwargs: 
            developer options
        """

        # kwargs
        flag_raw_mode = raw_mode
        flag_overwrite = overwrite
        flag_resize = resize

        ## path conversion
        image_dir = image_dir.replace(os.sep, "/")
        image_dir = os.path.abspath(image_dir)

        ## collect filepaths
        filepaths, duplicates = _file_walker(
            directory=image_dir,
            search_mode=search_mode,
            unique_mode=unique_mode,
            filetypes=filetypes,
            exclude=exclude,
            include=include,
        )

        ## feedback
        print("--------------------------------------------")
        print("phenopype will search for files at\n")
        print(image_dir)
        print("\nusing the following settings:\n")
        print(
            "filetypes: "
            + str(filetypes)
            + ", include: "
            + str(include)
            + ", exclude: "
            + str(exclude)
            + ", raw_mode: "
            + str(raw_mode)
            + ", search_mode: "
            + str(search_mode)
            + ", unique_mode: "
            + str(unique_mode)
            + "\n"
        )

        ## loop through files
        for filepath in filepaths:

            ## generate phenopype dir-tree
            relpath = os.path.relpath(filepath, image_dir)
            depth = relpath.count("\\")
            relpath_flat = os.path.dirname(relpath).replace("\\", "__")
            if depth > 0:
                subfolder_prefix = str(depth) + "__" + relpath_flat + "__"
            else:
                subfolder_prefix = str(depth) + "__"
            dirname = subfolder_prefix + os.path.splitext(os.path.basename(filepath))[0]
            dirpath = os.path.join(self.root_dir, "data", dirname)

            ## make image-specific directories
            if os.path.isdir(dirpath) and flag_overwrite == False:
                print(
                    "Found image "
                    + relpath
                    + " - "
                    + dirname
                    + " already exists (overwrite=False)"
                )
                continue
            if os.path.isdir(dirpath) and flag_overwrite == True:
                rmtree(dirpath, ignore_errors=True, onerror=_del_rw)
                print(
                    "Found image "
                    + relpath
                    + " - "
                    + "phenopype-project folder "
                    + dirname
                    + " created (overwritten)"
                )
                os.mkdir(dirpath)
            else:
                print(
                    "Found image "
                    + relpath
                    + " - "
                    + "phenopype-project folder "
                    + dirname
                    + " created"
                )
                os.mkdir(dirpath)

            ## load image
            image = load_image(filepath, resize=flag_resize)

            ## copy or link raw files
            if flag_raw_mode == "copy":
                raw_path = os.path.join(
                    self.data_dir,
                    dirname,
                    "raw" + os.path.splitext(os.path.basename(filepath))[1],
                )
                if resize < 1:
                    cv2.imwrite(raw_path, image)
                else:
                    copyfile(filepath, raw_path)

            elif flag_raw_mode == "link":
                if resize < 1:
                    warnings.warn("cannot resize image in link mode")
                raw_path = filepath

            ## path reformatting
            raw_relpath = os.path.relpath(raw_path, self.root_dir)
            raw_relpath = raw_relpath.replace(os.sep, "/")
            dir_relpath = os.path.relpath(dirpath, self.root_dir)
            dir_relpath = dir_relpath.replace(os.sep, "/")

            ## collect attribute-data and save
            image_data = load_image_data(filepath, flag_resize)
            meta_data = load_meta_data(filepath)
            project_data = {
                "dirname": dirname,
                "dirpath": dir_relpath,
                "raw_mode": flag_raw_mode,
                "raw_path": raw_relpath,
            }

            if meta_data:
                attributes = {
                    "image": image_data,
                    "meta": meta_data,
                    "project": project_data,
                }
            else:
                attributes = {"image": image_data, "project": project_data}

            ## write attributes file
            _save_yaml(
                attributes, os.path.join(self.root_dir, dir_relpath, "attributes.yaml")
            )

            ## add to project object
            if not dirname in self.dirnames:
                ## directories
                self.dirnames.append(dirname)
                self.dirpaths_rel.append(dir_relpath)
                self.dirpaths.append(os.path.join(self.root_dir, dir_relpath))
                ## files
                self.filenames.append(image_data["filename"])
                self.filepaths_rel.append(raw_relpath)
                self.filepaths.append(os.path.join(self.root_dir, raw_relpath))

        print("\nFound {} files".format(len(filepaths)))
        print("--------------------------------------------")