{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "import cv2\n",
    "%matplotlib inline"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "To perform the block binarization operation, the `binarizer` function was written.\n",
    "It takes 3 arguments: the image, the number of splits on the vertical axis and\n",
    "the number of splits on the horizontal axis.\n",
    "The image is divided into equal sections. Their size is determined by the ratio\n",
    "of the image size to the number of splits along each axis. If the size of the\n",
    "image is not a multiple of the number of splits, then the last block in\n",
    "the iteration of the cycle (these are the right and bottom edges of the image)\n",
    "have a smaller size than all the other blocks. An empty matrix is created,\n",
    "then, in the cycle, the binarization of individual blocks and the joining of these\n",
    "blocks to the created matrix takes place. This is how the entire image is\n",
    "processed, after which the function returns the processed image."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "def binarizer(input_img, v_blocks, h_blocks):\n",
    "    # This programs splits an image into 'v_blocks' blocks in vertical direction\n",
    "    # and into 'h_blocks' blocks in horizontal direction. All the blocks are equal except the last ones.\n",
    "\n",
    "    height, width = input_img.shape # size of the input image\n",
    "    output_img = np.nan # output image\n",
    "    temp = np.nan\n",
    "    \n",
    "    merge = lambda tmp, aim, n: aim if np.isnan(tmp).any() else np.concatenate((tmp, aim), axis=n)\n",
    "    \n",
    "    for row in range(v_blocks):\n",
    "        # horizontal range\n",
    "        row_start = row * np.floor(height / v_blocks)\n",
    "        row_end = height if row == v_blocks - 1 else (row + 1) * np.floor(height / v_blocks)\n",
    "            \n",
    "        for col in range(h_blocks):\n",
    "            # horizontal range\n",
    "            col_start = col  * np.floor(width / h_blocks)\n",
    "            col_end = width if col == width - 1 else (col + 1) * np.floor(width / h_blocks)\n",
    "\n",
    "            _, img_bin = cv2.threshold(input_img[int(row_start):int(row_end), int(col_start):int(col_end)], 0, 255, cv2.THRESH_OTSU)\n",
    "            temp = merge(temp, img_bin, 1)\n",
    "        output_img = merge(output_img, temp, 0)\n",
    "        temp = np.nan\n",
    "    \n",
    "    return output_img"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Function `plot_binarized` is intended to plot image binarized without using blocks\n",
    "and with blocks. For reading and processing image OpenCV library is used.\n",
    "First of all, we upload an image. We are looking for the binarization threshold\n",
    "for the entire image using the graythresh function. Then, we build a histogram\n",
    "and add a line to the histogram indicating the binarization threshold."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "def plot_binarized(img_path, v_blocks, h_blocks):\n",
    "    im = cv2.imread(img_path, 0)\n",
    "    thresh, im_bin = cv2.threshold(im, 0, 255, cv2.THRESH_OTSU) # threshold for binarization\n",
    "\n",
    "    fig1, axes = plt.subplots(2, 2, figsize=(10, 10))\n",
    "    axes[0][0].imshow(im, cmap='gray')\n",
    "    axes[0][0].set_title('Original image')\n",
    "    axes[0][1].hist(im.ravel(), 256, [0,256])\n",
    "    axes[0][1].axvline(np.around(thresh), color='r') # threshold marker\n",
    "    axes[0][1].set_title('Image histogram\\nRed line - threshold ({})'.format(thresh))\n",
    "\n",
    "    axes[1][0].imshow(im_bin, cmap='gray')\n",
    "    axes[1][0].set_title('Whole image binarization')\n",
    "\n",
    "    # binarizing using blocks\n",
    "    im_bin_block = binarizer(im, v_blocks, h_blocks)\n",
    "    axes[1][1].imshow(im_bin_block, cmap='gray')\n",
    "    axes[1][1].set_title('Block binarization: {} $\\\\times$ {} blocks'.format(v_blocks, h_blocks))\n",
    "    plt.show()"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# First image\n",
    "\n",
    "Let's upload image with the rice grains and use 10 x 10 blocks."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 388936 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Loading the 1st image\n",
    "plot_binarized(img_path='rice.tif', v_blocks=10, h_blocks=10)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Comparing the obtained images, you can see that with block binarization, all the\n",
    "drawings have clear edges, while in the right part of the left image they have\n",
    "lost their integrity or disappeared from the image altogether. There are also no\n",
    "extra white dots that are present on the left image in the left part."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Second image\n",
    "\n",
    "Upload the image \"figure.png\". We do the same operations with it as with the\n",
    "first image."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 91512 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Loading the 2nd image\n",
    "plot_binarized(img_path='figure.png', v_blocks=  10, h_blocks=10)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It can be seen that both with conventional and block binarization, a bad result\n",
    "is obtained. With binarization of the entire image, we get that only half of the\n",
    "image is processed correctly, with block binarization, artifacts arise. This is\n",
    "due to the low contrast of the image. From the histogram above, you can see that\n",
    "most of the pixels have low intensity values.\n",
    "Let's try to change the number and configuration of blocks to 10x1."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 88476 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "# Loading the 2nd image\n",
    "plot_binarized(img_path='figure.png', v_blocks=  10, h_blocks=1)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "It can be seen that in this case, block binarization gives an acceptable result.\n",
    "And although the algorithm did not fully cope at the very bottom of the image,\n",
    "block binarization gave a better result than binarization at the global threshold.\n",
    "You can also apply similar reasoning to the first image and perform binarization\n",
    "only along the horizontal axis."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 387064 characters]",
      "text/plain": [
       "<Figure size 1000x1000 with 4 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "plot_binarized(img_path='rice.tif', v_blocks=1, h_blocks=10)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The result is identical to what is shown in the first section. However, there\n",
    "are point artifacts in the right part of the figure that are not present\n",
    "in image above, so we can say that block binarization along the change\n",
    "in illumination coped better in this case."
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Conclusion\n",
    "\n",
    "In this work, an algorithm for block binarization of an image was implemented\n",
    "in the Python program with the choice of a separate threshold for each block,\n",
    "for which a special binarize function was written.\n",
    "The following results were obtained:\n",
    "* the block binarization algorithm copes well with images\n",
    "with high contrast\n",
    "* the best results can be achieved if you split the image\n",
    "along the change in illumination\n",
    "* it follows from the previous paragraph that it is necessary to select the optimal\n",
    "the number and size of blocks within which binarization will be performed."
   ]
  }
 ],
 "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.10.5"
  },
  "vscode": {
   "interpreter": {
    "hash": "369f2c481f4da34e4445cda3fffd2e751bd1c4d706f27375911949ba6bb62e1c"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
