From 2b5b65262005f226ffd98d9f21304604b8060c51 Mon Sep 17 00:00:00 2001
From: beoffre <felix.yriarte@gmail.com>
Date: Mon, 11 Jul 2022 21:05:07 +0200
Subject: [PATCH] loops ok ; composantes connexes ok

---
 graph.ipynb | 1459 +++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 1459 insertions(+)
 create mode 100644 graph.ipynb

diff --git a/graph.ipynb b/graph.ipynb
new file mode 100644
index 0000000..4b639c6
--- /dev/null
+++ b/graph.ipynb
@@ -0,0 +1,1459 @@
+{
+  "cells": [
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "pw1KdvqxE_cI"
+      },
+      "source": [
+        "# Packages and general functions"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "_rMTtJoU6hJ8"
+      },
+      "source": [
+        "## Used packages"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "M18yRYkw6cJz"
+      },
+      "outputs": [],
+      "source": [
+        "import math\n",
+        "import matplotlib.pyplot as plt\n",
+        "import matplotlib\n",
+        "import numpy as np\n",
+        "\n",
+        "\n",
+        "\n",
+        "import os \n",
+        "from os import listdir\n",
+        "from os.path import isfile, join, splitext\n",
+        "import cv2\n",
+        "\n",
+        "from skimage.color import gray2rgb\n",
+        "from skimage.io import imread, imshow, imsave\n",
+        "from skimage.util import invert\n",
+        "from skimage.transform import resize, rotate\n",
+        "from skimage.morphology import erosion, dilation, opening, closing, skeletonize, square\n",
+        "from skimage.filters import threshold_isodata, threshold_li, threshold_mean, threshold_minimum, threshold_otsu, threshold_triangle, threshold_yen\n",
+        "#from sklearn.cluster import KMeans"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "O213yc3k9vcI"
+      },
+      "source": [
+        "## Image processing functions"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "OF2OuaTGDQMl"
+      },
+      "outputs": [],
+      "source": [
+        "# Load an image using io.imread. Note that morphology functions only work on gray-scale or binary images: set as_gray = True\n",
+        "\n",
+        "def load_image(path):\n",
+        "  im_gray = imread(path, as_gray=True)\n",
+        "  return im_gray\n",
+        "\n",
+        "def erosion_image(image, structural_elem = 'None'):   \n",
+        "  if structural_elem != 'None':\n",
+        "    return erosion(image, structural_elem)\n",
+        "  else:\n",
+        "    return erosion(image)\n",
+        "\n",
+        "def dilation_image(image, structural_elem = 'None'):  \n",
+        "  if structural_elem != 'None':\n",
+        "    return dilation(image, structural_elem)\n",
+        "  else:\n",
+        "    return dilation(image)\n",
+        "\n",
+        "def opening_image(image, structural_elem = 'None'):\n",
+        "  if structural_elem != 'None':\n",
+        "    return opening(image, structural_elem)\n",
+        "  else:\n",
+        "    return opening(image)\n",
+        "\n",
+        "def closing_image(image, structural_elem = 'None'):\n",
+        "  if structural_elem != 'None':\n",
+        "    return closing(image, structural_elem)\n",
+        "  else:\n",
+        "    return closing(image)\n",
+        "\n",
+        "def skeletonization_image(image_bin, method):\n",
+        "    image_bin = invert(image_bin)\n",
+        "    if method == \"lee\":\n",
+        "      skel = skeletonize(image_bin, method = \"lee\")\n",
+        "    else: \n",
+        "      skel = skeletonize(image_bin, method = \"zhang\")\n",
+        "    return skel\n",
+        "  \n",
+        "def binarization_image(image, method):\n",
+        "  if method == \"isodata\":\n",
+        "    th = threshold_isodata(image)\n",
+        "  elif method == \"li\":\n",
+        "    th = threshold_li(image)\n",
+        "  elif method == \"mean\":\n",
+        "    th = threshold_mean(image)\n",
+        "  elif method == \"minimum\":\n",
+        "    th = threshold_minimum(image)\n",
+        "  elif method == \"triangle\":\n",
+        "    th = threshold_triangle(image)\n",
+        "  elif method == \"yen\":\n",
+        "    th = threshold_yen(image)\n",
+        "  else:\n",
+        "    th = threshold_otsu(image)\n",
+        "  print(\"threshold found : \"+str(th))\n",
+        "  binary =( image > th)\n",
+        "  return binary"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "f_F9Oy9rFSnB"
+      },
+      "source": [
+        "# FuzzyDoc functions "
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "2oa-b7dNPYEX"
+      },
+      "outputs": [],
+      "source": [
+        "def euclidean_distance_minutia(m1, m2):\n",
+        "  return math.sqrt((m1[0] - m2[0])*(m1[0] - m2[0]) + (m1[1] - m2[1])*(m1[1] - m2[1]))"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "gzvBZgRc63am"
+      },
+      "source": [
+        "## P&S character pre-processing"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "rbC1URKf7JNp"
+      },
+      "source": [
+        "## Feature extraction"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "boY51esQcZrj"
+      },
+      "outputs": [],
+      "source": [
+        "def minutia_extraction(im_skeleton):\n",
+        "  minutia = []\n",
+        "  h = im_skeleton.shape[0]\n",
+        "  w = im_skeleton.shape[1]\n",
+        "\t\n",
+        "  for i in range(1, h-1):\n",
+        "    for j in range(1, w-1):\n",
+        "      if im_skeleton[i][j] !=0:\n",
+        "        P = [ im_skeleton[i][j+1], im_skeleton[i-1][j+1], im_skeleton[i-1][j], im_skeleton[i-1][j-1], im_skeleton[i][j-1], im_skeleton[i+1][j-1], im_skeleton[i+1][j], im_skeleton[i+1][j+1], im_skeleton[i][j+1] ]\n",
+        "        CN = 0\n",
+        "        for k in range(8):\n",
+        "          CN += abs(P[k]/255 - P[k+1]/255)\n",
+        "        CN = 0.5*CN\n",
+        "\t\t\t\n",
+        "\t\t\t# 0 : Isolated point\n",
+        "\t\t\t# 1 : Ending point\n",
+        "\t\t\t# 2 : Connective point\n",
+        "\t\t\t# 3 : Bifurcation point\n",
+        "\t\t\t# 4 : Crossing point\n",
+        "\t\t\t# only consider 0,1,3,4 CN values\n",
+        "\t\t\t\n",
+        "      \n",
+        "        if CN==0:\n",
+        "          minutia.append((i,j,0))\n",
+        "        elif CN == 1:\n",
+        "          minutia.append((i,j,1))\n",
+        "        elif CN == 3:\n",
+        "          minutia.append((i,j,3))\n",
+        "        elif CN == 4:\n",
+        "          minutia.append((i,j,4))\n",
+        "  \n",
+        "  return minutia"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "gdkvRQpQgGCr"
+      },
+      "outputs": [],
+      "source": [
+        "def draw_minutia(minutia, im_skeleton):\n",
+        "  h = im_skeleton.shape[0]\n",
+        "  w = im_skeleton.shape[1]\n",
+        "  im_skeleton_color = gray2rgb(im_skeleton)\n",
+        "  for m in minutia:\n",
+        "    im_skeleton_color[m[0]][m[1]] = (255, 0, 0)\n",
+        "  return im_skeleton_color"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "YRjY5rKTPJrV"
+      },
+      "source": [
+        "## Smoothing operation"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {
+        "id": "zrZmYUQ0PQ_i"
+      },
+      "outputs": [],
+      "source": [
+        "def smoothing(minutia, threshold):\n",
+        "  smooth_minutia = []\n",
+        "  ending_points = []\n",
+        "  smooth_ending_points = []\n",
+        "  pb = []\n",
+        "\n",
+        "  for m in minutia:\n",
+        "    if m[2] != 1:\n",
+        "      smooth_minutia.append(m)\n",
+        "    else:\n",
+        "      ending_points.append(m)\n",
+        "\n",
+        "  if smooth_minutia == []:\n",
+        "    return minutia\n",
+        "  else:\n",
+        "    for m in ending_points:\n",
+        "      i = 0\n",
+        "      while (i < len(smooth_minutia)) and (euclidean_distance_minutia(m, smooth_minutia[i]) > threshold):\n",
+        "        i = i+1\n",
+        "      if (i == len(smooth_minutia)):\n",
+        "        smooth_ending_points.append(m)\n",
+        "      else:\n",
+        "        pb.append(smooth_minutia[i])\n",
+        "\n",
+        "  pb = list(set(pb))\n",
+        "\n",
+        "  for m in pb:\n",
+        "    smooth_minutia.remove(m)\n",
+        "\n",
+        "  return smooth_minutia + smooth_ending_points"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {
+        "id": "zaVruB2WFgGU"
+      },
+      "source": [
+        "# Main program"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {},
+      "source": [
+        "# Geometric transformation correction"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def findCorners(im,struct):         #corner detection used to cancel geometric transformations\n",
+        "\n",
+        "    binPre=binarization_image(im,'otsu')\n",
+        "    bin=closing_image(binPre,structural_elem=struct)\n",
+        "\n",
+        "    #HAUTGAUCHE\n",
+        "\n",
+        "    cornerHG=[-1,-1]\n",
+        "\n",
+        "    found=False\n",
+        "    dist=0\n",
+        "    while not(found):        \n",
+        "        i=0\n",
+        "        while i<=dist and not(found):\n",
+        "            x=i\n",
+        "            y=dist-i\n",
+        "            # if x>=im.shape[0] or y>=im.shape[1]:\n",
+        "            #     return(-1,-1,-1,-1,-1,-1,-1,-1) HG\n",
+        "            if not(bin[x][y]):\n",
+        "                found=True\n",
+        "                cornerHG=[x,y]\n",
+        "            i+=1\n",
+        "        dist+=1\n",
+        "\n",
+        "    \n",
+        "    #HAUTDROIT\n",
+        "\n",
+        "    cornerHD=[-1,-1]\n",
+        "\n",
+        "    found=False\n",
+        "    dist=0\n",
+        "    while not(found):        \n",
+        "        i=0\n",
+        "        while i<=dist and not(found):\n",
+        "            x=dist - i\n",
+        "            y=im.shape[1]-1-i\n",
+        "            # if x>=im.shape[0] or y>=im.shape[1]:\n",
+        "            #     return(-1,-1,-1,-1,-1,-1,-1,-1) HD\n",
+        "            if not(bin[x][y]):\n",
+        "                found=True\n",
+        "                cornerHD=[x,y]\n",
+        "            i+=1\n",
+        "        dist+=1\n",
+        "\n",
+        "\n",
+        "    #BASGAUCHE\n",
+        "\n",
+        "    cornerBG=[-1,-1]\n",
+        "\n",
+        "    found=False\n",
+        "    dist=0\n",
+        "    while not(found):        \n",
+        "        i=0\n",
+        "        while i<=dist and not(found):\n",
+        "            x=im.shape[0]-1 - i\n",
+        "            y=dist-i\n",
+        "            # if x>=im.shape[0] or y>=im.shape[1]:\n",
+        "            #     return(-1,-1,-1,-1,-1,-1,-1,-1) BG\n",
+        "            if not(bin[x][y]):\n",
+        "                found=True\n",
+        "                cornerBG=[x,y]\n",
+        "            i+=1\n",
+        "        dist+=1\n",
+        "\n",
+        "        #BASGAUCHE\n",
+        "\n",
+        "    cornerBD=[-1,-1]\n",
+        "\n",
+        "    found=False\n",
+        "    dist=0\n",
+        "    while not(found):        \n",
+        "        i=0\n",
+        "        while i<=dist and not(found):\n",
+        "            x=im.shape[0]-1 - dist + i\n",
+        "            y=im.shape[1] - 1 - i\n",
+        "            # if x>=im.shape[0] or y>=im.shape[1]:\n",
+        "            #     return(-1,-1,-1,-1,-1,-1,-1,-1) BD\n",
+        "            if not(bin[x][y]):\n",
+        "                found=True\n",
+        "                cornerBD=[x,y]\n",
+        "            i+=1\n",
+        "        dist+=1\n",
+        "\n",
+        "    return(cornerHG[0],cornerBD[0],cornerBG[1],cornerHD[1],cornerHG[1],cornerBD[1],cornerBG[0],cornerHD[0])\n",
+        "\n",
+        "\n",
+        "\n",
+        "def removeTransfos(path,struct):        # detect corner, then correct translation and rotation noise\n",
+        "    \n",
+        "    im=load_image(path)\n",
+        "    \n",
+        "\n",
+        "    firstBV,lastBV, firstBH, lastBH,firstBVy,lastBVy, firstBHx, lastBHx=findCorners(im,struct)  \n",
+        "    minus=1.0\n",
+        "    ax,cx,dy,by,ay,cy,dx,bx=firstBV,lastBV, firstBH, lastBH,firstBVy,lastBVy, firstBHx, lastBHx\n",
+        "    if firstBV>lastBHx:\n",
+        "        minus=-1.0\n",
+        "    theta1=minus*180/math.pi*math.acos((by-ay)/math.sqrt((bx-ax)*(bx-ax)+(by-ay)*(by-ay)))      #   A--------B\n",
+        "    theta2=minus*180/math.pi*math.acos((cx-bx)/math.sqrt((cx-bx)*(cx-bx)+(by-cy)*(by-cy)))      #   |        |\n",
+        "    theta3=minus*180/math.pi*math.acos((cy-dy)/math.sqrt((cx-dx)*(cx-dx)+(cy-dy)*(cy-dy)))      #   |        |\n",
+        "    theta4=minus*180/math.pi*math.acos((dx-ax)/math.sqrt((dx-ax)*(dx-ax)+(ay-dy)*(ay-dy)))      #   D--------C\n",
+        "    \n",
+        "    #rotation angles are estimated\n",
+        "\n",
+        "    L=[theta1,theta2,theta3,theta4]\n",
+        "    for i in range(len(L)):\n",
+        "        \n",
+        "        if L[i]>45.0:\n",
+        "            L[i]=L[i] - 90.0\n",
+        "    \n",
+        "\n",
+        "   \n",
+        "    print(theta1,theta2,theta3,theta4, float(sum(L)/float(len(L))))\n",
+        "    testIm=rotate(im,float(sum(L)/float(len(L))),mode='constant',resize=True,cval=255,preserve_range=True,center=None)\n",
+        "\n",
+        "    # the image is rotated\n",
+        "\n",
+        "    plt.imsave('rotPreCrop.png',testIm)\n",
+        "    firstBV,lastBV, firstBH, lastBH,firstBVy,lastBVy, firstBHx, lastBHx=findCorners(testIm,struct)\n",
+        "    minX=min(firstBV,lastBHx,lastBV,firstBHx)\n",
+        "    maxX=max(firstBV,lastBHx,lastBV,firstBHx)\n",
+        "    minY=min(firstBVy,lastBH,lastBVy,firstBH)\n",
+        "    maxY=max(firstBVy,lastBH,lastBVy,firstBH)\n",
+        "    print(minX,maxX,minY,maxY)\n",
+        "    crop=np.zeros((int(maxX-minX),int(maxY-minY)))\n",
+        "\n",
+        "    # then cropped\n",
+        "\n",
+        "    for i in range(int(maxX-minX)):\n",
+        "        for j in range(int(maxY-minY)):\n",
+        "            crop[i][j]=testIm[int(i+minX)][int(j+minY)]\n",
+        "    \n",
+        "    \n",
+        "    \n",
+        "    return(crop)"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {},
+      "source": [
+        "# Dynamic mask detection"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def detectTextAreas(path,maskName,struct):      # text content detection (spot and table removal)\n",
+        "    img = removeTransfos(path,struct)\n",
+        "    img = skeletonization_image(binarization_image(img,'otsu'),'lee')\n",
+        "    imsave(\"NoBorders.png\",img)\n",
+        "\n",
+        "    img = cv2.imread(\"NoBorders.png\")\n",
+        "    gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)\n",
+        "\n",
+        "    out = np.zeros(gray.shape)\n",
+        "\n",
+        "    # Apply adaptive threshold\n",
+        "    #thresh = cv2.adaptiveThreshold(gray,255,1,1,11,2)\n",
+        "\n",
+        "    # Find the contours\n",
+        "    contours,hierarchy = cv2.findContours(gray,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)\n",
+        "        \n",
+        "    for cnt in contours:\n",
+        "        x,y,w,h = cv2.boundingRect(cnt)\n",
+        "        if (w > 2 and h > 2 and h < 150 and w < 150): # 5 vs 15 ? \n",
+        "            cv2.rectangle(out,(x,y),(x+w,y+h),255,-1)\n",
+        "    \n",
+        "    for i in range(out.shape[0]):\n",
+        "        for j in range(out.shape[1]):\n",
+        "            if out[i][j] == 255:\n",
+        "                k = 1\n",
+        "                while j+k < out.shape[1] and out[i][j+k] != 255 and k < 100:\n",
+        "                    k = k+1\n",
+        "                if k < 100 and j+k < out.shape[1]:\n",
+        "                    out[i][j+1] = 255\n",
+        "\n",
+        "    cv2.imwrite('contours1.png', out)\n",
+        "    out = cv2.imread('contours1.png')\n",
+        "    out = cv2.cvtColor(out,cv2.COLOR_BGR2GRAY)\n",
+        "\n",
+        "    im_out = cv2.imread('NoBorders.png')\n",
+        "    im_out = cv2.cvtColor(im_out,cv2.COLOR_BGR2GRAY)\n",
+        "\n",
+        "    contours,hierarchy = cv2.findContours(out,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)\n",
+        "    for cnt in contours:\n",
+        "        x,y,w,h = cv2.boundingRect(cnt)\n",
+        "        cv2.rectangle(out,(x,y),(x+w,y+h),255,-1)\n",
+        "\n",
+        "    for i in range(out.shape[0]):\n",
+        "        for j in range(out.shape[1]):\n",
+        "            im_out[i][j] = im_out[i][j] and out[i][j]\n",
+        "\n",
+        "    # Finally show the image\n",
+        "    cv2.imwrite(maskName, out)\n",
+        "    cv2.imwrite('imMask.png', im_out)\n",
+        "    return(load_image('imMask.png'))\n",
+        "\n",
+        "#detectTextAreas(\"payslips/Payslip_dataset_P&S/ForgedN1/Numeric/imitation/02_600dpi/Imitation_1_PaySlip_Arial_10_1-f_1.jpg\")"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {},
+      "source": [
+        "# Matching"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def fastFindMatch(set1,set2,maxH,maxW):     #low computational cost distance based matching function\n",
+        "    resCN=[]                                #\n",
+        "    resDist=[]                              #for all CNs in set1, return the closest CN in set2\n",
+        "    spatialLocation=np.full((maxH,maxW),-1)\n",
+        "    for cn in range(len(set2)):\n",
+        "        spatialLocation[set2[cn][0]][set2[cn][1]]=cn\n",
+        "    for cn in set1:\n",
+        "        found=False\n",
+        "        count=0\n",
+        "        while not(found):\n",
+        "            tempCN=()\n",
+        "            tempDist=float('infinity')\n",
+        "            for i in range(-count,count+1,1):\n",
+        "                for j in range(-count,count+1,1):\n",
+        "                    x=cn[0]+i\n",
+        "                    y=cn[1]+j\n",
+        "                    if max(abs(i),abs(j))==count and x>=0 and x<maxH and y>=0 and y<maxW:\n",
+        "                        if spatialLocation[x][y]!=-1:\n",
+        "                            tempCN=set2[spatialLocation[x][y]]\n",
+        "                            tempDist=min(tempDist,count)\n",
+        "                            found=True\n",
+        "            if found:\n",
+        "                resCN.append(tempCN)\n",
+        "                resDist.append(tempDist)\n",
+        "            count+=1\n",
+        "    return(resDist,resCN)\n",
+        "        "
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def fastMapCleanPMASK(pathG,pathF,gridX,gridY, thresholdG, thresholdF,struct,name1='maskPaulineG.png',name2='maskPaulineF.png'):    #preprocess then matching\n",
+        "    \n",
+        "    #transformation correction and mask detection\n",
+        "    imG=removeTransfos(pathG,struct)\n",
+        "    imsave(\"GnoBorders.png\",imG)\n",
+        "    maskedG=detectTextAreas('GnoBorders.png',name1,struct)\n",
+        "    imsave(\"GMasked.png\",maskedG)\n",
+        "    imF=removeTransfos(pathF,struct)\n",
+        "    imsave(\"FnoBorders.png\",imF)\n",
+        "    maskedF=detectTextAreas('FnoBorders.png',name2,struct)\n",
+        "    imsave('FMasked.png',maskedF)\n",
+        "\n",
+        "    #minutia extraction, serifs removal, and scaling correction\n",
+        "    minG=minutia_extraction(maskedG)\n",
+        "    minG=smoothing(minG,thresholdG)\n",
+        "    minFNoScale=minutia_extraction(maskedF)\n",
+        "    minF=[]\n",
+        "    for i in range(len(minFNoScale)):\n",
+        "        minF.append((int(minFNoScale[i][0]*maskedG.shape[0]/maskedF.shape[0]),int(minFNoScale[i][1]*maskedG.shape[1]/maskedF.shape[1]),minFNoScale[i][2]))\n",
+        "        \n",
+        "    minF=smoothing(minF,thresholdF)\n",
+        "\n",
+        "    #distance  computing for both CN sets\n",
+        "    distances,correspondingCNs=fastFindMatch(minG,minF,maskedG.shape[0],maskedG.shape[1])\n",
+        "    corres=[]\n",
+        "    maxRad=50\n",
+        "    tab=[]\n",
+        "    for i in range(gridX):\n",
+        "        tab.append([])\n",
+        "        for j in range(gridY):\n",
+        "            tab[i].append([])\n",
+        "    stepX=maskedG.shape[0]/gridX    #imF instead of imG ?\n",
+        "    stepY=maskedG.shape[1]/gridY    #imF instead of imG ?\n",
+        "    for i in range(len(minG)):\n",
+        "        tab[int(minG[i][0]//stepX)][int(minG[i][1]//stepY)].append(i)\n",
+        "        corres.append(min(maxRad,distances[i]))\n",
+        "    moys=np.zeros(maskedG.shape)\n",
+        "\n",
+        "    maxs=np.zeros(maskedG.shape)\n",
+        "    colors=[]\n",
+        "    colors2=[]\n",
+        "    all=[]\n",
+        "    CNs=[]\n",
+        "    for i in range(gridX):\n",
+        "        colors.append([])\n",
+        "        colors2.append([])\n",
+        "        for j in range(gridY):\n",
+        "            moy=0\n",
+        "            maxi=0\n",
+        "            for c in range(len(tab[i][j])):\n",
+        "                moy+=corres[tab[i][j][c]]/len(tab[i][j])\n",
+        "                maxi=max(maxi,corres[tab[i][j][c]])\n",
+        "                all.append(corres[tab[i][j][c]])\n",
+        "                CNs.append(minG[tab[i][j][c]])\n",
+        "            colors[i].append(moy)\n",
+        "            colors2[i].append(maxi)\n",
+        "    for i in range(maskedG.shape[0]):\n",
+        "        for j in range(maskedG.shape[1]):\n",
+        "            moys[i][j]=colors[int(i//stepX)][int(j//stepY)]\n",
+        "            maxs[i][j]=colors2[int(i//stepX)][int(j//stepY)]\n",
+        "    return moys,maxs,all,CNs"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {},
+      "source": [
+        "# Dispersion analysis"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def readMask(mask):                 #from mask to box set\n",
+        "    res=[] #[((xD1,yD1),(xF1,yF1))]\n",
+        "    for i in range(mask.shape[0]):\n",
+        "        j=0\n",
+        "        while j<mask.shape[1]:\n",
+        "            flag=False\n",
+        "            if mask[i][j]:\n",
+        "                for test in range(len(res)):\n",
+        "                    if i>=res[test][0][0] and i<=res[test][1][0] and j>=res[test][0][1] and j<=res[test][1][1]:\n",
+        "                        flag=True\n",
+        "                if not(flag):\n",
+        "                    pix=j\n",
+        "                    pixI=i\n",
+        "                    while pixI<mask.shape[0] and mask[pixI][j]:\n",
+        "                        pixI+=1\n",
+        "                    while pix<mask.shape[1] and mask[i][pix]:\n",
+        "                        pix+=1\n",
+        "                    res.append(((i,j),(pixI-1,pix-1)))\n",
+        "                    j=pix-1\n",
+        "            j+=1\n",
+        "    return(res)"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def radiPerBox(listCNs, listRadi, mask):        #sort distance values in corresponding mask boxes\n",
+        "    posMask=readMask(mask)\n",
+        "    res=[]\n",
+        "    for i in range(len(posMask)):\n",
+        "        res.append([])\n",
+        "    for i in range(len(listCNs)):\n",
+        "        for box in range(len(posMask)):\n",
+        "            if listCNs[i][0]>=posMask[box][0][0] and listCNs[i][0]<=posMask[box][1][0] and listCNs[i][1]>=posMask[box][0][1] and listCNs[i][1]<=posMask[box][1][1]:\n",
+        "                res[box].append(listRadi[i])\n",
+        "    return res"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def entropy(dist, max, nbBin):              # entropy computing ; dispersion analysis\n",
+        "    effObs = np.zeros(nbBin)\n",
+        "    for d in dist:\n",
+        "        effObs[int(d//(max/nbBin))-1] += 1\n",
+        "    res = 0\n",
+        "    for e in effObs:\n",
+        "        if e != 0:\n",
+        "            res += (e / len(dist)) * math.log2((e / len(dist)))\n",
+        "    return (-1.0)*res"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def ecartMoy(listeOccus,maxV,bins=None):    #dispersion analysis !! deprecated !!\n",
+        "    if bins is None:\n",
+        "        bins=maxV+1\n",
+        "    res=0.0\n",
+        "    hist=np.zeros(bins)\n",
+        "    step=(maxV+1)/bins\n",
+        "    for i in listeOccus:\n",
+        "        hist[int(i//step)]+=1\n",
+        "    for i in range(len(hist)):\n",
+        "        res+=(hist[i]-len(listeOccus)/maxV)**2\n",
+        "    if len(listeOccus)==0:\n",
+        "        return 0.0\n",
+        "    res/=len(listeOccus)**2\n",
+        "    return(res)"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {},
+      "source": [
+        "# Integrity check"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def check(pathG,pathF, thresholdSerifsNUM=5,thresholdSerifsPS=5, thresholdDist=12, thresholdCNNumber=2,thresholdEntropy=0.5,dirOut='./',struct=square(25)):\n",
+        "    pathMaskG=dirOut+'maskG.png'\n",
+        "    pathMaskF=dirOut+'maskF.png'\n",
+        "    moyList,maxList,dist,cn=fastMapCleanPMASK(pathG,pathF,1,1,thresholdSerifsNUM,thresholdSerifsNUM,struct,pathMaskG,pathMaskF)\n",
+        "\n",
+        "\n",
+        "    cnWithoutBorder = []\n",
+        "    distWithoutBorder = []\n",
+        "    for i in range(len(cn)):\n",
+        "        if cn[i][0] > 80 and cn[i][0] < 3219-78:\n",
+        "            cnWithoutBorder.append(cn[i])\n",
+        "            distWithoutBorder.append(dist[i])\n",
+        "\n",
+        "    carres=load_image(pathMaskG)\n",
+        "    for i in range(carres.shape[0]):\n",
+        "        for j in range(carres.shape[1]):\n",
+        "            if i < 80 or i > carres.shape[0]-78:\n",
+        "                carres[i][j] = 0\n",
+        "    ordonnes=radiPerBox(cnWithoutBorder,distWithoutBorder,carres)\n",
+        "    maxDist = max(distWithoutBorder)\n",
+        "    listG=readMask(carres)\n",
+        "    color=[]\n",
+        "\n",
+        "    flag1=[]\n",
+        "    flag2=[]\n",
+        "    sortedEntropy=[]\n",
+        "    for i in range(len(ordonnes)):\n",
+        "        if len(ordonnes[i]) != 0: \n",
+        "            color.append(np.mean(ordonnes[i]))\n",
+        "            sortedEntropy.append(entropy(ordonnes[i],maxDist,10))\n",
+        "        else:\n",
+        "            color.append(0)\n",
+        "            sortedEntropy.append(0)\n",
+        "        count=0\n",
+        "        for d in range(len(ordonnes[i])):\n",
+        "            if ordonnes[i][d]>=thresholdDist:\n",
+        "                count+=1\n",
+        "        if count>=thresholdCNNumber:\n",
+        "            flag1.append(i)\n",
+        "            if entropy(ordonnes[i],maxDist,10)>thresholdEntropy:\n",
+        "                flag2.append(i)\n",
+        "    \n",
+        "\n",
+        "\n",
+        "    mapF=np.zeros(carres.shape)\n",
+        "    HmapF=np.zeros(carres.shape)\n",
+        "    for i in range(len(listG)):\n",
+        "        for pixi in range(listG[i][0][0],listG[i][1][0]+1):\n",
+        "            for pixj in range(listG[i][0][1],listG[i][1][1]+1):\n",
+        "                mapF[pixi][pixj]=color[i]\n",
+        "                HmapF[pixi][pixj]=sortedEntropy[i]\n",
+        "    plt.figure(figsize=(30,25))\n",
+        "    plt.imshow(mapF,vmin=0.0,vmax=20)\n",
+        "    plt.colorbar()\n",
+        "    plt.savefig(dirOut+\"a-field.png\")\n",
+        "    plt.figure(figsize=(30,25))\n",
+        "    plt.imshow(HmapF,vmin=0.0,vmax=1.0)\n",
+        "    plt.colorbar()\n",
+        "    plt.savefig(dirOut+\"entropy-field.png\")\n",
+        "    \n",
+        "    return(flag1,flag2)"
+      ]
+    },
+    {
+      "cell_type": "markdown",
+      "metadata": {},
+      "source": [
+        "# Graph"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": 46,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def indexOfHighestOneMin(mins):\n",
+        "    if len(mins)!=0:\n",
+        "        anyOne=-1\n",
+        "        flag=False\n",
+        "        for min in range(len(mins)):\n",
+        "            if mins[min][2]==1:\n",
+        "                flag=True\n",
+        "                anyOne=min\n",
+        "        if flag:            \n",
+        "            highest=mins[anyOne][0]\n",
+        "            idx=anyOne\n",
+        "            for i in range(len(mins)):\n",
+        "                if mins[i][2]==1 and mins[i][0]<highest:\n",
+        "                    idx=i\n",
+        "                    highest=mins[idx][0]\n",
+        "            return idx\n",
+        "    return(-1)\n",
+        "\n",
+        "def listSum(a,b):\n",
+        "    res =[]\n",
+        "    for elem in a:\n",
+        "        res.append(elem)\n",
+        "    if len(a)==len(b):\n",
+        "        for e in range(len(a)) : \n",
+        "            res[e]+=b[e]\n",
+        "        return res\n",
+        "    return \n",
+        "\n",
+        "def indexOfHighestThreeMin(mins):\n",
+        "    if len(mins)!=0:\n",
+        "        anyOne=-1\n",
+        "        flag=False\n",
+        "        for min in range(len(mins)):\n",
+        "            if mins[min][2]==3:\n",
+        "                flag=True\n",
+        "                anyOne=min\n",
+        "        if flag:            \n",
+        "            highest=mins[anyOne][0]\n",
+        "            idx=anyOne\n",
+        "            for i in range(len(mins)):\n",
+        "                if mins[i][2]==3 and mins[i][0]>highest:\n",
+        "                    idx=i\n",
+        "                    highest=mins[idx][0]\n",
+        "            return idx\n",
+        "    return(-1)\n",
+        "\n",
+        "def relativeWhiteDir(skeleton,x,y):\n",
+        "    res=[]\n",
+        "    for i in [(-1,0),(1,0),(0,1),(0,-1),(-1,-1),(-1,1),(1,-1),(1,1)]:\n",
+        "        if skeleton[x+i[0]][y+i[1]]:\n",
+        "            res.append(i)\n",
+        "    return res\n",
+        "\n",
+        "\n",
+        "\n",
+        "def getPosFromMins(mins):\n",
+        "    res=[]\n",
+        "    for i in range(len(mins)):\n",
+        "        res.append((mins[i][0],mins[i][1]))\n",
+        "    return res\n",
+        "\n",
+        "def findWhitePix(skel):\n",
+        "    for i in range(skel.shape[0]):\n",
+        "        for j in range(skel.shape[1]):\n",
+        "            if skel[i][j]:\n",
+        "                return (i,j)\n",
+        "    return(False)\n",
+        "\n",
+        "def recurseGraph(skel,mins,depart,posMinDepart):\n",
+        "    \n",
+        "    global debug\n",
+        "    global veryDebug\n",
+        "    global blocked\n",
+        "    visited=[depart]\n",
+        "    \n",
+        "    posMins=getPosFromMins(mins)\n",
+        "    nextWhite=relativeWhiteDir(skel,depart[0],depart[1])\n",
+        "    flag=False\n",
+        "    if debug:\n",
+        "        print('pos depart : ',depart,posMins.index(posMinDepart))\n",
+        "    if len(nextWhite)==0:\n",
+        "        return [(posMins.index(posMinDepart),posMins.index(posMinDepart))],[]\n",
+        "    dir=(0,0)\n",
+        "    for i in range(len(nextWhite)):\n",
+        "        if not((nextWhite[i][0]+depart[0],nextWhite[i][1]+depart[1]) in blocked) and (nextWhite[i][0]+depart[0],nextWhite[i][1]+depart[1])!=posMinDepart:\n",
+        "            flag=True\n",
+        "            dir=(nextWhite[i][0],nextWhite[i][1])\n",
+        "            break\n",
+        "    if not(flag):\n",
+        "        return [],[]\n",
+        "    \n",
+        "    \n",
+        "\n",
+        "\n",
+        "    last=depart\n",
+        "    next=(last[0]+dir[0],last[1]+dir[1])\n",
+        "    visited.append(next)\n",
+        "\n",
+        "    while not(next in posMins):\n",
+        "        if veryDebug:\n",
+        "            print(dir)\n",
+        "        nextWhite=relativeWhiteDir(skel,next[0],next[1])\n",
+        "\n",
+        "        last=next\n",
+        "        for i in range(len(nextWhite)):\n",
+        "            if not(nextWhite[i]==(-dir[0],-dir[1])):\n",
+        "                dir=(nextWhite[i][0],nextWhite[i][1])\n",
+        "                break\n",
+        "        next=(last[0]+dir[0],last[1]+dir[1])\n",
+        "        visited.append(next)\n",
+        "    blocked.append(last)\n",
+        "    if debug:\n",
+        "        print('blocked :',blocked,'point arrivee :',posMins.index(next))    \n",
+        "    res=[]\n",
+        "    res.append((posMins.index(posMinDepart),posMins.index(next)))\n",
+        "    nextWhite=relativeWhiteDir(skel,next[0],next[1])\n",
+        "    for i in range(len(nextWhite)):\n",
+        "        \n",
+        "        if not((next[0]+nextWhite[i][0],next[1]+nextWhite[i][1]) in blocked) and not((next[0]+nextWhite[i][0],next[1]+nextWhite[i][1])==last):\n",
+        "            curr=(next[0]+nextWhite[i][0],next[1]+nextWhite[i][1])\n",
+        "            blocked.append((next[0]+nextWhite[i][0],next[1]+nextWhite[i][1]))\n",
+        "            childList,visitedChildren=recurseGraph(skel,mins,(next[0]+nextWhite[i][0],next[1]+nextWhite[i][1]),(next[0],next[1]))\n",
+        "            for child in childList:\n",
+        "                res.append(child)\n",
+        "            for i in visitedChildren:\n",
+        "                visited.append(i)\n",
+        "    return res,visited\n",
+        "    "
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": 68,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "def graph(path):\n",
+        "    im=load_image(path)\n",
+        "    skel=skeletonization_image(binarization_image(im,'otsu'),'lee')\n",
+        "    skel2=skeletonization_image(binarization_image(im,'otsu'),'lee')\n",
+        "    CNs=minutia_extraction(skel)\n",
+        "\n",
+        "    global blocked\n",
+        "    global debug\n",
+        "    global veryDebug\n",
+        "    blocked=[]\n",
+        "    global encounteredCNs\n",
+        "    global nonEncountered\n",
+        "    encounteredCNs=[]\n",
+        "    nonEncountered=[]\n",
+        "    for i in range(len(CNs)):\n",
+        "        nonEncountered.append(CNs[i])\n",
+        "    tot=[]\n",
+        "    count=0\n",
+        "    while len(nonEncountered)!=0 or findWhitePix(skel2):\n",
+        "        print(findWhitePix(skel2))\n",
+        "        if count>50:\n",
+        "            return tot\n",
+        "        idx=indexOfHighestOneMin(nonEncountered)\n",
+        "        print(idx)\n",
+        "        if idx<0:\n",
+        "            idx=indexOfHighestThreeMin(nonEncountered)\n",
+        "            #print('3 ! : '+idx)\n",
+        "            if idx<0:\n",
+        "                print('loooooop')\n",
+        "                a=findWhitePix(skel2)\n",
+        "                if a:\n",
+        "                    nonEncountered.append((a[0],a[1],5))\n",
+        "                    idx=0\n",
+        "        compCon,visited=recurseGraph(skel,nonEncountered,(nonEncountered[idx][0],nonEncountered[idx][1]),(nonEncountered[idx][0],nonEncountered[idx][1]))\n",
+        "        for i in range(len(compCon)):\n",
+        "            if not(nonEncountered[compCon[i][0]] in encounteredCNs):\n",
+        "                encounteredCNs.append(nonEncountered[compCon[i][0]])\n",
+        "            if not(nonEncountered[compCon[i][1]] in encounteredCNs):\n",
+        "                encounteredCNs.append(nonEncountered[compCon[i][1]])\n",
+        "            \n",
+        "            # if (compCon[i][0] in nonEncountered):\n",
+        "            #     encounteredCNs.append(nonEncountered[compCon[i][0]])\n",
+        "            #     nonEncountered.remove(compCon[i][0])\n",
+        "            # if (compCon[i][1] in nonEncountered):\n",
+        "            #     encounteredCNs.append(nonEncountered[compCon[i][1]])\n",
+        "            #     nonEncountered.remove(compCon[i][1])\n",
+        "        for i in range(len(encounteredCNs)):\n",
+        "            if encounteredCNs[i] in nonEncountered:\n",
+        "                nonEncountered.remove(encounteredCNs[i])\n",
+        "        for i in range(len(visited)):\n",
+        "            skel2[visited[i][0]][visited[i][1]]=False\n",
+        "        plt.imshow(skel2)\n",
+        "        tot.append(compCon)\n",
+        "        count+=1\n",
+        "        #print(nonEncountered)\n",
+        "    #imshow()\n",
+        "    return(tot)"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": [
+        "path=\"/home/felix/Documents/work/FuzzyDoc/fuzzydoc/imgs/1_truth.png\"\n",
+        "im=load_image(path)\n",
+        "skel=skeletonization_image(binarization_image(im,'otsu'),'lee')\n",
+        "cn=minutia_extraction(skel)\n",
+        "\n",
+        "print(cn)\n",
+        "\n",
+        "idx=indexOfHighestOneMin(cn)\n",
+        "print(idx)\n",
+        "\n",
+        "debug=True ; veryDebug=True ; blocked=[]\n",
+        "\n",
+        "print(recurseGraph(skel,cn,(cn[0][0],cn[0][1]),(cn[0][0],cn[0][1]))[0])"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": 64,
+      "metadata": {},
+      "outputs": [
+        {
+          "data": {
+            "text/plain": [
+              "-1"
+            ]
+          },
+          "execution_count": 64,
+          "metadata": {},
+          "output_type": "execute_result"
+        }
+      ],
+      "source": [
+        "indexOfHighestOneMin([])"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": 69,
+      "metadata": {},
+      "outputs": [
+        {
+          "name": "stdout",
+          "output_type": "stream",
+          "text": [
+            "threshold found : 0.533203125\n",
+            "threshold found : 0.533203125\n",
+            "threshold found : 0.533203125\n",
+            "(4, 16)\n",
+            "0\n",
+            "pos depart :  (31, 20) 0\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "blocked : [(43, 25)] point arrivee : 1\n",
+            "(4, 16)\n",
+            "-1\n",
+            "loooooop\n",
+            "pos depart :  (4, 16) 0\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(0, 1)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, -1)\n",
+            "(1, -1)\n",
+            "(1, -1)\n",
+            "(0, -1)\n",
+            "(1, -1)\n",
+            "(0, -1)\n",
+            "(1, -1)\n",
+            "(0, -1)\n",
+            "(1, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(-1, -1)\n",
+            "(0, -1)\n",
+            "(-1, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(-1, 1)\n",
+            "(-1, 1)\n",
+            "blocked : [(43, 25), (5, 15)] point arrivee : 0\n",
+            "pos depart :  (4, 17) 0\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(0, 1)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, 0)\n",
+            "(1, -1)\n",
+            "(1, -1)\n",
+            "(1, -1)\n",
+            "(1, -1)\n",
+            "(0, -1)\n",
+            "(1, -1)\n",
+            "(0, -1)\n",
+            "(1, -1)\n",
+            "(0, -1)\n",
+            "(1, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(-1, -1)\n",
+            "(0, -1)\n",
+            "(-1, -1)\n",
+            "(0, -1)\n",
+            "(0, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, -1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 0)\n",
+            "(-1, 1)\n",
+            "(-1, 1)\n",
+            "(0, 1)\n",
+            "(0, 1)\n",
+            "(-1, 1)\n",
+            "(-1, 1)\n",
+            "blocked : [(43, 25), (5, 15), (4, 17), (5, 15)] point arrivee : 0\n",
+            "[[(0, 1)], [(0, 0), (0, 0)]]\n"
+          ]
+        },
+        {
+          "data": {
+            "image/png": "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",
+            "text/plain": [
+              "<Figure size 432x288 with 1 Axes>"
+            ]
+          },
+          "metadata": {
+            "needs_background": "light"
+          },
+          "output_type": "display_data"
+        }
+      ],
+      "source": [
+        "path=\"/home/felix/Documents/work/FuzzyDoc/fuzzydoc/imgs/1_truth.png\"\n",
+        "path=\"/home/felix/Documents/work/FuzzyDoc/fuzzydoc/0Dot.jpg\"\n",
+        "plt.imshow(skeletonization_image(binarization_image(load_image(path),'otsu'),'lee'))\n",
+        "debug=True ; veryDebug=True ; blocked=[]\n",
+        "print(graph(path))"
+      ]
+    },
+    {
+      "cell_type": "code",
+      "execution_count": 45,
+      "metadata": {},
+      "outputs": [
+        {
+          "ename": "AttributeError",
+          "evalue": "'numpy.ndarray' object has no attribute 'index'",
+          "output_type": "error",
+          "traceback": [
+            "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
+            "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
+            "\u001b[0;32m/tmp/ipykernel_6160/2426019096.py\u001b[0m in \u001b[0;36m<module>\u001b[0;34m\u001b[0m\n\u001b[0;32m----> 1\u001b[0;31m \u001b[0mim\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mindex\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;36m255\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
+            "\u001b[0;31mAttributeError\u001b[0m: 'numpy.ndarray' object has no attribute 'index'"
+          ]
+        }
+      ],
+      "source": []
+    },
+    {
+      "cell_type": "code",
+      "execution_count": null,
+      "metadata": {},
+      "outputs": [],
+      "source": []
+    }
+  ],
+  "metadata": {
+    "colab": {
+      "collapsed_sections": [],
+      "name": "ICDAR2021_FuzzyDoc.ipynb",
+      "provenance": []
+    },
+    "kernelspec": {
+      "display_name": "Python 3.9.7 ('fuzzydoc')",
+      "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.7"
+    },
+    "vscode": {
+      "interpreter": {
+        "hash": "2ea461435760feefc946351affc8bb014dcf9b35b55b0a6a7baea12203c1e3dc"
+      }
+    }
+  },
+  "nbformat": 4,
+  "nbformat_minor": 0
+}
-- 
GitLab