{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "3dbeb166",
   "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": "10b5abca",
   "metadata": {},
   "source": [
    "# Kymographs\n",
    "\n",
    "[Download this page as a Jupyter notebook](_downloads/da5ae3011d8c5847e1a5811afce1ec21/kymographs.ipynb)\n",
    "\n",
    "To load an HDF5 file and lists all of the kymographs inside of it, run:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "f82a1a99",
   "metadata": {},
   "outputs": [],
   "source": [
    "import lumicks.pylake as lk\n",
    "\n",
    "file = lk.File(\"example.h5\")\n",
    "list(file.kymos)  # e.g. shows: \"['cas9', 'reference']\""
   ]
  },
  {
   "cell_type": "markdown",
   "id": "b1a65ac2",
   "metadata": {},
   "source": [
    "Once again, [`.kymos`](tutorial/../_api/lumicks.pylake.File.html#lumicks.pylake.File.kymos) is a regular Python dictionary so we can easily iterate over it:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "0701afc2",
   "metadata": {},
   "outputs": [],
   "source": [
    "for name, kymo in file.kymos.items():"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "f0785c1a",
   "metadata": {},
   "source": [
    "Or just pick a single one:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "884b6762",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo = file.kymos[\"cas9\"]\n",
    "kymo.plot(channel=\"green\", aspect=\"auto\", adjustment=lk.ColorAdjustment(0, 5))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "c6907a77",
   "metadata": {},
   "source": [
    "Here we see the [`plot()`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.plot) convenience function. The `channel` argument accepts the strings “red”, “green”, “blue”, or “rgb”. This function accepts keyword arguments that are passed to [`plt.imshow()`](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html#matplotlib.pyplot.imshow) internally. Note also, the axes are labeled with the appropriate time and position units.\n",
    "\n",
    "The kymograph can also be exported to TIFF format:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "db4a3080",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo.export_tiff(\"image.tiff\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3bbf0b12",
   "metadata": {},
   "source": [
    "## Kymo data and details\n",
    "\n",
    "We can access the raw image data as `numpy` arrays:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "004fb9f9",
   "metadata": {},
   "outputs": [],
   "source": [
    "rgb = kymo.get_image(\"rgb\")  # matrix with `shape == (h, w, 3)`\n",
    "blue = kymo.get_image(\"blue\")  # single color so `shape == (h, w)`\n",
    "\n",
    "# Plot manually\n",
    "plt.imshow(kymo.get_image(\"green\"), aspect=\"auto\", adjustment=lk.ColorAdjustment(0, 5))"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "7a1c69fc",
   "metadata": {},
   "source": [
    "There are also several properties available for convenient access to the kymograph metadata:\n",
    "\n",
    "* [`kymo.center_point_um`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.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",
    "* [`kymo.size_um`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.size_um) provides a list of scan sizes in micrometers along the axes of the scan\n",
    "  \n",
    "* [`kymo.pixelsize_um`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.pixelsize_um) provides the pixel size in micrometers\n",
    "  \n",
    "* [`kymo.pixels_per_line`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.pixels_per_line) provides the number of pixels in each line of the kymograph\n",
    "  \n",
    "* [`kymo.fast_axis`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.fast_axis) provides the axis that was scanned (x or y)\n",
    "  \n",
    "* [`kymo.line_time_seconds`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.line_time_seconds) provides the time between successive lines\n",
    "  \n",
    "* [`kymo.pixel_time_seconds`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.pixel_time_seconds) provides the pixel dwell time.\n",
    "  \n",
    "* [`kymo.duration`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.duration) provides the full duration of the kymograph in seconds. This is equivalent to the number of scan lines times `line_time_seconds`.\n",
    "  \n",
    "## Cropping and slicing\n",
    "\n",
    "It is possible to crop a kymograph to a specific coordinate range, by using the function [`Kymo.crop_by_distance()`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.crop_by_distance). For example, we can crop the region from `6` micron to `24` micron using the following command:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "4eaf7903",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo.crop_by_distance(6, 24).plot(\"green\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "5ff2ce94",
   "metadata": {},
   "source": [
    "Kymographs can also be sliced in order to obtain a specific time range. For example, one can plot the region of the kymograph between 114.2 and 164.6 seconds using:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "2501c704",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo[\"114.2s\":\"164.6s\"].plot(\"green\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3ee105f7",
   "metadata": {},
   "source": [
    "Note, slicing in time is currently only supported for unprocessed kymographs. If you want to both crop and slice a kymo, the order of operations is important:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "5d3238ac",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo_sliced = kymo[\"114.2s\":\"164.6s\"]\n",
    "kymo_cropped = kymo_sliced.crop_by_distance(6, 24)\n",
    "\n",
    "kymo_cropped.plot(\"green\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "a03af59b",
   "metadata": {},
   "source": [
    "## Calibrating to base pairs\n",
    "\n",
    "By default, kymographs are constructed with units of microns for the position axis. If, however, the kymograph spans a known length of DNA (for example, lambda DNA) we can calibrate the position axis to kilobase pairs:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "14bc7aa9",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo_kbp = kymo_cropped.calibrate_to_kbp(48.502)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "19278e02",
   "metadata": {},
   "source": [
    "Now if we plot the image, the y-axis will be labeled in kbp:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7b940535",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo_kbp.plot(\"green\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "bab9bc00",
   "metadata": {},
   "source": [
    "These units are also carried forward to any downstream operations such as kymotracking algorithms and MSD analysis, . Note: currently this is a static calibration, meaning it is only valid if the traps do not change position during the time of the kymograph.\n",
    "\n",
    "We can also interactively slice, crop, and calibrate kymographs using:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "56c764c1",
   "metadata": {},
   "outputs": [],
   "source": [
    "widget = kymo.crop_and_calibrate(channel=\"green\", tether_length_kbp=48.502)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "4f856447",
   "metadata": {},
   "source": [
    "Simply click and drag the rectangle selector to the desired ROI. After closing the widget, we can access the edited kymograph with:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "81344e59",
   "metadata": {},
   "outputs": [],
   "source": [
    "new_kymo = widget.kymo\n",
    "new_kymo.plot(\"green\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "32e82323",
   "metadata": {},
   "source": [
    "If the optional `tether_length_kbp` argument is supplied, the kymograph is automatically calibrated to the desired length in kilobase pairs. If this argument is missing (the default value `None`) the edited kymograph is only sliced and cropped.\n",
    "\n",
    "Note that you can also flip a kymograph along its positional axis using [`kymo.flip()`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.flip). This returns a new (but flipped) [`Kymo`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo).\n",
    "\n",
    "## Downsampling\n",
    "\n",
    "We can downsample a kymograph in time by invoking:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7f51f9bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo_ds = kymo_cropped.downsampled_by(time_factor=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "90dc73ba",
   "metadata": {},
   "source": [
    "Or in space by invoking:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "7bf9b04c",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo_ds = kymo_cropped.downsampled_by(position_factor=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "57715223",
   "metadata": {},
   "source": [
    "Or both:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "d6da49c2",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo_ds = kymo_cropped.downsampled_by(time_factor=2, position_factor=2)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "924b2cbb",
   "metadata": {},
   "source": [
    "Note however, that not all functionalities are present anymore when downsampling a kymograph. For example, if we downsample a kymograph by time, we can no longer access the per pixel timestamps:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "299f8bca",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo_ds.timestamps"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "20dbdea7",
   "metadata": {},
   "source": [
    "## Plotting and exporting\n",
    "\n",
    "There are also convenience functions to plot individual color channels and the full RGB image:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "e091a419",
   "metadata": {},
   "outputs": [],
   "source": [
    "plt.subplot(2, 1, 1)\n",
    "kymo.plot(\"rgb\")\n",
    "plt.subplot(2, 1, 2)\n",
    "kymo.plot(\"blue\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "30c7b82c",
   "metadata": {},
   "source": [
    "The images can also be exported in the TIFF format:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6f691559",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo.export_tiff(\"image.tiff\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "e2a1b8dd",
   "metadata": {},
   "source": [
    "## Correlating with force\n",
    "\n",
    "We can downsample channel data according to the lines in a kymo. We can use [`line_timestamp_ranges()`](tutorial/../_api/lumicks.pylake.kymo.Kymo.html#lumicks.pylake.kymo.Kymo.line_timestamp_ranges) for this:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "b623906b",
   "metadata": {},
   "outputs": [],
   "source": [
    "line_timestamp_ranges = kymo.line_timestamp_ranges()"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "42dda260",
   "metadata": {},
   "source": [
    "This returns a list of start and stop timestamps that can be passed directly to [`downsampled_to()`](tutorial/../_api/lumicks.pylake.channel.Slice.html#lumicks.pylake.channel.Slice.downsampled_to), which will then return a [`Slice`](tutorial/../_api/lumicks.pylake.channel.Slice.html#lumicks.pylake.channel.Slice) with a datapoint per line:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "23d76fb9",
   "metadata": {},
   "outputs": [],
   "source": [
    "downsampled = f.force1x.downsampled_over(line_timestamp_ranges)"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "68ba0290",
   "metadata": {},
   "source": [
    "There is also a convenience function to plot a kymograph along with a downsampled force trace:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "6a0f000d",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo.plot_with_force(\"1x\", \"green\")"
   ]
  },
  {
   "cell_type": "markdown",
   "id": "3476b75e",
   "metadata": {},
   "source": [
    "This will average the forces over each Kymograph line and plot them in a correlated fashion. The function can also take a dictionary of extra arguments to customize the kymograph plot. These parameter values get forwarded to [`matplotlib.pyplot.imshow()`](https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.imshow.html#matplotlib.pyplot.imshow). For instance, if a few pixels dominate the image, it might be preferable to set the scale by hand. This can be accomplished by providing a [`ColorAdjustment`](tutorial/../_api/lumicks.pylake.ColorAdjustment.html#lumicks.pylake.ColorAdjustment):"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "a2ca928a",
   "metadata": {},
   "outputs": [],
   "source": [
    "kymo.plot_with_force(\"1x\", \"green\", adjustment=lk.ColorAdjustment(0, 3))"
   ]
  }
 ],
 "metadata": {},
 "nbformat": 4,
 "nbformat_minor": 5
}