diff --git a/readme.md b/readme.md index d1a75de..a266ce2 100644 --- a/readme.md +++ b/readme.md @@ -5,7 +5,7 @@ conda env create -f environment.yaml bim定位锁定bim区域xx 画出高清摄像头应该在的区域,是固定的比例,适当的等比例放大一些。xx 获取高清摄像头识别出来的ROI坐标数据 -高清摄像头的ROI坐标转换为bim下的坐标 +高清摄像头的ROI坐标转换为bim下的坐标xxx 遍历范围内的完整的矩形,对比高清摄像头ROI矩形的中心点是否在 bim中矩形的内部 如果在内部说明是同一个编号的预埋件。 diff --git a/search.py b/search.py index 8ebdc06..9fcffdb 100644 --- a/search.py +++ b/search.py @@ -1,8 +1,8 @@ import cv2 -from utils import filter_rectangle, get_hd_cam_rect +from utils import filter_rectangle, get_hd_cam_rect, get_hd_roi_from_txt -print(cv2.__version__) # 4.9.0 +print(cv2.__version__) # 4.9.0 import json import math import copy @@ -11,8 +11,10 @@ import time from scipy.spatial import distance from scipy.optimize import linear_sum_assignment + def get_params(num_param, start, end): - return copy.deepcopy(num_param[start:end+1]) + return copy.deepcopy(num_param[start:end + 1]) + def parmas_to_num(text_param): for item in text_param: @@ -23,6 +25,7 @@ def parmas_to_num(text_param): item['h'] = int(item['h']) return text_param + def parmas_to_text(num_param): for item in num_param: item['center'] = str(item['center']) @@ -41,28 +44,33 @@ def parmas_to_text(num_param): item['y_center'] = str(item['y_center']) return num_param + def sort_params(params): sorted_params = sorted(params, key=lambda item: (item['center'], item['x'])) return sorted_params + def print_params(sort_params): for param in sort_params: print(param["center"], param["x"], param["w"], param["h"]) + def print_path(search_path): for path in search_path: print(path[0], path[1]) + def search_path(sort_params): searchPath = [] for i in range(len(sort_params) - 1): - (r, theta) = cartesian_to_polar(sort_params[i]["x"], sort_params[i]["center"], - sort_params[i + 1]["x"], sort_params[i + 1]["center"]) + (r, theta) = cartesian_to_polar(sort_params[i]["x"], sort_params[i]["center"], + sort_params[i + 1]["x"], sort_params[i + 1]["center"]) searchPath.append([r, theta]) return searchPath + def normalize_params_and_path(sort_params, search_path, index=0): - base = sort_params[index]["h"] + base = sort_params[index]["h"] for param in sort_params: param['center'] /= base param['x'] /= base @@ -75,25 +83,29 @@ def normalize_params_and_path(sort_params, search_path, index=0): # path[1] /= base return sort_params, search_path + def read_from_json(file_path): with open(file_path, 'r') as f: loaded_array = json.load(f) return loaded_array + def cartesian_to_polar(x1, y1, x2, y2): dx = x2 - x1 dy = y2 - y1 - r = math.sqrt(dx**2 + dy**2) + r = math.sqrt(dx ** 2 + dy ** 2) theta = math.atan2(dy, dx) return r, theta + def calculate_second_point(x1, y1, r, theta_radians): # theta_radians = math.radians(theta_degrees) x2 = x1 + r * math.cos(theta_radians) y2 = y1 + r * math.sin(theta_radians) - + return x2, y2 + def cal_c1c2c3c4(param, heigt): ''' 按照上左、上右、下右、下左的顺时针顺序. @@ -115,14 +127,15 @@ def cal_c1c2c3c4(param, heigt): param['y_center'] = int((param['y1'] + param['y3']) / 2) return param + def gen_im_from_params(params, type="lines"): # 依据bim数据生成bim图 # type: # line points ## 确定整个bim图的长度和宽度边界 - max_y = -999999 # y坐标最大值 - max_y_idx = -1 # y坐标最大值的索引 - max_x = -999999 # x坐标最大值 - max_x_idx = -1 # x坐标最大值的索引 + max_y = -999999 # y坐标最大值 + max_y_idx = -1 # y坐标最大值的索引 + max_x = -999999 # x坐标最大值 + max_x_idx = -1 # x坐标最大值的索引 # 遍历,找到矩形x坐标最大值和矩形y坐标最大值 for i, param in enumerate(params): @@ -137,7 +150,7 @@ def gen_im_from_params(params, type="lines"): max_y = param["center"] + param["h"] / 2 max_y_idx = i - padding_value = 1000 # 内边距,避免整个bim图片贴着边缘展示 + padding_value = 1000 # 内边距,避免整个bim图片贴着边缘展示 bim_width = int(max_x) + padding_value print(f"[bim_width] ====== [{bim_width}]") bim_height = int(max_y) + padding_value @@ -149,16 +162,16 @@ def gen_im_from_params(params, type="lines"): cal_c1c2c3c4(param, bim_height) if type == "lines": pts = np.asarray([[param['x1'], param['y1']], - [param['x2'], param['y2']], - [param['x3'], param['y3']], - [param['x4'], param['y4']]]) - cv2.polylines(im, [pts], True, (255,255,0), 8) + [param['x2'], param['y2']], + [param['x3'], param['y3']], + [param['x4'], param['y4']]]) + cv2.polylines(im, [pts], True, (255, 255, 0), 8) elif type == "points": cv2.circle(im, (param['x1'], param['y1']), 1, 255, 20) cv2.circle(im, (param['x2'], param['y2']), 1, 255, 20) cv2.circle(im, (param['x3'], param['y3']), 1, 255, 20) cv2.circle(im, (param['x4'], param['y4']), 1, 255, 20) - cv2.circle(im, (param['x'], int((param['y3'] + param['y2']) / 2 )), 1, (255,0,0), 8) + cv2.circle(im, (param['x'], int((param['y3'] + param['y2']) / 2)), 1, (255, 0, 0), 8) return im @@ -169,11 +182,13 @@ def gen_im_from_params(params, type="lines"): False 不在 如果没有提供区域,不做判断,返回True """ + + def is_inside_roi(point, roi_w, roi_h): if (roi_w != None and roi_h != None): x = point[0] y = point[1] - if (x <= 0 or y <= 0 or y >= roi_h or x >= roi_w ): + if (x <= 0 or y <= 0 or y >= roi_h or x >= roi_w): # 不在区域内部 return False else: @@ -184,7 +199,7 @@ def is_inside_roi(point, roi_w, roi_h): return True -def gen_points_from_params(params,roi_w=None,roi_h=None): +def gen_points_from_params(params, roi_w=None, roi_h=None): # 依据bim数据生成bim图 # type line points ## 计算bim图长和宽 @@ -207,20 +222,21 @@ def gen_points_from_params(params,roi_w=None,roi_h=None): if (roi_w == None and roi_h == None): cal_c1c2c3c4(param, bim_height) # 过滤点,把在roi区域之外的点全部过滤掉. - if(is_inside_roi([param['x1'], param['y1']], roi_w, roi_h)): + if (is_inside_roi([param['x1'], param['y1']], roi_w, roi_h)): points.append([param['x1'], param['y1'], i]) - if(is_inside_roi([param['x2'], param['y2']], roi_w, roi_h)): + if (is_inside_roi([param['x2'], param['y2']], roi_w, roi_h)): points.append([param['x2'], param['y2'], i]) - if(is_inside_roi([param['x3'], param['y3']], roi_w, roi_h)): + if (is_inside_roi([param['x3'], param['y3']], roi_w, roi_h)): points.append([param['x3'], param['y3'], i]) - if(is_inside_roi([param['x4'], param['y4']], roi_w, roi_h)): + if (is_inside_roi([param['x4'], param['y4']], roi_w, roi_h)): points.append([param['x4'], param['y4'], i]) - if(is_inside_roi([param['x_center'], param['y_center']], roi_w, roi_h)): + if (is_inside_roi([param['x_center'], param['y_center']], roi_w, roi_h)): points.append([param['x_center'], param['y_center'], i]) if (roi_w != None and roi_h != None): print(f"[经区域过滤之后的点数一共为] ====== [{len(points)}]") return points + def topological_similarity(adj1, adj2): """ 计算两个拓扑结构的相似度 @@ -229,6 +245,7 @@ def topological_similarity(adj1, adj2): similarity = np.sum(adj1 == adj2) / (adj1.shape[0] * adj2.shape[1]) return similarity + def find_topological_matches(points1, adj1, points2, adj2, threshold=0.8): """ 基于拓扑结构寻找匹配点集 @@ -244,8 +261,10 @@ def find_topological_matches(points1, adj1, points2, adj2, threshold=0.8): matches.sort(key=lambda x: x[2], reverse=True) return matches + from sklearn.linear_model import RANSACRegressor + def ransac_shape_matching(points, reference_points): model_ransac = RANSACRegressor(random_state=42) try: @@ -253,12 +272,13 @@ def ransac_shape_matching(points, reference_points): except ValueError as e: print("Error fitting the model:", e) return [] - + inlier_mask = model_ransac.inlier_mask_ best_match_subset = points[inlier_mask] return best_match_subset + def polar_to_cartesian(polar_points): r = polar_points[:, 0] theta = polar_points[:, 1] @@ -266,6 +286,7 @@ def polar_to_cartesian(polar_points): y = r * np.sin(theta) return np.vstack((x, y)).T + def compute_shape_context(points, nbins_r=5, nbins_theta=12): n = points.shape[0] shape_contexts = [] @@ -281,14 +302,16 @@ def compute_shape_context(points, nbins_r=5, nbins_theta=12): H, _, _ = np.histogram2d(thetas, rs, bins=[theta_edges, r_edges]) H /= np.sum(H) + 1e-8 # 归一化 shape_contexts.append(H.flatten()) - + return np.array(shape_contexts) + def match_shapes(sc1, sc2): cost_matrix = distance.cdist(sc1, sc2, metric='sqeuclidean') row_ind, col_ind = linear_sum_assignment(cost_matrix) return cost_matrix[row_ind, col_ind].sum() + def _sobel(image): ''' _sobel @@ -301,9 +324,10 @@ def _sobel(image): _sobelx = np.uint8(np.absolute(_sobelx)) _sobely = np.uint8(np.absolute(_sobely)) - _sobelcombine = cv2.bitwise_or(_sobelx,_sobely) + _sobelcombine = cv2.bitwise_or(_sobelx, _sobely) return _sobelcombine + def _findContours(image): ''' _findContours @@ -312,6 +336,32 @@ def _findContours(image): contours, _ = cv2.findContours(image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) return sorted(contours, key=cv2.contourArea, reverse=True) +def bim_compare_to_hd_roi(hd_roi_list,hd_img_width,hd_img_height,hd_cam_w_bim,hd_cam_h_bim): + """ + 预埋件号码匹配 + + 将高清摄像头照片的ROI和实际BIM图中的号码匹配上 + + 参数: + hd_roi_list : 高清摄像头的ROI列表,每个ROI表示一个可能的预埋件区域 + 1.每个第二层子数组中的坐标顺序为左上、右上、右下、左下(顺时针) + 2.是以左上角为原点的常规的图片坐标系。 + 格式如下。 + [ + [[x1, y1], [x2, y2], [x3, y3], [x4, y4]], + [[x1, y1], [x2, y2], [x3, y3], [x4, y4]], + ] + hd_img_width (int): 高清相机拍摄图片原始宽度 + hd_img_height (int): 高清相机拍摄图片原始高度 + hd_cam_w_bim (int): 高清相机矩形示意框在bim图中的宽度 + hd_cam_h_bim (int): 高清相机矩形示意框在bim图中的高度 + + 返回: + + """ + + + if __name__ == "__main__": # 读取并处理数据 data_bim = {} @@ -328,7 +378,7 @@ if __name__ == "__main__": wide_cam_left_cut_rate = 0.1 wide_cam_right_cut_rate = 0.1 # 高清相机的视场矩形在广角相机里面的坐标。cv2下的图片坐标系,以左上角为坐标原点 - hd_cam_x, hd_cam_y,hd_cam_w, hd_cam_h, = get_hd_cam_rect(wide_cam_left_cut_rate) + hd_cam_x, hd_cam_y, hd_cam_w, hd_cam_h, = get_hd_cam_rect(wide_cam_left_cut_rate) # 创建测试子集 # sub_im = cv2.imread("wide_image.png") @@ -348,7 +398,8 @@ if __name__ == "__main__": # 过滤矩形 # cnts 过滤之后的矩形 # sub_im 裁剪之后的图像 - cnts,sub_im = filter_rectangle("data_sub/test_1/wide_image.png", original_rectangle,wide_cam_left_cut_rate,wide_cam_right_cut_rate) + cnts, sub_im = filter_rectangle("data_sub/test_1/wide_image.png", original_rectangle, wide_cam_left_cut_rate, + wide_cam_right_cut_rate) sub_zero = np.zeros_like(sub_im) for contour in cnts: # x, y, w, h = cv2.boundingRect(contour) @@ -358,7 +409,7 @@ if __name__ == "__main__": h = contour["height"] # 由于定位框大小大于预埋件大小,因此这里需要做缩放处理 - kh = int(h * 0.01) # roi1 + kh = int(h * 0.01) # roi1 kw = int(w * 0.01) # roi1 x += int(kw) y += int(kh) @@ -381,10 +432,10 @@ if __name__ == "__main__": param['center'] = sub_im.shape[0] - int((param["y1"] + param["y3"]) / 2) data_sub["params"].append(param) cv2.rectangle(sub_zero, (x, y), (x + w, y + h), (0, 255, 0), 1) - bim_im = gen_im_from_params(data_bim["params"]) + # bim_im = gen_im_from_params(data_bim["params"]) # cv2.namedWindow("bim", cv2.WINDOW_NORMAL) # cv2.imshow("bim", bim_im) - cv2.imwrite("bim_im.png", bim_im) + # cv2.imwrite("bim_im.png", bim_im) # cv2.waitKey(0) cv2.imshow("sub_zero", sub_zero) # cv2.waitKey(0) @@ -409,34 +460,34 @@ if __name__ == "__main__": sub_roi_height = sub_im.shape[0] sub_roi_width = sub_im.shape[1] sub_roi_params_select_id = -1 - sub_roi_w_base_len = 0 # 选中预埋件的宽度作为基础长度 + sub_roi_w_base_len = 0 # 选中预埋件的宽度作为基础长度 sub_roi_divide_w_h = 1 - polar_origin_x = 0 # 极坐标原点 x - polar_origin_y = 0 # 极坐标原点 y + polar_origin_x = 0 # 极坐标原点 x + polar_origin_y = 0 # 极坐标原点 y start_x = 0 start_y = 0 ## 1.2 选择一块完整的预埋件 for i, param in enumerate(_sub_sort_params): if 0 < param['x1'] and param['x2'] < sub_roi_width \ - and 0 < param['y1'] and param['y3'] < sub_roi_height: + and 0 < param['y1'] and param['y3'] < sub_roi_height: sub_roi_params_select_id = i sub_roi_w_base_len = param['x2'] - param['x1'] sub_roi_divide_w_h = param['w'] / param['h'] - polar_origin_x = int(param['x1']) # 当前选择的预埋件的左上角 x 坐标 - polar_origin_y = int(param['y1']) # 当前选择的预埋件的左上角 y 坐标 + polar_origin_x = int(param['x1']) # 当前选择的预埋件的左上角 x 坐标 + polar_origin_y = int(param['y1']) # 当前选择的预埋件的左上角 y 坐标 break - if sub_roi_params_select_id == -1 or sub_roi_w_base_len == 0 : + if sub_roi_params_select_id == -1 or sub_roi_w_base_len == 0: print("[ERROR]\t 拍摄的图像中没有完整的预埋件信息\n") - assert(0) + assert (0) ## 1.2.2 将其他预埋件相对于它的极坐标进行填写 for i, param in enumerate(_sub_sort_params): if i != sub_roi_params_select_id: param['r'], param['theta'] = cartesian_to_polar(_sub_sort_params[sub_roi_params_select_id]['x_center'], _sub_sort_params[sub_roi_params_select_id]['y_center'], - param['x_center'],param['y_center']) - + param['x_center'], param['y_center']) + ## 1.3计算所有点到该预埋件左上点的,点个数,平均极半径 和 平均极角度 - sum_r, sum_theta = 0.0,0 + sum_r, sum_theta = 0.0, 0 count = 0 # 测试,画出所有的pts # for i, p in enumerate(_sub_sort_params): @@ -444,14 +495,14 @@ if __name__ == "__main__": # cv2.circle(sub_im, (p["x_center"], p["y_center"]), 2, (0, 255, 255), -1) # cv2.imshow("_sub_sort_params", sub_im) # cv2.waitKey(0) - pts = gen_points_from_params(_sub_sort_params,sub_roi_width,sub_roi_height) + pts = gen_points_from_params(_sub_sort_params, sub_roi_width, sub_roi_height) # # 测试,画出所有的pts for i, p in enumerate(pts): # 画点 cv2.circle(sub_im, (p[0], p[1]), 2, (0, 255, 255), -1) # 写编号 cv2.putText(sub_im, str(i), (p[0], p[1]), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 1) - cv2.rectangle(sub_im,(hd_cam_x,hd_cam_y),(hd_cam_x+hd_cam_w,hd_cam_y+hd_cam_h), (0, 255, 0), 4) + cv2.rectangle(sub_im, (hd_cam_x, hd_cam_y), (hd_cam_x + hd_cam_w, hd_cam_y + hd_cam_h), (0, 255, 0), 4) cv2.imshow("sub_im_points", sub_im) # cv2.waitKey(0) polar_list = [] @@ -483,7 +534,7 @@ if __name__ == "__main__": for i, param in enumerate(candi_params): tmp_roi_w_base_len = param['x2'] - param['x1'] scale = tmp_roi_w_base_len / sub_roi_w_base_len - tmp_roi_width = int(scale * sub_roi_width) + tmp_roi_width = int(scale * sub_roi_width) tmp_roi_height = int(scale * sub_roi_height) # 相对于bim图的坐标 tmp_roi_start_x = int(param['x1'] - scale * polar_origin_x) @@ -498,18 +549,17 @@ if __name__ == "__main__": hd_cam_w_bim = int(scale * hd_cam_w) hd_cam_h_bim = int(scale * hd_cam_h) - tmp_roi_conners = [[tmp_roi_start_x, tmp_roi_start_y], - [tmp_roi_end_x, tmp_roi_start_y], - [tmp_roi_end_x, tmp_roi_end_y], - [tmp_roi_start_x, tmp_roi_end_y]] + [tmp_roi_end_x, tmp_roi_start_y], + [tmp_roi_end_x, tmp_roi_end_y], + [tmp_roi_start_x, tmp_roi_end_y]] tmp_sum_r, tmp_sum_theta = 0.0, 0 tmp_count = 0 tmp_polar_list = [] # 这里需要把选中的预埋件也提取出来 param['effective_points'] = [] for j, pt in enumerate(bim_all_pts): - if cv2.pointPolygonTest(np.asarray(tmp_roi_conners), (pt[0],pt[1]), False) > 0: + if cv2.pointPolygonTest(np.asarray(tmp_roi_conners), (pt[0], pt[1]), False) > 0: r, theta = cartesian_to_polar(param['x1'], param['y1'], pt[0], pt[1]) tmp_sum_r += r tmp_sum_theta += theta @@ -519,24 +569,29 @@ if __name__ == "__main__": param['effective_points'].append(pt) tmp_sum_r /= tmp_count * tmp_roi_w_base_len tmp_sum_theta /= tmp_count - + # 预埋件数量相差 30%,则不进行计算 # if tmp_count / count > 1.3 or tmp_count / count < 0.77: continue - if abs(tmp_count - count) == 0: score = 0.5 - elif abs(tmp_count - count) <= 10: score = 0.4 - elif abs(tmp_count - count) <= 20: score = 0.3 - elif abs(tmp_count - count) <= 30: score = 0.2 - else: score = 0.0 + if abs(tmp_count - count) == 0: + score = 0.5 + elif abs(tmp_count - count) <= 10: + score = 0.4 + elif abs(tmp_count - count) <= 20: + score = 0.3 + elif abs(tmp_count - count) <= 30: + score = 0.2 + else: + score = 0.0 # else: score = (1 / abs(tmp_count - count) ) * 0.7 score += (1 - abs(tmp_sum_r - sum_r) / sub_roi_width) * 0.25 score += (1 - abs(tmp_sum_theta - sum_theta) / 3.14) * 0.35 print("score=======", str(score)) - if score > 0.6: #???? - cartesian_points1 = polar_to_cartesian(np.asarray(tmp_polar_list)) # bim上的坐标 - cartesian_points2 = polar_to_cartesian(np.asarray(polar_list)) # sub上的坐标 + if score > 0.6: #???? + cartesian_points1 = polar_to_cartesian(np.asarray(tmp_polar_list)) # bim上的坐标 + cartesian_points2 = polar_to_cartesian(np.asarray(polar_list)) # sub上的坐标 sc1 = compute_shape_context(cartesian_points1) sc2 = compute_shape_context(cartesian_points2) match_score = match_shapes(sc1, sc2) @@ -546,10 +601,10 @@ if __name__ == "__main__": print(f"[tmp_count] ====== [{tmp_count}]") print(f"[tmp_sum_r] ====== [{tmp_sum_r}]") print(f"[tmp_sum_theta] ====== [{tmp_sum_theta}]") - if match_score < 5.0: #???? + if match_score < 5.0: #???? param["start_point"] = (tmp_roi_start_x, tmp_roi_start_y) param["end_point"] = (tmp_roi_end_x, tmp_roi_end_y) - param["score"] = (score, tmp_count, tmp_sum_r*tmp_roi_w_base_len, tmp_sum_theta) + param["score"] = (score, tmp_count, tmp_sum_r * tmp_roi_w_base_len, tmp_sum_theta) param['match_score'] = match_score # 高清相机的矩形数据 param['hd_cam_x_bim'] = hd_cam_x_bim @@ -561,8 +616,8 @@ if __name__ == "__main__": print(f"[start_point] ====== [{param["start_point"]}]") print(f"[end_point] ====== [{param["end_point"]}]") rst_params.append(param) - + # 找到得分最大的 max_score = -99999 max_index = -1 for i, param in enumerate(rst_params): @@ -572,9 +627,71 @@ if __name__ == "__main__": max_score = abs(score[0] / match_score) max_index = i rst_p = rst_params[max_index] - bim_im = cv2.rectangle(bim_im,rst_p["start_point"], rst_p["end_point"], 100 * (i + 1), 50) + bim_im = cv2.rectangle(bim_im, rst_p["start_point"], rst_p["end_point"], 100 * (i + 1), 50) # 画出高清相机矩形框 - bim_im = cv2.rectangle(bim_im, (rst_p["hd_cam_x_bim"], rst_p["hd_cam_y_bim"]),(rst_p["hd_cam_x_bim"] + rst_p["hd_cam_w_bim"], rst_p["hd_cam_y_bim"] + rst_p["hd_cam_h_bim"],), (0, 0, 255), 40) + bim_im = cv2.rectangle(bim_im, (rst_p["hd_cam_x_bim"], rst_p["hd_cam_y_bim"]), ( + rst_p["hd_cam_x_bim"] + rst_p["hd_cam_w_bim"], rst_p["hd_cam_y_bim"] + rst_p["hd_cam_h_bim"],), (0, 0, 255), 40) + + # ====================== 预埋件号码匹配 开始 ========================= + hd_roi_list = get_hd_roi_from_txt("data_sub/test_1/roi_conners.txt") # 高清相机识别的ROI坐标 + # 高清相机坐标转换为bim图坐标 + hd_img_width = 9144 # 高清相机的图片宽度 + hd_img_height = 7000 # 高清相机的图片高度 + # 宽高肯定都是等比的直接按照bim上面高清矩形的宽度,算下比例。 + scale_w = rst_p["hd_cam_w_bim"] / hd_img_width + scale_h = rst_p["hd_cam_h_bim"] / hd_img_height + hd_roi_out_of_range_index = [] # 超出边界的ROI的索引 + for hd_roi_index, hd_roi in enumerate(hd_roi_list): + for i in range(4): + # 所有高清照片的ROI先按照这个比例转换 ,并且加上坐标偏移,转换为bim上实际坐标 + hd_roi[i][0] = int(hd_roi[i][0] * scale_w) + rst_p["hd_cam_x_bim"] + hd_roi[i][1] = int(hd_roi[i][1] * scale_h) + rst_p["hd_cam_y_bim"] + # 如果ROI坐标形成的矩形,横跨bim图上高清矩形范围边界框,就剔除这个矩形 + is_x_cross_left_border_line = hd_roi[0][0] < rst_p["hd_cam_x_bim"] <= hd_roi[1][0] + is_x_cross_right_border_line = hd_roi[0][0] < rst_p["hd_cam_x_bim"] + rst_p["hd_cam_w_bim"] <= hd_roi[1][0] + is_y_cross_top_border_line = hd_roi[0][1] < rst_p["hd_cam_y_bim"] <= hd_roi[2][1] + is_y_cross_bottom_border_line = hd_roi[0][1] < rst_p["hd_cam_y_bim"] + rst_p["hd_cam_h_bim"] <= hd_roi[2][1] + if is_x_cross_left_border_line or is_x_cross_right_border_line or is_y_cross_top_border_line or is_y_cross_bottom_border_line: + hd_roi_out_of_range_index.append(hd_roi_index) + # 画出来试试 + # bim_im = cv2.rectangle(bim_im, (hd_roi[0][0], hd_roi[0][1]), (hd_roi[2][0], hd_roi[2][1]), (0, 0, 255), 30) + # 写上i编号 + # cv2.putText(bim_im, str(hd_roi_index), (hd_roi[0][0], hd_roi[0][1]), cv2.FONT_HERSHEY_SIMPLEX, 5, (0, 0, 255),20) + print(f"超出边界的ROI的索引是:{hd_roi_out_of_range_index}") + + # bim所有的矩形,注意bim原数据是以左下角为原点的坐标系 !!! + # bim所有的矩形,注意bim原数据是以左下角为原点的坐标系 !!! + # bim所有的矩形,注意bim原数据是以左下角为原点的坐标系 !!! + bim_rect_list = data_bim["params"] + # 遍历bim所有的矩形,找到与高清相机ROI匹配的矩形 + bim_rect_hit_list = [] + for bim_rect_index, bim_rect_item in enumerate(bim_rect_list): + # x不需要转换 + bim_rect_item_center_x = bim_rect_item["x"] + # 把y坐标系转换成左上角坐标系 + bim_im_height = bim_im.shape[0] + bim_rect_item_center_y = bim_im_height - bim_rect_item["center"] + # 逐一和高清相机ROI坐标进行匹配 + for hd_roi_index, hd_roi in enumerate(hd_roi_list): + # 如果中心点的坐标在某个高清相机ROI内,就认为匹配上了。 + bim_rect_item_center_x_inside = hd_roi[0][0] < bim_rect_item_center_x < hd_roi[1][0] + bim_rect_item_center_y_inside = hd_roi[0][1] < bim_rect_item_center_y < hd_roi[2][1] + if bim_rect_item_center_x_inside and bim_rect_item_center_y_inside: + bim_rect_hit_list.append({ + "bim_rect": bim_rect_item, + "hd_roi": hd_roi + }) + # 画出bim_rect_hit_list + for bim_rect_hit_item in bim_rect_hit_list: + bim_rect = bim_rect_hit_item["bim_rect"] + hd_roi = bim_rect_hit_item["hd_roi"] + bim_im = cv2.rectangle(bim_im, (hd_roi[0][0], hd_roi[0][1]), (hd_roi[2][0], hd_roi[2][1]), (0, 0, 255), 30) + # 写上i编号 + cv2.putText(bim_im, str(bim_rect["code"]), (hd_roi[0][0]+100, hd_roi[0][1]+200), cv2.FONT_HERSHEY_SIMPLEX, 5, (0, 0, 255), + 20) + + # ===================== 预埋件号码匹配 结束 =========================== + # # 预埋件匹配 # for i, param in enumerate(rst_params): # score = param["score"] @@ -598,10 +715,10 @@ if __name__ == "__main__": # print(f"[end_point] ====== [{param["end_point"]}]") # # bim_im = cv2.rectangle(bim_im, param["start_point"], param["end_point"], 100 * (i + 1), 50) - + elapsed_time = time.time() - start_time print(f"Execution time: {elapsed_time:.4f} seconds") - img_matches = cv2.resize(bim_im, (int(bim_im.shape[1]/6), int(bim_im.shape[0]/6))) + img_matches = cv2.resize(bim_im, (int(bim_im.shape[1] / 6), int(bim_im.shape[0] / 6))) # sub_im = cv2.resize(sub_im, (int(sub_im.shape[1]/10), int(sub_im.shape[0]/10))) cv2.imshow("2", img_matches) # cnts的矩形画在sub_im 上 @@ -611,4 +728,4 @@ if __name__ == "__main__": # # 写编号 # cv2.putText(sub_im, str(i), (p['x'], p['y']), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2) # cv2.imshow("sub_im_after_filter", sub_im) - cv2.waitKey(0) \ No newline at end of file + cv2.waitKey(0) diff --git a/utils.py b/utils.py index b2b7557..4260829 100644 --- a/utils.py +++ b/utils.py @@ -3,33 +3,31 @@ import logging import cv2 -""" -根据裁剪之后的图片,每个点的坐标需要重新计算,以新的图片的宽高作为坐标系 -""" - - def re_cal_point(point, offset): + """ + 根据裁剪之后的图片,每个点的坐标需要重新计算,以新的图片的宽高作为坐标系 + """ + # 相当于所有的x坐标向左平移了offset个距离 point['x'] = point['x'] - offset -""" -根据左右裁剪之后的图像,过滤矩形。在裁剪之后,整个矩形已经不在图片里面的,就去掉。 - 1 高度过大的不要 - 2 整个矩形全部身体都在裁剪区域之外的不要 - - image_path:图片路径 - points:要过滤的点 - wide_cam_left_cut_rate:# 左边界的裁剪比例,从左边开始裁剪百分之多少 - wide_cam_right_cut_rate # 右边界的裁剪比例,从右边开始裁剪百分之多少 - - 返回值: - 1 过滤之后的矩形 - 2 裁剪之后的图片 -""" - def filter_rectangle(image_path, points, wide_cam_left_cut_rate, wide_cam_right_cut_rate): + """ + 根据左右裁剪之后的图像,过滤矩形。在裁剪之后,整个矩形已经不在图片里面的,就去掉。 + 1 高度过大的不要 + 2 整个矩形全部身体都在裁剪区域之外的不要 + + image_path:图片路径 + points:要过滤的点 + wide_cam_left_cut_rate:# 左边界的裁剪比例,从左边开始裁剪百分之多少 + wide_cam_right_cut_rate # 右边界的裁剪比例,从右边开始裁剪百分之多少 + + 返回值: + 1 过滤之后的矩形 + 2 裁剪之后的图片 + """ # 高度过大矩形过滤参数 max_height_rate = 0.5 # 矩形高度占整个画面高度的最大比例,如果超过该比例,则认为是无效矩形 @@ -89,15 +87,13 @@ def filter_rectangle(image_path, points, wide_cam_left_cut_rate, wide_cam_right_ return filtered_points, cropped_image -""" -获取高清相机的视场矩形在广角相机里面的坐标。cv2下的图片坐标系,以左上角为坐标原点 - - wide_cam_left_cut_rate:# 广角相机,左边界的裁剪比例,从左边开始裁剪百分之多少 - wide_cam_right_cut_rate # 广角相机,右边界的裁剪比例,从右边开始裁剪百分之多少 -""" - def get_hd_cam_rect(wide_cam_left_cut_rate): + """ + 获取高清相机的视场矩形在广角相机里面的坐标。cv2下的图片坐标系,以左上角为坐标原点 + + wide_cam_left_cut_rate:# 广角相机,左边界的裁剪比例,从左边开始裁剪百分之多少 + """ # 下面参数是几乎标准无偏差的高清相机在广角相机里面的视场角矩形 广角为640x480 x = 128 y = 140 @@ -121,7 +117,62 @@ def get_hd_cam_rect(wide_cam_left_cut_rate): return int(scale_x), int(scale_y), int(scale_w), int(scale_h) -# 测试代码 + +def get_hd_roi_from_txt(file_path): + """ + 读取本地文件,解析ROI矩形坐标。 + 每八行为一个ROI矩形坐标,每两行为x,y,顺序为左上、右上、右下、左下(顺时针)。 + + Args: + file_path (str): 文件路径 + + Returns: + list:数组格式,第一层是全部都矩形,第二次是每个矩形的全部的坐标,第三层是每个坐标都x,y + [ + [[x1, y1], [x2, y2], [x3, y3], [x4, y4]], + [[x1, y1], [x2, y2], [x3, y3], [x4, y4]], + ] + """ + roi_list = [] + try: + with open(file_path, 'r') as f: + lines = f.readlines() + # 确保行数是8的倍数 + if len(lines) % 8 != 0: + raise ValueError("文件格式错误,行数不是8的倍数") + + for i in range(0, len(lines), 8): + roi = [] + for j in range(0, 8, 2): + x = float(lines[i + j].strip()) + y = float(lines[i + j + 1].strip()) + roi.append([x, y]) + roi_list.append(roi) + + except FileNotFoundError: + print(f"文件未找到: {file_path}") + except ValueError as e: + print(f"数据格式错误: {e}") + except Exception as e: + print(f"发生未知错误: {e}") + + return roi_list + + + + +# 测试 get_hd_rou_from_txt +# rois = get_hd_roi_from_txt("data_sub/test_1/roi_conners.txt") +# print(rois[0]) +# sub_im = cv2.imread("data_sub/test_1/output.jpg") +# for roi in rois: +# cv2.rectangle(sub_im, (int(roi[0][0]), int(roi[0][1])), (int(roi[2][0]), int(roi[2][1])), (0, 0, 255), 100) +# # 图片缩小展示 +# sub_im = cv2.resize(sub_im, (int(sub_im.shape[1]/12), int(sub_im.shape[0]/12))) +# cv2.imshow("sub_im", sub_im) +# cv2.waitKey(0) + +# 测试filter_rectangle # def read_from_json(file_path): # with open(file_path, 'r') as f: # loaded_array = json.load(f)