bim号码匹配

This commit is contained in:
leon 2025-03-02 20:49:58 +08:00
parent d4ed1a8bf2
commit 4c3742b010
3 changed files with 266 additions and 98 deletions

View File

@ -5,7 +5,7 @@ conda env create -f environment.yaml
bim定位锁定bim区域xx bim定位锁定bim区域xx
画出高清摄像头应该在的区域是固定的比例适当的等比例放大一些。xx 画出高清摄像头应该在的区域是固定的比例适当的等比例放大一些。xx
获取高清摄像头识别出来的ROI坐标数据 获取高清摄像头识别出来的ROI坐标数据
高清摄像头的ROI坐标转换为bim下的坐标 高清摄像头的ROI坐标转换为bim下的坐标xxx
遍历范围内的完整的矩形对比高清摄像头ROI矩形的中心点是否在 bim中矩形的内部 遍历范围内的完整的矩形对比高清摄像头ROI矩形的中心点是否在 bim中矩形的内部
如果在内部说明是同一个编号的预埋件。 如果在内部说明是同一个编号的预埋件。

235
search.py
View File

@ -1,8 +1,8 @@
import cv2 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 json
import math import math
import copy import copy
@ -11,8 +11,10 @@ import time
from scipy.spatial import distance from scipy.spatial import distance
from scipy.optimize import linear_sum_assignment from scipy.optimize import linear_sum_assignment
def get_params(num_param, start, end): 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): def parmas_to_num(text_param):
for item in text_param: for item in text_param:
@ -23,6 +25,7 @@ def parmas_to_num(text_param):
item['h'] = int(item['h']) item['h'] = int(item['h'])
return text_param return text_param
def parmas_to_text(num_param): def parmas_to_text(num_param):
for item in num_param: for item in num_param:
item['center'] = str(item['center']) item['center'] = str(item['center'])
@ -41,26 +44,31 @@ def parmas_to_text(num_param):
item['y_center'] = str(item['y_center']) item['y_center'] = str(item['y_center'])
return num_param return num_param
def sort_params(params): def sort_params(params):
sorted_params = sorted(params, key=lambda item: (item['center'], item['x'])) sorted_params = sorted(params, key=lambda item: (item['center'], item['x']))
return sorted_params return sorted_params
def print_params(sort_params): def print_params(sort_params):
for param in sort_params: for param in sort_params:
print(param["center"], param["x"], param["w"], param["h"]) print(param["center"], param["x"], param["w"], param["h"])
def print_path(search_path): def print_path(search_path):
for path in search_path: for path in search_path:
print(path[0], path[1]) print(path[0], path[1])
def search_path(sort_params): def search_path(sort_params):
searchPath = [] searchPath = []
for i in range(len(sort_params) - 1): for i in range(len(sort_params) - 1):
(r, theta) = cartesian_to_polar(sort_params[i]["x"], sort_params[i]["center"], (r, theta) = cartesian_to_polar(sort_params[i]["x"], sort_params[i]["center"],
sort_params[i + 1]["x"], sort_params[i + 1]["center"]) sort_params[i + 1]["x"], sort_params[i + 1]["center"])
searchPath.append([r, theta]) searchPath.append([r, theta])
return searchPath return searchPath
def normalize_params_and_path(sort_params, search_path, index=0): 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: for param in sort_params:
@ -75,18 +83,21 @@ def normalize_params_and_path(sort_params, search_path, index=0):
# path[1] /= base # path[1] /= base
return sort_params, search_path return sort_params, search_path
def read_from_json(file_path): def read_from_json(file_path):
with open(file_path, 'r') as f: with open(file_path, 'r') as f:
loaded_array = json.load(f) loaded_array = json.load(f)
return loaded_array return loaded_array
def cartesian_to_polar(x1, y1, x2, y2): def cartesian_to_polar(x1, y1, x2, y2):
dx = x2 - x1 dx = x2 - x1
dy = y2 - y1 dy = y2 - y1
r = math.sqrt(dx**2 + dy**2) r = math.sqrt(dx ** 2 + dy ** 2)
theta = math.atan2(dy, dx) theta = math.atan2(dy, dx)
return r, theta return r, theta
def calculate_second_point(x1, y1, r, theta_radians): def calculate_second_point(x1, y1, r, theta_radians):
# theta_radians = math.radians(theta_degrees) # theta_radians = math.radians(theta_degrees)
x2 = x1 + r * math.cos(theta_radians) x2 = x1 + r * math.cos(theta_radians)
@ -94,6 +105,7 @@ def calculate_second_point(x1, y1, r, theta_radians):
return x2, y2 return x2, y2
def cal_c1c2c3c4(param, heigt): def cal_c1c2c3c4(param, heigt):
''' '''
按照上左上右下右下左的顺时针顺序. 按照上左上右下右下左的顺时针顺序.
@ -115,14 +127,15 @@ def cal_c1c2c3c4(param, heigt):
param['y_center'] = int((param['y1'] + param['y3']) / 2) param['y_center'] = int((param['y1'] + param['y3']) / 2)
return param return param
def gen_im_from_params(params, type="lines"): def gen_im_from_params(params, type="lines"):
# 依据bim数据生成bim图 # 依据bim数据生成bim图
# type: # line points # type: # line points
## 确定整个bim图的长度和宽度边界 ## 确定整个bim图的长度和宽度边界
max_y = -999999 # y坐标最大值 max_y = -999999 # y坐标最大值
max_y_idx = -1 # y坐标最大值的索引 max_y_idx = -1 # y坐标最大值的索引
max_x = -999999 # x坐标最大值 max_x = -999999 # x坐标最大值
max_x_idx = -1 # x坐标最大值的索引 max_x_idx = -1 # x坐标最大值的索引
# 遍历,找到矩形x坐标最大值和矩形y坐标最大值 # 遍历,找到矩形x坐标最大值和矩形y坐标最大值
for i, param in enumerate(params): 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 = param["center"] + param["h"] / 2
max_y_idx = i max_y_idx = i
padding_value = 1000 # 内边距,避免整个bim图片贴着边缘展示 padding_value = 1000 # 内边距,避免整个bim图片贴着边缘展示
bim_width = int(max_x) + padding_value bim_width = int(max_x) + padding_value
print(f"[bim_width] ====== [{bim_width}]") print(f"[bim_width] ====== [{bim_width}]")
bim_height = int(max_y) + padding_value bim_height = int(max_y) + padding_value
@ -149,16 +162,16 @@ def gen_im_from_params(params, type="lines"):
cal_c1c2c3c4(param, bim_height) cal_c1c2c3c4(param, bim_height)
if type == "lines": if type == "lines":
pts = np.asarray([[param['x1'], param['y1']], pts = np.asarray([[param['x1'], param['y1']],
[param['x2'], param['y2']], [param['x2'], param['y2']],
[param['x3'], param['y3']], [param['x3'], param['y3']],
[param['x4'], param['y4']]]) [param['x4'], param['y4']]])
cv2.polylines(im, [pts], True, (255,255,0), 8) cv2.polylines(im, [pts], True, (255, 255, 0), 8)
elif type == "points": elif type == "points":
cv2.circle(im, (param['x1'], param['y1']), 1, 255, 20) cv2.circle(im, (param['x1'], param['y1']), 1, 255, 20)
cv2.circle(im, (param['x2'], param['y2']), 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['x3'], param['y3']), 1, 255, 20)
cv2.circle(im, (param['x4'], param['y4']), 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 return im
@ -169,11 +182,13 @@ def gen_im_from_params(params, type="lines"):
False 不在 False 不在
如果没有提供区域,不做判断,返回True 如果没有提供区域,不做判断,返回True
""" """
def is_inside_roi(point, roi_w, roi_h): def is_inside_roi(point, roi_w, roi_h):
if (roi_w != None and roi_h != None): if (roi_w != None and roi_h != None):
x = point[0] x = point[0]
y = point[1] 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 return False
else: else:
@ -184,7 +199,7 @@ def is_inside_roi(point, roi_w, roi_h):
return True 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图 # 依据bim数据生成bim图
# type line points # type line points
## 计算bim图长和宽 ## 计算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): if (roi_w == None and roi_h == None):
cal_c1c2c3c4(param, bim_height) cal_c1c2c3c4(param, bim_height)
# 过滤点,把在roi区域之外的点全部过滤掉. # 过滤点,把在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]) 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]) 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]) 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]) 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]) points.append([param['x_center'], param['y_center'], i])
if (roi_w != None and roi_h != None): if (roi_w != None and roi_h != None):
print(f"[经区域过滤之后的点数一共为] ====== [{len(points)}]") print(f"[经区域过滤之后的点数一共为] ====== [{len(points)}]")
return points return points
def topological_similarity(adj1, adj2): 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]) similarity = np.sum(adj1 == adj2) / (adj1.shape[0] * adj2.shape[1])
return similarity return similarity
def find_topological_matches(points1, adj1, points2, adj2, threshold=0.8): 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) matches.sort(key=lambda x: x[2], reverse=True)
return matches return matches
from sklearn.linear_model import RANSACRegressor from sklearn.linear_model import RANSACRegressor
def ransac_shape_matching(points, reference_points): def ransac_shape_matching(points, reference_points):
model_ransac = RANSACRegressor(random_state=42) model_ransac = RANSACRegressor(random_state=42)
try: try:
@ -259,6 +278,7 @@ def ransac_shape_matching(points, reference_points):
return best_match_subset return best_match_subset
def polar_to_cartesian(polar_points): def polar_to_cartesian(polar_points):
r = polar_points[:, 0] r = polar_points[:, 0]
theta = polar_points[:, 1] theta = polar_points[:, 1]
@ -266,6 +286,7 @@ def polar_to_cartesian(polar_points):
y = r * np.sin(theta) y = r * np.sin(theta)
return np.vstack((x, y)).T return np.vstack((x, y)).T
def compute_shape_context(points, nbins_r=5, nbins_theta=12): def compute_shape_context(points, nbins_r=5, nbins_theta=12):
n = points.shape[0] n = points.shape[0]
shape_contexts = [] shape_contexts = []
@ -284,11 +305,13 @@ def compute_shape_context(points, nbins_r=5, nbins_theta=12):
return np.array(shape_contexts) return np.array(shape_contexts)
def match_shapes(sc1, sc2): def match_shapes(sc1, sc2):
cost_matrix = distance.cdist(sc1, sc2, metric='sqeuclidean') cost_matrix = distance.cdist(sc1, sc2, metric='sqeuclidean')
row_ind, col_ind = linear_sum_assignment(cost_matrix) row_ind, col_ind = linear_sum_assignment(cost_matrix)
return cost_matrix[row_ind, col_ind].sum() return cost_matrix[row_ind, col_ind].sum()
def _sobel(image): def _sobel(image):
''' '''
_sobel _sobel
@ -301,9 +324,10 @@ def _sobel(image):
_sobelx = np.uint8(np.absolute(_sobelx)) _sobelx = np.uint8(np.absolute(_sobelx))
_sobely = np.uint8(np.absolute(_sobely)) _sobely = np.uint8(np.absolute(_sobely))
_sobelcombine = cv2.bitwise_or(_sobelx,_sobely) _sobelcombine = cv2.bitwise_or(_sobelx, _sobely)
return _sobelcombine return _sobelcombine
def _findContours(image): def _findContours(image):
''' '''
_findContours _findContours
@ -312,6 +336,32 @@ def _findContours(image):
contours, _ = cv2.findContours(image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE) contours, _ = cv2.findContours(image.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
return sorted(contours, key=cv2.contourArea, reverse=True) 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__": if __name__ == "__main__":
# 读取并处理数据 # 读取并处理数据
data_bim = {} data_bim = {}
@ -328,7 +378,7 @@ if __name__ == "__main__":
wide_cam_left_cut_rate = 0.1 wide_cam_left_cut_rate = 0.1
wide_cam_right_cut_rate = 0.1 wide_cam_right_cut_rate = 0.1
# 高清相机的视场矩形在广角相机里面的坐标。cv2下的图片坐标系以左上角为坐标原点 # 高清相机的视场矩形在广角相机里面的坐标。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") # sub_im = cv2.imread("wide_image.png")
@ -348,7 +398,8 @@ if __name__ == "__main__":
# 过滤矩形 # 过滤矩形
# cnts 过滤之后的矩形 # cnts 过滤之后的矩形
# sub_im 裁剪之后的图像 # 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) sub_zero = np.zeros_like(sub_im)
for contour in cnts: for contour in cnts:
# x, y, w, h = cv2.boundingRect(contour) # x, y, w, h = cv2.boundingRect(contour)
@ -358,7 +409,7 @@ if __name__ == "__main__":
h = contour["height"] h = contour["height"]
# 由于定位框大小大于预埋件大小,因此这里需要做缩放处理 # 由于定位框大小大于预埋件大小,因此这里需要做缩放处理
kh = int(h * 0.01) # roi1 kh = int(h * 0.01) # roi1
kw = int(w * 0.01) # roi1 kw = int(w * 0.01) # roi1
x += int(kw) x += int(kw)
y += int(kh) y += int(kh)
@ -381,10 +432,10 @@ if __name__ == "__main__":
param['center'] = sub_im.shape[0] - int((param["y1"] + param["y3"]) / 2) param['center'] = sub_im.shape[0] - int((param["y1"] + param["y3"]) / 2)
data_sub["params"].append(param) data_sub["params"].append(param)
cv2.rectangle(sub_zero, (x, y), (x + w, y + h), (0, 255, 0), 1) 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.namedWindow("bim", cv2.WINDOW_NORMAL)
# cv2.imshow("bim", bim_im) # cv2.imshow("bim", bim_im)
cv2.imwrite("bim_im.png", bim_im) # cv2.imwrite("bim_im.png", bim_im)
# cv2.waitKey(0) # cv2.waitKey(0)
cv2.imshow("sub_zero", sub_zero) cv2.imshow("sub_zero", sub_zero)
# cv2.waitKey(0) # cv2.waitKey(0)
@ -409,34 +460,34 @@ if __name__ == "__main__":
sub_roi_height = sub_im.shape[0] sub_roi_height = sub_im.shape[0]
sub_roi_width = sub_im.shape[1] sub_roi_width = sub_im.shape[1]
sub_roi_params_select_id = -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 sub_roi_divide_w_h = 1
polar_origin_x = 0 # 极坐标原点 x polar_origin_x = 0 # 极坐标原点 x
polar_origin_y = 0 # 极坐标原点 y polar_origin_y = 0 # 极坐标原点 y
start_x = 0 start_x = 0
start_y = 0 start_y = 0
## 1.2 选择一块完整的预埋件 ## 1.2 选择一块完整的预埋件
for i, param in enumerate(_sub_sort_params): for i, param in enumerate(_sub_sort_params):
if 0 < param['x1'] and param['x2'] < sub_roi_width \ 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_params_select_id = i
sub_roi_w_base_len = param['x2'] - param['x1'] sub_roi_w_base_len = param['x2'] - param['x1']
sub_roi_divide_w_h = param['w'] / param['h'] sub_roi_divide_w_h = param['w'] / param['h']
polar_origin_x = int(param['x1']) # 当前选择的预埋件的左上角 x 坐标 polar_origin_x = int(param['x1']) # 当前选择的预埋件的左上角 x 坐标
polar_origin_y = int(param['y1']) # 当前选择的预埋件的左上角 y 坐标 polar_origin_y = int(param['y1']) # 当前选择的预埋件的左上角 y 坐标
break 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") print("[ERROR]\t 拍摄的图像中没有完整的预埋件信息\n")
assert(0) assert (0)
## 1.2.2 将其他预埋件相对于它的极坐标进行填写 ## 1.2.2 将其他预埋件相对于它的极坐标进行填写
for i, param in enumerate(_sub_sort_params): for i, param in enumerate(_sub_sort_params):
if i != sub_roi_params_select_id: if i != sub_roi_params_select_id:
param['r'], param['theta'] = cartesian_to_polar(_sub_sort_params[sub_roi_params_select_id]['x_center'], 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'], _sub_sort_params[sub_roi_params_select_id]['y_center'],
param['x_center'],param['y_center']) param['x_center'], param['y_center'])
## 1.3计算所有点到该预埋件左上点的,点个数,平均极半径 和 平均极角度 ## 1.3计算所有点到该预埋件左上点的,点个数,平均极半径 和 平均极角度
sum_r, sum_theta = 0.0,0 sum_r, sum_theta = 0.0, 0
count = 0 count = 0
# 测试,画出所有的pts # 测试,画出所有的pts
# for i, p in enumerate(_sub_sort_params): # 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.circle(sub_im, (p["x_center"], p["y_center"]), 2, (0, 255, 255), -1)
# cv2.imshow("_sub_sort_params", sub_im) # cv2.imshow("_sub_sort_params", sub_im)
# cv2.waitKey(0) # 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 # # 测试,画出所有的pts
for i, p in enumerate(pts): for i, p in enumerate(pts):
# 画点 # 画点
cv2.circle(sub_im, (p[0], p[1]), 2, (0, 255, 255), -1) 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.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.imshow("sub_im_points", sub_im)
# cv2.waitKey(0) # cv2.waitKey(0)
polar_list = [] polar_list = []
@ -498,18 +549,17 @@ if __name__ == "__main__":
hd_cam_w_bim = int(scale * hd_cam_w) hd_cam_w_bim = int(scale * hd_cam_w)
hd_cam_h_bim = int(scale * hd_cam_h) hd_cam_h_bim = int(scale * hd_cam_h)
tmp_roi_conners = [[tmp_roi_start_x, tmp_roi_start_y], 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_start_y],
[tmp_roi_end_x, tmp_roi_end_y], [tmp_roi_end_x, tmp_roi_end_y],
[tmp_roi_start_x, tmp_roi_end_y]] [tmp_roi_start_x, tmp_roi_end_y]]
tmp_sum_r, tmp_sum_theta = 0.0, 0 tmp_sum_r, tmp_sum_theta = 0.0, 0
tmp_count = 0 tmp_count = 0
tmp_polar_list = [] tmp_polar_list = []
# 这里需要把选中的预埋件也提取出来 # 这里需要把选中的预埋件也提取出来
param['effective_points'] = [] param['effective_points'] = []
for j, pt in enumerate(bim_all_pts): 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]) r, theta = cartesian_to_polar(param['x1'], param['y1'], pt[0], pt[1])
tmp_sum_r += r tmp_sum_r += r
tmp_sum_theta += theta tmp_sum_theta += theta
@ -523,20 +573,25 @@ if __name__ == "__main__":
# 预埋件数量相差 30%,则不进行计算 # 预埋件数量相差 30%,则不进行计算
# if tmp_count / count > 1.3 or tmp_count / count < 0.77: continue # if tmp_count / count > 1.3 or tmp_count / count < 0.77: continue
if abs(tmp_count - count) == 0: score = 0.5 if abs(tmp_count - count) == 0:
elif abs(tmp_count - count) <= 10: score = 0.4 score = 0.5
elif abs(tmp_count - count) <= 20: score = 0.3 elif abs(tmp_count - count) <= 10:
elif abs(tmp_count - count) <= 30: score = 0.2 score = 0.4
else: score = 0.0 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 # 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_r - sum_r) / sub_roi_width) * 0.25
score += (1 - abs(tmp_sum_theta - sum_theta) / 3.14) * 0.35 score += (1 - abs(tmp_sum_theta - sum_theta) / 3.14) * 0.35
print("score=======", str(score)) print("score=======", str(score))
if score > 0.6: #???? if score > 0.6: #????
cartesian_points1 = polar_to_cartesian(np.asarray(tmp_polar_list)) # bim上的坐标 cartesian_points1 = polar_to_cartesian(np.asarray(tmp_polar_list)) # bim上的坐标
cartesian_points2 = polar_to_cartesian(np.asarray(polar_list)) # sub上的坐标 cartesian_points2 = polar_to_cartesian(np.asarray(polar_list)) # sub上的坐标
sc1 = compute_shape_context(cartesian_points1) sc1 = compute_shape_context(cartesian_points1)
sc2 = compute_shape_context(cartesian_points2) sc2 = compute_shape_context(cartesian_points2)
match_score = match_shapes(sc1, sc2) match_score = match_shapes(sc1, sc2)
@ -546,10 +601,10 @@ if __name__ == "__main__":
print(f"[tmp_count] ====== [{tmp_count}]") print(f"[tmp_count] ====== [{tmp_count}]")
print(f"[tmp_sum_r] ====== [{tmp_sum_r}]") print(f"[tmp_sum_r] ====== [{tmp_sum_r}]")
print(f"[tmp_sum_theta] ====== [{tmp_sum_theta}]") 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["start_point"] = (tmp_roi_start_x, tmp_roi_start_y)
param["end_point"] = (tmp_roi_end_x, tmp_roi_end_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['match_score'] = match_score
# 高清相机的矩形数据 # 高清相机的矩形数据
param['hd_cam_x_bim'] = hd_cam_x_bim param['hd_cam_x_bim'] = hd_cam_x_bim
@ -562,7 +617,7 @@ if __name__ == "__main__":
print(f"[end_point] ====== [{param["end_point"]}]") print(f"[end_point] ====== [{param["end_point"]}]")
rst_params.append(param) rst_params.append(param)
# 找到得分最大的
max_score = -99999 max_score = -99999
max_index = -1 max_index = -1
for i, param in enumerate(rst_params): for i, param in enumerate(rst_params):
@ -572,9 +627,71 @@ if __name__ == "__main__":
max_score = abs(score[0] / match_score) max_score = abs(score[0] / match_score)
max_index = i max_index = i
rst_p = rst_params[max_index] 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): # for i, param in enumerate(rst_params):
# score = param["score"] # score = param["score"]
@ -601,7 +718,7 @@ if __name__ == "__main__":
elapsed_time = time.time() - start_time elapsed_time = time.time() - start_time
print(f"Execution time: {elapsed_time:.4f} seconds") 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))) # sub_im = cv2.resize(sub_im, (int(sub_im.shape[1]/10), int(sub_im.shape[0]/10)))
cv2.imshow("2", img_matches) cv2.imshow("2", img_matches)
# cnts的矩形画在sub_im 上 # cnts的矩形画在sub_im 上

107
utils.py
View File

@ -3,33 +3,31 @@ import logging
import cv2 import cv2
"""
根据裁剪之后的图片,每个点的坐标需要重新计算,以新的图片的宽高作为坐标系
"""
def re_cal_point(point, offset): def re_cal_point(point, offset):
"""
根据裁剪之后的图片,每个点的坐标需要重新计算,以新的图片的宽高作为坐标系
"""
# 相当于所有的x坐标向左平移了offset个距离 # 相当于所有的x坐标向左平移了offset个距离
point['x'] = point['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): 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 # 矩形高度占整个画面高度的最大比例,如果超过该比例,则认为是无效矩形 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 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): def get_hd_cam_rect(wide_cam_left_cut_rate):
"""
获取高清相机的视场矩形在广角相机里面的坐标cv2下的图片坐标系以左上角为坐标原点
wide_cam_left_cut_rate# 广角相机,左边界的裁剪比例,从左边开始裁剪百分之多少
"""
# 下面参数是几乎标准无偏差的高清相机在广角相机里面的视场角矩形 广角为640x480 # 下面参数是几乎标准无偏差的高清相机在广角相机里面的视场角矩形 广角为640x480
x = 128 x = 128
y = 140 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) return int(scale_x), int(scale_y), int(scale_w), int(scale_h)
# 测试代码
def get_hd_roi_from_txt(file_path):
"""
读取本地文件解析ROI矩形坐标
每八行为一个ROI矩形坐标每两行为xy顺序为左上右上右下左下顺时针
Args:
file_path (str): 文件路径
Returns:
list:数组格式第一层是全部都矩形第二次是每个矩形的全部的坐标第三层是每个坐标都xy
[
[[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): # def read_from_json(file_path):
# with open(file_path, 'r') as f: # with open(file_path, 'r') as f:
# loaded_array = json.load(f) # loaded_array = json.load(f)