import copy import math import cv2 import numpy as np from matplotlib import pyplot from numpy import where, mean from scipy import optimize from skimage import morphology from sklearn.cluster import OPTICS from config import opt # 图片预处理 def process_map(img, opt): def get_wall_area(map, area): ''' . map 获取墙壁的图像 . stats 连通域信息 . area 面积 ''' map_ = copy.copy(map) map_[map != 255] = 0 num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(map_, connectivity=8) temp = [] for i in range(1, num_labels): if stats[i, 4] > area: temp.append(i) wall_area = np.zeros((map.shape[0], map.shape[1]), np.uint8) for i in range(0, len(temp)): mask = labels == temp[i] wall_area[:, :][mask] = 255 return wall_area def get_NONwall_area(map, stats, area, num_labels, labels): ''' . map 获取墙壁的图像 . stats 连通域信息 . area 面积 ''' temp = [] for i in range(1, num_labels): mask = labels == i if stats[i, 4] < area: temp.append(i) elif np.max(map[:, :][mask]) in Intensity_value: temp.append(i) NONwall_area = np.zeros((map.shape[0], map.shape[1]), np.uint8) for i in range(0, len(temp)): mask = labels == temp[i] NONwall_area[:, :][mask] = 255 return NONwall_area def get_rotate_angle(wall_area, length, opt): temp = copy.copy(wall_area) # temp = cv2.pyrUp(temp) # temp = cv2.pyrUp(temp) edges = cv2.Canny(temp, 50, 150, apertureSize=5) lines = cv2.HoughLines(edges, 1, np.pi / 180, length) # if (lines == None): # return wall_area temp = np.zeros((wall_area.shape[0], wall_area.shape[1])) np.zeros((wall_area.shape[0], wall_area.shape[1])) alfa = 0 theta_temp = [0] for line in lines: n = 0 rho = line[0][0] theta = line[0][1] a = np.cos(theta) b = np.sin(theta) x0 = a * rho y0 = b * rho x1 = int(x0 + 1000 * (-b)) y1 = int(y0 + 1000 * (a)) x2 = int(x0 - 1000 * (-b)) y2 = int(y0 - 1000 * (a)) # alfa = alfa + np.mod(theta, 90 / 360 * np.pi) # alfa = theta theta_array = np.array(theta_temp) n = 0 for i in range(theta_array.shape[0]): x = np.abs(theta - theta_array[i]) if (0 < x < 0.15 * np.pi): n = 1 break if (n == 1): continue theta_temp.append(theta) wall_area = rotate_bound(wall_area, -np.mod(theta, 180 / 360 * np.pi) / np.pi * 180) # 摆正图像 temp = rotate_bound(temp, -np.mod(theta, 180 / 360 * np.pi) / np.pi * 180) # 摆正图像 # wall_area_ = cv2.dilate(wall_area_, kernel) # 腐蚀膨胀,去除墙上语义点 temp_ = connect_door(wall_area, opt.k, opt.m) # 封闭区域 temp[temp_ > 0] = 0 temp = temp_ + temp wall_area = rotate_bound(wall_area, np.mod(theta, 180 / 360 * np.pi) / np.pi * 180) temp = rotate_bound(temp, np.mod(theta, 180 / 360 * np.pi) / np.pi * 180) wall_area[temp > 0] = 255 return wall_area def rotate_bound(image, angle): ''' . 旋转图片 . @param image opencv读取后的图像 . @param angle (逆)旋转角度 ''' # img = cv2.imread("img/1.jpg") (h, w) = image.shape[:2] # 返回(高,宽,色彩通道数),此处取前两个值返回 # 抓取旋转矩阵(应用角度的负值顺时针旋转)。参数1为旋转中心点;参数2为旋转角度,正的值表示逆时针旋转;参数3为各向同性的比例因子 M = cv2.getRotationMatrix2D((w / 2, h / 2), -angle, 1.0) # 计算图像的新边界维数 # 调整旋转矩阵以考虑平移 # 执行实际的旋转并返回图像 return cv2.warpAffine(image, M, (image.shape[1], image.shape[0])) # borderValue 缺省,默认是黑色 # return M def get_map(img, ratio): """ :param img: :param ratio: 获取地图,2表示有效面积的2倍 :return: """ ratio = (ratio - 1) / 2 # b = np.nonzero(img) # x_min = np.min(b[0]) # y_min = np.min(b[1]) # x_max = np.max(b[0]) # y_max = np.max(b[1]) # x_l = x_max - x_min # y_l = y_max - y_min # if x_l < 150: # ratio = ratio + 0.5 # map = np.zeros((x_max - x_min, y_max - y_min)) # temp = img[int(max(x_min - ratio * x_l, 0)):int(min(x_max + x_l * ratio, img.shape[1])), # int(max(0, y_min - ratio * y_l)):int(min(img.shape[0], ratio * y_l + y_max))] ''' 去掉未占用区域 ''' temp_x = np.zeros((img.shape[0], 50), np.uint8) map = np.hstack((temp_x, img)) temp_y = np.zeros((50, map.shape[1]), np.uint8) map = np.vstack((temp_y, map)) # map = np.zeros((img.shape[0], 50), np.uint8) temp_x = np.zeros((map.shape[0], 40), np.uint8) map = np.hstack((map, temp_x)) temp_y = np.zeros((40, map.shape[1]), np.uint8) map = np.vstack((map, temp_y)) # map = copy.copy(img) map[map < 70] = 255 map[map <= 100] = 0 map[map == 0] = 1 map[map == 255] = 0 map[map == 1] = 255 "墙壁灰度值为1" return map def draw_rgb(map): def rgb_semantics(number): output = np.array((5, 5, 5), np.uint8) if number == 115: '0号物体' output[0] = 232 output[1] = 221 output[2] = 203 elif number == 116: '1号物体' output[0] = 205 output[1] = 179 output[2] = 128 elif number == 117: '2号物体' output[0] = 3 output[1] = 101 output[2] = 100 elif number == 118: '3号物体' output[0] = 3 output[1] = 54 output[2] = 73 elif number == 119: '4号物体' output[0] = 3 output[1] = 22 output[2] = 52 elif number == 120: '5号物体' output[0] = 237 output[1] = 222 output[2] = 139 elif number == 121: '6号物体' output[0] = 251 output[1] = 178 output[2] = 23 elif number == 150: '7号物体' output[0] = 96 output[1] = 143 output[2] = 159 elif number == 151: '8号物体' output[0] = 1 output[1] = 77 output[2] = 103 elif number == 152: '9号物体' output[0] = 254 output[1] = 67 output[2] = 101 elif number == 153: '10号物' output[0] = 252 output[1] = 157 output[2] = 154 elif number == 154: '11号物体' output[0] = 249 output[1] = 205 output[2] = 173 elif number == 155: '12号物体' output[0] = 200 output[1] = 200 output[2] = 169 elif number == 156: '13号物体' output[0] = 131 output[1] = 175 output[2] = 155 elif number == 200: '14号物体' output[0] = 229 output[1] = 187 output[2] = 129 elif number == 201: '15号物体' output[0] = 161 output[1] = 23 output[2] = 21 elif number == 202: '16号物体' output[0] = 118 output[1] = 77 output[2] = 57 elif number == 203: '17号物体' output[0] = 17 output[1] = 63 output[2] = 61 elif number == 204: '18号物体' output[0] = 60 output[1] = 79 output[2] = 57 elif number == 205: '19号物体' output[0] = 95 output[1] = 92 output[2] = 51 elif number == 206: '20号物体' output[0] = 179 output[1] = 214 output[2] = 110 elif number == 207: '21号物体' output[0] = 227 output[1] = 160 output[2] = 93 elif number == 208: '22号物体' output[0] = 178 output[1] = 190 output[2] = 126 elif number == 209: '23号物体' output[0] = 56 output[1] = 13 output[2] = 49 else: output[0] = 5 output[1] = 5 output[2] = 5 return output output = np.zeros((map.shape[0], map.shape[1], 3), np.uint8) output_b = copy.copy(map) output_g = copy.copy(map) output_r = copy.copy(map) for i in range(np.array(Intensity_value).shape[0]): intensity = Intensity_value[i] output_r[map == intensity] = rgb_semantics(intensity)[0] output_g[map == intensity] = rgb_semantics(intensity)[1] output_b[map == intensity] = rgb_semantics(intensity)[2] output[:, :, 0] = output_b output[:, :, 1] = output_g output[:, :, 2] = output_r return output # 行扫描,间隔k时,进行填充,填充值为1 # 将门口连起来 def connect_door(wall_area, k, m): """ :param wall_area: :param k: 门宽 :param m: 墙壁长度 :return: """ def edge_connection(img, size, k, m): for i in range(size): Yi = np.where(img[i, :] > 220) if len(Yi[0]) >= m: # 可调整 (墙壁长度) for j in range(0, len(Yi[0]) - 1): if Yi[0][j + 1] - Yi[0][j] <= k: # img[min(i - 1, img.shape[0] - 1), Yi[0][j]:Yi[0][j + 1]] = 255 img[min(i, img.shape[0] - 1), Yi[0][j]:Yi[0][j + 1]] = 255 # img[i, Yi[0][j]:Yi[0][j + 1]] = 255 # img[min(i + 1, img.shape[0]-1), Yi[0][j]:Yi[0][j + 1]] = 255 return img img = copy.copy(wall_area) g = edge_connection(img, img.shape[0], k, m) # k设的是门的宽度, m是判为墙壁的长度 g = cv2.rotate(g, 0) g = edge_connection(g, img.shape[1], k, m) g = cv2.rotate(g, 2) g = g.astype(np.uint8) return g ''' 用于绘制地图及物体形状绘制 ''' '定义语义强度' Intensity_value = [115, 116, 117, 118, 119, 120, 121, 150, 151, 152, 153, 154, 155, 156, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209] # Intensity_value = [153] map = get_map(img, 4) '插值处理,提高分辨率' map = cv2.resize(map, (map.shape[1] * 2, map.shape[0] * 2), interpolation=cv2.INTER_NEAREST) map_ori = copy.copy(map) wall_area = get_wall_area(map, opt.acreage) # 获取墙壁区域 NON_wall_area = map - wall_area kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3)) wall_area = cv2.dilate(wall_area, kernel) connect_line = get_rotate_angle(wall_area, opt.length, opt) # 连接 connect_line = cv2.erode(connect_line, kernel, iterations=3) connect_line[connect_line < 150] = 0 connect_line[connect_line > 0] = 255 temp1 = copy.copy(connect_line) temp2 = copy.copy(NON_wall_area) temp1[temp1 > 0] = 1 temp = temp1 * temp2 NON_wall_area[temp != 0] = 0 result = connect_line + NON_wall_area result[result > 255] = 255 result = result.astype(np.uint8) result[result == 0] = 254 result[result == 255] = 0 result[result == 254] = 255 # cv2.imwrite("result.png", result) ''' 用于提取区域 ''' map_ = copy.copy(map) # 用于封闭区域 map_[map_ > 0] = 255 num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(map_, connectivity=8) wall_area_ = get_wall_area(map_, opt.acreage) # 获取墙壁区域 NON_wall_area_ = map_ - wall_area_ wall_area_ = cv2.dilate(wall_area_, kernel) connect_line_ = get_rotate_angle(wall_area_, opt.length, opt) # 连接 connect_line_[connect_line_ < 150] = 0 connect_line_[connect_line_ > 0] = 255 temp1_ = copy.copy(connect_line_) temp2_ = copy.copy(NON_wall_area_) temp1_[temp1_ > 0] = 1 temp_ = temp1_ * temp2_ NON_wall_area[temp_ != 0] = 0 result_ = connect_line_ + NON_wall_area_ result_[result_ > 255] = 255 result_ = result_.astype(np.uint8) result_[result_ == 0] = 254 result_[result_ == 255] = 0 result_[result_ == 254] = 255 # wall_area_ = rotate_bound(wall_area, -alfa / np.pi * 180) # 摆正图像 # # wall_area_ = cv2.dilate(wall_area_, kernel) # 腐蚀膨胀,去除墙上语义点 # # connect_line = connect_door(wall_area_, opt.k, opt.m) # 封闭区域 # connect_line_ = rotate_bound(connect_line, alfa / np.pi * 180) map_ori[map_ori == 0] = 254 map_ori[map_ori == 255] = 0 map_ori[map_ori == 254] = 255 return result, result_, map_ori # 绘制物体形状 def obj_outline(map, opt, map_ori): ''' . 绘制物体形状 . @param map 语义地图 . @param Semantic_map 场景分类图 . @param opt 参数 ''' def rotate_bound(image, angle): ''' . 旋转图片 . @param image opencv读取后的图像 . @param angle (逆)旋转角度 ''' # img = cv2.imread("img/1.jpg") (h, w) = image.shape[:2] # 返回(高,宽,色彩通道数),此处取前两个值返回 # 抓取旋转矩阵(应用角度的负值顺时针旋转)。参数1为旋转中心点;参数2为旋转角度,正的值表示逆时针旋转;参数3为各向同性的比例因子 M = cv2.getRotationMatrix2D((w / 2, h / 2), -angle, 1.0) # 计算图像的新边界维数 # 调整旋转矩阵以考虑平移 # 执行实际的旋转并返回图像 return cv2.warpAffine(image, M, (image.shape[1], image.shape[0])) # borderValue 缺省,默认是黑色 # return M # 根据不同语义物体得到不同参数 def get_parameter(item, opt): ''' . 得到物体参数 . @param item 物体的强度值 . @param opt 物体参数 ''' if (item == 115): min_samples = opt.min_samples_115 max_eps = opt.max_eps_115 shape = opt.shape_115 # 0是圆形,1是矩形 size = opt.size_115 # 0是按轮廓,1是直接画正矩形 color = [232, 221, 203] filter_point_num = 0 elif (item == 116): min_samples = opt.min_samples_116 max_eps = opt.max_eps_116 shape = opt.shape_116 # 0是圆形,1是矩形 size = opt.size_116 # 0是按轮廓,1是直接画正矩形 color = [205, 179, 128] filter_point_num = 0 elif (item == 117): min_samples = opt.min_samples_117 max_eps = opt.max_eps_117 shape = opt.shape_117 # 0是圆形,1是矩形 size = opt.size_117 # 0是按轮廓,1是直接画正矩形 color = [3, 101, 100] filter_point_num = 0 elif (item == 118): min_samples = opt.min_samples_118 max_eps = opt.max_eps_118 shape = opt.shape_118 # 0是圆形,1是矩形 size = opt.size_118 # 0是按轮廓,1是直接画正矩形 color = [3, 54, 73] filter_point_num = 0 elif (item == 119): min_samples = opt.min_samples_119 max_eps = opt.max_eps_119 shape = opt.shape_119 # 0是圆形,1是矩形 size = opt.size_119 # 0是按轮廓,1是直接画正矩形 color = [3, 22, 52] filter_point_num = 0 elif (item == 120): min_samples = opt.min_samples_120 max_eps = opt.max_eps_120 shape = opt.shape_120 # 0是圆形,1是矩形 size = opt.size_120 # 0是按轮廓,1是直接画正矩形 color = [237, 22, 139] filter_point_num = 0 elif (item == 121): min_samples = opt.min_samples_121 max_eps = opt.max_eps_121 shape = opt.shape_121 # 0是圆形,1是矩形 size = opt.size_121 # 0是按轮廓,1是直接画正矩形 color = [251, 178, 23] filter_point_num = 0 elif (item == 150): min_samples = opt.min_samples_150 max_eps = opt.max_eps_150 shape = opt.shape_150 # 0是圆形,1是矩形 size = opt.size_150 # 0是按轮廓,1是直接画正矩形 color = [96, 143, 159] filter_point_num = 0 elif (item == 151): min_samples = opt.min_samples_151 max_eps = opt.max_eps_151 shape = opt.shape_151 # 0是圆形,1是矩形 size = opt.size_151 # 0是按轮廓,1是直接画正矩形 color = [1, 77, 103] filter_point_num = 0 elif (item == 152): min_samples = opt.min_samples_152 max_eps = opt.max_eps_152 shape = opt.shape_152 # 0是圆形,1是矩形 size = opt.size_152 # 0是按轮廓,1是直接画正矩形 color = [254, 67, 101] filter_point_num = 0 elif (item == 153): min_samples = opt.min_samples_153 max_eps = opt.max_eps_153 shape = opt.shape_153 # 0是圆形,1是矩形 size = opt.size_153 # 0是按轮廓,1是直接画正矩形 color = [252, 157, 154] filter_point_num = 0 elif (item == 154): min_samples = opt.min_samples_154 max_eps = opt.max_eps_154 shape = opt.shape_154 # 0是圆形,1是矩形 size = opt.size_154 # 0是按轮廓,1是直接画正矩形 color = [249, 205, 173] filter_point_num = 0 elif (item == 155): min_samples = opt.min_samples_155 max_eps = opt.max_eps_155 shape = opt.shape_155 # 0是圆形,1是矩形 size = opt.size_155 # 0是按轮廓,1是直接画正矩形 color = [200, 200, 169] filter_point_num = 0 elif (item == 156): min_samples = opt.min_samples_156 max_eps = opt.max_eps_156 shape = opt.shape_156 # 0是圆形,1是矩形 size = opt.size_156 # 0是按轮廓,1是直接画正矩形 color = [131, 175, 155] filter_point_num = 0 elif (item == 200): min_samples = opt.min_samples_200 max_eps = opt.max_eps_200 shape = opt.shape_200 # 0是圆形,1是矩形 size = opt.size_200 # 0是按轮廓,1是直接画正矩形 color = [229, 187, 129] filter_point_num = 0 elif (item == 201): min_samples = opt.min_samples_201 max_eps = opt.max_eps_201 shape = opt.shape_201 # 0是圆形,1是矩形 size = opt.size_201 # 0是按轮廓,1是直接画正矩形 color = [161, 23, 21] filter_point_num = 0 elif (item == 202): min_samples = opt.min_samples_202 max_eps = opt.max_eps_202 shape = opt.shape_202 # 0是圆形,1是矩形 size = opt.size_202 # 0是按轮廓,1是直接画正矩形 color = [118, 77, 57] filter_point_num = 0 elif (item == 203): min_samples = opt.min_samples_203 max_eps = opt.max_eps_203 shape = opt.shape_203 # 0是圆形,1是矩形 size = opt.size_203 # 0是按轮廓,1是直接画正矩形 color = [17, 63, 61] filter_point_num = 0 elif (item == 204): min_samples = opt.min_samples_204 max_eps = opt.max_eps_204 shape = opt.shape_204 # 0是圆形,1是矩形 size = opt.size_204 # 0是按轮廓,1是直接画正矩形 color = [60, 79, 57] filter_point_num = 0 elif (item == 205): min_samples = opt.min_samples_205 max_eps = opt.max_eps_205 shape = opt.shape_205 # 0是圆形,1是矩形 size = opt.size_205 # 0是按轮廓,1是直接画正矩形 color = [95, 92, 51] filter_point_num = 0 elif (item == 209): min_samples = opt.min_samples_209 max_eps = opt.max_eps_209 shape = opt.shape_209 # 0是圆形,1是矩形 size = opt.size_209 # 0是按轮廓,1是直接画正矩形 color = [56, 13, 49] filter_point_num = 0 return min_samples, max_eps, shape, size, color, filter_point_num def filter_point(X, filter_point_num): obj_collect = np.zeros((map.shape[0], map.shape[1]), np.uint8) row = X[:, 0].astype(int) col = X[:, 1].astype(int) obj_collect[row, col] = 1 num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(obj_collect, connectivity=8) if (item == 204): for i in range(num_labels): if (stats[i, 4] <= filter_point_num): mask = labels == i obj_collect[:, :][mask] = 0 y, x = np.where(obj_collect == 1) # y是行,x是列 X = np.zeros((x.shape[0], 2)) X[:, 0] = y X[:, 1] = x return X # 画物体轮廓 def draw_outline(X, item, min_samples, max_eps, shape, color, size, filter_point_num, output): ''' . 得到物体参数 . @param X 语义物体的行列坐标 . @param item 语义物体的强度值 . @param min_samples 聚类的最小样本数 . @param max_eps 聚类的最大半径 . @param shape 语义物体绘制形状参数( 0代表绘制圆形,1代表绘制矩形) . @param color 语义物体的色彩 . @param size 语义物体绘制轮廓的参数(0代表按轮廓绘制,1代表绘制正矩形) . @param output 已经绘制过某个物体形状的 map图片 ''' # 聚类(OPTICS算法) # X = X.astype(np.uint8) yhat = OPTICS(min_samples=min_samples, max_eps=max_eps, cluster_method='dbscan').fit_predict(X) # 检索唯一群集 clusters = np.unique(yhat) ''' 画散点图 为每个群集的样本创建散点图 ''' # for cluster in clusters: # 获取此群集的示例的行索引 # row_ix = where(yhat == cluster) # 创建这些样本的散布 # pyplot.scatter(X[row_ix, 0], X[row_ix, 1]) # 绘制散点图 # pyplot.show() ''' 找最小外接矩形 (绘制形状) ''' color = color[::-1] for cluster in clusters: n = 0 # 获取此群集的示例的行索引,将每个物体从map中提取出来 if (cluster == -1): n = 1 continue row_ix = where(yhat == cluster) if row_ix[0].shape[0] <= 1: continue obj = np.zeros((map.shape[0], map.shape[1]), np.uint8) row = X[row_ix, 0].astype(int) col = X[row_ix, 1].astype(int) obj[row, col] = 1 if (item == 118 or item == 119 or item == 153 or item == 202): # if (item == 899): skeleton0 = morphology.skeletonize(obj) skeleton = skeleton0.astype(np.uint8) * 255 temps = copy.copy(skeleton) temp_outline = np.zeros((output.shape[0], output.shape[1], 3), np.uint8) for i in range(60, 0,-1): lines = cv2.HoughLines(skeleton, 1, np.pi / 180, i) if (lines is not None): if lines.shape[0] >0: break if (lines is None): n = 1 continue alfa = 0 for line in lines: rho = line[0][0] theta = line[0][1] a = np.cos(theta) b = np.sin(theta) x0 = a * rho y0 = b * rho theta = np.mod(theta, 90 / 180 * np.pi) if (theta > 45 / 180 * np.pi): theta = abs(theta - 90 / 180 * np.pi) x1 = int(x0 + 1000 * (-b)) y1 = int(y0 + 1000 * (a)) x2 = int(x0 - 1000 * (-b)) y2 = int(y0 - 1000 * (a)) # cv2.line(temps, (x1, y1), (x2, y2), 255, 2) alfa = theta + alfa alfa = alfa / lines.shape[0] skeleton = rotate_bound(skeleton, np.mod(alfa, 90 / 180 * np.pi) / np.pi * 180) # 摆正图像 角度为正值的时候,是顺时针旋转 skeleton_ = copy.copy(skeleton) # skeleton_[skeleton_ != 0] = 9 skeleton_ = (skeleton_ / 255 * 9).astype(np.uint8) # output = rotate_bound(output, -np.mod(alfa, 90 / 360 * np.pi) / np.pi * 180) img_blur = cv2.blur(copy.copy(skeleton_), (3, 3)) # img_blur = img_blur * 9 img_blur[img_blur != 2] = 0 img_blur[img_blur == 2] = 255 # img_blur[img_blur == 2] = 255 # img_blur[img_blur != 255] = 0 img_blur[skeleton == 0] = 0 corners = np.where(img_blur != 0) corners = np.array(corners) # corners = cv2.goodFeaturesToTrack(skeleton, 50, 0.1, 10) corners = np.int0(corners) temp_x = [] temp_y = [] for i in corners: x, y = i.ravel() # x是行,y是列 temp_x.append(x) temp_y.append(y) temp_X = np.array(temp_x) temp_Y = np.array(temp_y) a = np.min(temp_X) b = np.min(temp_Y) c = np.max(temp_X) d = np.max(temp_Y) cv2.rectangle(temp_outline, (a, b), (c, d), color, -1) temp_outline = rotate_bound(temp_outline, -np.mod(alfa, 90 / 180 * np.pi) / np.pi * 180) temp = cv2.cvtColor(temp_outline, cv2.COLOR_BGR2GRAY) # 方便用mask temp[temp > 0] = 255 output[temp_outline > 0] = 0 mask = 255 == temp output[:, :, 0][mask] = color[0] output[:, :, 1][mask] = color[1] output[:, :, 2][mask] = color[2] continue if (shape == 1): # 计算外接矩形 box = outer_rectangle(obj, size) # color = color[::-1] # 画图 if (size == 0): # 外接矩形缩放 # vertices = shrink_rectangle(box) vertices = box output = cv2.drawContours(output, [vertices], 0, color, -1, lineType=cv2.LINE_4) elif (size == 1): # 外接矩形缩放 # vertices = shrink_rectangle(box) vertices = box cv2.rectangle(output, (vertices[0, 0], vertices[0, 1]), (vertices[2, 0], vertices[2, 1]), color, -1) elif (shape == 0): # 计算最小拟合圆 circle_x = col circle_y = row center, radius = fit_circle(circle_x, circle_y) # binary, contours, hierarchy = cv2.findContours(obj, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) # center, radius = outer_circle(obj) cv2.circle(output, center, radius, color, -1) # else: # ellipse = outer_ellipse(obj) # if (len(ellipse) != 0): # cv2.ellipse(output, ellipse, color, -1) return output, n # 计算最小外接矩形 def outer_rectangle(obj, size): ''' . 计算最小外接矩形 . @param obj 语义物体的位置信息 . @param size 语义物体绘制轮廓的参数(0代表按轮廓绘制,1代表绘制正矩形) ''' binary, contours, hierarchy = cv2.findContours(obj, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contour = [] for cont in contours: contour.extend(cont) if (size == 0): min_rect = cv2.minAreaRect(np.array(contour)) box = cv2.boxPoints(min_rect) # box = np.round(box) box = np.int0(box) # 排序 temp = np.where(box == np.min(box[:, 0])) # box的第0列是列,第1列是行 if temp[0].shape[0] > 1: left = np.min(box[:, 0]) right = np.max(box[:, 0]) up = np.min(box[:, 1]) down = np.max(box[:, 1]) top_point_x = left top_point_y = up right_point_x = right right_point_y = up bottom_point_x = right bottom_point_y = down left_point_x = left left_point_y = down vertices = np.array( [[top_point_x, top_point_y], [right_point_x, right_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y]]) return vertices left_point_x = np.min(box[:, 0]) right_point_x = np.max(box[:, 0]) top_point_y = np.min(box[:, 1]) bottom_point_y = np.max(box[:, 1]) left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0] right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0] top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0] bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0] vertices = np.array( [[top_point_x, top_point_y], [right_point_x, right_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y]]) return vertices elif (size == 1): x, y, w, h = cv2.boundingRect(np.array(contour)) vertices = np.array([[x, y], [x + w, y], [x + w, y + h], [x, y + h]]) return vertices # 计算最小拟合椭圆 def outer_ellipse(obj): ''' . 计算最小拟合椭圆 . @param obj 语义物体的位置信息 ''' binary, contours, hierarchy = cv2.findContours(obj, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) contour = [] for cont in contours: contour.extend(cont) if (len(contour) < 6): ellipse = () else: ellipse = cv2.fitEllipse(np.array(contour)) return ellipse # 计算最小外接圆 def outer_circle(obj): ''' . 计算最小外接圆 . @param obj 语义物体的位置信息 ''' binary, contours, hierarchy = cv2.findContours(obj, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE) contour = [] for cont in contours: contour.extend(cont) (x, y), radius = cv2.minEnclosingCircle(np.array(contour)) center = (int(round(x)), int(round(y))) radius = int(round(radius)) return center, radius # 计算最小拟合圆 def calc_R(xc, yc, x, y): return np.sqrt((x - xc) ** 2 + (y - yc) ** 2) def f_2(c, x, y): Ri = calc_R(*c, x, y) return Ri - Ri.mean() def fit_circle(x, y): ''' . 计算最小外接圆 . @param x 语义物体的所在列 . @param x 语义物体的所在行 ''' x_m = mean(x) y_m = mean(y) x = x.ravel() y = y.ravel() center_estimate = x_m, y_m center_2, _ = optimize.leastsq(f_2, center_estimate, args=(x, y)) xc_2, yc_2 = center_2 Ri_2 = calc_R(xc_2, yc_2, x, y) # 拟合圆的半径 R_2 = Ri_2.mean() center = (int(round(xc_2)), int(round(yc_2))) radius = int(round(R_2)) return center, radius # 外接矩形内缩 def shrink_rectangle(box): ''' . 外接矩形内缩 . @param box 语义物体的轮廓信息 ''' temp = np.where(box == np.min(box[:, 0])) # box的第0列是列,第1列是行 if temp[0].shape[0] > 1: left = np.min(box[:, 0]) right = np.max(box[:, 0]) up = np.min(box[:, 1]) down = np.max(box[:, 1]) top_point_x = left + 1 top_point_y = up + 1 right_point_x = right - 1 right_point_y = up + 1 bottom_point_x = right - 1 bottom_point_y = down - 1 left_point_x = left + 1 left_point_y = down - 1 vertices = np.array( [[top_point_x, top_point_y], [right_point_x, right_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y]]) return vertices left_point_x = np.min(box[:, 0]) right_point_x = np.max(box[:, 0]) top_point_y = np.min(box[:, 1]) bottom_point_y = np.max(box[:, 1]) left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0] right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0] top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0] bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0] vertices = np.array( [[top_point_x, top_point_y], [right_point_x, right_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y]]) if top_point_x > bottom_point_x: vertices[0, 0] = vertices[0, 0] - 1 vertices[0, 1] = vertices[0, 1] + 1 vertices[2, 0] = vertices[2, 0] + 1 vertices[2, 1] = vertices[2, 1] - 1 if top_point_x < bottom_point_x: vertices[0, 0] = vertices[0, 0] + 1 vertices[0, 1] = vertices[0, 1] + 1 vertices[2, 0] = vertices[2, 0] - 1 vertices[2, 1] = vertices[2, 1] - 1 if right_point_y > left_point_y: vertices[1, 0] = vertices[1, 0] - 1 vertices[1, 1] = vertices[1, 1] - 1 vertices[3, 0] = vertices[3, 0] + 1 vertices[3, 1] = vertices[3, 1] + 1 if right_point_y < left_point_y: vertices[1, 0] = vertices[1, 0] - 1 vertices[1, 1] = vertices[1, 1] + 1 vertices[3, 0] = vertices[3, 0] + 1 vertices[3, 1] = vertices[3, 1] - 1 return vertices # 添加图例 def draw_legend(img, item, color, k): obj_name = {'115': 'Dog basin', '116': 'Bar chair base', '117': 'Fan base', '118': 'Washing machine', '119': 'Refrigerator', '120': 'Toilet', '121': 'Weighing scale', '150': 'Wire', '151': 'TV', '152': 'Desk', '153': 'Carpet', '154': 'Rag', '155': 'Tea table', '156': 'TV cabinet', '200': 'Slippers', '201': 'Sock', '202': 'Wardrobe', '203': 'Bed', '204': 'Sofa', '205': 'Chair', '209': 'Pet feces'} color = color[::-1] text = obj_name[str(item)] font = cv2.FONT_HERSHEY_TRIPLEX # cv2.rectangle(img, (output.shape[1] - 140, 10 + k * 17), (output.shape[1] - 120, 20 + k * 17), color, -1) # cv2.putText(img, text, (output.shape[1] - 100, 20 + k * 17), font, 0.4, color, 1, cv2.LINE_AA) cv2.rectangle(img, (10, 10 + k * 17), (30, 20 + k * 17), color, -1) cv2.putText(img, text, (40, 20 + k * 17), font, 0.5, color, 1, cv2.LINE_8) return img # 语义物体的强度值 # obj_value = [152, 121, 200, 115, 205, 117, 203] obj_value = [115, 116, 117, 118, 119, 120, 121, 150, 151, 152, 153, 154, 155, 156, 200, 201, 202, 203, 204, 205, 209] # obj_value = [153] obj_legend = [] # output是最后画上物体形状的总图 # output = Semantic_map # output = np.zeros((map.shape[0], map.shape[1], 3), np.uint8) # output[:, :, 0] = map # output[:, :, 1] = map # output[:, :, 2] = map k = 0 output = np.zeros((map.shape[0], map.shape[1], 3), np.uint8) legend_color = np.zeros((map.shape[0], map.shape[1], 3), np.uint8) # output[:, :, 0] = map # output[:, :, 1] = map # output[:, :, 2] = map # 对每个物体绘制形状 for item in iter(obj_value): if ((map == item).any()): # min_samples, max_eps, shape, size, color = get_parameter(item) min_samples, max_eps, shape, size, color, filter_point_num = get_parameter(item, opt) # num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(map, connectivity=8) y, x = np.where(map == item) # y是行,x是列 X = np.zeros((x.shape[0], 2)) X[:, 0] = y X[:, 1] = x X = filter_point(X, filter_point_num) if ((X.shape[0] == 0) or (min_samples > X.shape[0])): continue else: n = 0 output, n = draw_outline(X, item, min_samples, max_eps, shape, color, size, filter_point_num, output) if n != 1: if item not in obj_legend: obj_legend.append(item) legend_color = draw_legend(legend_color, item, color, k) k = k + 1 output = output + legend_color # legend_color[legend_color == 0] = 255 # img1 = cv2.cvtColor(output, cv2.COLOR_BGR2GRAY) # 方便统计尺寸 return output # 场景识别 def get_labels(img, semantic, opt, img_, map_ori): def get_wall_area(map, num_labels, labels, stats, area): ''' . map 获取墙壁的图像 . stats 连通域信息 . area 面积 ''' temp = [] for i in range(1, num_labels): if stats[i, 4] > area: # 第i个区域的面积大于阈值,则为墙壁 temp.append(i) wall_area = np.zeros((map.shape[0], map.shape[1]), np.uint8) for i in range(0, len(temp)): mask = labels == temp[i] wall_area[:, :][mask] = 255 return wall_area # def draw_legend(img,scene,color,k): # obj_name = {'0':'Dog basin','1':'Fan base','2':'Weighing scale','3':'Desk','4':'Slippers'} # color = color[::-1] # text = obj_name[str(scene)] # font = cv2.FONT_HERSHEY_DUPLEX # cv2.rectangle(img, (output.shape[1] - 140, 50+k*17), (output.shape[1] - 120, 60+k*17), color, -1) # cv2.putText(img, text, (output.shape[1] - 100, 60+k*17), font, 0.4, color, 1, cv2.LINE_AA) # return img def Scene(obj_label): ''' :param obj_label: :return: index "bedroom","livingroom","bathroom","kitchen" ,"unknow" ''' Intensity_value = [115, 116, 117, 118, 119, 120, 121, 150, 151, 152, 153, 154, 155, 156, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209] p_sce = np.ones((1, 5)) * 0.25 p_sce[0][4] = 0 p_obj_sce = np.array([[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0.01, 0.04, 0.8, 0], [0.02, 0.2, 0, 0.8], [0, 0, 0.99, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0.4, 0.4, 0, 0], [0.2, 0.4, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0.1, 0.4, 0, 0], [0.4, 0.6, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0.8, 0, 0, 0], [0.99, 0, 0, 0], [0.05, 0.5, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0] ]) p_temp = 1.0 p_obj = 0.3 # 目标检测出的置信度 pre_scene = np.zeros((1, 5)) if (len(obj_label) == 0): pre_scene[0][4] = 1 else: for i in range(4): for j in range(len(obj_label)): k = obj_label[j] sum_temp = np.sum(p_obj_sce[k]) if (sum_temp == 0): pre_scene[0][4] = 1 continue m = p_obj_sce[k, i] p_temp = (1.0 - (m / sum_temp * p_obj)) * p_temp pre_scene[0][i] = 1 - p_temp p_temp = 1.0 if np.max(pre_scene[0, 0:4]) > 0.1: pre_scene[0][4] = 0 sce_index = np.argmax(pre_scene) return sce_index def connect(pic, opt): hahaha = copy.copy(pic) # hahaha[hahaha == 0] = 254 # hahaha[hahaha == 255] = 0 hahaha[hahaha == 255] = 1 skeleton0 = morphology.skeletonize(hahaha) skeleton_ = skeleton0.astype(np.uint8) * 9 skeleton = skeleton0.astype(np.uint8) * 255 img_blur = cv2.blur(copy.copy(skeleton_), (3, 3)) # img_blur = img_blur * 9 img_blur[img_blur != 2] = 0 img_blur[img_blur == 2] = 255 img_blur[skeleton_ == 0] = 0 corners = np.where(img_blur != 0) corners = np.array(corners) # corners = cv2.goodFeaturesToTrack(skeleton, 200, 0.01, 3) # corners = np.int0(corners) # skeleton = skeleton / 255 # kernel = np.ones((1, 9)) temp_x = [] temp_y = [] for i in range(corners.shape[1]): x = corners[0, i] y = corners[1, i] # cov_corner = np.array([[skeleton[y - 1, x - 1], skeleton[y, x - 1], skeleton[y + 1, x - 1]], # [skeleton[y - 1, x], skeleton[y, x], skeleton[y + 1, x]], # [skeleton[y - 1, x + 1], skeleton[y, x + 1], skeleton[y + 1, x + 1]]]) # cov_corner[cov_corner > 0] = 1 # cov_corner = cov_corner.reshape(9, 1) # a3 = np.dot(kernel, cov_corner) # if a3 == 2: temp_x.append(x) temp_y.append(y) cv2.circle(skeleton, (y, x), 3, 255, -1) temp_X = np.array(temp_x) temp_Y = np.array(temp_y) temp = np.vstack((temp_X, temp_Y)).transpose() distance = 10000 * np.ones((temp.shape[0], temp.shape[0])) for i in range(temp.shape[0]): for j in range(i, temp.shape[0]): distance[i, j] = np.sqrt((temp[i][0] - temp[j][0]) ** 2 + (temp[i][1] - temp[j][1]) ** 2) distance[distance == 0] = 10000 if distance[i, j] < opt.distance: # z = np.argmin(distance[i, :]) cv2.line(pic, (temp[i, 1], temp[i, 0]), (temp[j, 1], temp[j, 0]), 255, 3) return pic # 计算最小外接矩形 def outer_rectangle(obj): ''' . 计算最小外接矩形 . @param obj 语义物体的位置信息 . @param size 语义物体绘制轮廓的参数(0代表按轮廓绘制,1代表绘制正矩形) ''' binary, contours, hierarchy = cv2.findContours(obj, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) contour = [] for cont in contours: contour.extend(cont) min_rect = cv2.minAreaRect(np.array(contour)) box = cv2.boxPoints(min_rect) # box = np.round(box) box = np.int0(box) # 排序 temp = np.where(box == np.min(box[:, 0])) # box的第0列是列,第1列是行 if temp[0].shape[0] > 1: left = np.min(box[:, 0]) right = np.max(box[:, 0]) up = np.min(box[:, 1]) down = np.max(box[:, 1]) top_point_x = left top_point_y = up right_point_x = right right_point_y = up bottom_point_x = right bottom_point_y = down left_point_x = left left_point_y = down vertices = np.array( [[top_point_x, top_point_y], [right_point_x, right_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y]]) return vertices left_point_x = np.min(box[:, 0]) right_point_x = np.max(box[:, 0]) top_point_y = np.min(box[:, 1]) bottom_point_y = np.max(box[:, 1]) left_point_y = box[:, 1][np.where(box[:, 0] == left_point_x)][0] right_point_y = box[:, 1][np.where(box[:, 0] == right_point_x)][0] top_point_x = box[:, 0][np.where(box[:, 1] == top_point_y)][0] bottom_point_x = box[:, 0][np.where(box[:, 1] == bottom_point_y)][0] vertices = np.array( [[top_point_x, top_point_y], [right_point_x, right_point_y], [bottom_point_x, bottom_point_y], [left_point_x, left_point_y]]) return vertices img1 = cv2.cvtColor(semantic, cv2.COLOR_BGR2GRAY) # 方便统计尺寸 img1[img1 > 0] = 1 # 提取语义 img2 = img * img1 # 提取语义 Intensity_value = [115, 116, 117, 118, 119, 120, 121, 150, 151, 152, 153, 154, 155, 156, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209] '只保留墙壁空间' temp = copy.copy(img) temp[temp == 0] = 254 temp[temp == 255] = 0 temp[temp == 254] = 255 temp[temp < 254] = 0 img3 = copy.copy(temp) # 提取非语义非墙壁区域 img3[img3 > 0] = 1 img3[img1 > 0] = 0 Semantic_map = np.zeros((img.shape[0], img.shape[1], 3), np.uint8) temp = temp.astype(np.uint8) num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(temp, connectivity=8) wall_area = get_wall_area(img, num_labels, labels, stats, opt.length) img_[img_ == 0] = 254 img_[img_ == 255] = 0 img_[img_ == 254] = 255 num_labels, labels, stats, centroids = cv2.connectedComponentsWithStats(img_, connectivity=8) wall_area_ = get_wall_area(img_, num_labels, labels, stats, opt.length) "pic 为分割区域的结果,作为一张区域分割后的地图,将分割结果传给后续场景识别,场景识别结果绘制在图Semantic_map," pic = copy.copy(wall_area_) pic = connect(pic, opt) img3[wall_area > 0] = 0 img3 = img3 * 255 img3[img3 == 0] = 254 img3[img3 == 255] = 0 img3[img3 == 254] = 255 wall_area[wall_area == 0] = 254 wall_area[wall_area == 255] = 0 wall_area[wall_area == 254] = 255 pic[pic == 0] = 254 pic[pic == 255] = 0 pic[pic == 254] = 255 wall_area_color = np.zeros((wall_area.shape[0], wall_area.shape[1], 3), np.uint8) wall_area_color[:, :, 0] = wall_area wall_area_color[:, :, 1] = wall_area wall_area_color[:, :, 2] = wall_area '提取房间' room_num_labels, room_labels, room_stats, room_centroids = cv2.connectedComponentsWithStats(pic, connectivity=8) k = 0 scene_legend = [] for i in range(2, room_num_labels): if room_stats[i, 4] < 200: continue room = np.zeros((wall_area.shape[0], wall_area.shape[1])) obj_label = [] mask = room_labels == i room[:, :][mask] = 1 kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5)) room = cv2.dilate(room, kernel, iterations=1) room_temp = copy.copy(room) room_temp = room_temp.astype(np.uint8) room_box = outer_rectangle(room_temp) room_outline_RGB = np.zeros((room_temp.shape[0], room_temp.shape[1], 3), np.uint8) room_outline_RGB = cv2.drawContours(room_outline_RGB, [room_box], 0, (0, 255, 0), -1, lineType=cv2.LINE_4) room_outline = cv2.cvtColor(room_outline_RGB, cv2.COLOR_BGR2GRAY) room_outline[room_outline > 0] = 1 room = room_outline # Semantic_area = room_outline * img2 Semantic_area = room * img2 Semantic_area = list(Semantic_area.flatten()) Semantic_area = list(set(Semantic_area)) Semantic_area.sort() for i in range(len(Semantic_area)): if Semantic_area[i] in Intensity_value: obj_label.append(Intensity_value.index(Semantic_area[i])) # obj_label_test = [] # 用于测试 # cv2.imshow("room",room*255) # cv2.waitKey(0) scene = Scene(obj_label) if scene == 0: Semantic_map[:, :, 0][room > 0] = 70 Semantic_map[:, :, 1][room > 0] = 80 Semantic_map[:, :, 2][room > 0] = 90 color = [70, 80, 90] if scene == 1: Semantic_map[:, :, 0][room > 0] = 100 Semantic_map[:, :, 1][room > 0] = 180 Semantic_map[:, :, 2][room > 0] = 120 color = [100, 180, 120] if scene == 2: Semantic_map[:, :, 0][room > 0] = 210 Semantic_map[:, :, 1][room > 0] = 67 Semantic_map[:, :, 2][room > 0] = 170 color = [210, 67, 170] if scene == 3: Semantic_map[:, :, 0][room > 0] = 150 Semantic_map[:, :, 1][room > 0] = 48 Semantic_map[:, :, 2][room > 0] = 88 color = [150, 48, 88] # if scene == 4: # Semantic_map[:, :, 0][mask] = 134 # Semantic_map[:, :, 1][mask] = 231 # Semantic_map[:, :, 2][mask] = 143 # color = [134, 231, 143] # if scene not in scene_legend: # scene_legend.append(scene) # Semantic_map = draw_legend(Semantic_map, scene, color, k) # k = k + 1 # img3[img3 > 0] = 255 Semantic_map = Semantic_map + wall_area_color Semantic_map[img1 > 0] = 0 # semantic[img1 < 1] = 0 Semantic_map = Semantic_map + semantic Semantic_map[img3 == 0] = 0 p = cv2.cvtColor(copy.copy(Semantic_map), cv2.COLOR_BGR2GRAY) # 方便统计尺寸 map_ori_RGB = np.zeros((map_ori.shape[0], map_ori.shape[1], 3), np.uint8) map_ori_RGB[:, :, 0][map_ori > 0] = 255 map_ori_RGB[:, :, 1][map_ori > 0] = 255 map_ori_RGB[:, :, 2][map_ori > 0] = 255 map_ori[p > 0] = 0 map_ori_RGB = map_ori_RGB + Semantic_map map_ori_RGB[img1 > 0] = 0 map_ori_RGB = map_ori_RGB + semantic map_ori_RGB[img3 == 0] = 0 return map_ori_RGB if __name__ == "__main__": img = cv2.imread("path/map/map6.png", 0) cv2.flip(img, 0, img) # 图片翻转 map, map_, map_ori = process_map(img, opt) # map:仅把门连起来; map_:把门连起来,成片语义点当成墙; map_ori: 无处理原图 # map_test = cv2.imread("mappp.jpg", 0) output = obj_outline(map_ori, opt, map_ori) # 参考map,画map_ori上 Semantic_map = get_labels(map, output, opt, map_, map_ori) cv2.imwrite("/path/result/Semantic_map.png",Semantic_map) cv2.imshow("Semantic_map",Semantic_map) cv2.waitKey(0)