【My Project】图像匹配平台二

图像匹配 平台二

SkySeraph Mar 29th 2011  HQU

Email:zgzhaobo@gmail.com    QQ:452728574

Latest Modified Date:Mar 29th 2011 HQU

一 工具:VS2010+OpenCV2.0

  语言:CPP

原理: 平台一采用的匹配算法相同,但是是采用OpenCV自带函数cvMatchTemplate实现的,相对来说较为简单

三 核心源码

说明:下面匹配源码里包含两种方法,其中是旋转的方法来源于网上,稍微复杂;后面一种直接用cvRectangle描绘出匹配出,比较简单

//匹配
void CTestDlgDlg::OnBnClickedTemplatematch()
{
		if(!image)
		return;
	if(!image_model)
		return;

    CvPoint pp1, pp2;
    CvPoint leftup,rightup,leftdown,rightdown; //定义匹配结果的4个点
	double minVal, maxVal;
	int resultW,resultH; //匹配结果的长宽
 
	image_result=cvCloneImage( image);
	image_rotation=cvCloneImage(image);
	//image_rotation=cvCreateImage(cvSize(image->height,image->width), IPL_DEPTH_32F, 1);
	model_rotation=cvCloneImage(image_model);
    //model_rotation=cvCreateImage(cvSize(image_model->height,image_model->width), IPL_DEPTH_32F, 1);

	///* 第一种方法	
	//第一次匹配,求左上角的点坐标
	resultW=image->width-image_model->width+1;
	resultH=image->height-image_model->height+1;
    match_result=cvCreateImage(cvSize(resultW,resultH), IPL_DEPTH_32F, 1);
	cvMatchTemplate(image, image_model,match_result, CV_TM_CCOEFF); 
	cvMinMaxLoc(match_result,  &minVal, &maxVal, &pp1, &pp2);
	leftup.x=pp2.x;
	leftup.y=pp2.y;

	//第二次匹配,求右上角的点坐标
	ImageRotation(image,image_rotation,90); //把图像逆时针旋转90度
	ImageRotation(image_model,model_rotation,90); //把模板逆时针旋转90度
	//cvNamedWindow ("旋转图像", 1);
    //cvShowImage ("旋转图像", image_rotation);
	//cvNamedWindow ("旋转模板", 1);
    //cvShowImage ("旋转模板", model_rotation);

	resultW=image_rotation->width-model_rotation->width+1;
	resultH=image_rotation->height-model_rotation->height+1;
    cvReleaseImage(&match_result);  //释放第一次匹配结果
	match_result=cvCreateImage(cvSize(resultW,resultH), IPL_DEPTH_32F, 1);
	cvMatchTemplate(image_rotation, model_rotation,match_result, CV_TM_CCOEFF); 
	cvMinMaxLoc(match_result,  &minVal, &maxVal, &pp1, &pp2);
	rightup.x=image_rotation->height-pp2.y;
	rightup.y=pp2.x;

	//第三次匹配,求右下角的点坐标
    
	ImageRotation(image,image_rotation,180); //把图像逆时针旋转90度
	ImageRotation(image_model,model_rotation,180); //把模板逆时针旋转90度

	resultW=image_rotation->width-model_rotation->width+1;
	resultH=image_rotation->height-model_rotation->height+1;
    cvReleaseImage(&match_result);  //释放第二次匹配结果
	match_result=cvCreateImage(cvSize(resultW,resultH), IPL_DEPTH_32F, 1);
	cvMatchTemplate(image_rotation, model_rotation,match_result, CV_TM_CCOEFF); 
	cvMinMaxLoc(match_result,  &minVal, &maxVal, &pp1, &pp2);
	rightdown.x=image_rotation->height-pp2.x;
	rightdown.y=image_rotation->height-pp2.y;

	//第四次匹配,求左下角坐标
	ImageRotation(image,image_rotation,270); //把图像逆时针旋转90度
	ImageRotation(image_model,model_rotation,270); //把模板逆时针旋转90度

	resultW=image_rotation->width-model_rotation->width+1;
	resultH=image_rotation->height-model_rotation->height+1;
    cvReleaseImage(&match_result);  //释放第二次匹配结果
	match_result=cvCreateImage(cvSize(resultW,resultH), IPL_DEPTH_32F, 1);
	cvMatchTemplate(image_rotation, model_rotation,match_result, CV_TM_CCOEFF); 
	cvMinMaxLoc(match_result,  &minVal, &maxVal, &pp1, &pp2);
	leftdown.x=pp2.y;
	leftdown.y=image_rotation->height-pp2.x;

	//cvCircle( image_rotation, rightup, 10,CV_RGB(255,0,0), 2 );
	cvLine(image_result,leftup,rightup,CV_RGB(255,0,0),2);
	cvLine(image_result,rightup,rightdown,CV_RGB(255,0,0),2);
	cvLine(image_result,rightdown,leftdown,CV_RGB(255,0,0),2);
	cvLine(image_result,leftdown,leftup,CV_RGB(255,0,0),2);
	//cvRectangle(image_result,leftup,rightdown,CV_RGB(255,0,0),2);
	DrawPicToHDC(image_result, IDC_PICSHOW3);
	//*/

	/*  第二种方法
	resultW=image->width-image_model->width+1;
	resultH=image->height-image_model->height+1;
    match_result=cvCreateImage(cvSize(resultW,resultH), IPL_DEPTH_32F, 1);
	cvMatchTemplate(image, image_model,match_result, CV_TM_CCOEFF); 
	cvMinMaxLoc(match_result,  &minVal, &maxVal, &pp1, &pp2);

    CvPoint pt1;
    CvPoint pt2;
    CvRect rect;
    rect=cvRect(pp2.x,pp2.y,image_model->width,image_model->height);//最佳的匹配区域
    pt1=cvPoint(rect.x,rect.y);
    pt2=cvPoint(rect.x+rect.width,rect.y+rect.height);
    cvRectangle( image,pt1, pt2, cvScalar(0,0,255),1, 8, 0 );
	
	DrawPicToHDC(image, IDC_PICSHOW3);
	*/
}

四 效果

2011032910520978.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、付费专栏及课程。

余额充值