experiment to localize page corners with colorful circle blobs

This commit is contained in:
H.T. Kruitbosch committed 2018-04-19 12:16:45 +02:00
commit 4950cefe82
10 files changed
+569

No files matched your search

+2
View File
@@ -0,0 +1,2 @@
*.pyc
notebooks/.ipynb_checkpoints/
+6
View File
@@ -0,0 +1,6 @@
# Find corners of pages
For the eye-tracking project of Jacolien van Rij we want to stabily localize pages recorded by the eye-tracking glasses, in order to determine what a child is looking at on a page.
[This notebook](notebooks/Example%2C find corners via circles.ipynb) illustrates the process and [this notebook](notebooks/Finding page corners for several images.ipynb) shows the result for several (2) example images.
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 MiB

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 MiB

@@ -0,0 +1,281 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Locate corners by paperprograms.js-like markers\n",
"\n",
"Uses the opencv blobdetector, distance-based neighbor based clustering and cosine similarity to respectively find the markers, the markers beloning to the same corner and the markers in corner belonging to the same edge."
]
},
{
"cell_type": "code",
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"sys.path.append('..')\n",
"from pagelocalizer import find_corner_circles, cluster_circles_per_corner, intersection_per_corner, separate_lines, find_line_coefficients_per_corner\n",
"import numpy\n",
"import glob\n",
"import cv2\n",
"\n",
"%matplotlib inline\n",
"from matplotlib import pyplot, rcParams\n",
"\n",
"rcParams['figure.figsize'] = (9, 7)\n",
"rcParams['figure.dpi'] = '100'"
]
},
{
"cell_type": "code",
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
"im = cv2.imread(\"../images/img4.jpg\", cv2.IMREAD_COLOR)"
]
},
{
"cell_type": "code",
"execution_count": 14,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x7f8912ce8dd8>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"pyplot.imshow(im)\n",
"pyplot.axis('off')\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Keypoints\n",
"\n",
"Use the opencv blob detector to find the circles around the corners, with possible false positives but hopefully no false negatives."
]
},
{
"cell_type": "code",
"execution_count": 15,
"metadata": {},
"outputs": [],
"source": [
"keypoints = find_corner_circles(im,\n",
" area=(5000 / 4, 5000 * 4),\n",
" color=None,\n",
" convexity=0.9,\n",
" circularity=0.7,\n",
" inertia=None,\n",
" thresholdstep=1\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 18,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x7f8912b6ca58>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"pyplot.imshow(im)\n",
"pyplot.plot(*zip(*[kp.pt for kp in keypoints]), 'or', markersize=12, fillstyle='none', markeredgewidth=2)\n",
"pyplot.axis('off')\n",
"pyplot.tight_layout()\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Cluster by corner\n",
"\n",
"Group together points that are closeby, closer than 1.5 times the sum of the radi of both circles."
]
},
{
"cell_type": "code",
"execution_count": 19,
"metadata": {},
"outputs": [],
"source": [
"keypoints, corner_circles = cluster_circles_per_corner(keypoints, distance_factor=1.5)"
]
},
{
"cell_type": "code",
"execution_count": 20,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x7f8912c49710>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"pyplot.imshow(im)\n",
"pyplot.axis('off')\n",
"\n",
"for corner in corner_circles:\n",
" pyplot.plot(*zip(*[keypoints[i].pt for i in corner]), 'o', markersize=12, fillstyle='none', markeredgewidth=2)\n",
"None"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Fit lines\n",
"\n",
"Within each corner, find preferrably two lines.\n",
"\n",
"Lines are seperated by calculating cosine similarities. For each three points $P$, $Q$ and $R$ the cosine similarity between vectors $Q - P$ and $R - Q$ is calculated as a (#points, #points, #points) tensor.\n",
"\n",
"If the similarity is close to -1 or 1 (angle of 0° or 180° between both vectors), this means the three points lie on approximtely a line. They are then grouped together.\n",
"\n",
"**TODO**: use scikit-learn to find connected compontents in a connectivity matrix, instead of the default-dicts solution which I think might have a bug if due to numerical instability or assymetry of the cosine not all permutations of (i, j, k) are below the cosine threshold if one is and hence `j in groups[i]` does not imply `i in groups[j]`.\n",
"\n",
"**TODO**: the method finds a relation (i, j, k) of points lying on one line. What you want is to group those pairs (i, j, k) that have at least overlap of two elements, this way a point can belong to two seperate clusters, like a point exactly at the corner."
]
},
{
"cell_type": "code",
"execution_count": 21,
"metadata": {},
"outputs": [],
"source": [
"lines_per_corner = find_line_coefficients_per_corner(keypoints, corner_circles, cosine_threshold=0.00137046524)\n"
]
},
{
"cell_type": "code",
"execution_count": 22,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.image.AxesImage at 0x7f890c18d080>"
]
},
"execution_count": 22,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "iVBORw0KGgoAAAANSUhEUgAABlQAAAS7CAYAAADng1fGAAAABHNCSVQICAgIfAhkiAAAAAlwSFlzAAAPYQAAD2EBqD+naQAAIABJREFUeJzsvUmsJVl63/f7zjkRce997+XLqSqz5qpu9shiq1tskWpYtmTLC8HDwgtBgNfe2YAN7+yNh428sWDAAgTYC8PaeWN4Z5CiLUGyZTa7SXZTXT2yi11TVmZWTm+8NyLO4MV3TkTcl0U3SFlgCzx/IJGZ792IG3GGb/h/w5GUEhUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFRV/PMyf9QNUVFRUVFRUVFRUVFRUVFRUVFRUVFRUVFT8oqMGVCoqKioqKioqKioqKioqKioqKioqKioqKn4OakCloqKioqKioqKioqKioqKioqKioqKioqLi56AGVCoqKioqKioqKioqKioqKioqKioqKioqKn4OakCloqKioqKioqKioqKioqKioqKioqKioqLi56AGVCoqKioqKioqKioqKioqKioqKioqKioqKn4OakCloqKioqKioqKioqKioqKioqKioqKioqLi56AGVCoqKioqKioqKioqKioqKioqKioqKioqKn4OakCloqKioqKioqKioqKioqKioqKioqKioqLi56AGVCoqKioqKioqKioqKioqKioqKioqKioqKn4OfqEDKiLyH4rIz0RkJyLfFJFf+7N+poqKioqKioqKioqKioqKioqKioqKioqKP3/4hQ2oiMjfAv4O8F8BfxH4LvAbIvLin+mDVVRUVFRUVFRUVFRUVFRUVFRUVFRUVFT8uYOklP6sn+FTISLfBL6VUvqP8v8N8AHw36eU/ps/04erqKioqKioqKioqKioqKioqKioqKioqPhzBfdn/QCfBhFpgV8F/nb5WUopishvAd/4Y67pgO7Kj28CT/5FPWdFRUVFRUVFRUVFRUVFRUVFRUVFRUVFxb9UOALupT9FtckvZEAFuA1Y4MGVnz8AvvjHXPOfAf/Fv8iHqqioqKioqKioqKioqKioqKioqKioqKj4lx6vAh/9SS/6RQ2o/Gnwt9EzVwqOgA//1n/3v9J26+mHPkZCCDjnMMZAjIgIKSUwhpj/LyIEH/UzkhARjFj9fUrEGLHWMo4j1s3XWeMIIYAxpJT0vjIHuiQlrLWklIgh4Zxj8B4Ac+X7Y4zYxhFjJKWIgF5LIAZAZLoOIMZIjGlxfZh+J/n3Nv/OGEOI+jOx+r3AdG0K+lkkTeMVQsAPO8jjlmJCdHjYC+alRCJi8r3KfUOMSB4XgBQjbdsxxgASMcDRZsUf/fQn9JfnPPn4Hh//7F3Onz3Ghp6NM4gkoh8wgB96drsdxhjG0SPoe41hoG1aHf8Ycc7hvcfk7xYRYp6HcRzpug7vvf7O6M9DCKSk8zP2A03TEGNk9B4jQtN0OocxEpInJdhs1gzDQAhhGmPvA8YIKc5zFZMnpkTXtuz6nuNr1+j7flovxhj8GKc12vf9tEabptHxSwkf9J2t1XXpnCPmsY35GUKeV2sMwzjSNg3GWra7HV3bTuMTQqDrOoKP7Pod1listSDCdrfT6/L6DDGPjXXEFEEcIeT1bw3OOcRaXSeAEct2u+PgcI0zlkSC0ZNCRAw8ffqAxhra1iEijD4BBmcgoGvaWkOMibZtdH/l9ZwiJKBtGt2L1mKsjpX3AWf13dq2JYTA3f/4Os2tfbHXfzxy/++d6TiVvVnWaJ6PJay1uKZhHHfEqDLCisM2DUPfI04gRrz3WNOQJ173Vl5/Kc9n3/c457BWn8knizjHYCzXb95htTmkuXYDjGNzeIxPwsHRURYmQkoBETvLFxHKTkxRQCIihpQizjWEkIAA5H2edH0YhETCGkuMIAgxRayxJBGcs7RtOz2rMQYxaZoLEYE076/pZ5TXFxJxbxxDCNP4GCN5TsseEFKCptFxiQmapiH4yOrgAGMb7KoDEaLAen1AMkLbtfn6vBdE8DER8t7y3uu6Zn62InNjjIwhQEqM40iKke12O8vElFivVjR5rsp+sFb3StjucG1DSIkwDhyRePDeu9jtOY8ePuS3f/ub+MeP4dl9OFjB5bk+wMEGzi8wh2viOEBK/Lf/RuA/+AuBq3j97znORojjiLG2bLGsI9L0nClGbN7XOtg6FuT9ijOAwThHxMDFFlIAd4C99QJhewk0vPS5z3P95i1eeONNrt1+EbPZ0B4eYpqGmK9PIoi1jOOAEYss1oPkB7RWZa73IyGoXpLF/jLWYhGV4Xn/Ej0GSEkY+kuIAT/uGMcdJgHjwDAMjL5n2G7phx2GRCIhUedQRCadV2T8Ul5673V+idNYWWun9QAQvKftOmLWlwBJ4ry+s86ddG3++XRvEZKwd8/y2ZQSKX8+pYQttsjiPiklrGmmOU5O5UhKieg9bdtmWeqmsV3ewy9k/PL9m1b1X1k/XdcxDMP0XUUWlmtEBGctY99zo2v5/u/8ExyRGAcwsnjemG0CsCYhxuDz2BoxqrtjoGkahmHAGIc1NtsdIweN4CTQ+kvWEmgMWIEQPUgi+jHrwWyvZPtjKatV9yVc2yAGXHvABw9OeP/eYzCOZnPI6vCI6y/dBTG4dsVms6HpuknWtF2Hs1b3+eixrY6LMxZJiWbVYvsdp/c/ZvvoE/rtJY0Tttstu95z59VXufvKK0QMj09PMMbStpbV5pCDgwMwNuv0dhq36EdaAzYMnDz6hEd/9FMe33+IEaFrV9B1fOWvfAN3dJjXk2RdbOmc4+L0KZ/87F3OP77HxdMn2K7jM5/7POboGk8fP2F3ccHD+w/oh4G/8W//WyQL667j+7/3He598D4+jNy48yJf+NKXaVdrUt/zzu/9Pn57SUyJu2+8znq9pl1v8IjKcKs2ibGWrnW0bcsQRg6Pr7HeHLK6dqx73GVd0nWqN8VO82WMIVr9t+tW+jP27cfyx5j9+U4pIc5Oe2XaX0l1VJGJy/1kEnt6vdwvStl3eh9b9nia96W1Rte2NQSi6nIWNr4+ucouEibr32k/pll2lOcotqf+bciLWz8fguqs/A5SnsPIc/4CgDPCOHpMY0lxvv9k+0a1tZfjYpjtnZhmnR5SIsWAkVleiQjOGGLw+YVjvi5iop/GLYY0+SQA/W4LMdBKQrwnjj0y7Hj26DH9J5/w4Cc/Zd00nDx9ytnFJUeHx2ojDSNbArZ1hN1Af3GJM0JrHOCxxnK4OcAYy/rGNTY3bzCQGJPuVXd4xJ07L3JwuCGlxPvvfcjZ2Smf+/KX2RweYUfPH77zA37yB39A17ZcjiNvfePXefXzX+DWkT7DloCMAzJ6VjFy+vgRP/jBDwnW8NkvfIkXX3gBGxPf/f3f58mzU77wy29z89YtPvyjd3nnd38fvz1n3a2wTQPWQddy57Of5frdO4QkuASdbfjRj36MD5HgWm7eeZmbr73FpXEghmINpCCEbE0NXm3VYIQUVPdthxHjHCEkYhKGkPSP9/iofqcYo3o/r9cQArvLLYMfs203+4neq99WbNcYgvpxMWKdJcbZXhNRUyOGgHF5HZPNjvL8zEgpTXYn+d5NlrMGvZdzDiNqZ2AsXdeBcZAErJl8WWMMmFlemGxzL/VeCOoHRmZ/LISgskLUXr4Ks/icFUNW5mpppGzbpkQg7smrIuNIAessKc4WcOEFpv2XZtkUsh1Q/O7CTRTbdfIhUkIwan+l/CwWxmJf5XsUO3V6x4wYo/q7/Tm3Hv3kufe+uPYS26O7xGSIRQxm2TDJQuzk0xaIUZ9PRIjZ744xTj5Q8mnyDW2Weep/lDHQ95hkljXEkGVKTFmmM8nRMmXWGGL2KWMqNqjM8rws0Oy3G2vzXM3PXWiMNP9jksNJFvcov4tMnIYxui6KvGM5T3l/uMy1lHWDCCFk3VDWm5HJn84UTHnszElYxtHjnF34M2pvOucmn1Qk+1Nm1l155vXxynuLQLYjKPqnrEXvIXMiKZhJz6fC5cSoY1DWvZVpjei95/UKTOuxSIFpbWRdb0hE9L39GDFWkJgI6lToHMWofu/SL09p8jvK+5d3K9zLp8HEgZf+j7//3M/PX/kiT3/5r5AHEYNRnwOUDzQzz5aS+hsx+/wh+xnG6Jyk6HHZZwxZl5Z5VF1v531uZzvHOqf7O8/dGAPWuGkGY0yTHl/O7NJ/noYnD1XZK4mgdoTZ5/7m4czcZYy4Pd2f9n4vUnS88qJiIPmAK5xkSJD092ojqW+an0Z9J2Pw44hrGkJUWanXhtm2yfKlzG2xeyb5meVzsXXK71nYPWUduMVnTZarQr6nm9eUvbKWIJKsmb43xohdcK+qN830fpMdp4OLMfs8TYxRObjF8y75yXnMZW9uQrYHY5x91sK1ej/Oky5J9VV+vhiC8lbDMH2XMYaDg8NpvQz9SEjZ76bouMwLpTjxQiEVXrEhJj/zDtN82Mw/q4/jxBCTPoMxbrKZLZI5VLUhrXOzrMsyhnBFJxfbP8u9GP3eWJd5iXlsGufwfpgMD7VZIMWAGLVxXeMwCca+53/4L/9zgDP+FPhFDag8Qlm/O1d+fge4/2kXpJR6oC//Lwuw7dY0q81Mfi1+p4qcSWjJwlEr5F0xlMifswuHRkQwTZgJIFSANM1srC3vW7532jzZCJR23jzL64wxiDWkEAlxVpxi0mQcee8JC/ID2CNCUv5d+d4mb0R9dx0T4+ZrVbnmZ41qnBTh08REaNX5994rudBYkg/TdxTSMu9jfZdsGI5ZEEwkGtBYR9+fs7vc0ljD+z98l+/8zm/z6IOfYWOks4nOgWst/W6LMxD9SApBySARkjG0bTc7e5mYjyEgJZhi1fjouo4xk5EJsDkIkUA3qEQSKgxNdmaNtTkwYXCuze8agYRrLAZD13X0fa+BmmzUCEpAdesVQ+/x3tO0LQk7jVPbtvTDMAV4vPck0M+lhA+BpummNbHb7ZTANYa2WRFTVjqovCiC2GYLine truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x7f890c18d748>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"xx = numpy.arange(0, im.shape[1]-1,0.25)\n",
"for (a, b), (c, d) in lines_per_corner:\n",
" pyplot.plot(xx, a*xx + b, linewidth=4)\n",
" pyplot.plot(xx, c*xx + d, linewidth=4)\n",
"\n",
"pyplot.imshow(im)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Find corners by intersection\n",
"\n",
"Each corner should contain two lines, intersect them and find a 4-sized polygon."
]
},
{
"cell_type": "code",
"execution_count": 23,
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"<matplotlib.image.AxesImage at 0x7f8912bf71d0>"
]
},
"execution_count": 23,
"metadata": {},
"output_type": "execute_result"
},
{
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x7f8912bf7518>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"corners, order = intersection_per_corner(lines_per_corner)\n",
"polygon_corners = numpy.array([corners[i] for i in order] + [corners[order[0]]])\n",
"\n",
"pyplot.plot(*polygon_corners.T, 'r-', linewidth=4)\n",
"pyplot.imshow(im)"
]
}
],
"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.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
@@ -0,0 +1,115 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# Finding corners, tested on several images\n",
"\n",
"\n",
"It turns out the cosine_threshold is quite tricky, maybe it should be searched for as the lowest or a reasonably low veresion such that exactly two lines are found per corner. Also, it might help to force overlap of two out ofthree points when clusting points that lie on one line.\n"
]
},
{
"cell_type": "code",
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
"import sys\n",
"sys.path.append('..')\n",
"from pagelocalizer import find_corner_circles, cluster_circles_per_corner, intersection_per_corner, separate_lines, find_line_coefficients_per_corner\n",
"import numpy\n",
"import glob\n",
"import cv2\n",
"\n",
"%matplotlib inline\n",
"from matplotlib import pyplot, rcParams\n",
"\n",
"rcParams['figure.figsize'] = (20, 16)\n",
"rcParams['figure.dpi'] = '100'"
]
},
{
"cell_type": "code",
"execution_count": 10,
"metadata": {},
"outputs": [
{
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x7f3e20d46b38>"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"image/png": "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 truncated
"text/plain": [
"<matplotlib.figure.Figure at 0x7f3e25fd78d0>"
]
},
"metadata": {},
"output_type": "display_data"
}
],
"source": [
"for filename in ['../images/img2.jpg', '../images/img4.jpg']:\n",
" im = cv2.imread(filename, cv2.IMREAD_COLOR)\n",
" pyplot.figure()\n",
" pyplot.imshow(im)\n",
" \n",
" keypoints = find_corner_circles(im,\n",
" area=(5000 / 4, 5000 * 4),\n",
" color=None,\n",
" convexity=0.9,\n",
" circularity=0.7,\n",
" inertia=None,\n",
" thresholdstep=1\n",
" )\n",
" keypoints, corner_circles = cluster_circles_per_corner(keypoints, distance_factor=1.5)\n",
" for corner in corner_circles:\n",
" pyplot.plot(*zip(*[keypoints[i].pt for i in corner]), 'o', markersize=12, fillstyle='none', markeredgewidth=2)\n",
" \n",
" lines_per_corner = find_line_coefficients_per_corner(\n",
" keypoints, corner_circles,\n",
" cosine_threshold=0.00137046524)\n",
" xx = numpy.arange(0, im.shape[1]-1,0.25)\n",
" for (a, b), (c, d) in lines_per_corner:\n",
" pyplot.plot(xx, a*xx + b, linewidth=4)\n",
" pyplot.plot(xx, c*xx + d, linewidth=4)\n",
" \n",
" corners, order = intersection_per_corner(lines_per_corner)\n",
" polygon_corners = numpy.array([corners[i] for i in order] + [corners[order[0]]])\n",
"\n",
" pyplot.plot(*polygon_corners.T, 'r-', linewidth=4)\n",
" pyplot.imshow(im)\n",
" pyplot.axis('off')"
]
}
],
"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.5.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
+2
View File
@@ -0,0 +1,2 @@
from .blobs import find_corner_circles
from .corners import cluster_circles_per_corner, separate_lines, find_line_coefficients_per_corner, intersection_per_corner
+32
View File
@@ -0,0 +1,32 @@
import cv2
def find_corner_circles(im, area=(5000 / 4, 5000 * 4), color=None, convexity=0.9, circularity=0.7, inertia=None, thresholdstep=1):
assert color is None and inertia is None, "Inertia and color not implemented (easy to do though)"
params = cv2.SimpleBlobDetector_Params()
params.filterByArea = area is not None
if area is not None:
params.minArea, params.maxArea = area
params.filterByColor = color is not None
params.filterByInertia = inertia is not None
# This step should be lower than 10, preferrably as low as time allows, to avoud false negatives.
params.thresholdStep = thresholdstep
params.filterByCircularity = circularity is not None
if circularity is not None:
params.minCircularity = circularity
params.filterByConvexity = convexity is not None
if convexity is not None:
params.minConvexity = convexity
if int((cv2.__version__).split('.')[0]) < 3 :
detector = cv2.SimpleBlobDetector(params)
else:
detector = cv2.SimpleBlobDetector_create(params)
return detector.detect(im)
+131
View File
@@ -0,0 +1,131 @@
from scipy.spatial import ConvexHull
import numpy
from collections import defaultdict
def distance(x, y):
"""returns eucledean distance between two points from `find_corner_circles`."""
return numpy.sqrt(
sum(
(v - w) ** 2
for v, w in zip(x.pt, y.pt)
)
)
def find_neighbors(keypoints, size_factor=1.5):
"""Returns keypoints sorted by x-index and determines for each index
in this sorted list of kepoints, what keypoints are close by, defines
as closer than `size_factor` times the size of both keypoints."""
keypoints = sorted(keypoints, key=lambda x: x.pt[0])
neighbors = [set() for _ in range(len(keypoints))]
for i, x in enumerate(keypoints):
for j, y in enumerate(keypoints[i+1:], start=i+1):
if y.pt[0] - x.pt[0] > 2 * size_factor * y.size:
break
if distance(x, y) < 1.5 * (x.size + y.size):
neighbors[i].add(j)
neighbors[j].add(i)
return keypoints, neighbors
def cluster_circles_per_corner(keypoints, distance_factor=1.5):
"""Clusters circles that are close by (1.5 times the sum of both sizes)
into a group of keypoints. Returns the keypoints soerted w.r.t. x-coordinate
and returns a set of tuple, each tuple containing indices of keypoints
in the sorted array. One cluster is represented by one tuple."""
keypoints, neighbors = find_neighbors(keypoints, size_factor=distance_factor)
shapes = []
left = set(range(len(keypoints)))
def find_shape(shape, left):
nonlocal neighbors
def f(left):
nonlocal shape
if left == 0:
return True
lastIndex = shape[-1];
for index in neighbors[lastIndex]:
if index in shape:
continue
shape.append(index)
if f(left - 1):
return True
shape = shape[:-1]
return False
if f(left):
return shape
else:
return None
while len(left) > 0:
shape = find_shape([left.pop()], 7 - 1)
if shape:
shapes.append(shape)
left -= set(shape)
return keypoints, shapes
def separate_lines(keypoints, shape, cosine_threshold=0.00137046524): # accept approx 3 degrees from straight
"""Find points that lie on a line using cosine similarity for triples of points, e.g. the cosine similarity
between vectors from A to B and C to B for points A, B and C. If 1-abs(cosine similarity) is smaller than
`cosine_threshold`, the points lie on a line with at most cos(cosine_threshold) angle deviation. Those
points are grouped together. This method should yet be improved in two ways:
- assure that iff (i,j,k) meets the threshold, so do all permutations (not true now due to numerical
errors and assymetry of the cosine function for angle 0 and angle pi corners.
- only group points iff for (i, j, k) and (p, q, r) at least two of the indices match. This way circles
can form a (straingt) corner and hence lie on two lines, without grouping the points of these two
perpendicular lines together.
Both could be solved brutally, since shape will in general only hold few shapes.
Returns a set of tuple, each tuple refering to indices of keypoints and being a subset of `shape`."""
groups = defaultdict(set)
# n x 2 matrix with points
P = numpy.array([keypoints[i].pt for i in shape])
# n x n x 2 tensor with vectors between two points
V = (P[:,None] - P[None])
# n x n tensor squared lengths of each vector
L = (V * V).sum(2)
# n x n x n tensor between each vector with a matching point in P
D = (V[None] * V[:, :, None]).sum(3)
# n x n x n tensor with cosine similarity between vectors with a matching point
S = (1e-15 + D) / (1e-15 + numpy.sqrt(L[None] * L[:, :, None]))
# We want to test whether the cosine similarity isclose to 1 or -1 (angle 0° or 180°)
S = numpy.abs(S)
for i, j, k in zip(*numpy.where(1 - S < cosine_threshold)):
if i!=j and j!=k and k!=i:
groups[i] = groups[i] | {i, j, k}
return set(map(tuple, groups.values()))
def find_line_coefficients_per_corner(keypoints, corner_circles, cosine_threshold=0.00137046524):
"""Linearly fits lines on keypoints that are part of one line as returned by `separate_lines`. Returns
a list of 2-sized lists with tuples of coefficients (a,b) representing a line a*x + b."""
lines = []
for shape in corner_circles:
lines.append([])
for piece in separate_lines(keypoints, shape, cosine_threshold):
a, b = numpy.polyfit(*zip(*[keypoints[shape[p]].pt for p in piece]), 1)
lines[-1].append((a,b))
if len(lines[-1]) != 2:
print("Warning, did not find two lines for a shape: {}".format(lines))
lines.pop()
# assert len(lines[-1]) == 2, "Did not find two lines for a shape: {}".format(lines)
return lines
def intersection_per_corner(line_coefficients):
"""For each two lines as returned by `fit_linear` find the intersection. Returns the corners
in the order of line_oefficients, but also returns the order in which they form a simple
polygon in counter-clockwise orientation."""
corners = [
(x, a * x + b)
for (a, b), (c, d) in line_coefficients
for x in [(d - b) / (a - c)] # alias
]
return corners, ConvexHull(corners).vertices