{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"\n# Circular and Elliptical Hough Transforms\n\nThe Hough transform in its simplest form is a [method to detect\nstraight lines](https://en.wikipedia.org/wiki/Hough_transform)_\nbut it can also be used to detect circles or ellipses.\nThe algorithm assumes that the edge is detected and it is robust against\nnoise or missing points.\n\n## Circle detection\n\nIn the following example, the Hough transform is used to detect\ncoin positions and match their edges. We provide a range of\nplausible radii. For each radius, two circles are extracted and\nwe finally keep the five most prominent candidates.\nThe result shows that coin positions are well-detected.\n\n\n### Algorithm overview\n\nGiven a black circle on a white background, we first guess its\nradius (or a range of radii) to construct a new circle.\nThis circle is applied on each black pixel of the original picture\nand the coordinates of this circle are voting in an accumulator.\nFrom this geometrical construction, the original circle center\nposition receives the highest score.\n\nNote that the accumulator size is built to be larger than the\noriginal picture in order to detect centers outside the frame.\nIts size is extended by two times the larger radius.\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import numpy as np\nimport matplotlib.pyplot as plt\n\nfrom skimage import data, color\nfrom skimage.transform import hough_circle, hough_circle_peaks\nfrom skimage.feature import canny\nfrom skimage.draw import circle_perimeter\nfrom skimage.util import img_as_ubyte\n\n\n# Load picture and detect edges\nimage = img_as_ubyte(data.coins()[160:230, 70:270])\nedges = canny(image, sigma=3, low_threshold=10, high_threshold=50)\n\n\n# Detect two radii\nhough_radii = np.arange(20, 35, 2)\nhough_res = hough_circle(edges, hough_radii)\n\n# Select the most prominent 3 circles\naccums, cx, cy, radii = hough_circle_peaks(hough_res, hough_radii, total_num_peaks=3)\n\n# Draw them\nfig, ax = plt.subplots(ncols=1, nrows=1, figsize=(10, 4))\nimage = color.gray2rgb(image)\nfor center_y, center_x, radius in zip(cy, cx, radii):\n circy, circx = circle_perimeter(center_y, center_x, radius, shape=image.shape)\n image[circy, circx] = (220, 20, 20)\n\nax.imshow(image, cmap=plt.cm.gray)\nplt.show()"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Ellipse detection\n\nIn this second example, the aim is to detect the edge of a coffee cup.\nBasically, this is a projection of a circle, i.e. an ellipse. The problem\nto solve is much more difficult because five parameters have to be\ndetermined, instead of three for circles.\n\n### Algorithm overview\n\nThe algorithm takes two different points belonging to the ellipse. It\nassumes that these two points form the major axis. A loop on all the\nother points determines the minor axis length for candidate ellipses.\nThe latter are included in the results if enough 'valid' candidates have\nsimilar minor axis lengths. By valid, we mean candidates for which the\nminor and major axis lengths fall within the prescribed bounds.\nA full description of the algorithm can be found in reference [1]_.\n\n### References\n.. [1] Xie, Yonghong, and Qiang Ji. \"A new efficient\n ellipse detection method.\" Pattern Recognition, 2002. Proceedings.\n 16th International Conference on. Vol. 2. IEEE, 2002\n\n"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"collapsed": false
},
"outputs": [],
"source": [
"import matplotlib.pyplot as plt\n\nfrom skimage import data, color, img_as_ubyte\nfrom skimage.feature import canny\nfrom skimage.transform import hough_ellipse\nfrom skimage.draw import ellipse_perimeter\n\n# Load picture, convert to grayscale and detect edges\nimage_rgb = data.coffee()[0:220, 160:420]\nimage_gray = color.rgb2gray(image_rgb)\nedges = canny(image_gray, sigma=2.0, low_threshold=0.55, high_threshold=0.8)\n\n# Perform a Hough Transform\n# The accuracy corresponds to the bin size of the histogram for minor axis lengths.\n# A higher `accuracy` value will lead to more ellipses being found, at the\n# cost of a lower precision on the minor axis length estimation.\n# A higher `threshold` will lead to less ellipses being found, filtering out those\n# with fewer edge points (as found above by the Canny detector) on their perimeter.\nresult = hough_ellipse(edges, accuracy=20, threshold=250, min_size=100, max_size=120)\nresult.sort(order='accumulator')\n\n# Estimated parameters for the ellipse\nbest = list(result[-1])\nyc, xc, a, b = (int(round(x)) for x in best[1:5])\norientation = best[5]\n\n# Draw the ellipse on the original image\ncy, cx = ellipse_perimeter(yc, xc, a, b, orientation)\nimage_rgb[cy, cx] = (0, 0, 255)\n# Draw the edge (white) and the resulting ellipse (red)\nedges = color.gray2rgb(img_as_ubyte(edges))\nedges[cy, cx] = (250, 0, 0)\n\nfig2, (ax1, ax2) = plt.subplots(\n ncols=2, nrows=1, figsize=(8, 4), sharex=True, sharey=True\n)\n\nax1.set_title('Original picture')\nax1.imshow(image_rgb)\n\nax2.set_title('Edge (white) and result (red)')\nax2.imshow(edges)\n\nplt.show()"
]
}
],
"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.12.3"
}
},
"nbformat": 4,
"nbformat_minor": 0
}