{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "Skeletonization unsupervised\n", "========================\n", "\n", "In this notebook we use the script `skeletons/main_unsupervised_skeleton_estimation.py` to automatically extract the length of the organisms from clips. No pre-trained model is necessary for this module (i.e. unsupervised). " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "First, we need to set up some running parameters for the script to know where to find input images and where to write outputs. " ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "import yaml\n", "from pathlib import Path\n", "\n", "ROOT_DIR = Path(\"D:\\mzb-workflow\") #Path(\"/home/jovyan/work/mzb-workflow\")\n", "\n", "arguments = {\n", " \"config_file\": ROOT_DIR / \"configs/mzb_example_config.yaml\", \n", " \"input_dir\": ROOT_DIR / \"data/mzb_example_data/derived/blobs\", \n", " \"errors\": ROOT_DIR / \"data/mzb_example_data/derived/classification\", \n", " \"output_dir\": ROOT_DIR / \"results/bgb/skeletons/skeletons_unsupervised\", \n", " \"save_masks\": ROOT_DIR / \"data/bgb/skeletons/skeletons_unsupervised\", \n", " \"list_of_files\": None\n", " }\n", " \n", "with open(str(arguments[\"config_file\"]), \"r\") as f:\n", " cfg = yaml.load(f, Loader=yaml.FullLoader)\n", "\n", "cfg[\"trcl_gpu_ids\"] = None # this sets the number of available GPUs to zero, since this script doesn't benefit from GPU compute. " ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Convert to dictionary for Python script using the custom function `cfg_to_arguments`: " ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "{'glob_random_seed': 222, 'glob_root_folder': '/home/jovyan/work/mzb-workflow/', 'glob_blobs_folder': '/home/jovyan/work/mzb-workflow/data/derived/blobs/', 'glob_local_format': 'pdf', 'model_logger': 'wandb', 'impa_image_format': 'jpg', 'impa_clip_areas': [2700, 4700, -1, -1], 'impa_area_threshold': 5000, 'impa_gaussian_blur': [21, 21], 'impa_gaussian_blur_passes': 3, 'impa_adaptive_threshold_block_size': 351, 'impa_mask_postprocess_kernel': [11, 11], 'impa_mask_postprocess_passes': 5, 'impa_bounding_box_buffer': 200, 'impa_save_clips_plus_features': True, 'lset_class_cut': 'order', 'lset_val_size': 0.1, 'trcl_learning_rate': 0.0001, 'trcl_batch_size': 8, 'trcl_weight_decay': 0, 'trcl_step_size_decay': 5, 'trcl_number_epochs': 75, 'trcl_save_topk': 1, 'trcl_num_classes': 8, 'trcl_model_pretrarch': 'convnext-small', 'trcl_num_workers': 16, 'trcl_wandb_project_name': 'mzb-classifiers', 'trcl_logger': 'wandb', 'trsk_learning_rate': 0.001, 'trsk_batch_size': 32, 'trsk_weight_decay': 0, 'trsk_step_size_decay': 25, 'trsk_number_epochs': 400, 'trsk_save_topk': 1, 'trsk_num_classes': 2, 'trsk_model_pretrarch': 'mit_b2', 'trsk_num_workers': 16, 'trsk_wandb_project_name': 'mzb-skeletons', 'trsk_logger': 'wandb', 'infe_model_ckpt': 'last', 'infe_num_classes': 8, 'infe_image_glob': '*_rgb.jpg', 'skel_class_exclude': 'errors', 'skel_conv_rate': 131.6625, 'skel_label_thickness': 3, 'skel_label_buffer_on_preds': 25, 'skel_label_clip_with_mask': False, 'trcl_gpu_ids': None}\n" ] } ], "source": [ "from mzbsuite.utils import cfg_to_arguments\n", "\n", "# Transforms configurations dicts to argparse arguments\n", "args = cfg_to_arguments(arguments)\n", "cfg = cfg_to_arguments(cfg)\n", "print(str(cfg))" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "\u001b[1;31mSignature:\u001b[0m \u001b[0munsupervised_skeletonization\u001b[0m\u001b[1;33m(\u001b[0m\u001b[0margs\u001b[0m\u001b[1;33m,\u001b[0m \u001b[0mcfg\u001b[0m\u001b[1;33m)\u001b[0m\u001b[1;33m\u001b[0m\u001b[1;33m\u001b[0m\u001b[0m\n", "\u001b[1;31mDocstring:\u001b[0m\n", "Main function for skeleton estimation (body size) in the unsupervised setting.\n", "\n", "Parameters\n", "----------\n", "args : argparse.Namespace\n", " Arguments parsed from command line. Namely:\n", " \n", " - config_file: path to the configuration file\n", " - input_dir: path to the directory containing the masks\n", " - output_dir: path to the directory where to save the results\n", " - save_masks: path to the directory where to save the masks as jpg\n", " - list_of_files: path to the csv file containing the classification predictions\n", " - v (verbose): whether to print more info\n", " \n", "cfg : argparse.Namespace\n", " Arguments parsed from the configuration file.\n", "\n", "Returns\n", "-------\n", "None. All is saved to disk at specified locations.\n", "\u001b[1;31mFile:\u001b[0m d:\\mzb-workflow\\scripts\\skeletons\\main_unsupervised_skeleton_estimation.py\n", "\u001b[1;31mType:\u001b[0m function" ] } ], "source": [ "from scripts.skeletons.main_unsupervised_skeleton_estimation import main as unsupervised_skeletonization\n", "?unsupervised_skeletonization" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Load in clips, excluding those predicted to be `error` by the DL model. \n", "\n", "> 🐛 BUG: the path to the folder with the clips classified as error is currently hardcoded in the script! \n", "\n", "In a nutshell, it uses the configuration parameters provided before to apply a series of morphological operations on the binary mask of each organism's clip, subsequently thinning it into segment(s), eventually connecting and calculating the longest path through them, thus producing the skeleton, which should approximate well the length of the organism." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "unsupervised_skeletonization(args, cfg)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.14" } }, "nbformat": 4, "nbformat_minor": 2 }