experiment to localize page corners with colorful circle blobs
This commit is contained in:
commit
4950cefe82
10 files changed
+569
No files matched your search
@@ -0,0 +1,2 @@
|
|||||||
|
*.pyc
|
||||||
|
notebooks/.ipynb_checkpoints/
|
||||||
@@ -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.
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 MiB |
Binary file not shown.
|
After Width: | Height: | Size: 4.2 MiB |
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+X7Y4zY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 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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
@@ -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)
|
||||||
|
|
||||||
@@ -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
|
||||||
|
|
||||||
Reference in new issue
Block a user