来源与署名:scikit-image team(原页未署个人作者);未完纪翻译整理。原文:Circular and Elliptical Hough Transforms。本文为中文翻译整理;技术核对日期为 2026 年 10 月 5 日。

Hough 变换最常见的用途是找直线,也可以识别圆和椭圆。它以已经提取出的边缘为输入,通过参数空间中的投票寻找共同支持的几何形状,因此能够容忍一定的噪声和边缘缺失。这里沿用 scikit-image 0.26.x 官方实例,将硬币圆形和咖啡杯口椭圆连成一个完整案例。
本文固定核对 0.26.0 文档与对应 tag 的代码。没有安装或执行示例;下方两幅检测结果图直接来自官方原页,已经下载并检查,不是本次运行生成的输出。算法给出的是像素位置与尺度,不是硬币或杯口的真实物理尺寸。
圆形:让边缘像素为可能的圆心投票
可以先把原图想成白色背景上的黑色圆。给定一个候选半径,在每个边缘像素周围画出同半径圆,这些圆经过的坐标在累加器中获得投票。真实圆心会被许多边缘点共同支持,因此分数更高。半径未知时,就对一组合理半径分别构造累加器,再从中选择显著峰值。
原文介绍可通过扩大累加器来发现落在画面之外的圆心。编辑核对:0.26.0 API 的 full_output 默认是 False;只有设为 True 时,输出宽高才各增加两倍最大半径。本例没有开启这个选项,所以不能把扩展累加器当作本段代码的默认行为。
先从 data.coins() 中裁出第 160~229 行、第 70~269 列,再使用 Canny 提取边缘。该图是 uint8 灰度图,阈值 10 和 50 与像素数值范围对应;后面的咖啡图会转成浮点灰度,不能直接照搬这组阈值。
import numpy as np
import matplotlib.pyplot as plt
from skimage import data, color
from skimage.transform import hough_circle, hough_circle_peaks
from skimage.feature import canny
from skimage.draw import circle_perimeter
from skimage.util import img_as_ubyte
# 载入裁剪后的硬币图,并提取边缘
image = img_as_ubyte(data.coins()[160:230, 70:270])
edges = canny(image, sigma=3, low_threshold=10, high_threshold=50)
# 检查 20、22、24、26、28、30、32、34 像素半径
hough_radii = np.arange(20, 35, 2)
hough_res = hough_circle(edges, hough_radii)
accums, cx, cy, radii = hough_circle_peaks(
hough_res, hough_radii, total_num_peaks=3
)
fig, ax = plt.subplots(ncols=1, nrows=1, figsize=(10, 4))
image = color.gray2rgb(image)
for center_y, center_x, radius in zip(cy, cx, radii):
circy, circx = circle_perimeter(
center_y, center_x, radius, shape=image.shape
)
image[circy, circx] = (220, 20, 20)
ax.imshow(image, cmap=plt.cm.gray)
plt.show()
np.arange(20, 35, 2) 枚举八个半径,而不是原代码旧注释所说的“两种半径”。hough_circle_peaks 的 total_num_peaks=3 保留全局最显著的三个圆。源文叙述曾写“每个半径取两个,最终保留五个”,与固定版本代码不一致;本稿以代码中的三个候选为准,不另造峰值限制。
返回的 cx 是列坐标、cy 是行坐标;circle_perimeter 接受的顺序却是行、列,所以循环使用 center_y、center_x。灰度图先转为 RGB,才能将轮廓写成 (220, 20, 20)。shape 限制绘制坐标落在图像范围内。

椭圆:用轴长与方向解释杯口投影
第二个例子检测咖啡杯口。一个圆在斜视投影中会呈现椭圆;与圆的中心两坐标和半径三个参数相比,椭圆需要中心两坐标、两条半轴以及方向共五个参数,因此搜索更复杂。
源文采用的算法先选择两个可能属于椭圆的点,并假定它们组成长轴。随后遍历其余边缘点,为候选椭圆计算短轴长度;当足够多的有效候选落入相近的短轴长度区间时,形成一个结果。“有效”意味着轴长处于指定范围。完整算法见 Yonghong Xie 和 Qiang Ji 的论文《A new efficient ellipse detection method》,16th International Conference on Pattern Recognition,Vol. 2,IEEE,2002。
本例裁出 data.coffee() 的第 0~219 行、第 160~419 列;rgb2gray 生成浮点灰度图,再以 sigma=2.0、低阈值 0.55、高阈值 0.8 提取边缘。accuracy 控制短轴长度直方图的分箱,值越大通常允许更多候选进入同一箱,但轴长估计变粗;threshold 越高,要求轮廓边缘点的支持越强,候选通常越少。
import matplotlib.pyplot as plt
from skimage import data, color, img_as_ubyte
from skimage.feature import canny
from skimage.transform import hough_ellipse
from skimage.draw import ellipse_perimeter
image_rgb = data.coffee()[0:220, 160:420]
image_gray = color.rgb2gray(image_rgb)
edges = canny(image_gray, sigma=2.0,
low_threshold=0.55, high_threshold=0.8)
result = hough_ellipse(edges, accuracy=20, threshold=250,
min_size=100, max_size=120)
result.sort(order='accumulator')
best = list(result[-1])
yc, xc, a, b = (int(round(x)) for x in best[1:5])
orientation = best[5]
cy, cx = ellipse_perimeter(yc, xc, a, b, orientation)
image_rgb[cy, cx] = (0, 0, 255)
edges = color.gray2rgb(img_as_ubyte(edges))
edges[cy, cx] = (250, 0, 0)
fig2, (ax1, ax2) = plt.subplots(
ncols=2, nrows=1, figsize=(8, 4), sharex=True, sharey=True
)
ax1.set_title('Original picture')
ax1.imshow(image_rgb)
ax2.set_title('Edge (white) and result (red)')
ax2.imshow(edges)
plt.show()
hough_ellipse 返回带 accumulator、yc、xc、a、b、orientation 字段的记录。按 accumulator 排序后取最后一个,得到分数最高的候选;中心和轴参数在绘制前四舍五入为整数,方向保留浮点值。左图在原彩色裁剪图上用蓝色画椭圆,右图以白色显示边缘、红色显示椭圆,用于直接比较。

让示例适应不同输入时,要补齐边界检查
原图和参数互相匹配,不代表任何输入都会得到椭圆。原代码直接使用 result[-1],当结果为空会抛出异常;ellipse_perimeter 未指定 shape,换图后还可能产生越界索引或负索引回绕。以下是编辑补充的替换片段,覆盖原代码从排序之后到轮廓坐标生成这一段;其余绘制逻辑保持原样。本次只做静态核对,没有运行修订版。
if result.size == 0:
raise ValueError("未找到满足阈值和轴长范围的椭圆")
best = list(result[-1])
yc, xc, a, b = (int(round(x)) for x in best[1:5])
orientation = best[5]
cy, cx = ellipse_perimeter(
yc, xc, a, b, orientation, shape=image_rgb.shape[:2]
)
shape 参数的依据是 官方绘制 API。如果需要保留输入不被修改,还可把裁剪语句改为 data.coffee()[0:220, 160:420].copy()。圆检测也可能没有候选,应根据应用需求判断 radii.size,而不是保证一定找到三个圆。
处理外部图片时还要限制图像像素数、边缘点数量和候选尺寸范围。尤其椭圆搜索的组合数量可能很大,任意扩大范围会增加内存和计算开销。源例没有网络接口、凭据、shell 调用或代码执行入口;这不等于对所有图像解码器和底层依赖做过安全证明。
复现边界、输入来源与许可
本文所列的静态依赖基线为 CPython 3.11、scikit-image 0.26.0、NumPy 2.2.6、SciPy 1.15.3、Matplotlib 3.10.3。这只是直接版本约束相容的静态判断,但不代表完整锁定环境或二进制测试已经完成。本次没有安装这些包,也没有重新验证该依赖组合。原页标注的运行时间 5.369 秒属于官方构建环境,不能作为读者机器的耗时保证。
输入分别为 coins.png 和 coffee.png。固定 tag 的数据获取代码记录:coins 来自 Brooklyn Museum Collection,无已知版权限制;coffee 为 Rachel Michetti 提供的 CC0 照片。代码应通过 scikit-image 的数据接口获取实际样本;不要把本稿中的压缩绘图结果作为算法输入。
原页面未署个人作者,维护方为 scikit-image team,页脚 © 2013–2025。对应 v0.26.0 仓库总许可为 BSD-3-Clause,部分文件另有例外;本例与数据图的归属分别保留。下方许可文本保留版权、再分发条件和免责条款;许可来源为 官方 LICENSE.txt。
原始版权与许可全文
Applicable default license excerpt from scikit-image v0.26.0 LICENSE.txt. Source: https://raw.githubusercontent.com/scikit-image/scikit-image/v0.26.0/LICENSE.txt The source license file lists exceptions for other files. This file preserves the default BSD-3-Clause notice applicable to the reproduced gallery example, not a relicensing of third-party data images. Files: * Copyright: 2009-2022 the scikit-image team License: BSD-3-Clause Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 3. Neither the name of the University nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. Image attribution independently verified from v0.26.0 src/skimage/data/_fetchers.py: coins: Brooklyn Museum Collection, https://www.brooklynmuseum.org/opencollection/archives/image/51611 ; no known copyright restrictions. coffee: photograph courtesy of Pikolo Espresso Bar; CC0 by photographer Rachel Michetti. Source: https://raw.githubusercontent.com/scikit-image/scikit-image/v0.26.0/src/skimage/data/_fetchers.py












暂无评论内容