你现在作为一名学习计算机视觉的学生,现在根据我发送的代码2中的canny代码加进代码1中在自定义霍夫圆检测的代码canny算法(不能调用OpenCV的canny算法)(能够正确检测钱币数量)(以下代码检测钱币数量少于正确数量);
代码2:(
# 导入必要的库
import cv2
import math
import numpy as np
import os
import time
# ====================== 路径配置(与示例格式一致) ======================
# 路径配置
INPUT_PATH = "input/coins.jpg" # 输入图像路径
CANNY_SAVE = "result/canny_edges.png" # Canny边缘图保存路径
HOUGH_SAVE = "result/hough_result.png" # 检测结果图保存路径
PARAMS_SAVE = "result/circle_params.txt"# 圆心半径参数保存路径
# ====================== Canny边缘检测实现类 ======================
class CannyEdgeDetector:
def __init__(self, 高斯核尺寸, 输入图像, 高阈值, 低阈值):
self.gaussian_kernel_size = 高斯核尺寸
self.img = 输入图像.copy()
self.rows, self.cols = self.img.shape[0:2]
self.gradient_angle = np.zeros((self.rows, self.cols))
self.gradient_img_origin = None
self.kernel_x = np.array([[-1, 1]])
self.kernel_y = np.array([[-1], [1]])
self.canny_high_threshold = 高阈值
self.canny_low_threshold = 低阈值
def 计算梯度图(self):
grad_x = np.zeros((self.rows, self.cols), dtype=np.float64)
grad_y = np.zeros((self.rows, self.cols), dtype=np.float64)
for col in range(self.cols):
for row in range(self.rows):
if row == 0:
grad_y[row, col] = 1
else:
pixel_block = np.array([[self.img[row-1, col]], [self.img[row, col]]])
grad_y[row, col] = np.sum(pixel_block * self.kernel_y)
if col == 0:
grad_x[row, col] = 1
else:
pixel_block = np.array([self.img[row, col-1], self.img[row, col]])
grad_x[row, col] = np.sum(pixel_block * self.kernel_x)
gradient_magnitude, self.gradient_angle = cv2.cartToPolar(grad_x, grad_y)
self.gradient_angle = np.tan(self.gradient_angle)
self.img = gradient_magnitude.astype(np.uint8)
return self.img
def 非极大值抑制(self):
nms_result = np.zeros((self.rows, self.cols))
for row in range(1, self.rows - 1):
for col in range(1, self.cols - 1):
current_pixel = self.img[row, col]
if abs(current_pixel) <= 4:
nms_result[row, col] = 0
continue
angle = self.gradient_angle[row, col]
g1 = g2 = g3 = g4 = 0
if abs(angle) > 1:
g2 = self.img[row-1, col]
g4 = self.img[row+1, col]
if angle > 0:
g1 = self.img[row-1, col-1]
g3 = self.img[row+1, col+1]
else:
g1 = self.img[row-1, col+1]
g3 = self.img[row+1, col-1]
else:
g2 = self.img[row, col-1]
g4 = self.img[row, col+1]
if angle > 0:
g1 = self.img[row-1, col-1]
g3 = self.img[row+1, col+1]
else:
g3 = self.img[row-1, col+1]
g1 = self.img[row+1, col-1]
abs_angle = abs(angle)
interpolate1 = abs_angle * g1 + (1 - abs_angle) * g2
interpolate2 = abs_angle * g3 + (1 - abs_angle) * g4
if current_pixel >= interpolate1 and current_pixel >= interpolate2:
nms_result[row, col] = current_pixel
else:
nms_result[row, col] = 0
self.img = nms_result
return self.img
def 双阈值滞后处理(self):
for row in range(1, self.rows - 1):
for col in range(1, self.cols - 1):
current_pixel = self.img[row, col]
if current_pixel >= self.canny_high_threshold:
angle = self.gradient_angle[row, col]
if abs(angle) < 1:
if self.gradient_img_origin[row-1, col] > self.canny_low_threshold:
self.img[row-1, col] = self.canny_high_threshold
if self.gradient_img_origin[row+1, col] > self.canny_low_threshold:
self.img[row+1, col] = self.canny_high_threshold
if angle < 0:
if self.gradient_img_origin[row-1, col-1] > self.canny_low_threshold:
self.img[row-1, col-1] = self.canny_high_threshold
if self.gradient_img_origin[row+1, col+1] > self.canny_low_threshold:
self.img[row+1, col+1] = self.canny_high_threshold
else:
if self.gradient_img_origin[row-1, col+1] > self.canny_low_threshold:
self.img[row-1, col+1] = self.canny_high_threshold
if self.gradient_img_origin[row+1, col-1] > self.canny_low_threshold:
self.img[row+1, col-1] = self.canny_high_threshold
else:
if self.gradient_img_origin[row, col-1] > self.canny_low_threshold:
self.img[row, col-1] = self.canny_high_threshold
if self.gradient_img_origin[row, col+1] > self.canny_low_threshold:
self.img[row, col+1] = self.canny_high_threshold
if angle < 0:
if self.gradient_img_origin[row-1, col-1] > self.canny_low_threshold:
self.img[row-1, col-1] = self.canny_high_threshold
if self.gradient_img_origin[row+1, col+1] > self.canny_low_threshold:
self.img[row+1, col+1] = self.canny_high_threshold
else:
if self.gradient_img_origin[row-1, col+1] > self.canny_low_threshold:
self.img[row-1, col+1] = self.canny_high_threshold
if self.gradient_img_origin[row+1, col-1] > self.canny_low_threshold:
self.img[row+1, col-1] = self.canny_high_threshold
return self.img
def 执行Canny检测(self):
self.img = cv2.GaussianBlur(self.img, (self.gaussian_kernel_size, self.gaussian_kernel_size), 0)
self.计算梯度图()
self.gradient_img_origin = self.img.copy()
self.非极大值抑制()
self.双阈值滞后处理()
return self.img
# ====================== 霍夫圆检测实现类 ======================
class HoughCircleDetector:
def __init__(self, 边缘图像, 梯度角度, 步长=5, 投票阈值=135):
self.edge_img = 边缘图像
self.angle_matrix = 梯度角度
self.rows, self.cols = self.edge_img.shape[0:2]
self.max_radius = math.ceil(math.hypot(self.rows, self.cols))
self.step = 步长
self.vote_threshold = 投票阈值
self.vote_matrix = np.zeros((
math.ceil(self.rows / self.step),
math.ceil(self.cols / self.step),
math.ceil(self.max_radius / self.step)
))
self.detected_circles = []
def 霍夫空间投票(self):
for row in range(1, self.rows - 1):
for col in range(1, self.cols - 1):
if self.edge_img[row, col] > 0:
y, x, r = row, col, 0
while 0 <= y < self.rows and 0 <= x < self.cols:
vote_row = math.floor(y / self.step)
vote_col = math.floor(x / self.step)
vote_r = math.floor(r / self.step)
self.vote_matrix[vote_row, vote_col, vote_r] += 1
y += self.step * self.angle_matrix[row, col]
x += self.step
r += math.hypot(self.step * self.angle_matrix[row, col], self.step)
y = row - self.step * self.angle_matrix[row, col]
x = col - self.step
r = math.hypot(self.step * self.angle_matrix[row, col], self.step)
while 0 <= y < self.rows and 0 <= x < self.cols:
vote_row = math.floor(y / self.step)
vote_col = math.floor(x / self.step)
vote_r = math.floor(r / self.step)
self.vote_matrix[vote_row, vote_col, vote_r] += 1
y -= self.step * self.angle_matrix[row, col]
x -= self.step
r += math.hypot(self.step * self.angle_matrix[row, col], self.step)
return self.vote_matrix
def 筛选圆参数(self):
candidate_circles = []
for vote_row in range(self.vote_matrix.shape[0]):
for vote_col in range(self.vote_matrix.shape[1]):
for vote_r in range(self.vote_matrix.shape[2]):
if self.vote_matrix[vote_row, vote_col, vote_r] >= self.vote_threshold:
x = vote_col * self.step + self.step / 2
y = vote_row * self.step + self.step / 2
r = vote_r * self.step + self.step / 2
candidate_circles.append((math.ceil(x), math.ceil(y), math.ceil(r)))
if not candidate_circles:
return
# 第一遍聚类
clustered_circles = []
current_cluster = [candidate_circles[0]]
curr_x, curr_y, _ = candidate_circles[0]
for circle in candidate_circles[1:]:
x, y, _ = circle
if abs(x - curr_x) <= 20 and abs(y - curr_y) <= 20:
current_cluster.append(circle)
else:
cluster_mean = np.array(current_cluster).mean(axis=0)
clustered_circles.append(tuple(cluster_mean))
current_cluster = [circle]
curr_x, curr_y = x, y
cluster_mean = np.array(current_cluster).mean(axis=0)
clustered_circles.append(tuple(cluster_mean))
# 按x坐标排序
clustered_circles.sort(key=lambda c: c[0])
# 第二遍聚类
final_clusters = []
current_cluster = [clustered_circles[0]]
curr_x, curr_y, _ = clustered_circles[0]
for circle in clustered_circles[1:]:
x, y, _ = circle
if abs(x - curr_x) <= 20 and abs(y - curr_y) <= 20:
current_cluster.append(circle)
else:
final_circle = np.array(current_cluster).mean(axis=0)
self.detected_circles.append(tuple(final_circle))
current_cluster = [circle]
curr_x, curr_y = x, y
final_circle = np.array(current_cluster).mean(axis=0)
self.detected_circles.append(tuple(final_circle))
def 执行圆检测(self):
self.霍夫空间投票()
self.筛选圆参数()
return self.detected_circles
# ====================== 圆形非极大值抑制(NMS)函数 ======================
def 圆形非极大值抑制(圆列表, IOU阈值=0.7):
if not 圆列表:
return []
circles_np = np.array(圆列表)
x_coords = circles_np[:, 0]
y_coords = circles_np[:, 1]
radiuses = circles_np[:, 2]
sorted_indices = np.argsort(-radiuses)
keep_indices = []
while len(sorted_indices) > 0:
current_idx = sorted_indices[0]
keep_indices.append(current_idx)
other_indices = sorted_indices[1:]
dx = x_coords[other_indices] - x_coords[current_idx]
dy = y_coords[other_indices] - y_coords[current_idx]
center_dist = np.sqrt(dx**2 + dy**2)
r1 = radiuses[current_idx]
r2 = radiuses[other_indices]
r_min = np.minimum(r1, r2)
r_max = np.maximum(r1, r2)
intersect_r = np.maximum(0, r_min - np.maximum(0, center_dist - r_max))
intersect_area = intersect_r**2 * np.pi
union_area = (r1**2 + r2**2) * np.pi - intersect_area
iou = intersect_area / union_area
sorted_indices = other_indices[iou < IOU阈值]
return circles_np[keep_indices].tolist()
# ====================== 主程序入口 ======================
if __name__ == '__main__':
# ========== 算法参数配置(可调整) ==========
高斯核尺寸 = 5 # 高斯滤波核大小
Canny高阈值 = 50 # Canny高阈值
Canny低阈值 = 20 # Canny低阈值
霍夫步长 = 6 # 霍夫变换步长
霍夫投票阈值 = 60 # 霍夫投票阈值
NMS_IOU阈值 = 0.7 # 圆形NMS的IOU阈值
图像缩放比例 = 2 # 图片缩放比例(提升速度)
# ========== 路径初始化:自动创建result文件夹 ==========
result_dir = os.path.dirname(CANNY_SAVE) # 从Canny保存路径中提取result文件夹路径
if not os.path.exists(result_dir):
os.makedirs(result_dir)
print(f"自动创建结果文件夹:{result_dir}")
# ========== 读取并预处理图片 ==========
# 读取图片
灰度图 = cv2.imread(INPUT_PATH, cv2.IMREAD_GRAYSCALE)
彩色图 = cv2.imread(INPUT_PATH)
if 灰度图 is None or 彩色图 is None:
print(f"错误:无法读取输入图片 {INPUT_PATH},请检查路径是否正确!")
exit(1)
# 图片缩放
原高, 原宽 = 灰度图.shape[0:2]
新宽 = int(原宽 / 图像缩放比例)
新高 = int(原高 / 图像缩放比例)
灰度图_缩放 = cv2.resize(灰度图, (新宽, 新高))
彩色图_缩放 = cv2.resize(彩色图, (新宽, 新高))
# ========== 执行Canny边缘检测 ==========
start_time = time.time()
canny_detector = CannyEdgeDetector(高斯核尺寸, 灰度图_缩放, Canny高阈值, Canny低阈值)
边缘检测结果 = canny_detector.执行Canny检测()
canny耗时 = time.time() - start_time
# 保存Canny边缘图
cv2.imwrite(CANNY_SAVE, 边缘检测结果)
# ========== 执行霍夫圆检测 ==========
start_time = time.time()
hough_detector = HoughCircleDetector(
边缘检测结果,
canny_detector.gradient_angle,
霍夫步长,
霍夫投票阈值
)
原始检测圆列表 = hough_detector.执行圆检测()
hough耗时 = time.time() - start_time
# ========== 圆形NMS去重 ==========
去重后圆列表 = 圆形非极大值抑制(原始检测圆列表, NMS_IOU阈值)
# ========== 保存结果:检测图+参数txt ==========
# 绘制圆并保存检测结果图
for 圆参数 in 去重后圆列表:
x = round(圆参数[0])
y = round(圆参数[1])
r = round(圆参数[2])
cv2.circle(彩色图_缩放, (x, y), r, (0, 0, 255), 3)
cv2.imwrite(HOUGH_SAVE, 彩色图_缩放)
# 保存圆心半径参数到txt
with open(PARAMS_SAVE, "w", encoding="utf-8") as f:
f.write("检测到的圆参数(格式:圆心x, 圆心y, 半径)\n")
for idx, 圆参数 in enumerate(去重后圆列表, 1):
x = round(圆参数[0])
y = round(圆参数[1])
r = round(圆参数[2])
f.write(f"圆{idx}:{x}, {y}, {r}\n")
# ========== 精简输出结果 ==========
print("="*40)
print("程序执行完成,核心信息:")
print(f"1. 输入图片:{INPUT_PATH}")
print(f"2. 图片缩放:{原宽}x{原高} → {新宽}x{新高}")
print(f"3. Canny耗时:{canny耗时:.2f}秒 → 保存至:{CANNY_SAVE}")
print(f"4. 霍夫耗时:{hough耗时:.2f}秒 → 检测图保存至:{HOUGH_SAVE}")
print(f"5. 最终检测圆数量:{len(去重后圆列表)} → 参数保存至:{PARAMS_SAVE}")
print("="*40)
)
代码1:(
import cv2
import numpy as np
import matplotlib.pyplot as plt
import os
import glob
from typing import List, Tuple
from scipy import ndimage
import math
# 设置matplotlib中文字体
plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签
plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号
class CoinDetector:
def __init__(self,
canny_low_threshold=40, # Canny边缘检测的低阈值,低于此值的像素点不被认为是边缘
canny_high_threshold=90, # Canny边缘检测的高阈值,高于此值的像素点被认为是强边缘
hough_min_radius=25, # 霍夫圆检测的最小半径(像素),过滤掉太小的圆
hough_max_radius=100): # 霍夫圆检测的最大半径(像素),过滤掉太大的圆
self.canny_low_threshold = canny_low_threshold
self.canny_high_threshold = canny_high_threshold
self.min_radius = hough_min_radius
self.max_radius = hough_max_radius
def detect_coins(self, image_path: str, use_opencv: bool = False):
"""
检测图像中的钱币(圆形)
返回:原图、边缘图、去重后的 circles 列表 [(x, y, r, vote_score)]
"""
# 1. 读取图像
image = cv2.imread(image_path)
if image is None:
print(f"❌ 无法读取图像: {image_path}")
return None, None, None
# 2. 图像预处理
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) # 将BGR彩色图像转换为灰度图像,简化计算
blurred = cv2.GaussianBlur(gray, (9, 9), 2) # 高斯模糊,使用9x9的高斯核,标准差为2,减少噪声
edges = cv2.Canny(blurred, self.canny_low_threshold, self.canny_high_threshold) # Canny边缘检测:先计算梯度,然后使用双阈值检测和连接边缘
circles = []
if use_opencv: # 使用OpenCV内置的霍夫圆检测
# === 🔧 使用优化参数的 OpenCV HoughCircles + 后处理去重 ===
detected = cv2.HoughCircles(
blurred, # 输入图像(已模糊的灰度图)
method=cv2.HOUGH_GRADIENT, # 检测方法:基于梯度的方法
dp=1.0, # 提高分辨率
minDist=70, # 圆心最小距离,防止密集误检
param1=80, # Canny 高阈值
param2=35, # 累加器阈值(投票数),降低 → 更敏感
minRadius=self.min_radius,
maxRadius=self.max_radius
)
if detected is not None: # 处理OpenCV检测结果
# 转换为整数坐标 [x, y, r]
detected = np.round(detected[0]).astype("int")
# 按半径从大到小排序,优先保留更大的圆(更稳定)
detected = sorted(detected, key=lambda x: -x[2])
kept_circles = []
for x, y, r in detected:
is_duplicate = False
for (xk, yk, rk, _) in kept_circles:
center_dist = math.sqrt((x - xk)**2 + (y - yk)**2)
# 若圆心太近且半径相近,则视为同一硬币
if center_dist < 0.6 * max(r, rk): # 小于较大半径的60%
is_duplicate = True
break
if not is_duplicate:
# 使用半径作为简单置信度模拟 score
kept_circles.append((x, y, r, float(r)))
circles = kept_circles
else:
# 使用自定义霍夫圆检测(保持原逻辑不变)
circles = self.custom_hough_circles(blurred, edges)
return image, edges, circles
def custom_hough_circles(self, blurred: np.ndarray, edges: np.ndarray) -> List[Tuple[int, int, int, float]]:
"""
改进版自定义霍夫圆检测
核心改进:
1. 提高检测灵敏度,避免漏检
2. 加强去重,避免一个钱币检测出多个圆
3. 限制最大检测数量为10个
4. 自适应参数调整以获得最佳检测效果
"""
height, width = edges.shape # 获取图像尺寸 获取边缘图像的高度和宽度
# Step 1: 计算梯度
gradient_x = cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize=3) # x方向梯度,使用Sobel算子
gradient_y = cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize=3)
# Step 2: 定义候选半径(使用更细的步长提高精度)
possible_radii = list(range(self.min_radius, self.max_radius + 1, 3))
# Step 3: 多尺度累加器
acc_maps = {}
for r in possible_radii:
acc_maps[r] = np.zeros((height, width), dtype=np.float32)
# Step 4: 获取边缘点
edge_ys, edge_xs = np.where(edges > 0) # 找出边缘图像中非零(白色)像素的位置
print(f"边缘点数量: {len(edge_xs)}")
# 自适应阈值
edge_density = len(edge_xs) / (height * width) # 计算边缘点密度
base_threshold = max(5, int(edge_density * 50)) # 根据密度计算基础阈值
# Step 5: 投票阶段
for x, y in zip(edge_xs, edge_ys): # 遍历每个边缘点
gx = gradient_x[y, x]
gy = gradient_y[y, x]
mag = np.hypot(gx, gy)
if mag < 1e-6: # 如果梯度幅值非常小(接近零)
continue # 跳过该点,避免除零错误
nx, ny = -gx / mag, -gy / mag # 计算梯度方向的单位向量,指向圆心
for r in possible_radii: # 对每个候选半径投票
cx = int(x + r * nx)
cy = int(y + r * ny)
if 0 <= cx < width and 0 <= cy < height:
acc_maps[r][cy, cx] += 1.0
for offset in [-0.2, 0.2]: # 添加偏移投票,增加鲁棒性
cx_offset = int(x + r * (nx + offset * ny))
cy_offset = int(y + r * (ny - offset * nx))
if 0 <= cx_offset < width and 0 <= cy_offset < height:
acc_maps[r][cy_offset, cx_offset] += 0.5
# Step 6: 多尺度峰值检测
all_candidates = []
from scipy.ndimage import maximum_filter
for r in possible_radii:
acc = acc_maps[r]
if acc.max() < base_threshold:
continue
window_size = max(15, int(r * 0.3))
max_filtered = maximum_filter(acc, size=window_size)
local_maxima = (acc == max_filtered) & (acc > 0)
ys, xs = np.where(local_maxima)
for y, x in zip(ys, xs):
vote_score = acc[y, x]
if vote_score < base_threshold * 2:
continue
total_points = max(24, int(2 * math.pi * r) // 6)
edge_points = 0
for i in range(total_points):
angle = 2 * math.pi * i / total_points
px = int(x + r * math.cos(angle))
py = int(y + r * math.sin(angle))
if 0 <= px < width and 0 <= py < height:
y_min = max(0, py-1); y_max = min(height, py+2)
x_min = max(0, px-1); x_max = min(width, px+2)
if np.any(edges[y_min:y_max, x_min:x_max] > 0):
edge_points += 1
edge_ratio = edge_points / total_points
normalized_vote = min(1.0, vote_score / (2 * base_threshold))
final_score = 0.4 * normalized_vote + 0.6 * edge_ratio
if final_score > 0.35 and edge_ratio > 0.25:
all_candidates.append({
'x': x, 'y': y, 'r': r, 'score': final_score,
'edge_ratio': edge_ratio, 'vote_score': vote_score
})
print(f"初步候选圆数量: {len(all_candidates)}")
if not all_candidates:
return []
# Step 7: 智能分组去重
def are_same_coin(c1, c2): # 定义判断两个圆形是否相同的函数
dx = c1['x'] - c2['x']; dy = c1['y'] - c2['y']
center_dist = math.sqrt(dx*dx + dy*dy)
avg_radius = (c1['r'] + c2['r']) / 2
radius_diff = abs(c1['r'] - c2['r'])
return center_dist < min(avg_radius * 0.8, 35) and radius_diff < 20 # 判断条件:距离小于平均半径的80%且小于35像素,且半径差小于20像素
all_candidates.sort(key=lambda c: -c['score']) # 按分数降序排序候选
groups = []; used = [False] * len(all_candidates)
for i in range(len(all_candidates)):
if used[i]: continue
current_group = [all_candidates[i]]; used[i] = True
for j in range(i+1, len(all_candidates)):
if used[j]: continue
if are_same_coin(all_candidates[i], all_candidates[j]):
current_group.append(all_candidates[j]); used[j] = True
groups.append(current_group)
print(f"分组数量(去重后): {len(groups)}")
# Step 8: 每组选最优
final_selection = []
for group in groups:
best_candidate = max(group, key=lambda c:
c['score'] * 0.5 + c['edge_ratio'] * 0.3 +
min(1.0, c['vote_score'] / (3 * base_threshold)) * 0.2
)
final_selection.append((
best_candidate['x'],
best_candidate['y'],
best_candidate['r'],
best_candidate['score']
))
# Step 9: 最终去重
unique_circles = []
for i, (x, y, r, score) in enumerate(final_selection):
is_duplicate = False
for j, (xj, yj, rj, scorej) in enumerate(unique_circles):
dist = math.sqrt((x - xj)**2 + (y - yj)**2)
if dist < max(r, rj) * 0.7:
is_duplicate = True
if score > scorej:
unique_circles[j] = (x, y, r, score)
break
if not is_duplicate:
unique_circles.append((x, y, r, score))
# Step 10: 排序并限数
unique_circles.sort(key=lambda c: -c[3])
if len(unique_circles) > 10:
unique_circles = unique_circles[:10]
# Step 11: 最终验证
verified_circles = []
for x, y, r, score in unique_circles:
verification_points = max(30, int(2 * math.pi * r) // 4)
verified_edges = 0
for i in range(verification_points):
angle = 2 * math.pi * i / verification_points
px = int(x + r * math.cos(angle))
py = int(y + r * math.sin(angle))
if 0 <= px < width and 0 <= py < height:
y_min = max(0, py-2); y_max = min(height, py+3)
x_min = max(0, px-2); x_max = min(width, px+3)
if np.any(edges[y_min:y_max, x_min:x_max] > 0):
verified_edges += 1
ratio = verified_edges / verification_points
if ratio > 0.2:
adjusted_score = 0.7 * score + 0.3 * ratio
verified_circles.append((x, y, r, adjusted_score))
print(f"最终检测到的钱币数量: {len(verified_circles)}")
verified_circles.sort(key=lambda c: -c[3])
return verified_circles
@staticmethod #辅助方法:去重
def remove_duplicate_circles(circles: List[Tuple[int, int, int, float]],
distance_thresh: int = 40,
radius_thresh: int = 20) -> List[Tuple[int, int, int, float]]:
if not circles:
return []
circles.sort(key=lambda c: -c[3])
kept = []
used = [False] * len(circles)
for i in range(len(circles)):
if used[i]: continue
kept.append(circles[i])
used[i] = True
xi, yi, ri, _ = circles[i]
for j in range(i + 1, len(circles)):
if used[j]: continue
xj, yj, rj, _ = circles[j]
d = math.sqrt((xi - xj)**2 + (yi - yj)**2)
if d < distance_thresh and abs(ri - rj) < radius_thresh:
used[j] = True
return kept
def print_detection_info(self, circles: List[Tuple[int, int, int, float]], name: str):
"""
打印检测信息
参数:
circles: 检测到的圆形列表
name: 图像名称
"""
print(f"✅ {name}: 检测到 {len(circles)} 个钱币")
for i, (x, y, r, v) in enumerate(circles):
print(f" 第{i+1}个: 中心({x},{y}), 半径={r}, 得分={v:.2f}")
def draw_results(self, image, edges, circles, output_path=None):
"""
绘制检测结果
参数:
image: 原始图像
edges: 边缘图像
circles: 检测到的圆形列表
output_path: 输出图像路径(可选)
返回:
combined: 合并后的显示图像
"""
display = image.copy()
for (x, y, r, v) in circles:
cv2.circle(display, (x, y), r, (0, 255, 0), 3)
cv2.circle(display, (x, y), 3, (0, 0, 255), -1)
cv2.putText(display, f'R={r}', (x - 40, y - r - 10),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 0, 0), 2)
edges_bgr = cv2.cvtColor(edges, cv2.COLOR_GRAY2BGR)
combined = np.hstack([edges_bgr, display])
if output_path:
cv2.imwrite(output_path, combined)
return combined
def process_images_in_folder(folder: str, use_opencv: bool = False):
"""
批量处理文件夹中的图像
参数:
folder: 包含图像的文件夹路径
use_opencv: 是否使用OpenCV内置方法
返回:
处理结果列表
"""
result_dir = os.path.join(folder, "results")
if not os.path.exists(result_dir):
os.makedirs(result_dir)
image_files = (glob.glob(os.path.join(folder, "*.jpg")) +
glob.glob(os.path.join(folder, "*.png")) +
glob.glob(os.path.join(folder, "*.jpeg")))
if len(image_files) == 0:
print("⚠️ 未在目录中找到图像文件")
return []
detector = CoinDetector(
canny_low_threshold=40,
canny_high_threshold=90,
hough_min_radius=25,
hough_max_radius=100
)
all_results = []
for path in image_files:
name = os.path.basename(path)
print(f"\n🔍 正在处理: {name}")
image, edges, circles = detector.detect_coins(path, use_opencv=use_opencv)
if image is None:
continue
detector.print_detection_info(circles, name)
txt_path = os.path.join(result_dir, f"res_{os.path.splitext(name)[0]}.txt")
with open(txt_path, 'w', encoding='utf-8') as f:
f.write(f"图像: {name}\n")
f.write(f"检测到钱币数: {len(circles)}\n")
for i, (x, y, r, v) in enumerate(circles):
f.write(f"第{i+1}个钱币: 圆心({x},{y}), 半径={r}, 得分={v:.2f}\n")
out_img = os.path.join(result_dir, f"result_{name}")
combined = detector.draw_results(image, edges, circles, out_img)
all_results.append({'name': name, 'count': len(circles), 'path': out_img})
plt.figure(figsize=(15, 6))
plt.imshow(cv2.cvtColor(combined, cv2.COLOR_BGR2RGB))
plt.title(f"{name} - 检测到 {len(circles)} 个钱币", fontsize=14)
plt.axis('off')
plt.tight_layout()
plt.show()
total_coins = sum(r['count'] for r in all_results)
summary = f"\n🎉 处理完成!共处理 {len(all_results)} 张图像,总计检测到 {total_coins} 个钱币"
print(summary)
with open(os.path.join(result_dir, "summary.txt"), 'w', encoding='utf-8') as f:
f.write(summary)
return all_results
# ==================== 主程序入口 ====================
if __name__ == "__main__":
folder = r"C:\Users\lzq\Desktop\zuoye\shijue\kechensheji"
if not os.path.exists(folder):
print("❌ 错误:指定路径不存在!请检查:\n", folder)
else:
choice = input("选择方法:\n1. 自定义霍夫圆检测(推荐)\n2. OpenCV 内置 HoughCircles\n请输入 (1/2): ")
use_opencv = (choice.strip() == '2')
results = process_images_in_folder(folder, use_opencv=use_opencv)
)
最新发布