{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "73805e47",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "import lumicks.pylake as lk\n",
    "\n",
    "%matplotlib inline"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f2f8f3dd",
   "metadata": {},
   "source": [
    "# Confocal images\n",
    "\n",
    "[Download this page as a Jupyter notebook](_downloads/331a3e4735768e2aaaaec37a7ff87bbd/images.ipynb)\n",
    "\n",
    "The following code uses scans as an example. Kymographs work the same way – just substitute [`file.scans`](tutorial/../_api/lumicks.pylake.File.html#lumicks.pylake.File.scans) with [`file.kymos`](tutorial/../_api/lumicks.pylake.File.html#lumicks.pylake.File.kymos). To load an HDF5 file and list all of the scans inside of it, run:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "41ab3a95",
   "metadata": {},
   "outputs": [],
   "source": [
    "import lumicks.pylake as lk\n",
    "\n",
    "file = lk.File(\"Scan_WTCas9_2Markers_unspecific.h5\")\n",
    "list(file.scans)  # e.g. shows: \"['reference', 'bleach', 'imaging']\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4affccfc",
   "metadata": {},
   "source": [
    "[`.scans`](tutorial/../_api/lumicks.pylake.File.html#lumicks.pylake.File.scans) is a regular Python dictionary so we can iterate over it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "8617d6f1",
   "metadata": {},
   "outputs": [],
   "source": [
    "# Plot all scans in a file\n",
    "for name, scan in file.scans.items():\n",
    "    scan.plot(channel=\"rgb\")\n",
    "    plt.savefig(name)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2b8f105f",
   "metadata": {},
   "source": [
    "Or just pick a single one by providing the name of the scan as `scan=file.scans[\"name\"]`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e8e5589f",
   "metadata": {},
   "outputs": [],
   "source": [
    "scan = file.scans[\"41\"]\n",
    "scan.plot(\"red\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7e627f5d",
   "metadata": {},
   "source": [
    "## Plotting and Exporting\n",
    "\n",
    "As shown above, there are convenience functions for plotting either the full RGB image or a single color channel:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0f4b55fe",
   "metadata": {},
   "outputs": [],
   "source": [
    "scan.plot(channel=\"rgb\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "1c9c1324",
   "metadata": {},
   "source": [
    "The `channel` argument accepts the strings `“red”`, `“green”`, `“blue”`, or `“rgb”`. Multi-frame scans are also supported:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a4af5748",
   "metadata": {},
   "outputs": [],
   "source": [
    "multiframe_file = lk.File(\"Scan_WTCas9_2Markers_binding on and off_stack.h5\")\n",
    "multiframe_scan = multiframe_file.scans[\"46\"]\n",
    "\n",
    "print(multiframe_scan.num_frames)\n",
    "print(multiframe_scan.get_image(\"blue\").shape)  # (self.num_frames, h, w) -> single color channel\n",
    "print(multiframe_scan.get_image(\"rgb\").shape)  # (self.num_frames, h, w, 3) -> three color channels\n",
    "\n",
    "# plot frame at index 3 (first frame is index 0)\n",
    "# defaults to the first frame if no argument is given\n",
    "multiframe_scan.plot(\"green\", frame=3)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bb158a05",
   "metadata": {},
   "source": [
    "Sometimes a few bright pixels can dominate the colormap of a scan. When this is the case, it may be beneficial to manually set the color limits for each of the channels. This can be accomplished by providing a [`ColorAdjustment`](tutorial/../_api/lumicks.pylake.ColorAdjustment.html#lumicks.pylake.ColorAdjustment) to plotting or export functions:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6d14b6de",
   "metadata": {},
   "outputs": [],
   "source": [
    "scan.plot(channel=\"rgb\", adjustment=lk.ColorAdjustment([0, 0, 0], [4, 4, 4]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ea1119a9",
   "metadata": {},
   "source": [
    "Here the first array gives the minimal values for the color scale of red, green and blue respectively (here `[0, 0, 0]`) and the second array gives the maximum values for the color scales. The color scale is linear by default, but [Gamma correction](https://en.wikipedia.org/wiki/Gamma_correction) can be applied in addition to the bounds by supplying an extra argument named `gamma`. For example, a gamma adjustment of `0.1` to the green channel can be applied as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f66149c",
   "metadata": {},
   "outputs": [],
   "source": [
    "scan.plot(channel=\"rgb\", adjustment=lk.ColorAdjustment([0, 0, 0], [4, 4, 4], gamma=[1, 0.1, 1]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "69deb0d6",
   "metadata": {},
   "source": [
    "The limits can also be specified in percentiles:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d45e5170",
   "metadata": {},
   "outputs": [],
   "source": [
    "scan.plot(channel=\"rgb\", adjustment=lk.ColorAdjustment([5, 5, 5], [95, 95, 95], mode=\"percentile\"))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "d3514c7e",
   "metadata": {},
   "source": [
    "Export an image in the TIFF format as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d8f0567a",
   "metadata": {},
   "outputs": [],
   "source": [
    "scan.export_tiff(\"image.tiff\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c969d1a9",
   "metadata": {},
   "source": [
    "Scans can also be exported to video formats. Exporting the red channel of a multi-scan GIF can be done as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "27c907a9",
   "metadata": {},
   "outputs": [],
   "source": [
    "multiframe_scan.export_video(\"red\", \"test_red.gif\", adjustment=lk.ColorAdjustment([0], [4]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2d08a784",
   "metadata": {},
   "source": [
    "Or if we want to export a subset of frames (the first frame being 2, and the last frame being 15) of all three channels at a frame rate of 2 frames per second, we can do this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "c87f67cd",
   "metadata": {},
   "outputs": [],
   "source": [
    "multiframe_scan.export_video(\"rgb\", \"test_rgb.gif\", start_frame=2, stop_frame=15, fps=2,adjustment=lk.ColorAdjustment([0, 0, 0], [4, 4, 4]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "ff2bdab1",
   "metadata": {},
   "source": [
    "For other video formats such as `.mp4` or `.avi`, ffmpeg must be installed. See [installation instructions](tutorial/../install.html#ffmpeg-installation) for more information on this.\n",
    "\n",
    "The images contain pixel data where each pixel represents summed photon counts. The photon count per pixel can be accessed as follows:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3a29e5b0",
   "metadata": {},
   "outputs": [],
   "source": [
    "photons = scan.red_photon_count\n",
    "plt.plot(photons.timestamps, photons.data)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "572a72c0",
   "metadata": {},
   "source": [
    "## Scan metadeta\n",
    "\n",
    "There are several properties available for convenient access to the scan metadata:\n",
    "\n",
    "* [`scan.center_point_um`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.center_point_um) provides a dictionary of the central x, y, and z coordinates of the scan in micrometers relative to the brightfield field of view\n",
    "  \n",
    "* [`scan.size_um`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.size_um) provides the scan size in micrometers along the axes of the scan\n",
    "  \n",
    "* [`scan.pixelsize_um`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.pixelsize_um) provides the pixel size in micrometers\n",
    "  \n",
    "* [`scan.lines_per_frame`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.lines_per_frame) provides the number scanned lines in each frame (number of rows in the raw data array)\n",
    "  \n",
    "* [`scan.pixels_per_line`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.pixels_per_line) provides the number of pixels in each line of the scan (number of columns in the raw data array)\n",
    "  \n",
    "* [`scan.fast_axis`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.fast_axis) provides the fastest axis that was scanned (x or y)\n",
    "  \n",
    "* [`scan.num_frames`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.num_frames) provides the number of frames available\n",
    "  \n",
    "* [`kymo.pixel_time_seconds`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.pixel_time_seconds) provides the pixel dwell time.\n",
    "  \n",
    "## Raw data and data selection\n",
    "\n",
    "You can access the raw image data directly. For a [`Scan`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan) with only a single frame:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14e29832",
   "metadata": {},
   "outputs": [],
   "source": [
    "rgb = scan.get_image(\"rgb\")  # matrix with `shape == (h, w, 3)`\n",
    "blue = scan.get_image(\"blue\")  # single color so `shape == (h, w)`\n",
    "\n",
    "# Plot manually\n",
    "plt.imshow(rgb)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "898262e7",
   "metadata": {},
   "source": [
    "For scans with multiple frames:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "85c9d847",
   "metadata": {},
   "outputs": [],
   "source": [
    "# returned data has `shape == (n_frames, h, w, 3)`\n",
    "rgb = multiframe_scan.get_image(\"rgb\")\n",
    "# returned data has `shape == (n_frames, h, w)`\n",
    "blue = multiframe_scan.get_image(\"blue\")\n",
    "\n",
    "# Manually plot the RGB image of the first frame.\n",
    "plt.imshow(rgb[0, :, :, :])"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bd7566ba",
   "metadata": {},
   "source": [
    "We can also slice out a subset of frames from an image stack:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5315f025",
   "metadata": {},
   "outputs": [],
   "source": [
    "sliced_scan = multiframe_scan[5:10]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "2768f623",
   "metadata": {},
   "source": [
    "This will return a new [`Scan`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan) containing data equivalent to:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4343cbae",
   "metadata": {},
   "outputs": [],
   "source": [
    "multiframe_scan.get_image(\"rgb\")[5:10, :, :, :]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "0c6f9d53",
   "metadata": {},
   "source": [
    "We can also slice the frames by time:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "dfa4764b",
   "metadata": {},
   "outputs": [],
   "source": [
    "# get frames corresponding to the time range 30 through 90 seconds\n",
    "sliced_scan = multiframe_scan[\"30s\":\"90s\"]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "83bc250a",
   "metadata": {},
   "source": [
    "Or directly using timestamps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6d33a271",
   "metadata": {},
   "outputs": [],
   "source": [
    "# get frames that fall between the start and stop of a force channel\n",
    "multiframe_scan[multiframe_file.force1x.start:multiframe_file.force1x.stop]"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "40e3cae0",
   "metadata": {},
   "source": [
    "## Correlating a multiframe scan with data channels\n",
    "\n",
    "The frames of a multiframe scan can be correlated to the force or other data channels. Downsample channel data according to the frames in a scan using [`frame_timestamp_ranges()`](tutorial/../_api/lumicks.pylake.scan.Scan.html#lumicks.pylake.scan.Scan.frame_timestamp_ranges):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d939cde6",
   "metadata": {},
   "outputs": [],
   "source": [
    "frame_timestamp_ranges = multiframe_scan.frame_timestamp_ranges()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5e2c6bbe",
   "metadata": {},
   "source": [
    "You can choose to add the flag `include_dead_time = True` if you want to include the dead time at the end of each frame (default is `False`). This returns a list of start and stop timestamps that can be passed directly to [`downsampled_over()`](tutorial/../_api/lumicks.pylake.channel.Slice.html#lumicks.pylake.channel.Slice.downsampled_over), which will then return a [`Slice`](tutorial/../_api/lumicks.pylake.channel.Slice.html#lumicks.pylake.channel.Slice) with a datapoint per frame:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b1d2404c",
   "metadata": {},
   "outputs": [],
   "source": [
    "downsampled = multiframe_file.force1x.downsampled_over(frame_timestamp_ranges)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "86623a79",
   "metadata": {},
   "source": [
    "The multi-frame confocal scans can also be correlated with a channel [`Slice`](tutorial/../_api/lumicks.pylake.channel.Slice.html#lumicks.pylake.channel.Slice) using an interactive plot."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7e4442c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "multiframe_scan.plot_correlated(multiframe_file.force1x, adjustment=lk.ColorAdjustment([0, 0, 0], [4, 4, 4]))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "501dc119",
   "metadata": {},
   "source": [
    "Note that you need an interactive backend for this plot to work; instead of running `%matplotlib inline` at the top of the notebook, run `%matplotlib notebook`. If some cells were already executed, you will need to restart the kernel as well."
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}