{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import torch\n",
    "from torchvision.transforms.functional import rotate\n",
    "from pytomography.utils import rotate_detector_z\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Last Time\n",
    "\n",
    "We implemented $g=Hf$ on an object $f$ and for the case of a system matrix $H$ that doesn't model any phenomena"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "x = torch.linspace(-1,1,128)\n",
    "xv, yv, zv = torch.meshgrid(x,x,x, indexing='ij')\n",
    "obj = (xv**2 + 0.9*zv**2 < 0.5) * (torch.abs(yv)<0.8)\n",
    "obj = obj.to(torch.float).unsqueeze(dim=0)\n",
    "\n",
    "angles = np.arange(0,360.,3)\n",
    "image = torch.zeros((1,len(angles),128,128))\n",
    "for i,angle in enumerate(angles):\n",
    "    object_i = rotate_detector_z(obj,angle)\n",
    "    image[:,i] = object_i.sum(axis=1)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "Text(0, 0.5, 'r')"
      ]
     },
     "execution_count": 4,
     "metadata": {},
     "output_type": "execute_result"
    },
    {
     "data": {
      "image/png": "[encoded data, 30940 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plt.pcolormesh(image[0,:,:,64].T)\n",
    "plt.xlabel('Angle')\n",
    "plt.ylabel('r')"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# This Time"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "We'll talk about how to model $\\hat{f} = H^T g$ for the same operator $H$. This time, we'll start by creating an empty object. I claim that the following implements back projection from a single angle:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 22,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "torch.Size([1, 1, 128, 128])"
      ]
     },
     "execution_count": 22,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "image[:,0].unsqueeze(dim=1).shape"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 23,
   "metadata": {},
   "outputs": [],
   "source": [
    "bp_single_angle = torch.ones([1, 128, 128, 128]) * image[:,0].unsqueeze(dim=1)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "**Example**: If forward projection is\n",
    "\n",
    "$$\\begin{bmatrix} 1&1&1&0&0&0 \\\\ 0&0&0&1&1&1 \\end{bmatrix} \\begin{bmatrix} 2 \\\\ 3 \\\\ 1 \\\\ 4 \\\\ 5 \\\\ 6 \\end{bmatrix} = \\begin{bmatrix} 6 \\\\ 15 \\end{bmatrix} $$\n",
    "\n",
    "then back projection is\n",
    "\n",
    "$$\\begin{bmatrix} 1&0 \\\\ 1&0 \\\\1&0 \\\\ 0&1 \\\\ 0&1\\\\ 0&1\\end{bmatrix} \\begin{bmatrix} 6 \\\\ 15 \\end{bmatrix} = \\begin{bmatrix} 6 \\\\ 6 \\\\ 6 \\\\ 15 \\\\ 15 \\\\ 15 \\end{bmatrix}$$\n",
    "\n",
    "So there is an element of duplication going on."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Nopw I need to back project for each angle, and rotate the coordinate system back to the original object coordinate axes, and add everything together"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 24,
   "metadata": {},
   "outputs": [],
   "source": [
    "obj_bp = torch.zeros([1, 128, 128, 128])\n",
    "for i,angle in enumerate(angles):\n",
    "    bp_single_angle = torch.ones([1, 128, 128, 128]) * image[:,i].unsqueeze(dim=1)\n",
    "    bp_single_angle = rotate_detector_z(bp_single_angle, angle, negative=True)\n",
    "    obj_bp += bp_single_angle"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Just like we did for `obj` in tutorial 1, we'll look at different projections for `obj_bp`"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 25,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 51756 characters]",
      "text/plain": [
       "<Figure size 1000x300 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "fig, _ = plt.subplots(1,3,figsize=(10,3))\n",
    "plt.subplot(131)\n",
    "plt.pcolormesh(x,x,obj_bp[0].sum(axis=0).T, cmap='Greys_r')\n",
    "plt.xlabel('y')\n",
    "plt.ylabel('z')\n",
    "plt.title('Coronal')\n",
    "plt.subplot(132)\n",
    "plt.pcolormesh(x,x,obj_bp[0].sum(axis=1).T, cmap='Greys_r')\n",
    "plt.xlabel('x')\n",
    "plt.ylabel('z')\n",
    "plt.title('Sagittal')\n",
    "plt.subplot(133)\n",
    "plt.pcolormesh(obj_bp[0].sum(axis=2).T, cmap='Greys_r')\n",
    "plt.xlabel('x')\n",
    "plt.ylabel('y')\n",
    "plt.title('Axial')\n",
    "fig.tight_layout()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Clearly theres some blurring going on: $H^T g$ does **not** give the original object $f$ (if it did, then our job would be easy, and there'd be no need for reconstruction algorithms)."
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "pytomographytest",
   "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.9.16"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
