{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 159,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import argparse\n",
    "import imutils\n",
    "import cv2\n",
    "import os\n",
    "import matplotlib.pyplot as plt\n",
    "import doxapy\n",
    "import sys"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 172,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 191032 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "inputfile = '/home/elena/Documents/Projects/kassenzettel-django-react/testings/receipe-upload-_oqp9us3.jpg'\n",
    "#inputfile = '/home/elena/Documents/Projects/kassenzettel-django-react/testings/testbild.png'\n",
    "img = cv2.imread(inputfile, cv2.IMREAD_GRAYSCALE)\n",
    "start = 200\n",
    "stop = start + 700\n",
    "\n",
    "img_snippet = img[start:stop,:]\n",
    "img_copy = cv2.cvtColor(img_snippet,cv2.COLOR_GRAY2RGB)\n",
    "\n",
    "binary_image = np.empty(img_snippet.shape, img_snippet.dtype)\n",
    "sauvola = doxapy.Binarization(doxapy.Binarization.Algorithms.ISAUVOLA)\n",
    "sauvola.initialize(img_snippet)\n",
    "sauvola.to_binary(binary_image, {\"window\": 45, \"k\": 0.009})\n",
    "\n",
    "\n",
    "#Plot the snippet\n",
    "plt.figure(figsize=(10,10))\n",
    "plt.imshow(binary_image, cmap='gray')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 176,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 347952 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "#Invert the binary image\n",
    "binary_image = cv2.bitwise_not(binary_image)\n",
    "# Calc connected components\n",
    "num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(binary_image, connectivity=8)\n",
    "\n",
    "# Plot centers and bounding boxes of connected components in the snippet\n",
    "plt.figure(figsize=(10,10))\n",
    "plt.imshow(img_copy, cmap='gray')\n",
    "for i in range(70,71):#range(1,num_labels):\n",
    "    x = stats[i, cv2.CC_STAT_LEFT]\n",
    "    y = stats[i, cv2.CC_STAT_TOP]\n",
    "    w = stats[i, cv2.CC_STAT_WIDTH]\n",
    "    h = stats[i, cv2.CC_STAT_HEIGHT]\n",
    "    cx = centroids[i,0]\n",
    "    cy = centroids[i,1]\n",
    "    # Plot bounding box as rectangle\n",
    "    plt.plot([x,x+w],[y,y],'r-')\n",
    "    plt.plot([x,x],[y,y+h],'r-')\n",
    "    plt.plot([x,x+w],[y+h,y+h],'r-')\n",
    "    plt.plot([x+w,x+w],[y,y+h],'r-')\n",
    "\n",
    "    # Plot center as cross\n",
    "    plt.plot([cx-5,cx+5],[cy,cy],'g-')\n",
    "    plt.plot([cx,cx],[cy-5,cy+5],'g-')\n",
    "\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 170,
   "metadata": {},
   "outputs": [
    {
     "ename": "IndexError",
     "evalue": "boolean index did not match indexed array along dimension 0; dimension is 700 but corresponding boolean dimension is 699",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mIndexError\u001b[0m                                Traceback (most recent call last)",
      "\u001b[1;32m/home/elena/Documents/Projects/kassenzettel-django-react/testings/linedetection_paper.ipynb Cell 4\u001b[0m line \u001b[0;36m1\n\u001b[1;32m     <a href='vscode-notebook-cell:/home/elena/Documents/Projects/kassenzettel-django-react/testings/linedetection_paper.ipynb#W3sZmlsZQ%3D%3D?line=10'>11</a>\u001b[0m \u001b[39mfor\u001b[39;00m i \u001b[39min\u001b[39;00m \u001b[39mrange\u001b[39m(\u001b[39m1\u001b[39m,num_labels):\n\u001b[1;32m     <a href='vscode-notebook-cell:/home/elena/Documents/Projects/kassenzettel-django-react/testings/linedetection_paper.ipynb#W3sZmlsZQ%3D%3D?line=11'>12</a>\u001b[0m     mask \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39mzeros(binary_image\u001b[39m.\u001b[39mshape, np\u001b[39m.\u001b[39muint8)\n\u001b[0;32m---> <a href='vscode-notebook-cell:/home/elena/Documents/Projects/kassenzettel-django-react/testings/linedetection_paper.ipynb#W3sZmlsZQ%3D%3D?line=12'>13</a>\u001b[0m     mask[labels \u001b[39m==\u001b[39;49m i] \u001b[39m=\u001b[39m \u001b[39m255\u001b[39m\n\u001b[1;32m     <a href='vscode-notebook-cell:/home/elena/Documents/Projects/kassenzettel-django-react/testings/linedetection_paper.ipynb#W3sZmlsZQ%3D%3D?line=14'>15</a>\u001b[0m     xy_coords \u001b[39m=\u001b[39m np\u001b[39m.\u001b[39mflip(np\u001b[39m.\u001b[39mcolumn_stack(np\u001b[39m.\u001b[39mwhere(mask \u001b[39m==\u001b[39m \u001b[39m255\u001b[39m)), axis\u001b[39m=\u001b[39m\u001b[39m1\u001b[39m)\n\u001b[1;32m     <a href='vscode-notebook-cell:/home/elena/Documents/Projects/kassenzettel-django-react/testings/linedetection_paper.ipynb#W3sZmlsZQ%3D%3D?line=15'>16</a>\u001b[0m     \u001b[39m#means[i] = np.mean(xy_coords, axis=0)  # Is equal to the centroid\u001b[39;00m\n",
      "\u001b[0;31mIndexError\u001b[0m: boolean index did not match indexed array along dimension 0; dimension is 700 but corresponding boolean dimension is 699"
     ]
    }
   ],
   "source": [
    "# Calculate the mean vector and the covariance matrix of the pixels in each connected component\n",
    "means = np.empty((num_labels,2))\n",
    "covs = np.empty((num_labels,2,2))\n",
    "eigenvalues = np.empty((num_labels,2))\n",
    "eigenvectors = np.empty((num_labels,2,2))\n",
    "\n",
    "img_copy = cv2.cvtColor(img_snippet,cv2.COLOR_GRAY2RGB)\n",
    "\n",
    "labelsForDelete = []\n",
    "\n",
    "for i in range(1,num_labels):\n",
    "    mask = np.zeros(binary_image.shape, np.uint8)\n",
    "    mask[labels == i] = 255\n",
    "    \n",
    "    xy_coords = np.flip(np.column_stack(np.where(mask == 255)), axis=1)\n",
    "    #means[i] = np.mean(xy_coords, axis=0)  # Is equal to the centroid\n",
    "    covs[i] = np.cov(xy_coords, rowvar=False)\n",
    "\n",
    "    # Calculate the eienvalues and eigenvectors of the covariance matrix\n",
    "    eigenvalues[i], eigenvectors[i] = np.linalg.eig(covs[i])\n",
    "\n",
    "    #Sort the eigenvalues and eigenvectors\n",
    "    idx = eigenvalues[i].argsort()[::-1]\n",
    "    eigenvalues[i] = eigenvalues[i][idx]\n",
    "    eigenvectors[i] = eigenvectors[i][:,idx]\n",
    "\n",
    "    #If eigenvalue1 / eigenvalue2 > 15, remove the corresponding connected component\n",
    "    if eigenvalues[i,0] / eigenvalues[i,1] > 15**2:\n",
    "        labelsForDelete.append(i)\n",
    "\n",
    "\n",
    "#Delete the connected components in list\n",
    "labels = np.delete(labels, labelsForDelete, axis=0)\n",
    "num_labels = num_labels - len(labelsForDelete)\n",
    "stats = np.delete(stats, labelsForDelete, axis=0)\n",
    "centroids = np.delete(centroids, labelsForDelete, axis=0)\n",
    "eigenvalues = np.delete(eigenvalues, labelsForDelete, axis=0)\n",
    "eigenvectors = np.delete(eigenvectors, labelsForDelete, axis=0)\n",
    "\n",
    "# Plot eigenvectors and eigenvalues as ellipses\n",
    "plt.figure(figsize=(10,10))\n",
    "print(num_labels)\n",
    "for i in range(1,num_labels):\n",
    "    cx = centroids[i,0]\n",
    "    cy = centroids[i,1]\n",
    "    \n",
    "    # Plot eigenvalues as ellipses\n",
    "    cv2.ellipse(img_copy,(int(centroids[i,0]),int(centroids[i,1])),(int(np.sqrt(eigenvalues[i,0])),int(np.sqrt(eigenvalues[i,1]))),np.rad2deg(np.arctan2(eigenvectors[i,0,1],eigenvectors[i,0,0])),0,360,(0,255,0),2)\n",
    "plt.imshow(img_copy, cmap='gray')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 143,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 92176 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Create a new image with only the ellipses\n",
    "ellipseMask = np.zeros(binary_image.shape, np.uint8)\n",
    "for i in range(1,num_labels):\n",
    "    # Plot eigenvalues as ellipses in black and white image\n",
    "    cv2.ellipse(ellipseMask,(int(centroids[i,0]),int(centroids[i,1])),(int(np.sqrt(eigenvalues[i,0])),int(np.sqrt(eigenvalues[i,1]))),np.rad2deg(np.arctan2(eigenvectors[i,0,1],eigenvectors[i,0,0])),0,360,(255,255,255),-1)\n",
    "\n",
    "plt.figure(figsize=(10,10))\n",
    "plt.imshow(ellipseMask, cmap='gray')\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 153,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[661.46630728  60.69811321]\n",
      "(700, 256)\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 20788 characters]",
      "text/plain": [
       "<Figure size 400x400 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 31676 characters]",
      "text/plain": [
       "<Figure size 1000x200 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Project the ellipses to the x-axis\n",
    "#for n in range(0,ellipseMask.shape[0]):\n",
    "#    s = ellipseMask[n,:].tostring()\n",
    "#    print(s)\n",
    "from itertools import groupby \n",
    "\n",
    "#Itertools solution\n",
    "def changes_in(sequence):\n",
    "    return len(list(groupby(sequence))) - 1\n",
    "\n",
    "#Bitwise solution: Ruffly 2 times faster than itertools solution\n",
    "#https://stackoverflow.com/questions/70300297/is-there-a-built-in-python-function-to-count-bit-flip-in-a-binary-string#:~:text=There%20is%20a%20very%20fast%20way%20to%20do,string%20to%20count%20the%20number%20of%20bit%20flips.\n",
    "def changes_in_bitoperation(sequence):\n",
    "    # Convert the sequence to an integer: [0,1,1,0,1] -> 0b01101\n",
    "    s = np.array2string(sequence, separator='',max_line_width=1000000)\n",
    "    n = int(s[1:-1], 2)\n",
    "\n",
    "    # Build a binary mask to skip the most significant bit of n: 0b01101 -> 0b01111\n",
    "    mask = (1 << (len(s)-1)) - 1\n",
    "\n",
    "    # Check if the ith bit of n is different from the (i+1)th bit of n using a bit-wise XOR:\n",
    "    # 0b01101 & 0b01111 -> 0b1101  (discard the first bit)\n",
    "    # 0b01101 >> 1      -> 0b0110\n",
    "    # 0b1101 ^ 0b0110   -> 0b1011\n",
    "    bitFlips = (n & mask) ^ (n >> 1)\n",
    "\n",
    "    # Convert the integer back to a string and count the bit flips: 0b1011 -> \"0b1011\" -> 3\n",
    "    flipCount = bin(bitFlips).count('1')\n",
    "    return flipCount\n",
    "\n",
    "Np = 256\n",
    "\n",
    "xprojection = np.zeros(Np)\n",
    "\n",
    "centroidNum = 7\n",
    "print(centroids[centroidNum,:])\n",
    "\n",
    "paddedMask = np.zeros((ellipseMask.shape[0],Np), np.uint8)\n",
    "\n",
    "if centroids[centroidNum,0]-Np/2 < 0 and centroids[centroidNum,0]+Np/2 < ellipseMask.shape[1]:\n",
    "    #Pad at the beginning\n",
    "    paddedMask = np.pad(ellipseMask, ((0,0),(int(Np/2 - centroids[centroidNum,0]),0)), 'reflect')[:,0:Np]\n",
    "    print(paddedMask.shape)\n",
    "\n",
    "elif centroids[centroidNum,0]-Np/2 > 0 and centroids[centroidNum,0]+Np/2 > ellipseMask.shape[1]:\n",
    "    #Pad at the end\n",
    "    paddedMask = np.pad(ellipseMask, ((0,0),(0,int(Np/2 - (ellipseMask.shape[1] - centroids[centroidNum,0])))), 'reflect')[:,0:Np]\n",
    "elif centroids[centroidNum,0]-Np/2 < 0 and centroids[centroidNum,0]+Np/2 > ellipseMask.shape[1]:\n",
    "    #Pad at the beginning and at the end\n",
    "    paddedMask = np.pad(ellipseMask, ((0,0),(int(Np/2 - centroids[centroidNum,0]),int(Np/2 - (ellipseMask.shape[1] - centroids[centroidNum,0])))), 'reflect')[:,0:Np]\n",
    "else:\n",
    "    #No padding\n",
    "    paddedMask = ellipseMask[:,int(centroids[centroidNum,0]-Np/2):int(centroids[centroidNum,0]+Np/2)]\n",
    "    print(paddedMask.shape)\n",
    "\n",
    "#plot paddedMask\n",
    "plt.figure(figsize=(4,4))\n",
    "plt.imshow(paddedMask, cmap='gray')\n",
    "plt.show()\n",
    "\n",
    "\n",
    "for rowNumber in range(0,paddedMask.shape[1]):\n",
    "    row = (paddedMask[:,rowNumber]/255).astype(np.uint8)\n",
    "    flipps = changes_in_bitoperation(row)\n",
    "    if row[0] > 0 and row[-1] == 0:\n",
    "        flipps += 1\n",
    "    elif row[-1] > 0 and row[0] == 0:\n",
    "        flipps += 1\n",
    "    elif row[0] > 0 and row[-1] > 0:\n",
    "        flipps += 2\n",
    "\n",
    "    xprojection[rowNumber] = flipps/2\n",
    "\n",
    "#Plot the xprojection as line chart\n",
    "plt.figure(figsize=(10,2))\n",
    "plt.plot(xprojection)\n",
    "plt.show()\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 152,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "kp is not local maximum\n",
      "0.01\n"
     ]
    }
   ],
   "source": [
    "epsilon = 0.01\n",
    "kp = 5\n",
    "\n",
    "Vp = 0\n",
    "#Check if xprojection is smaler or equal than 3 for all values\n",
    "if np.all(xprojection <= 3):\n",
    "    Vp = epsilon\n",
    "else:\n",
    "    # Calculate the rfft of the xprojection\n",
    "    dft = np.fft.rfft(xprojection)\n",
    "    #print(dft)\n",
    "\n",
    "    #Check if dft[kp] is local maximum\n",
    "    if kp > 0 and kp < len(dft)-1:\n",
    "        if np.abs(dft[kp]) > np.abs(dft[kp-1]) and np.abs(dft[kp]) > np.abs(dft[kp+1]):\n",
    "            Vp = -np.log(np.abs(dft[kp])**2/np.abs(dft[0])**2)\n",
    "        else:\n",
    "            Vp = epsilon\n",
    "            print(\"kp is not local maximum\")\n",
    "\n",
    "print(Vp)"
   ]
  }
 ],
 "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.11.5"
  },
  "orig_nbformat": 4
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
