【My Project】图像匹配平台一

图像匹配 平台一

SkySeraph Mar 29th 2011  HQU

Email:zgzhaobo@gmail.com    QQ:452728574

Latest Modified Date:Mar 29th 2011 HQU

一 工具:VS2010+OpenCV2.0

     语言:CPP

二 原理:采用的是归一化积相关灰度匹配

              原理可参考:http://wenku.baidu.com/view/48b0e93467ec102de2bd89eb.html

三 核心源码

执行

//----------------------------------------------
////模板匹配
void CTemplateMatchDlg::OnBnClickedTemplatematch()
{
	//  验证
	if(pTmpImg == NULL)
	{
		AfxMessageBox(_T("请先选择模板!"));
		return;
	}
	if(iTmpImgType!=1 && iTmpImgType!=-1 )
	{
		AfxMessageBox(_T("模板必须是灰度/二值图像,请进行灰度化转换!"));
		return;
	}
	//  验证模板
	int h = pTmpImg->height;
	int w = pTmpImg->width;
	if(w > IMAGE_WIDTH || h > IMAGE_HEIGHT)
	{
		AfxMessageBox(_T("模板尺寸大于源图像尺寸,请重新选择模板!"));
		return;
	}

	//  更改光标形状
	BeginWaitCursor();

	//  进行模板匹配
	TemplateMatch(pWorkImg,pTmpImg);

	//  显示
	ShowImage(pWorkImg, IDC_PICSHOW3);
	
	//  恢复光标形状
	EndWaitCursor();

}

匹配

//----------------------------------------------
////模板匹配:归一化积相关灰度匹配
/*----------------------------------------------
/----------------------------------------------*/
void CTemplateMatchDlg::TemplateMatch(IplImage* img, IplImage* tmpImg)
{
	//循环变量
	int i, j, m, n;

	double dSumT; //模板元素的平方和
	double dSumS; //图像子区域元素的平方和
	double dSumST; //图像子区域和模板的点积	

	//响应值/相似性测度
	double R;

	//记录当前的最大响应
	double MaxR;

	//最大响应出现位置
	int nMaxX = 0;
	int nMaxY = 0;

	//源图像的高、宽
	int nHeight = img->width;
	int nWidth = img->height;
	int nStep = img->widthStep;
	uchar* cData;
	cData = (uchar*)img->imageData;
	//模板的高、宽
	int nTplHeight = tmpImg->width;
	int nTplWidth = tmpImg->height;
	int nTplStep = tmpImg->widthStep;
	uchar* cTplData;
	cTplData = (uchar*)tmpImg->imageData;

	//计算 dSumT:模板元素的平方和
	dSumT = 0;
	for (m = 0;m < nTplHeight ;m++)
	{
		for(n = 0;n < nTplWidth ;n++)
		{
			// 模板图像第m行,第n个象素的灰度值
			int nGray = cTplData[m*nTplStep+n];
			dSumT += (double)nGray*nGray;
		}
	}

	//找到图像中最大响应/最大相似性的出现位置
	MaxR = 0;
	for (i = 0;i < nHeight - nTplHeight +1 ;i++)
	{
		for(j = 0;j < nWidth - nTplWidth + 1;j++)
		{
			dSumST = 0;
			dSumS = 0;

			for (m = 0;m < nTplHeight ;m++)
			{
				for(n = 0;n < nTplWidth ;n++)
				{
					// 原图像第i+m行,第j+n列象素的灰度值
					int nGraySrc  = cData[(i+m)*nStep+(j+n)];

					// 模板图像第m行,第n个象素的灰度值
					int nGrayTpl = cTplData[m*nTplStep+ m];

					dSumS += (double)nGraySrc*nGraySrc;
					dSumST += (double)nGraySrc*nGrayTpl;
				}
			}

			R = dSumST / ( sqrt(dSumS)*sqrt(dSumT));//计算相关响应/相似性

			//与最大相似性比较
			if (R > MaxR)
			{
				MaxR = R;
				nMaxX = j;
				nMaxY = i;
			}
		}
	}

	//清空目标图像/对目标图像的像素进行赋值	
	if(img)
		for (m = 0;m < nHeight ;m++)
		{
			for(n = 0;n < nWidth ;n++)
			{
				cData[m*nStep+n] = cData[m*nStep+n]/2;  //像素值减半
			}
		}
		//cvZero(img); 

	//int nStep2 = img->widthStep;
	//uchar* cData2;
	//cData2 = (uchar*)img->imageData;


	//将找到的最佳匹配区域复制到目标图像
	for (m = 0;m < nTplHeight ;m++)
	{
		for(n = 0;n < nTplWidth ;n++)
		{
			int nGray = cTplData[m*nTplStep+n];
			//cData[m*nStep+n] = 0;
			cData[(nMaxY+m)*nStep+(nMaxX+n)] = RGB(nGray, nGray, nGray);
		}
	}
}

四 效果

界面

2011032910144623.png

处理结果

2011032910150494.png

 

   Author:         SKySeraph

Email/GTalk: zgzhaobo@gmail.com    QQ:452728574

From:         http://www.cnblogs.com/skyseraph/

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,请尊重作者的

import cv2 import os import time import numpy as np from glob import glob from PIL import Image, ImageDraw, ImageFont import math # ------------------------------ # 配置参数 - 集中管理可配置项 # ------------------------------ class Config: """配置参数类,集中管理所有可调整的参数""" # 路径配置 SAMPLES_DIR = r"E:\My Project\2409-Algorithm\python software\Test-photos\all" TEST_IMAGE_PATH = r"E:\My Project\2409-Algorithm\python software\Test-photos\A08.jpg" # ORB特征检测器参数 ORB_NFEATURES = 1000 # 最大特征点数量 ORB_EDGE_THRESHOLD = 31 # 边缘阈值,增大此值会减少边缘附近的特征点 ORB_SCALE_FACTOR = 1.2 # 金字塔缩放因子,接近1会生成更多特征点 ORB_NLEVELS = 8 # 金字塔层数 # 特征保留参数 KEEP_FEATURES = 500 # 保留的特征点数量 # 匹配参数 TOP_MATCHES = 100 # 保留的最佳匹配数量 RANSAC_THRESHOLD = 5.0 # RANSAC算法阈值 MIN_MATCHES_FOR_RANSAC = 10 # 应用RANSAC所需的最小匹配点数 # 缺陷检测参数 DIFF_THRESHOLD = 30 # 差分图像二值化阈值 MIN_DEFECT_AREA = 300 # 最小缺陷面积(像素) MORPH_ITERATIONS = 2 # 形态学运算迭代次数 # 显示参数 DISPLAY_WIDTH = 600 # 中间结果显示宽度 MATCH_DISPLAY_WIDTH = 1200 # 匹配结果显示宽度 # ------------------------------ # 工具函数 - 图像处理辅助功能 # ------------------------------ def cv2_add_chinese_text(img, text, position, text_size=30, text_color=(255, 0, 0)): """ 在OpenCV图像上添加中文文字 参数: img: OpenCV图像(BGR格式) text: 要添加的中文文字 position: 文字位置(x, y) text_size: 文字大小 text_color: 文字颜色(BGR格式) 返回: 添加文字后的图像 """ # 转换为PIL图像(RGB格式) img_pil = Image.fromarray(cv2.cvtColor(img, cv2.COLOR_BGR2RGB)) # 创建绘制对象 draw = ImageDraw.Draw(img_pil) # 加载支持中文的字体 font_paths = [ "C:/Windows/Fonts/simhei.ttf", # Windows系统黑体字体 "/System/Library/Fonts/PingFang.ttc", # macOS系统苹方字体 "/usr/share/fonts/opentype/noto/NotoSansCJK-Regular.ttc" # Linux系统 ] # 尝试加载字体 font = None for path in font_paths: try: if os.path.exists(path): font = ImageFont.truetype(path, text_size, encoding="utf-8") break except IOError: continue # 如果所有指定字体都加载失败,使用默认字体 if font is None: font = ImageFont.load_default() print("警告:无法加载指定字体,使用默认字体可能导致中文显示异常") # 绘制文字,注意颜色需要转换为RGB格式 rgb_color = (text_color[2], text_color[1], text_color[0]) draw.text(position, text, font=font, fill=rgb_color) # 转换回OpenCV格式(BGR) return cv2.cvtColor(np.array(img_pil), cv2.COLOR_RGB2BGR) def prepare_image_for_display(img, title, width=Config.DISPLAY_WIDTH): """ 调整图像大小并添加标题,用于中间结果显示 参数: img: 输入图像 title: 要添加的标题 width: 调整后的宽度 返回: 处理后的图像 """ # 调整大小 height, original_width = img.shape[:2] scale = width / original_width new_height = int(height * scale) # 如果是单通道图像,转换为三通道以便添加彩色文字 if len(img.shape) == 2: resized_img = cv2.cvtColor(cv2.resize(img, (width, new_height)), cv2.COLOR_GRAY2BGR) else: resized_img = cv2.resize(img, (width, new_height)) # 添加标题 return cv2_add_chinese_text(resized_img, title, (30, 30), 25, (0, 255, 0)) # ------------------------------ # 特征与变换处理函数 # ------------------------------ def extract_transform_params(homography_matrix): """ 从单应矩阵中提取旋转角度(度)和平移量(dx, dy) 单应矩阵H描述了两个平面之间的映射关系,形式如下: [h00 h01 h02] [h10 h11 h12] [h20 h21 h22] 我们可以从中提取出旋转、缩放和平移分量 参数: homography_matrix: 3x3单应矩阵 返回: (dx, dy, rotate_degrees): 平移量和旋转角度 """ # 单应矩阵的前两行前三列包含了旋转、缩放和平移信息 H = homography_matrix[:2, :3] # 提取旋转和缩放部分(前两列) R = H[:, :2] # 计算缩放因子(行列式的平方根) s = np.sqrt(np.linalg.det(R)) # 归化旋转矩阵(去除缩放影响) R_normalized = R / s # 计算旋转角度(弧度转换为度) # 通过旋转矩阵元素计算角度: cosθ = R[0,0], sinθ = R[1,0] rotate_radians = math.atan2(R_normalized[1, 0], R_normalized[0, 0]) rotate_degrees = math.degrees(rotate_radians) # 提取平移分量(考虑缩放) dx = H[0, 2] / s dy = H[1, 2] / s return dx, dy, rotate_degrees def extract_image_features(img, orb_detector, max_keep=Config.KEEP_FEATURES): """ 提取图像的ORB特征点和描述符 参数: img: 输入图像(BGR格式) orb_detector: ORB特征检测器 max_keep: 保留的最大特征点数量 返回: (keypoints, descriptors): 特征点和描述符 """ # 转换为灰度图 gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # 检测特征点并计算描述符 kp, des = orb_detector.detectAndCompute(gray, None) # 过滤并保留指定数量的特征点 if len(kp) > max_keep: kp = kp[:max_keep] des = des[:max_keep, :] return kp, des # ------------------------------ # 匹配与缺陷检测函数 # ------------------------------ def match_images(test_des, sample_des, matcher): """ 匹配测试图像和样本图像的特征描述符 参数: test_des: 测试图像的特征描述符 sample_des: 样本图像的特征描述符 matcher: 特征匹配器 返回: (matches, match_time): 匹配结果和耗时(毫秒) """ start_time = time.time() # 进行特征匹配 matches = matcher.match(test_des, sample_des) # 按距离排序(距离越小匹配越好) matches = sorted(matches, key=lambda x: x.distance) # 计算耗时 match_time = (time.time() - start_time) * 1000 # 转换为毫秒 return matches, match_time def detect_defects(test_img, sample_img, homography_matrix): """ 检测测试图像与样本图像之间的差异(缺陷) 参数: test_img: 测试图像 sample_img: 样本图像 homography_matrix: 单应矩阵(测试图像到样本图像的变换) 返回: 显示窗口名称列表 """ # 获取图像尺寸 s_h, s_w = sample_img.shape[:2] t_h, t_w = test_img.shape[:2] # 计算单应矩阵的逆矩阵,将样本图像变换到测试图像的视角 # 这步是为了使两张图像在同坐标系下对齐,便于比较 h_inv = np.linalg.inv(homography_matrix) aligned_sample = cv2.warpPerspective(sample_img, h_inv, (t_w, t_h)) # 转换为灰度图,便于计算差异 test_gray = cv2.cvtColor(test_img, cv2.COLOR_BGR2GRAY) aligned_sample_gray = cv2.cvtColor(aligned_sample, cv2.COLOR_BGR2GRAY) # 显示对齐结果对比 test_display = prepare_image_for_display(test_img, "原始测试图像(不变)") aligned_sample_display = prepare_image_for_display(aligned_sample, "变换后的样本图像(对齐到测试图)") aligned_combined = np.hstack((test_display, aligned_sample_display)) cv2.imshow("对齐结果对比", aligned_combined) # 计算图像差分:原始测试图与变换后的样本图的差异 # 差异越大,表示该区域可能存在缺陷 diff = cv2.absdiff(test_gray, aligned_sample_gray) # 应用固定阈值,将差异区域转换为二值图像 _, thresh = cv2.threshold(diff, Config.DIFF_THRESHOLD, 255, cv2.THRESH_BINARY) # 创建结构元素,用于形态学处理 kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5)) # 开运算:先腐蚀后膨胀,用于去除噪声和小面积区域 opening = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=Config.MORPH_ITERATIONS) # 查找所有轮廓 contours, _ = cv2.findContours(opening, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # 过滤面积大于阈值的轮廓(认为是真实缺陷) filtered_contours = [cnt for cnt in contours if cv2.contourArea(cnt) > Config.MIN_DEFECT_AREA] print(f"检测到 {len(contours)} 个轮廓,过滤后保留 {len(filtered_contours)} 个面积大于{Config.MIN_DEFECT_AREA}的轮廓") # 创建形态学处理图像的彩色副本,用于绘制所有缺陷 opening_color = cv2.cvtColor(opening, cv2.COLOR_GRAY2BGR) # 线宽设置 contour_thickness = 3 # 在形态学处理图像上绘制所有缺陷 for cnt in contours: cv2.drawContours(opening_color, [cnt], -1, (0, 0, 255), contour_thickness) # 在原始测试图像上只绘制面积达标的缺陷 result_img = test_img.copy() if len(filtered_contours) > 0: for cnt in filtered_contours: cv2.drawContours(result_img, [cnt], -1, (0, 0, 255), contour_thickness) # 准备所有要显示的中间结果 display_images = [ (diff, "差分图像(原始测试图 - 变换后样本图)", "Difference Image"), (thresh, f"二值化图像 (阈值={Config.DIFF_THRESHOLD})", "Thresholded Image"), (opening, f"形态学处理后 (开运算x{Config.MORPH_ITERATIONS})", "After Morphology"), (opening_color, "形态学图像上的所有缺陷", "All Defects on Morphology Image"), (result_img, f"原始测试图上的缺陷检测结果 (> {Config.MIN_DEFECT_AREA}像素)", "Defects on Original Image") ] # 显示所有中间结果图像 window_names = ["对齐结果对比"] for img, chinese_title, english_title in display_images: display_img = prepare_image_for_display(img, chinese_title) cv2.imshow(english_title, display_img) window_names.append(english_title) return window_names def show_matching_result(test_img, sample_path, test_kp, sample_kp, matches, mask=None, method_name="", window_name="Matching Result"): """ 显示图像匹配结果,包括特征点和匹配线 参数: test_img: 测试图像 sample_path: 样本图像路径 test_kp: 测试图像特征点 sample_kp: 样本图像特征点 matches: 匹配结果 mask: 用于标记内点的掩码(RANSAC后) method_name: 方法名称(用于标题) window_name: 窗口名称 返回: 窗口名称 """ # 读取样本图片 sample_img = cv2.imread(sample_path) if sample_img is None: print(f"无法读取样本图片: {sample_path}") return None # 复制原图用于标记特征点 test_img_marked = test_img.copy() sample_img_marked = sample_img.copy() # 标记特征点(用红色圆圈) for kp in test_kp: cv2.circle(test_img_marked, (int(kp.pt[0]), int(kp.pt[1])), 5, (0, 0, 255), 2) for kp in sample_kp: cv2.circle(sample_img_marked, (int(kp.pt[0]), int(kp.pt[1])), 5, (0, 0, 255), 2) # 绘制匹配线 if mask is not None: result_img = cv2.drawMatches( test_img_marked, test_kp, sample_img_marked, sample_kp, matches, None, matchColor=(0, 255, 0), # 内点匹配线为绿色 singlePointColor=(0, 0, 255), # 特征点为红色 matchesMask=mask, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS ) else: result_img = cv2.drawMatches( test_img_marked, test_kp, sample_img_marked, sample_kp, matches, None, matchColor=(0, 255, 0), # 匹配线为绿色 singlePointColor=(0, 0, 255), # 特征点为红色 matchesMask=None, flags=cv2.DrawMatchesFlags_NOT_DRAW_SINGLE_POINTS ) # 调整图片大小,保持比例 height, width = result_img.shape[:2] new_width = Config.MATCH_DISPLAY_WIDTH new_height = int((new_width / width) * height) resized_img = cv2.resize(result_img, (new_width, new_height)) # 添加标题 if method_name: resized_img = cv2_add_chinese_text(resized_img, method_name, (50, 50), 30, (0, 0, 255)) # 显示图片 cv2.imshow(window_name, resized_img) return window_name # ------------------------------ # 主函数 - 程序入口 # ------------------------------ def main(): """主函数,协调各个模块完成图像匹配和缺陷检测""" # 1. 初始化与参数校验 print("===== 初始化 =====") # 检查路径是否存在 if not os.path.exists(Config.SAMPLES_DIR): print(f"错误:样本目录不存在 - {Config.SAMPLES_DIR}") return if not os.path.exists(Config.TEST_IMAGE_PATH): print(f"错误:测试图片不存在 - {Config.TEST_IMAGE_PATH}") return # 获取所有样本图片路径 sample_paths = glob(os.path.join(Config.SAMPLES_DIR, "*.[jJ][pP][gG]")) + \ glob(os.path.join(Config.SAMPLES_DIR, "*.[pP][nN][gG]")) + \ glob(os.path.join(Config.SAMPLES_DIR, "*.[bB][mM][pP]")) if not sample_paths: print(f"错误:样本目录中没有找到图片 - {Config.SAMPLES_DIR}") return print(f"找到 {len(sample_paths)} 个样本图片") print(f"测试图片路径:{Config.TEST_IMAGE_PATH}") print("----------------------------------------") # 2. 加载图像并提取特征 print("\n===== 特征提取 =====") # 读取测试图片 test_img = cv2.imread(Config.TEST_IMAGE_PATH) if test_img is None: print(f"错误:无法读取测试图片 - {Config.TEST_IMAGE_PATH}") return test_img_gray = cv2.cvtColor(test_img, cv2.COLOR_BGR2GRAY) t_h, t_w = test_img_gray.shape[:2] print(f"测试图片尺寸:{t_w}x{t_h}") # 初始化ORB特征检测器,使用配置参数 orb = cv2.ORB_create( nfeatures=Config.ORB_NFEATURES, edgeThreshold=Config.ORB_EDGE_THRESHOLD, scaleFactor=Config.ORB_SCALE_FACTOR, nlevels=Config.ORB_NLEVELS ) # 提取测试图片特征 test_kp, test_des = extract_image_features(test_img, orb, Config.KEEP_FEATURES) print(f"测试图片提取到 {len(test_kp)} 个ORB特征点") # 预加载并提取所有样本图片的特征 print("正在加载样本图片并提取特征...") sample_features = [] # (kp, des, width, height) valid_samples = [] # 有效的样本路径 for path in sample_paths: try: img = cv2.imread(path) if img is None: continue kp, des = extract_image_features(img, orb, Config.KEEP_FEATURES) sample_features.append((kp, des, img.shape[1], img.shape[0])) valid_samples.append(path) except Exception as e: print(f"加载样本 {path} 失败: {e}") if not valid_samples: print("错误:没有有效样本图片可用于匹配") return print(f"成功加载 {len(valid_samples)} 个有效样本图片") print("----------------------------------------") # 3. 图像匹配处理 print("\n===== 图像匹配 =====") # 初始化BF匹配器 # 使用HAMMING距离,适用于ORB等二进制描述符 bf = cv2.BFMatcher(cv2.NORM_HAMMING, crossCheck=True) # 存储匹配结果:(路径, 匹配点数, 平均距离, 耗时, 匹配对象) bf_results = [] bf_total_time = 0 print("开始比较图片...") # 遍历所有样本进行匹配 for i, (sample_path, (sample_kp, sample_des, _, _)) in enumerate(zip(valid_samples, sample_features)): # 进度提示 if (i + 1) % 10 == 0: print(f"已处理 {i + 1}/{len(valid_samples)} 个样本") # 进行特征匹配 matches, match_time = match_images(test_des, sample_des, bf) bf_total_time += match_time # 取前N个最佳匹配 top_matches = matches[:Config.TOP_MATCHES] match_count = len(top_matches) avg_distance = sum(m.distance for m in top_matches) / match_count if match_count > 0 else 0 bf_results.append((sample_path, match_count, avg_distance, match_time, top_matches)) print(f"样本 {i + 1}/{len(valid_samples)}: {os.path.basename(sample_path)} - " f"匹配点数={match_count}, 平均距离={avg_distance:.2f}, 耗时={match_time:.2f}ms") # 找出最佳匹配(排除匹配点数为0的样本) bf_valid_results = [res for res in bf_results if res[1] > 0] if not bf_valid_results: print("警告:BF暴力匹配未找到任何有效匹配") return # 选择平均距离最小的作为最佳匹配 bf_best = min(bf_valid_results, key=lambda x: x[2]) # 4. 结果分析与可视化 print("\n===== 结果分析 =====") # 获取最佳匹配的相关信息 bf_best_idx = bf_results.index(bf_best) bf_best_matches = bf_results[bf_best_idx][4] best_bf_sample_idx = valid_samples.index(bf_best[0]) best_bf_sample_kp, _, _, _ = sample_features[best_bf_sample_idx] best_sample_img = cv2.imread(bf_best[0]) # 显示原始匹配结果 window_names = [] bf_window_name = "BF Matching - Original" window_names.append( show_matching_result(test_img, bf_best[0], test_kp, best_bf_sample_kp, bf_best_matches, None, "原始匹配结果", bf_window_name)) # 应用RANSAC算法剔除异常匹配点 if len(bf_best_matches) >= Config.MIN_MATCHES_FOR_RANSAC: # 提取匹配点的坐标 src_pts = np.float32([test_kp[m.queryIdx].pt for m in bf_best_matches]).reshape(-1, 1, 2) dst_pts = np.float32([best_bf_sample_kp[m.trainIdx].pt for m in bf_best_matches]).reshape(-1, 1, 2) # 使用RANSAC寻找单应矩阵并获取内点掩码 # 单应矩阵描述了两个平面之间的映射关系 homography_matrix, mask = cv2.findHomography(src_pts, dst_pts, cv2.RANSAC, Config.RANSAC_THRESHOLD) mask = mask.ravel().tolist() # 提取变换参数 dx, dy, rotate = extract_transform_params(homography_matrix) # 打印变换参数 print("\n变换参数:") print(f"dx (水平平移): {dx:.2f} 像素") print(f"dy (垂直平移): {dy:.2f} 像素") print(f"rotate (旋转角度): {rotate:.2f} 度") print(f"单应矩阵:\n{homography_matrix}") # 统计内点数量 inliers_count = sum(mask) print(f"\nRANSAC过滤结果: 原始匹配点 {len(bf_best_matches)}, " f"保留内点 {inliers_count}, 剔除异常点 {len(bf_best_matches) - inliers_count}") # 显示RANSAC过滤后的匹配结果 ransac_window_name = "BF Matching - After RANSAC" window_names.append( show_matching_result(test_img, bf_best[0], test_kp, best_bf_sample_kp, bf_best_matches, mask, f"RANSAC过滤后 (保留{inliers_count}个匹配点)", ransac_window_name)) # 显示缺陷检测结果及中间过程 defect_window_names = detect_defects(test_img, best_sample_img, homography_matrix) window_names.extend(defect_window_names) else: print(f"\n匹配点数量不足({len(bf_best_matches)} < {Config.MIN_MATCHES_FOR_RANSAC}),无法应用RANSAC过滤") # 短暂延迟确保窗口能显示出来 if window_names: print("匹配结果窗口已显示...") cv2.waitKey(1) # 非阻塞等待,确保窗口显示 # 输出匹配结果摘要 print("\n==================================================") print("匹配结果摘要") print("==================================================\n") print("BF暴力匹配:") print(f" 最相似样本: {os.path.basename(bf_best[0])}") print(f" 最佳匹配点数: {bf_best[1]}") print(f" 最佳平均距离: {bf_best[2]:.2f}(越小越好)") print(f" 平均耗时: {bf_total_time / len(valid_samples):.2f} ms") print(f" 总耗时: {bf_total_time:.2f} ms\n") print("==================================================") print("所有比较完成") # 保持窗口显示,直到用户手动关闭 if window_names: print("请查看匹配结果窗口,关闭窗口以结束程序...") cv2.waitKey(0) # 等待用户关闭窗口 cv2.destroyAllWindows() if __name__ == "__main__": main() 检查代码逻辑,合理性,并提出修改意见
最新发布
09-10
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值