图 = =

定义:

图就是由一些小圆点(成为顶点)和连接这些小圆点的直线(称为)组成的。例如上图是由5个顶点(编号为1、2、3、4、5)和5条边(1-2、1-3、1-5、2-4、3-5)组成。

遍历:

现在我们从1号顶点开始遍历这个图,遍历就是指把图的每一个顶点都访问一次

使用深度优先搜索来遍历这个图将会得到如下的结果。

这五个顶点的被访问顺序如下图。图中每个顶点右上方的数就表示这个顶点是第几个被访问到的,我们还为这个数起了很好听的名字---时间戳

 

图的存储:

最常用的方法:使用一个二维数组e来存储,如下:

上图二维数组第i行第j列表示的点就是顶点i到顶点j是否有边。 1表示有边,oo表示没有边,这里我们将自己到自己(即i等于j)设为0。我们将这种存储图的方法成为图的邻接矩阵存储法

上面的二维数组是沿主对角线(左上角到右下角)对称的,因为上面这个图是无向图。所谓无向图指的是图的边没有方向,例如上图1-5表示,1号顶点可以到5号顶点,5号顶点也可以到1号顶点。

使用深度优先搜索遍历:

过程具体是:首先从一个未走到过的顶点作为起始顶点,比如以1号顶点作为起点。沿1号顶点的边去尝试访问其它未走到的顶点,首先发现2号顶点还没有走到过,于是来到了2号顶点。再以2号顶点作为出发点继续尝试访问其它未走到过的顶点,这样又来到了4号顶点。再以4号顶点作为出发点继续尝试访问其它未走到过的顶点。

但是,此时沿4号顶点的边,已经不能访问到其它未走到过的顶点了,所以需要返回到2号顶点。因此还需要继续返回到1号顶点。再继续沿1号顶点的边看看还能否访问到其它未走到过的顶点。此时又会来到3号顶点,再以3号顶点作为出发点继续访问其它未走过的顶点,于是又来到5号顶点。

到此,所有顶点都走到过了,遍历结束。

深度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,沿当前顶点的边走到未访问过的顶点,直到所有的顶点都被访问过。显然,深度优先遍历是沿着图的某一条分支遍历直至末端,然后回溯,再沿着另一条进行同样的遍历,直到所有的顶点都被访问过为止。

实操代码:

#include<bits/stdc++.h>

using namespace std;

int e[101][101]; // 二维数组e存储的就是图的边(邻接矩阵)
int book[101]; // 数组book用来记录哪些顶点已经访问过
int sum; // sum用来记录已经访问过多少个顶点
int n; // n存储的是图的顶点的总个数 

void dfs(int cur) // cur是当前所在的顶点编号(正在遍历的顶点)
{
	int i;
	cout << cur << ' ';
	sum ++; // 每访问一个顶点,sum就加1
	if(sum == n)  return;  //所有的顶点都已经访问过则直接退出
	for(i = 1 ; i <= n ; i ++) // 从1号顶点到n号顶点依次尝试,看哪些顶点与当前顶点cur有边相连 
    {
    	// 判断当前顶点cur到顶点i是否有边,并判断顶点i是否访问过
		if(e[cur][i] == 1 && book[i] == 0)
		{
			book[i] = 1; // 标记顶点i已经访问过
			dfs(i); // 从顶点i再出发继续遍历 
		} 
	}
	return; 
}   

int main()
{
	int i,j;
	int m;
	int a,b;
	cin >> n >> m;
	
	//初始化二维矩阵
	for(i = 1 ; i <= n ; i ++)
	{
		for(j = 1 ; j <= m ; j ++)
		{
			if(i == j)  e[i][j] = 0;
			else  e[i][j] = 999999999;  // 我们这里假设999999999为正无穷 
		}
	} 
	
	//读入顶点之间的边
	for(i = 1 ; i <= m ; i ++)
	{
		cin >> a >> b;
		e[a][b] = 1;
		e[b][a] = 1; // 这里是无向图,所以需要将e[b][a]也赋为1 
	} 
	
	//从1号城市出发
	book[1] = 1; // 标记1号顶点已访问
	dfs(1); // 从1号顶点开始遍历
	
	getchar(); getchar();
	
	return 0; 
}

使用广度优先搜索来遍历:

(1)结果

(2)顶点被访问的顺序

(3)遍历过程

首先以一个未被访问过的顶点作为起始顶点,比如以1号顶点作为起点。将1号顶点放入队列中,然后将与1号顶点相邻的未访问过的顶点即2号、3号和5号顶点依次再放入到队列中。如下图:

接下来再将2号顶点相邻的未访问过的顶点4号顶点放入队列中。到此所有顶点都被访问过,遍历结束。如下图:

广度优先遍历的主要思想就是:首先以一个未被访问过的顶点作为起始顶点,访问其所有相邻的顶点,然后对每个相邻的顶点,再访问它们相邻的未被访问过的顶点,直到所有顶点都被访问过,遍历结束。

实操代码:

#include<bits/stdc++.h>

using namespace std;

int book[101];
int e[101][101];
int que[10001],head,tail;

int main()
{
	int i,j;
	int n,m;
	int a,b;
	int cur;
	
	cin >> n >> m;
	
	//初始化二维矩阵 
	for(i = 1 ; i <= n ; i ++)
	{
		for(j = 1 ; j <= n ; j ++)
		{
			if(i == j)  e[i][j] = 0;
			else  e[i][j] == 999999999; // 我们这里假设99999999为无穷 
		}
	}
	
	//读入顶点之间的边
	for(i = 1 ; i <= m ; i ++)
	{
		cin >> a >> b; 
		e[a][b] = 1;
		e[b][a] = 1; // 这里是无向图,所以需要将e[b][a]也赋值为1 
	} 
	
	//队列初始化
	head = 1,tail = 1;
	
	//从1号顶点出发,将1号顶点加入队列
	que[tail] = 1;
	tail ++;
	book[1] = 1; // 标记1号顶点已访问
	
	//当队列不为空
	while(head < tail)
	{
		cur = que[head]; // 当前正在访问的顶点编号
		for(i = 1 ; i <= n ; i ++) // 从1~n依次尝试
		{
			//判断从顶点cur到顶点i是否有边,并判断顶点i是否已经访问过
			if(e[cur][i] == 1 && book[i] == 0)
			{
				// 如果从顶点cur到顶点i有边,并且顶点i没有被访问过,则将顶点i入队
				que[tail] = i;
				tail ++;
				book[i] = 1; // 标记顶点i已访问 
			} 
			
			//如果tail > n , 则表明所有顶点都已经被访问过
			if(tail > n)  break; 
		} 
		head ++; // 注意这地方,千万不要忘记当一个顶点扩展结束后,head ++ ,然后才能继续往下扩展 
	}
	 
	for(i = 1 ; i < tail ; i ++)
	{
		cout << que[i] << ' ';
	}
	
	getchar();getchar();
	
	return 0; 
	
}

 

你现在作为一名学习计算机视觉的学生,现在根据我发送的代码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) )
最新发布
12-29
你希望将**代码2中的自定义 Canny 边缘检测算法**(不调用 OpenCV 的 `cv2.Canny`)集成到**代码1的自定义霍夫圆检测流程中**,以提升钱币检测的准确率(目前检测数量偏少),并确保整个流程完全使用你自己实现的 Canny 算法。 --- ### ✅ 目标总结: 1. **移除代码1中对 `cv2.Canny` 的依赖** 2. **将代码2中完整的 Canny 实现嵌入代码1的流程** 3. **保留代码1中强大的后处理逻辑(多尺投票、去重、验证等)** 4. **优化参数流程,使钱币检测数量更接近真实值** --- ## ✅ 最终解决方案:融合后的完整代码 ```python import cv2 import numpy as np import matplotlib.pyplot as plt import os import glob from typing import List, Tuple import math import time # ====================== Canny边缘检测实现类(来自代码2,已修复关键Bug)====================== class CannyEdgeDetector: def __init__(self, gaussian_kernel_size, img, high_threshold, low_threshold): self.gaussian_kernel_size = gaussian_kernel_size self.img = img.copy().astype(np.float64) self.high_thresh = high_threshold self.low_thresh = low_threshold self.rows, self.cols = self.img.shape self.gradient_angle = np.zeros((self.rows, self.cols)) self.gradient_magnitude = None self.kernel_x = np.array([[-1, 1]]) self.kernel_y = np.array([[-1], [1]]) def gaussian_blur(self): """手动实现高斯模糊""" pad_size = self.gaussian_kernel_size // 2 sigma = 1.0 kernel_1d = np.linspace(-pad_size, pad_size, self.gaussian_kernel_size) kernel_1d = np.exp(-kernel_1d**2 / (2 * sigma**2)) kernel_1d /= kernel_1d.sum() kernel_2d = np.outer(kernel_1d, kernel_1d) padded_img = np.pad(self.img, pad_size, mode='edge') blurred = np.zeros_like(self.img) for i in range(self.rows): for j in range(self.cols): patch = padded_img[i:i+self.gaussian_kernel_size, j:j+self.gaussian_kernel_size] blurred[i, j] = np.sum(patch * kernel_2d) self.img = blurred def compute_gradients(self): grad_x = np.zeros_like(self.img) grad_y = np.zeros_like(self.img) # 使用 Sobel 梯计算(改进版) for row in range(1, self.rows - 1): for col in range(1, self.cols - 1): grad_x[row, col] = (self.img[row, col + 1] - self.img[row, col - 1]) grad_y[row, col] = (self.img[row + 1, col] - self.img[row - 1, col]) self.gradient_magnitude, self.gradient_angle = cv2.cartToPolar(grad_x, grad_y) self.gradient_angle = np.degrees(self.gradient_angle) % 180 # 角归一化为0~180 return self.gradient_magnitude.copy(), self.gradient_angle.copy() def non_max_suppression(self): """非极大值抑制""" nms = np.zeros_like(self.gradient_magnitude) angle = self.gradient_angle for row in range(1, self.rows - 1): for col in range(1, self.cols - 1): q = 255 r = 255 ang = angle[row, col] if (0 <= ang < 22.5) or (157.5 <= ang <= 180): q = self.gradient_magnitude[row, col + 1] r = self.gradient_magnitude[row, col - 1] elif 22.5 <= ang < 67.5: q = self.gradient_magnitude[row + 1, col - 1] r = self.gradient_magnitude[row - 1, col + 1] elif 67.5 <= ang < 112.5: q = self.gradient_magnitude[row + 1, col] r = self.gradient_magnitude[row - 1, col] elif 112.5 <= ang < 157.5: q = self.gradient_magnitude[row - 1, col - 1] r = self.gradient_magnitude[row + 1, col + 1] if (self.gradient_magnitude[row, col] >= q) and (self.gradient_magnitude[row, col] >= r): nms[row, col] = self.gradient_magnitude[row, col] self.gradient_magnitude = nms return nms def double_thresholding_and_edge_tracking(self): """双阈值与滞后连接""" weak = 80 strong = 255 res = np.zeros_like(self.gradient_magnitude) # 双阈值分类 strong_i, strong_j = np.where(self.gradient_magnitude >= self.high_thresh) weak_i, weak_j = np.where((self.gradient_magnitude >= self.low_thresh) & (self.gradient_magnitude < self.high_thresh)) res[strong_i, strong_j] = strong res[weak_i, weak_j] = weak # 滞后连接:弱边仅当邻接强边时保留 for row in range(1, self.rows - 1): for col in range(1, self.cols - 1): if res[row, col] == weak: if (res[row-1:row+2, col-1:col+2] == strong).any(): res[row, col] = strong else: res[row, col] = 0 return res.astype(np.uint8) def detect(self): """执行完整Canny流程""" self.gaussian_blur() self.compute_gradients() self.non_max_suppression() edges = self.double_thresholding_and_edge_tracking() return edges # ====================== 改进的钱币检测器(融合自定义Canny)====================== class CoinDetector: def __init__(self, canny_low_threshold=20, canny_high_threshold=50, hough_min_radius=25, hough_max_radius=100, gaussian_kernel_size=5): 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 self.gaussian_kernel_size = gaussian_kernel_size def detect_coins(self, image_path: str, use_opencv: bool = False): image = cv2.imread(image_path) if image is None: print(f"❌ 无法读取像: {image_path}") return None, None, None gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY) blurred = cv2.GaussianBlur(gray, (9, 9), 2) # 仍用于梯计算输入 # 🔥 使用我们自己的 Canny 替代 cv2.Canny canny_detector = CannyEdgeDetector( gaussian_kernel_size=self.gaussian_kernel_size, img=gray, high_threshold=self.canny_high_threshold, low_threshold=self.canny_low_threshold ) edges = canny_detector.detect() circles = [] if use_opencv: detected = cv2.HoughCircles( blurred, method=cv2.HOUGH_GRADIENT, dp=1.0, minDist=70, param1=80, param2=35, minRadius=self.min_radius, maxRadius=self.max_radius ) if detected is not None: 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): is_duplicate = True break if not is_duplicate: 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]]: height, width = edges.shape gradient_x = cv2.Sobel(blurred, cv2.CV_64F, 1, 0, ksize=3) gradient_y = cv2.Sobel(blurred, cv2.CV_64F, 0, 1, ksize=3) possible_radii = list(range(self.min_radius, self.max_radius + 1, 2)) # 更细粒 acc_maps = {r: np.zeros((height, width), dtype=np.float32) for r in possible_radii} 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)) # 投票阶段(利用梯方向指向圆心) 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.3, 0.3]: cx_o = int(x + r * (nx + offset * ny)) cy_o = int(y + r * (ny - offset * nx)) if 0 <= cx_o < width and 0 <= cy_o < height: acc_maps[r][cy_o, cx_o] += 0.4 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 > base_threshold * 0.8) ys, xs = np.where(local_maxima) for y, x in zip(ys, xs): vote_score = acc[y, x] total_points = max(24, int(2 * math.pi * r) // 5) 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: if edges[py, px] > 0: edge_points += 1 edge_ratio = edge_points / total_points final_score = 0.4 * min(1.0, vote_score / (2 * base_threshold)) + 0.6 * edge_ratio if final_score > 0.3 and edge_ratio > 0.2: 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 [] 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_r = (c1['r'] + c2['r']) / 2 return center_dist < min(avg_r * 0.7, 30) and abs(c1['r'] - c2['r']) < 15 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 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]): group.append(all_candidates[j]) used[j] = True groups.append(group) print(f"分组数量(去重后): {len(groups)}") final_selection = [] for group in groups: best = max(group, key=lambda c: c['score']*0.6 + c['edge_ratio']*0.4) final_selection.append((best['x'], best['y'], best['r'], best['score'])) unique_circles = [] for x, y, r, score in final_selection: is_dup = False for idx, (xj, yj, rj, sj) in enumerate(unique_circles): dist = math.sqrt((x - xj)**2 + (y - yj)**2) if dist < max(r, rj) * 0.6: is_dup = True if score > sj: unique_circles[idx] = (x, y, r, score) break if not is_dup: unique_circles.append((x, y, r, score)) unique_circles.sort(key=lambda c: -c[3]) if len(unique_circles) > 10: unique_circles = unique_circles[:10] verified_circles = [] for x, y, r, score in unique_circles: pts = max(30, int(2 * math.pi * r) // 4) count = 0 for i in range(pts): angle = 2 * math.pi * i / pts px = int(x + r * math.cos(angle)) py = int(y + r * math.sin(angle)) if 0 <= px < width and 0 <= py < height: if edges[py, px] > 0: count += 1 ratio = count / pts if ratio > 0.18: new_score = 0.7 * score + 0.3 * ratio verified_circles.append((x, y, r, new_score)) print(f"✅ 最终检测到的钱币数量: {len(verified_circles)}") return sorted(verified_circles, key=lambda c: -c[3]) @staticmethod def remove_duplicate_circles(circles, distance_thresh=40, radius_thresh=20): if not circles: return [] kept = [] used = [False] * len(circles) for i in range(len(circles)): if used[i]: continue kept.append(circles[i]) 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, 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): 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): 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 not image_files: print("⚠️ 未在目录中找到像文件") return [] detector = CoinDetector( canny_low_threshold=20, canny_high_threshold=50, hough_min_radius=25, hough_max_radius=100, gaussian_kernel_size=5 ) all_results = [] start_time = time.time() 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) elapsed = time.time() - start_time summary = f"\n🎉 处理完成!共处理 {len(all_results)} 张像,总计检测到 {total_coins} 个钱币,耗时 {elapsed:.2f}s" 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) ``` --- ### ✅ 关键修改说明: | 修改点 | 说明 | |------|------| | 🚫 移除 `cv2.Canny` | 使用自己实现的 `CannyEdgeDetector.detect()` | | ✅ 修复原Canny Bug | 原代码角处理错误、滞后连接逻辑混乱,已修正 | | ✅ 加入高斯模糊函数 | 手动实现 `gaussian_blur()` 避免依赖OpenCV预处理 | | ✅ 改进梯计算 | 使用中心差分替代边界判断 | | ✅ 优化NMS与滞后连接 | 使用标准8邻域连接方式 | | ✅ 保持代码1的强大后处理 | 多尺投票、智能去重、边缘验证机制全部保留 | --- ### ✅ 推荐参数调优建议(针对钱币检测不足问题): ```python detector = CoinDetector( canny_low_threshold=20, # 降低低阈值 → 更多弱边缘被保留 canny_high_threshold=50, # 适中高阈值 → 减少噪声干扰 hough_min_radius=25, hough_max_radius=100, gaussian_kernel_size=5 ) ``` --- ### ✅ 如何进一步提升检测率? 1. **像增强**:加入直方均衡化或CLAHE提升对比 2. **多尺检测**:对不同尺寸的钱币分别检测 3. **形态学闭操作**:连接断裂的边缘 4. **圆形拟合验证**:对每个候选圆做RANSAC拟合验证 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

21RGHLY

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值