diff --git a/001-Jupyter/001-Tutorials/001-Basic-Tutorials/001-IPython-Kernel/Plotting in the Notebook with Matplotlib.ipynb b/001-Jupyter/001-Tutorials/001-Basic-Tutorials/001-IPython-Kernel/Plotting in the Notebook with Matplotlib.ipynb
index 7794fbb31ac6c3093d662019185b59f8da242086..376396d0f634977854e157e00bff39f9002138e6 100644
--- a/001-Jupyter/001-Tutorials/001-Basic-Tutorials/001-IPython-Kernel/Plotting in the Notebook with Matplotlib.ipynb	
+++ b/001-Jupyter/001-Tutorials/001-Basic-Tutorials/001-IPython-Kernel/Plotting in the Notebook with Matplotlib.ipynb	
@@ -36,6 +36,7 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
+    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -70,6 +71,7 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
+    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -84,6 +86,7 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
+    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -127,6 +130,7 @@
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
+    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
@@ -200,20 +204,17 @@
   {
    "cell_type": "code",
    "execution_count": null,
-   "metadata": {
-    "jupyter": {
-     "outputs_hidden": true
-    }
-   },
+   "metadata": {},
    "outputs": [],
    "source": [
-    "%matplotlib notebook"
+    "%matplotlib widget"
    ]
   },
   {
    "cell_type": "code",
    "execution_count": null,
    "metadata": {
+    "collapsed": false,
     "jupyter": {
      "outputs_hidden": false
     }
diff --git a/002-Methods/001-Computing/PyDeepLearningVersion.ipynb b/002-Methods/001-Computing/PyDeepLearningVersion.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..b6a66a5008614b1467c58a6c46ac2259b987f837
--- /dev/null
+++ b/002-Methods/001-Computing/PyDeepLearningVersion.ipynb
@@ -0,0 +1,610 @@
+{
+ "cells": [
+  {
+   "attachments": {
+    "88c90614-07ea-4ccb-aab3-7b63dc27a603.png": {
+     "image/png": "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"
+    }
+   },
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "<img src=attachment:88c90614-07ea-4ccb-aab3-7b63dc27a603.png width=400, height=41>\n",
+    "\n",
+    "Author: [Jonathan Windgassen](mailto:j.windgassen@fz-juelich.de)"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Python Face Blurer\n",
+    "\n",
+    "This Script will scan any images in the *Images*-folder or any videos in the *Video*-folder for faces/persons using the [Faster RCNN Open Images V4 Neural Network](https://tfhub.dev/google/faster_rcnn/openimages_v4/inception_resnet_v2/1) and apply a blur to all findings.\n",
+    "\n",
+    "\n",
+    "\n",
+    "#### Table of Contents:\n",
+    "1. Initializing the Project\n",
+    "    - Creating the Folder Structure\n",
+    "    - Imports\n",
+    "    - Configuration\n",
+    "2. Initializing the Blur\n",
+    "    - Functions for bluring objects\n",
+    "    - Functions for drawing boxes\n",
+    "3. Loading the Classifier\n",
+    "    - Pulling the Classifier\n",
+    "    - Input and Output Parsing\n",
+    "4. Fetching all Images and Video\n",
+    "5. Run Classifier and Apply Blur\n",
+    "    - Run Classifier over every Image and Video\n",
+    "    - Save to Output folder\n",
+    "    - Compact Frames into a Video\n",
+    "6. Cleanup\n",
+    "\n",
+    "-----------------------------------------------------"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Initializing the Project"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Creating the Folder Structure\n",
+    "\n",
+    "For Python to find the Images and Videos it needs the path to the folders where the Images are stored. By default the notebook expects the following folder structure, but you can change the paths below if you want:\n",
+    "<pre>\n",
+    "├── face_blurer.ipynb\n",
+    "├── Images\n",
+    "│   ├── Image.png\n",
+    "│   └── ...\n",
+    "├── Images_Out\n",
+    "├── Videos\n",
+    "│   ├── Video.mp4\n",
+    "│   └── ...\n",
+    "└── Videos_Out\n",
+    "</pre>\n",
+    "\n",
+    "Usage:\n",
+    "- **Images**: Put all Images here that you want to have blured. OpenCV can load almost all file formats, but for a complete list look [here](https://docs.opencv.org/3.4/d4/da8/group__imgcodecs.html#ga288b8b3da0892bd651fce07b3bbd3a56).\n",
+    "- **Images_Out**: After processing the new Images will be placed here. The name of the processed file can be specified in the config with *FILENAME_AFTER_BLURRING*.\n",
+    "- **Videos**: Same deal as with the Images. Put all Videos here that you want to have processed.\n",
+    "- **Videos_Out**: All processed Videos will be placed here after applying the algorithm. The new name will be the same as for the Images.\n",
+    "\n",
+    "> Note: If you want to load the files from a different place in storage you can do so by editing the variable in the configuration. More on that below! "
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Imports\n",
+    "\n",
+    "#### *Please use the PyDeepLearning*-kernel or the kernel from **kernel.ipynb** to run this\n",
+    "\n",
+    "Import everything we need for the whole operation.\n",
+    "- The main package for the model will be tensorflow, which will internally use [TensorflowHub](https://tfhub.dev/) to download the network.\n",
+    "- We will apply the blur using PIL, the Python Image Library that nativly gets delivered with python.\n",
+    "- matplotlib will be used to display images in the notebook. Usefull if you want to see the results of the blurring directly."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "#######################################\n",
+    "# Disable GPU Usage for TensorFlow\n",
+    "#######################################\n",
+    "import os\n",
+    "os.environ['CUDA_VISIBLE_DEVICES'] = '-1'\n",
+    "\n",
+    "# Main package for the ANN\n",
+    "import tensorflow as tf\n",
+    "import tensorflow_hub as hub\n",
+    "\n",
+    "# Opening Files and Bluring\n",
+    "import cv2 as cv\n",
+    "import numpy as np\n",
+    "\n",
+    "# Displaying the image\n",
+    "import matplotlib.pyplot as plt\n",
+    "\n",
+    "from time import time            # For measuring the inference time.\n",
+    "from os import listdir           # Getting available files\n",
+    "import re                        # Config\n",
+    "import tempfile\n",
+    "import shutil\n",
+    "\n",
+    "print(\"tensorflow version: \" + tf.__version__)\n",
+    "print(\"OpenCV version: \" + cv.__version__)\n",
+    "\n",
+    "# Check available GPU devices.\n",
+    "print(\"\\nNum GPUs Available: \", len(tf.config.experimental.list_physical_devices(\"GPU\")))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Configuration\n",
+    "\n",
+    "Here you can configure every aspect of the algorithm\n",
+    "\n",
+    "The most interesting option is probably *FILTER_OBJETCS*, where you can specify what object should be detected/blurred. You can see all available object classes in [this image](https://storage.googleapis.com/openimages/2018_04/bbox_labels_600_hierarchy_visualizer/circle.html) or take a direct look at the [Open Image Dataset](https://storage.googleapis.com/openimages/web/visualizer/index.html?). The filter is interpreted as a Regex, so you can also filter for multiple classes, eg. by chaining them together with `|` or using wildcard symbols like `.`. In most cases you'll probably want to use `Person` or `Human face`"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "FILTER_OBJECTS = \"Person\"  # Regex. What Object you want to detect. Everything else will be ignored.\n",
+    "FILENAME_AFTER_BLURRING = \"{}_blured\"  # {} replaces the original filename. Don't add the extension (.jpg, .mp3) here\n",
+    "DRAW_BOXES = False\n",
+    "BLUR_OBJECTS = True\n",
+    "BLUR_INTENSITY = 20\n",
+    "EXTEND_BOXES = 20  # Increases the size of the boxes by x pixels in each direction\n",
+    "\n",
+    "FORCE_IMAGE_SIZE = (None, None)  # Leave at None for no changes. You are free to only resize one Dimension and leave the other\n",
+    "FORCE_VIDEO_SIZE = (None, None)  # Same as above\n",
+    "CROP_VIDEO_LENGTH = None         # Time in seconds after which videos will be cropped. Recommended for testing\n",
+    "\n",
+    "VIDEO_CACHE_FOLDER = None  # Before we output the final video all frames will be saved as a .png-file in a folder. If you specify a folder here they won't be deleted afterwards. Leave None to use a tempdir.\n",
+    "FORCE_VIDEO_FORMAT = None  # Leave None for Original. You must not include the preceding \".\"\n",
+    "VIDEO_BITRATE = None  # String or Integer. Leave None for default\n",
+    "ENCODER = \"h264\"           # E.g. \"h264\", \"hevc\"(h265), \"asv1\" (AV1)\n",
+    "\n",
+    "# When paths are relative the origin will be the folder where this notebook is. You MUST put an / behind the folder paths\n",
+    "IMAGE_INPUT_FOLDER = \"./Images/\"\n",
+    "IMAGE_OUTPUT_FOLDER = \"./Images_Out/\"\n",
+    "VIDEO_INPUT_FOLDER = \"./Videos/\"\n",
+    "VIDEO_OUTPUT_FOLDER = \"./Videos_Out/\""
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "----------------------\n",
+    "## Initializing the Blur"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Functions for bluring objects"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def blur_objects(image, boxes, names, confidence, min_score=0.1):\n",
+    "    \"\"\"\n",
+    "    Apply the results from the network to the image and blur the objects specified above.\n",
+    "    \n",
+    "    From the network return values we need to pass where the objects are, what each object is and how confident the network if about the detection.\n",
+    "    Since those properties are given in seperate lists, each identified object needs to be in the same index for every parameter.\n",
+    "    \n",
+    "    :param image:      (numpy.ndarray) The image to be drawn on.\n",
+    "    :param boxes:      (numpy.ndarray) An Array of shape (n, 4) with the 4 coordinates for each box. Cordinate must be [ymin, xmin, ymax, xmax]\n",
+    "    :param names:      (numpy.ndarray) An Array of shape (n) with the name of each Object.\n",
+    "    :param confidence: (numpy.ndarray) An Array of shape (n) with the confidence for each Object.\n",
+    "    :param min_score:  (optional, int) The required confidence for the box to be applied\n",
+    "    \"\"\"\n",
+    "    \n",
+    "    for i in range(boxes.shape[0]):\n",
+    "        is_confident = confidence[i] >= min_score\n",
+    "        is_in_filter = re.search(FILTER_OBJECTS, names[i]) is not None\n",
+    "            \n",
+    "        if is_confident and is_in_filter:\n",
+    "            blur_object(image, boxes[i])\n",
+    "\n",
+    "\n",
+    "def blur_object(image, coordinates, intensity=BLUR_INTENSITY):\n",
+    "    height, width = image.shape[:2]\n",
+    "    (ymin, xmin, ymax, xmax) = coordinates\n",
+    "    \n",
+    "    (x, w, y, h) = (int(xmin * width), int(xmax * width), int(ymin * height), int(ymax * height))\n",
+    "    \n",
+    "    # Blur the area of the face\n",
+    "    sub_image = image[y:h, x:w]\n",
+    "    sub_image = cv.blur(sub_image, (intensity, intensity))\n",
+    "\n",
+    "    # Apply blurred face back to image\n",
+    "    image[y:h, x:w] = sub_image"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Functions for drawing boxes"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def draw_boxes(image, boxes, names, confidence, min_score=0.1):\n",
+    "    \"\"\"\n",
+    "    Apply the results from the network to the image and draw a box around each identified object.\n",
+    "    \n",
+    "    From the network return values we need to pass where the boxes are, what object each box represents and confident the network if about the detection.\n",
+    "    Since those properties are given in seperate lists, each identified object needs to be in the same index for every parameter.\n",
+    "    \n",
+    "    :param image:      (numpy.ndarray) The image to be drawn on.\n",
+    "    :param boxes:      (numpy.ndarray) An Array of shape (n, 4) with the 4 coordinates for each box. Cordinate must be [ymin, xmin, ymax, xmax]\n",
+    "    :param names:      (numpy.ndarray) An Array of shape (n) with the name of each Object.\n",
+    "    :param confidence: (numpy.ndarray) An Array of shape (n) with the confidence for each Object.\n",
+    "    :param min_score:  (optional, int) The required confidence for the box to be applied\n",
+    "    \"\"\"\n",
+    "    \n",
+    "    for i in range(boxes.shape[0]):\n",
+    "        is_confident = confidence[i] >= min_score\n",
+    "        is_in_filter = re.search(FILTER_OBJECTS, names[i]) is not None\n",
+    "            \n",
+    "        if is_confident and is_in_filter:\n",
+    "            display_str = f\"{names[i]}: {int(100 * confidence[i])}%\"\n",
+    "            draw_bounding_box_on_image(image, boxes[i], display_str)\n",
+    "\n",
+    "\n",
+    "def draw_bounding_box_on_image(image, coordinates, label=\"\", color=(0, 0, 255), thickness=4):\n",
+    "    \"\"\"\n",
+    "    Adds a bounding box to an image.\n",
+    "    \n",
+    "    :param image:       (np.ndarray) The Image to be drawn on.\n",
+    "    :param coordinates: (tuple) Coordinates of the Box: (ymin, xmin, ymax, xmax). Each coordinate must be between 0 and 1.\n",
+    "    :param color:       (optional, str) 7-digit String representing the Color in the format of '#rrggbb'.\n",
+    "    :param thickness:   (optional, int) How thick the box should be.\n",
+    "    \"\"\"\n",
+    "    # Draw the Box itself\n",
+    "    height, width = image.shape[:2]\n",
+    "    (left, top) = (int(coordinates[1] * width) - EXTEND_BOXES, int(coordinates[0] * height) - EXTEND_BOXES)\n",
+    "    (width, height) = (int(coordinates[3] * width) - left + 2 * EXTEND_BOXES, int(coordinates[2] * height) - top + 2 * EXTEND_BOXES)\n",
+    "    \n",
+    "    image = cv.rectangle(image, (left, top, width, height), color, thickness)\n",
+    "    \n",
+    "    # Calculate text specs\n",
+    "    (width, height), _ = cv.getTextSize(label, cv.FONT_HERSHEY_SIMPLEX, 0.6, 1)\n",
+    "    height += 10\n",
+    "    width += 10\n",
+    "    position = top if top < height else top - height  # Move label to the inside if not enough space above the box\n",
+    "    \n",
+    "    # Draw Text\n",
+    "    rect = (left, position, width, height)\n",
+    "    image = cv.rectangle(image, rect, color, -1)\n",
+    "    image = cv.putText(image, label, (left + 5, position + height - 5), cv.FONT_HERSHEY_SIMPLEX, 0.6, (255, 255, 255))"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "--------------\n",
+    "## Loading the Classifier"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Pulling the Classifier\n",
+    "\n",
+    "Here we use tensorflow_hub to Pull the ANN from the [Tensorflow Hub](https://tfhub.dev/google/faster_rcnn/openimages_v4/inception_resnet_v2/1). Be aware that depending on your setup the loading can take a few minutes."
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "module_handle = \"./model\"\n",
+    "classifier = hub.load(module_handle).signatures['default']"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# Input and Output Parsing\n",
+    "\n",
+    "These Functions will load an Image as an numpy.ndarray, which will then be passed to *run_detector*, were they will be converted to the right format and passed to the classifier"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "def display_image(image):\n",
+    "    \"\"\"Displays the image to the screen.\"\"\"\n",
+    "    plt.figure(figsize=(20, 15))\n",
+    "    plt.grid(False)\n",
+    "    plt.axis(\"off\")\n",
+    "    \n",
+    "    plt.imshow(image)\n",
+    "\n",
+    "\n",
+    "def open_image(path, display=False, resolution=FORCE_IMAGE_SIZE):\n",
+    "    \"\"\"\n",
+    "    Open and format an image from disk.\n",
+    "    \n",
+    "    Open a picture from file in the path variable. After if gets resized and converted to RGB if neccesary. If chosen if will also be displayed. \n",
+    "    \n",
+    "    :param path:    (str) The path to the Picture.\n",
+    "    :param display: (boolean, optional) Whether you want the image displayed\n",
+    "    :param width:   (tuple, optional) The desired resolution after rescaling.\n",
+    "    \"\"\"\n",
+    "    image = cv.imread(path, cv.IMREAD_COLOR)\n",
+    "    \n",
+    "    if (resolution != (None, None)):\n",
+    "        width = resolution[0] or image.shape[1]\n",
+    "        height = resolution[1] or image.shape[0]\n",
+    "        \n",
+    "        image = cv.resize(image, dsize=(width, height), interpolation=cv.INTER_CUBIC)\n",
+    "    \n",
+    "    if display:\n",
+    "        display_image(image)\n",
+    "    \n",
+    "    return image\n",
+    "\n",
+    "\n",
+    "def run_detector(detector, image):\n",
+    "    \"\"\"\n",
+    "    Runs the given classifier on the given image. Only works with detectors that accept the same input format as the original one.\n",
+    "    \n",
+    "    \n",
+    "    :param detector: (A trackable Object) The Classifier.\n",
+    "    :param image:    (np.ndarray) The Image.\n",
+    "    :return:         (Dict) A Dictionary with the objects found by the Classifier.\n",
+    "    \"\"\"\n",
+    "    img = image.astype(np.float32)\n",
+    "    img = np.expand_dims(img, axis=0)  # add required additional dimension. Same as img[np.newaxis, :], but better readable\n",
+    "    img /= 255  # Normalize\n",
+    "    \n",
+    "    result = detector(tf.convert_to_tensor(img))\n",
+    "    \n",
+    "    # Extract the numeric values\n",
+    "    result = {key: value.numpy() for key, value in result.items()}\n",
+    "    result[\"detection_class_entities\"] = [name.decode('ascii') for name in result[\"detection_class_entities\"]]\n",
+    "    \n",
+    "    return result"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "--------------\n",
+    "## Fetching all Images and Videos"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "images = listdir(IMAGE_INPUT_FOLDER)\n",
+    "videos = listdir(VIDEO_INPUT_FOLDER)\n",
+    "\n",
+    "# Remove hidden files (Starting with .) and convert to an tuple with (name, format)\n",
+    "images = [[file.split(\".\")[0], file.split(\".\")[-1]] for file in images if not file.startswith(\".\")]\n",
+    "videos = [[file.split(\".\")[0], file.split(\".\")[-1]] for file in videos if not file.startswith(\".\")]\n",
+    "\n",
+    "print(f\"Found {len(images)} Images\")\n",
+    "print(f\"Found {len(videos)} Videos\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "---------------------\n",
+    "\n",
+    "## Run Classifier and Blur"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Run Classifier on every Image"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for file in images:\n",
+    "    image = open_image(IMAGE_INPUT_FOLDER + file[0] + \".\" + file[1])\n",
+    "    start_time = time()\n",
+    "    \n",
+    "    #######################################\n",
+    "    # Code that failes\n",
+    "    #######################################\n",
+    "    result = run_detector(classifier, image)\n",
+    "        \n",
+    "    if BLUR_OBJECTS:\n",
+    "        blur_objects(image, result[\"detection_boxes\"], result[\"detection_class_entities\"], result[\"detection_scores\"])\n",
+    "        \n",
+    "    if DRAW_BOXES:\n",
+    "        draw_boxes(image, result[\"detection_boxes\"], result[\"detection_class_entities\"], result[\"detection_scores\"])\n",
+    "    \n",
+    "    cv.imwrite(IMAGE_OUTPUT_FOLDER + FILENAME_AFTER_BLURRING.format(file[0]) + \".\" + file[1], image)\n",
+    "    \n",
+    "    print(f\"Name: {file[0]}\\t Format: {file[1]}\\t Size: {image.shape[1]}x{image.shape[0]}\\t Time: {time() - start_time:.2f}s\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Run Classifier on every Video"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "cache_folder = VIDEO_CACHE_FOLDER if VIDEO_CACHE_FOLDER else tempfile.mkdtemp() + \"/\"\n",
+    "print(f\"Using folder {cache_folder}\")\n",
+    "\n",
+    "for file in videos:\n",
+    "    video = cv.VideoCapture(VIDEO_INPUT_FOLDER + file[0] + \".\" + file[1])\n",
+    "    start_time = time()\n",
+    "    \n",
+    "    width = FORCE_VIDEO_SIZE[0] or int(video.get(cv.CAP_PROP_FRAME_WIDTH))\n",
+    "    height = FORCE_VIDEO_SIZE[1] or int(video.get(cv.CAP_PROP_FRAME_HEIGHT))\n",
+    "    \n",
+    "    fps = video.get(cv.CAP_PROP_FPS)\n",
+    "    resolution = (width, height)\n",
+    "    frame_count = video.get(cv.CAP_PROP_FRAME_COUNT)\n",
+    "    \n",
+    "    # Will be needed for ffmpeg\n",
+    "    file.append(fps)\n",
+    "\n",
+    "    # Create output folder\n",
+    "    folderpath = f\"{cache_folder}{file[0]}-{file[1]}/\"\n",
+    "    !mkdir $folderpath\n",
+    "    file.append(folderpath)\n",
+    "    \n",
+    "    counter = 1\n",
+    "    length = frame_count if CROP_VIDEO_LENGTH is None else int(CROP_VIDEO_LENGTH * fps)\n",
+    "    while counter <= length:\n",
+    "        success, frame = video.read()\n",
+    "        \n",
+    "        # If no new Frame could be loaded (aka the video ended)\n",
+    "        if not success:\n",
+    "            break\n",
+    "        \n",
+    "        # Print progress\n",
+    "        if counter % int(length / 100) == 0:\n",
+    "            percentage = int(counter * 100 / length) + 1\n",
+    "            string = 'X' * percentage\n",
+    "            print(f\"{percentage}% [{string.ljust(100)}] {counter}/{int(length)}\", end=\"\\r\")\n",
+    "        \n",
+    "        frame = cv.cvtColor(frame, cv.COLOR_BGR2RGB)\n",
+    "        result = run_detector(classifier, frame)\n",
+    "        \n",
+    "        if BLUR_OBJECTS:\n",
+    "            blur_objects(frame, result[\"detection_boxes\"], result[\"detection_class_entities\"], result[\"detection_scores\"])\n",
+    "            \n",
+    "        if DRAW_BOXES:\n",
+    "            draw_boxes(frame, result[\"detection_boxes\"], result[\"detection_class_entities\"], result[\"detection_scores\"])\n",
+    "        \n",
+    "        # Resize Frames\n",
+    "        if (FORCE_VIDEO_SIZE != (None, None)):\n",
+    "            frame = cv.resize(frame, dsize=resolution, interpolation=cv.INTER_CUBIC)\n",
+    "        \n",
+    "        Save as png\n",
+    "        frame = cv.cvtColor(frame, cv.COLOR_RGB2BGR)\n",
+    "        name = f\"{folderpath}{str(counter).zfill(6)}.png\"\n",
+    "        cv.imwrite(name, frame, [cv.IMWRITE_PNG_COMPRESSION, 4])\n",
+    "        \n",
+    "        \n",
+    "        counter += 1\n",
+    "    \n",
+    "    video.release()\n",
+    "    \n",
+    "    print(f\"\\nName: {file[0]}\\t Format: {file[1]}\\t Size: {resolution}\\t FPS: {fps:.2f}\\t Duration: {(frame_count / fps):.1f}s\\t Time: {(time() - start_time):.2f}s\\n\")"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "### Compact Frames into a Video\n",
+    "\n",
+    "As a final step the single Frames will be compressed into a Video-File\n",
+    "\n",
+    "TODO: Add Audio"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "for file in videos:\n",
+    "    \n",
+    "    print(file)\n",
+    "    \n",
+    "    input_files = f\"{file[3]}%06d.png\"\n",
+    "    output_filename = f\"{VIDEO_OUTPUT_FOLDER}{FILENAME_AFTER_BLURRING.format(file[0])}.\" + (file[1] if FORCE_VIDEO_FORMAT is None else FORCE_VIDEO_FORMAT)\n",
+    "    fps = file[2]\n",
+    "    \n",
+    "    options = \"\"\n",
+    "    if VIDEO_BITRATE is not None:\n",
+    "        options += f\" -b:v {VIDEO_BITRATE}\"\n",
+    "    \n",
+    "    !ffmpeg -i $input_files -framerate $fps -y $options -loglevel +info $output_filename"
+   ]
+  },
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "## Cleanup\n",
+    "\n",
+    "Delete all tempdirs we created\n"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "if not VIDEO_CACHE_FOLDER:\n",
+    "    shutil.rmtree(cache_folder)"
+   ]
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "PyDeepLearning-1.0",
+   "language": "python",
+   "name": "pydeeplearning"
+  },
+  "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.8.5"
+  },
+  "toc-autonumbering": false,
+  "toc-showcode": false,
+  "toc-showmarkdowntxt": false
+ },
+ "nbformat": 4,
+ "nbformat_minor": 4
+}