def parse_json(json_file): annotations = mmcv.load(json_file)['annotations'] roofs, footprints, ignores, offsets = [], [], [], [] for annotation in annotations: roofs.append(wwtool.mask2polygon(annotation['roof'])) footprints.append(wwtool.mask2polygon(annotation['footprint'])) # ignore = annotation['ignore'] # offset = annotation['offset'] roof_polygons = geopandas.GeoSeries(roofs) footprint_polygons = geopandas.GeoSeries(footprints) if True: fig, ax = plt.subplots(1, 1) roof_df = geopandas.GeoDataFrame({'geometry': roof_polygons, 'foot_df':range(len(roof_polygons))}) footprint_df = geopandas.GeoDataFrame({'geometry': footprint_polygons, 'foot_df':range(len(footprint_polygons))}) roof_df.plot(ax=ax, color='red') footprint_df.plot(ax=ax, color='green') # plt.axis('off') # plt.savefig('./a.png', bbox_inches='tight', dpi=600, pad_inches=0.5) plt.show()
def mask_ratio(self): obb_ratio = [] hbb_ratio = [] for idx, _ in enumerate(self.imgIds): img = self.coco.loadImgs(self.imgIds[idx])[0] annIds = self.coco.getAnnIds(imgIds=img['id'], catIds=self.catIds, iscrowd=None) anns = self.coco.loadAnns(annIds) # per image for ann in anns: bbox = ann['bbox'] segmentation = ann['segmentation'] polygon = wwtool.mask2polygon(segmentation[0]) rbbox = polygon.minimum_rotated_rectangle polygon_area = polygon.area rbbox_area = rbbox.area bbox_area = bbox[2] * bbox[3] obb_ratio.append(polygon_area / rbbox_area) hbb_ratio.append(polygon_area / bbox_area) obb_ratio = np.array(obb_ratio) hbb_ratio = np.array(hbb_ratio) print("mean obb ratio: {}, mean hbb ratio: {}".format( obb_ratio.mean(), hbb_ratio.mean()))
def __simpletxt_parse__(self, label_file, image_file): """ (xmin, ymin, xmax, ymax) """ annotations = mmcv.load(label_file)['annotations'] # roof_mask, footprint_mask, roof_bbox, building_bbox, label, ignore, offset objects = [] for annotation in annotations: object_struct = {} roof_mask = annotation['roof'] roof_polygon = wwtool.mask2polygon(roof_mask) roof_bound = roof_polygon.bounds # xmin, ymin, xmax, ymax footprint_mask = annotation['footprint'] footprint_polygon = wwtool.mask2polygon(footprint_mask) footprint_bound = footprint_polygon.bounds building_xmin = np.minimum(roof_bound[0], footprint_bound[0]) building_ymin = np.minimum(roof_bound[1], footprint_bound[1]) building_xmax = np.maximum(roof_bound[2], footprint_bound[2]) building_ymax = np.maximum(roof_bound[3], footprint_bound[3]) building_bound = [ building_xmin, building_ymin, building_xmax, building_ymax ] xmin, ymin, xmax, ymax = list(roof_bound) bbox_w = xmax - xmin bbox_h = ymax - ymin object_struct['bbox'] = [xmin, ymin, bbox_w, bbox_h] object_struct['roof_bbox'] = object_struct['bbox'] xmin, ymin, xmax, ymax = list(building_bound) bbox_w = xmax - xmin bbox_h = ymax - ymin object_struct['building_bbox'] = [xmin, ymin, bbox_w, bbox_h] object_struct['roof_mask'] = roof_mask object_struct['footprint_mask'] = footprint_mask object_struct['ignore_flag'] = annotation['ignore'] object_struct['offset'] = annotation['offset'] object_struct['segmentation'] = roof_mask object_struct['label'] = 1 object_struct['iscrowd'] = object_struct['ignore_flag'] objects.append(object_struct) return objects
def simpletxt2json(self, image_fn): # 1. open the ignore file and get the polygons base_name = wwtool.get_basename(image_fn) sub_fold = base_name.split("__")[0].split('_')[0] ori_image_fn = "_".join(base_name.split("__")[0].split('_')[1:]) # if ori_image_fn in self.wrong_shp_file_dict[sub_fold]: # print("Skip this wrong shape file") # return coord_x, coord_y = base_name.split("__")[1].split( '_') # top left corner coord_x, coord_y = int(coord_x), int(coord_y) print( f"splitted items: {self.city}, {sub_fold}, {ori_image_fn}, {(coord_x, coord_y)}" ) ignore_file = './data/buildchange/{}/{}/{}/pixel_anno_v2/{}'.format( src_version, self.city, sub_fold, ori_image_fn + '.png') # print("ignore file name: ", ignore_file) roof_shp_file = './data/buildchange/{}/{}/{}/roof_shp_4326/{}'.format( src_version, self.city, sub_fold, ori_image_fn + '.shp') geo_info_file = './data/buildchange/{}/{}/{}/geo_info/{}'.format( src_version, self.city, sub_fold, ori_image_fn + '.png') objects = shp_parser(roof_shp_file, geo_info_file) roof_polygon_4326 = [obj['converted_polygon'] for obj in objects] roof_property = [obj['converted_property'] for obj in objects] pixel_anno = cv2.imread(ignore_file) if pixel_anno is None: return objects = mask_parser(pixel_anno[coord_y:coord_y + sub_img_h, coord_x:coord_x + sub_img_w, :], category=255) if objects == []: return ignore_polygons = [obj['polygon'] for obj in objects] # print("ignore polygon: ", ignore_polygons) # 2. read the simpletxt file and convert to polygons objects = self.simpletxt_parse( os.path.join(self.splitted_label_dir, base_name + '.txt')) roof_polygons = [ wwtool.mask2polygon(obj['polygon']) for obj in objects ] # print("roof polygon: ", roof_polygons) _, ignore_indexes = wwtool.cleaning_polygon_by_polygon( roof_polygons[:], ignore_polygons, show=False) ignore_list = len(roof_polygons) * [0] for ignore_index in ignore_indexes: ignore_list[ignore_index] = 1 new_anno_objects = [] for idx, roof_polygon in enumerate(roof_polygons): footprint_polygon, xoffset, yoffset = self.get_footprint( roof_polygon, [coord_x, coord_y], roof_polygon_4326, roof_property) object_struct = dict() ignore_flag = ignore_list[idx] object_struct['roof'] = wwtool.polygon2mask(roof_polygon) object_struct['footprint'] = wwtool.polygon2mask(footprint_polygon) object_struct['offset'] = [xoffset, yoffset] object_struct['ignore'] = ignore_flag new_anno_objects.append(object_struct) image_info = { "ori_filename": ori_image_fn + '.jpg', "subimage_filename": image_fn, "width": 1024, "height": 1024, "city": self.city, "sub_fold": sub_fold, "coordinate": [coord_x, coord_y] } json_data = {"image": image_info, "annotations": new_anno_objects} json_file = os.path.join(self.json_dir, f'{base_name}.json') with open(json_file, "w") as jsonfile: json.dump(json_data, jsonfile, indent=4)
def drop_subimage(self, subimages, subimage_coordinate, subimage_masks, center_area=2, small_object=64, show=False): """judge whether to drop the overlap image Arguments: subimages {dict} -- dict which contains all subimages (value) subimage_coordinate {tuple} -- the coordinate of subimage in original image subimage_masks {list} -- list of masks in subimages Keyword Arguments: center_area {int} -- the area of center line (default: {2}) show {bool} -- whether to show center line (default: {False}) Returns: drop flag -- True: drop the subimage, False: keep the subimage """ # black image if np.mean(subimages[subimage_coordinate]) == 0: return True # no object if len(subimage_masks) == 0: return True # keep the main subimage, just drop the overlap part if abs(subimage_coordinate[0] - subimage_coordinate[1]) in ( 0, 1024) and (subimage_coordinate[0] != 512 and subimage_coordinate[1] != 512): return False subimage_mask_area = [] subimage_mask_polygons = [] for subimage_mask in subimage_masks: subimage_mask_polygon = wwtool.mask2polygon(subimage_mask) subimage_mask_polygons.append(subimage_mask_polygon) subimage_mask_area.append(subimage_mask_polygon.area) # (horizontal, vertical) center_lines = [ Polygon([(0, 512 - center_area), (0, 512 + center_area), (1023, 512 + center_area), (1023, 512 - center_area), (0, 512 - center_area)]), Polygon([(512 - center_area, 0), (512 + center_area, 0), (512 + center_area, 1023), (512 - center_area, 1023), (512 - center_area, 0)]) ] if subimage_coordinate[0] == 512 and subimage_coordinate[1] != 512: center_lines = [center_lines[1]] elif subimage_coordinate[0] != 512 and subimage_coordinate[1] == 512: center_lines = [center_lines[0]] else: center_lines = center_lines subimage_mask_polygons = wwtool.clean_polygon(subimage_mask_polygons) subimage_mask_df = geopandas.GeoDataFrame({ 'geometry': subimage_mask_polygons, 'submask_df': range(len(subimage_mask_polygons)) }) center_line_df = geopandas.GeoDataFrame({ 'geometry': center_lines, 'center_df': range(len(center_lines)) }) image_border_polygon = [ Polygon([(0, 0), (1024 - 1, 0), (1024 - 1, 1024 - 1), (0, 1024 - 1), (0, 0)]) ] border_line_df = geopandas.GeoDataFrame({ 'geometry': image_border_polygon, 'border_df': range(len(image_border_polygon)) }) if show: fig, ax = plt.subplots() subimage_mask_df.plot(ax=ax, color='red') center_line_df.plot(ax=ax, facecolor='none', edgecolor='g') border_line_df.plot(ax=ax, facecolor='none', edgecolor='k') ax.set_title('{}_{}'.format(subimage_coordinate[0], subimage_coordinate[1])) plt.xticks([]) plt.yticks([]) plt.axis('off') # plt.show() plt.savefig('./{}_{}_{}.png'.format( self.image_fn.replace('.jpg', ''), subimage_coordinate[0], subimage_coordinate[1]), bbox_inches='tight', dpi=600, pad_inches=0.0) res_intersection = geopandas.overlay(subimage_mask_df, center_line_df, how='intersection') inter_dict = res_intersection.to_dict() ignore_indexes = list(set(inter_dict['submask_df'].values())) inter_areas = [] for ignore_index in ignore_indexes: inter_areas.append(subimage_mask_polygons[ignore_index].area) if len(inter_areas ) == 0 or max(inter_areas) < small_object * small_object: return True else: return False
def split_image(self, image_fn): self.image_fn = image_fn if not image_fn.endswith('.jpg'): return image_file = os.path.join(self.image_path, image_fn) shp_file = os.path.join(self.merged_shp_path, image_fn.replace('jpg', 'shp')) geo_file = os.path.join(self.geo_path, image_fn.replace('jpg', 'png')) pixel_anno_file = os.path.join(self.pixel_anno_path, image_fn.replace('jpg', 'png')) file_name = os.path.splitext(os.path.basename(image_file))[0] if not os.path.exists(shp_file): return img = cv2.imread(image_file) geo_info = rio.open(geo_file) print(pixel_anno_file) objects = self.shp_parser(shp_file, geo_info, ignore_file=pixel_anno_file, show_ignored_polygons=False) masks = np.array([obj['segmentation'] for obj in objects]) subimages = wwtool.split_image(img, subsize=self.subimage_size, gap=self.gap) subimage_coordinates = list(subimages.keys()) if masks.shape[0] == 0: return mask_centroids = [] for obj in objects: geometry = obj['converted_polygon'].centroid geo = geojson.Feature(geometry=geometry, properties={}) coordinate = geo.geometry["coordinates"] coordinate[0], coordinate[1] = abs(coordinate[0]), abs( coordinate[1]) mask_centroids.append(coordinate) mask_centroids = np.array(mask_centroids) mask_centroids_ = mask_centroids.copy() for subimage_coordinate in subimage_coordinates: objects = [] mask_centroids_[:, 0] = mask_centroids[:, 0] - subimage_coordinate[0] mask_centroids_[:, 1] = mask_centroids[:, 1] - subimage_coordinate[1] cx_bool = np.logical_and(mask_centroids_[:, 0] >= 0, mask_centroids_[:, 0] < subimage_size) cy_bool = np.logical_and(mask_centroids_[:, 1] >= 0, mask_centroids_[:, 1] < subimage_size) subimage_masks = masks[np.logical_and(cx_bool, cy_bool)] subimage_masks_ = [] for subimage_mask in subimage_masks: if wwtool.mask2polygon(subimage_mask).area < 5: continue subimage_mask_np = np.array(subimage_mask) subimage_mask_np[ 0::2] = subimage_mask_np[0::2] - subimage_coordinate[0] subimage_mask_np[ 1::2] = subimage_mask_np[1::2] - subimage_coordinate[1] subimage_masks_.append(subimage_mask_np.tolist()) subimage_masks = subimage_masks_ # cut the polygons by image boundary subimage_masks = wwtool.clip_mask(subimage_masks, image_size=(1024, 1024)) # judge whether to drop this subimage drop_flag = self.drop_subimage(subimages, subimage_coordinate, subimage_masks, show=self.show) if drop_flag: continue img = subimages[subimage_coordinate] label_save_file = os.path.join( self.label_save_path, '{}__{}_{}.txt'.format(file_name, subimage_coordinate[0], subimage_coordinate[1])) image_save_file = os.path.join( self.image_save_path, '{}__{}_{}.png'.format(file_name, subimage_coordinate[0], subimage_coordinate[1])) cv2.imwrite(image_save_file, img) for subimage_mask in subimage_masks: subimage_objects = dict() subimage_objects['mask'] = subimage_mask subimage_objects['label'] = 'building' objects.append(subimage_objects) wwtool.simpletxt_dump(objects, label_save_file, encode='mask')
cv2.CHAIN_APPROX_SIMPLE) contours = contours[0] if len( contours) == 2 else contours[1] if contours != []: cnt = max(contours, key=cv2.contourArea) if cv2.contourArea(cnt) < 5: continue mask = np.array(cnt).reshape(1, -1).tolist()[0] if len(mask) < 8: continue else: continue # TODO: convert to wkt format polygon = wwtool.mask2polygon(mask) roof_masks.append(mask) polygons.append(polygon) bbox = bboxes[i][0:4] w, h = bbox[2] - bbox[0], bbox[3] - bbox[1] transform_matrix = [ 1, 0, 0, 1, -1.0 * offset[0], -1.0 * offset[1] ] footprint_polygon = affinity.affine_transform( polygon, transform_matrix) footprint_mask = wwtool.polygon2mask(footprint_polygon) footprint_masks.append(footprint_mask) footprint_polygons.append(footprint_polygon) print(img['file_name'])
for key in keywords: csv_file = './data/buildchange/v2/xian_fine/xian_fine_{}_gt.csv'.format( key) first_in = True json_dir = './data/buildchange/v2/xian_fine/labels_json' rgb_img_dir = './data/buildchange/v2/xian_fine/images' for json_fn in os.listdir(json_dir): base_name = wwtool.get_basename(json_fn) rgb_img_file = os.path.join(rgb_img_dir, base_name + '.png') json_file = os.path.join(json_dir, json_fn) annotations = mmcv.load(json_file)['annotations'] masks = [wwtool.mask2polygon(anno[key]) for anno in annotations] csv_image = pandas.DataFrame({ 'ImageId': base_name, 'BuildingId': range(len(masks)), 'PolygonWKT_Pix': masks, 'Confidence': 1 }) if first_in: csv_dataset = csv_image first_in = False else: csv_dataset = csv_dataset.append(csv_image) csv_dataset.to_csv(csv_file, index=False)
rgb_img_file = os.path.join(rgb_img_dir, base_name + '.jpg') shp_file = os.path.join(shp_dir, shp_fn) rgb_img = cv2.imread(rgb_img_file) geo_info = rio.open(rgb_img_file) shp_parser = wwtool.ShpParse() objects = shp_parser(shp_file, geo_info, coord='pixel', merge_flag=True, connection_mode='floor') gt_polygons = [ wwtool.mask2polygon(obj['segmentation']) for obj in objects ] # wwtool.show_polygons_on_image(gt_masks, rgb_img, output_file=None) csv_image = pandas.DataFrame({ 'ImageId': sub_fold + '_' + base_name, 'BuildingId': range(len(gt_polygons)), 'PolygonWKT_Pix': gt_polygons, 'Confidence': 1 }) if first_in: csv_dataset = csv_image first_in = False else: csv_dataset = csv_dataset.append(csv_image)