def find_wall_ids_for_windows(windows, walls):
    #if DEBUG:
    #  windows = windows[15:17,:]
    #  Bbox3D.draw_points_bboxes(windows[:,0:3], walls, 'Z', False)
    win_in_walls = Bbox3D.points_in_bbox(windows[:, 0:3], walls)
    wall_nums_per_win = win_in_walls.sum(1)
    if wall_nums_per_win.max() > 1:
        #win_mw_ids, wall_ids_ = np.where(win_in_walls)
        win_mw_ids = np.where(wall_nums_per_win > 1)[0]

        windows_thickness_multi = windows[win_mw_ids, 4]
        windows_thickness_rate_multi = windows[win_mw_ids,
                                               4] / windows[win_mw_ids, 3]
        thickness_small = windows_thickness_multi.max() < 0.25
        thickness_rate_small = windows_thickness_rate_multi.max() < 0.25
        if not (thickness_small or thickness_rate_small):
            show_high(windows, walls, win_mw_ids, [])
            print(f'windows_missed:\n{windows[win_mw_ids]}')
            print(
                "There is some windows, find multiple responding walls and thickness is not small."
            )
            assert False
            import pdb
            pdb.set_trace()  # XXX BREAKPOINT
            pass

    if wall_nums_per_win.min() == 0:
        # a window centroid is inside of no wall
        missed_win_ids = np.where(wall_nums_per_win == 0)[0]
        windows_thickness_missed = windows[missed_win_ids, 4]
        windows_thickness_rate_missed = windows[missed_win_ids,
                                                4] / windows[missed_win_ids, 3]
        thickness_small = windows_thickness_missed.max() < 0.25
        thickness_rate_small = windows_thickness_rate_missed.max() < 0.25
        if not (thickness_small or thickness_rate_small):
            #show_high(windows, walls, missed_win_ids, [])
            print(f'windows_missed:\n{windows[missed_win_ids]}')
            print(
                "There is some windows, cannot find responding wall and thickness is not small."
            )
            assert False
            import pdb
            pdb.set_trace()  # XXX BREAKPOINT
            pass

        #cen_lines_wall = Bbox3D.bboxes_centroid_lines(walls, 'X', 'Z')
        #cen_lines_wall[:,:,2] = 0
        #for i in missed_win_ids:
        #  point_i = windows[i,0:3]
        #  point_i[2] = 0
        #  dis_i = vertical_dis_1point_lines(point_i, cen_lines_wall)
        #  #show_high(windows, walls, [i], [])
        #  pass

    win_bad_ids0, wall_ids0 = np.where(win_in_walls)
    wall_yaws0 = walls[wall_ids0, -1]
    # when responding wall yaw is not vertical or horizonal, the window is bad
    yaw_bad0 = (np.abs(wall_yaws0) / (np.pi * 0.5)) % 1 > 0.01
    win_bad_ids1 = np.where(yaw_bad0)[0]

    win_bad_ids = win_bad_ids0[win_bad_ids1]
    wall_ids_for_bad_win = wall_ids0[win_bad_ids1]

    if DEBUG and False:
        for i in range(win_bad_ids.shape[0]):
            print(f'bad window {i}/{win_bad_ids.shape[0]}')
            show_high(windows, walls, win_bad_ids[i], wall_ids_for_bad_win[i])
    return win_bad_ids, wall_ids_for_bad_win
def preprocess_cfr(ceilings_org, walls_org, obj):
    '''
    Z is up, Y is thickness
    A ceiling is good:
      (1) not contains multiple rooms:
        cover no other ceilings
      (2) >= 3 edge walls
    A edge wall of a ceiling:
      In three points of the wall cenline: two corners and centroid
      at least two are on an edge of ceiling
  '''
    assert ceilings_org.ndim == walls_org.ndim == 2
    dis_threshold = 0.07
    if ceilings_org.shape[0] == 0:
        return ceilings_org
    if walls_org.shape[0] == 0:
        return np.zeros(shape=[0, 7], dtype=np.float32)

    #Bbox3D.draw_bboxes(walls_org, 'Z', False)
    #Bbox3D.draw_bboxes(ceilings_org, 'Z', False)
    ceilings = ceilings_org.copy()
    ceilings, keep_ids0 = clean_repeat(ceilings)
    walls = walls_org.copy()
    cn = ceilings.shape[0]

    ceilings[:, 2] = 0
    walls[:, 2] = 0
    walls[:, 5] = 0

    ceil_corners0 = Bbox3D.bboxes_corners(ceilings, 'Z')
    ceil_corners = np.take(ceil_corners0, Bbox3D._zpos_vs, axis=1)
    ceil_corners[:, :, 2] = 0

    ceiling_cens = ceilings[:, 0:3]
    wall_cenlines = Bbox3D.bboxes_centroid_lines(walls, 'X', 'Z')

    good_ceiling_ids0 = []
    bad_small_ids = []

    for c in range(cn):
        # (1)
        #print(f'c:{c}')
        ceil_c = ceilings[c:c + 1].copy()
        ceil_c[:, 3:6] += 0.2
        mask_overlap = Bbox3D.points_in_bbox(ceil_corners.reshape([-1, 3]),
                                             ceil_c).reshape([cn, 4])
        mask_overlap = mask_overlap.all(1)
        num_overlap = mask_overlap.sum() - 1
        ol_ids = np.where(mask_overlap)[0]
        ol_ids = [i for i in ol_ids if i != c]
        #if any_overlap:
        #  area_ol = np.product(tmp[ol_ids,3:5], axis=1).sum()
        #  area_c = np.product(ceilings[c,3:5])
        #  area_ol_rate = area_ol / area_c
        #  import pdb; pdb.set_trace()  # XXX BREAKPOINT
        #  pass
        if Debug and 0:
            print(f'ceiling, contain {num_overlap} other celings')
            box_show = np.concatenate([walls_org, ceilings_org[c:c + 1]], 0)
            Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)
        if num_overlap > 1:
            continue
        #if num_overlap >0:
        #  bad_small_ids += ol_ids

        # (2)
        edge_wall_num, winc_state = is_edge_wall_of_ceiling(
            wall_cenlines, ceilings[c], walls_org)
        if edge_wall_num >= 3 or (edge_wall_num == 2 and
                                  (winc_state == 3).all()):
            good_ceiling_ids0.append(c)

    #good_ceiling_ids1 = [i for i in good_ceiling_ids0 if i not in bad_small_ids]
    good_ceiling_ids1 = keep_ids0[good_ceiling_ids0]
    good_ceiling_ids1 = np.array(good_ceiling_ids1).astype(np.int)
    rm_num = cn - good_ceiling_ids1.shape[0]

    bad_ceiling_ids = np.array(
        [i for i in range(cn) if i not in good_ceiling_ids1]).astype(np.int32)
    new_ceilings = ceilings_org[good_ceiling_ids1]

    if Debug and rm_num > 0:
        print(f'{cn} -> {good_ceiling_ids1.shape[0]}')
        box_show = np.concatenate([walls_org, ceilings_org[good_ceiling_ids1]],
                                  0)
        Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)
        if rm_num > 0:
            box_show = np.concatenate(
                [walls_org, ceilings_org[bad_ceiling_ids]], 0)
            Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)
        import pdb
        pdb.set_trace()  # XXX BREAKPOINT
        #show_walls_1by1(new_ceilings)
        pass
    return new_ceilings
  def split_bbox(bbox_fn, points_splited):
    '''
    bbox in file bbox_fn: up_axis='Y' with always x_size > z_size

    transform with cam2world_box:
      up_axis == 'Z'
      always: x_size > y_size
    '''
    obj = os.path.basename(bbox_fn).split('.')[0]
    min_point_num_per1sm = 10
    # thickness_aug for cropping x
    thickness_aug = 0.3
    assert IndoorData._block_size0[-1] == -1 # do  not crop box along z

    bboxes = np.loadtxt(bbox_fn).reshape([-1,7])
    #if DEBUG:
    #  #show_walls_1by1(bboxes)
    #  bboxes = bboxes[3:4]

    areas = bboxes[:,3] * bboxes[:,5]
    min_point_num = np.minimum( min_point_num_per1sm * areas, 200 )
    bboxes_aug = bboxes.copy()
    #bboxes_aug[:,4] += thickness_aug
    bboxes_aug[:,3:6] = np.clip(bboxes_aug[:,3:6],a_min= thickness_aug, a_max=None )
    bn = bboxes.shape[0]

    sn = len(points_splited)
    bboxes_splited = []
    for i in range(0, sn):
      #  Use to constrain size_x size_z
      point_masks_aug_i = Bbox3D.points_in_bbox(points_splited[i][:,0:3].copy(), bboxes_aug.copy())
      #  Use to constrain size_y (the thickness)
      bboxes_tc = IndoorData.adjust_box_for_thickness_crop(bboxes)
      point_masks_i = Bbox3D.points_in_bbox(points_splited[i][:,0:3].copy(), bboxes_tc)

      pn_in_box_aug_i = np.sum(point_masks_aug_i, 0)
      pn_in_box_i = np.sum(point_masks_i, 0)
      #print(f'no aug:{pn_in_box_i}\n auged:{pn_in_box_aug_i}')

      # (1) The bboxes with no points with thickness_aug will be removed firstly
      keep_box_aug_i = pn_in_box_aug_i > min_point_num
      bboxes_i = bboxes[keep_box_aug_i]

      if DEBUG and obj=='ceiling' and 0:
        rm_box_aug_i = pn_in_box_aug_i <= min_point_num
        print(rm_box_aug_i)
        bboxes_no_points_i = bboxes[rm_box_aug_i].copy()
        bboxes_no_points_i[:,0] += 30
        bboxes_show = np.concatenate([bboxes_i, bboxes_no_points_i],0)
        Bbox3D.draw_points_bboxes(points_splited[i], bboxes_show, up_axis='Z', is_yx_zb=False)

      points_aug_i = [points_splited[i][point_masks_aug_i[:,j]] for j in range(bn)]
      points_aug_i = [points_aug_i[j] for j in range(bn) if keep_box_aug_i[j]]

      points_i = [points_splited[i][point_masks_i[:,j]] for j in range(bn)]
      points_i = [points_i[j] for j in range(bn) if keep_box_aug_i[j]]

      # (2) Crop all the boxes by points and intersec_corners seperately
      bn_i = bboxes_i.shape[0]
      croped_bboxes_i = []
      keep_unseen_intersection = False
      if keep_unseen_intersection:
        intersec_corners_idx_i, intersec_corners_i = Bbox3D.detect_all_intersection_corners(bboxes_i, 'Z')
      else:
        intersec_corners_idx_i = [None]*bn_i
      invalid_bn = 0
      for k in range(bn_i):
        croped_box_k =  Bbox3D.crop_bbox_by_points(
                          bboxes_i[k], points_i[k], points_aug_i[k], 'Z', intersec_corners_idx_i[k])
        if  croped_box_k is  not None:
          croped_bboxes_i.append( croped_box_k )
        else:
          invalid_bn += 1
          #Bbox3D.draw_points_bboxes(points_splited[i], bboxes_i[k:k+1], up_axis='Z', is_yx_zb=False, points_keep_rate=1.0)
          pass
      if len(croped_bboxes_i) > 0:
        croped_bboxes_i = np.concatenate(croped_bboxes_i, 0)
      else:
        croped_bboxes_i = np.array([]).reshape([-1,7])

      # (3) Refine x size of each bbox by thickness croped of intersected bbox
      #croped_size = bboxes_i[:,3:6] - croped_bboxes_i[:,3:6]
      refine_x_by_intersection = False # not correct yet
      if refine_x_by_intersection:
        for k in range(bn_i):
          itsc0, itsc1 = intersec_corners_idx_i[k]
          crop_value = [0,0]
          if itsc0 >= 0:
            crop_value[0] = Bbox3D.refine_intersection_x(croped_bboxes_i[k], 'neg', croped_bboxes_i[itsc0], 'Z')
          if itsc1 >= 0:
            crop_value[1] = Bbox3D.refine_intersection_x(croped_bboxes_i[k], 'pos', croped_bboxes_i[itsc1], 'Z')
          if itsc0 >= 0 or itsc1 > 0:
            croped_bboxes_i[k] = Bbox3D.crop_bbox_size(croped_bboxes_i[k], 'X', crop_value)

          #crop_ysize_neg = croped_size[itsc0,1] if itsc0 >= 0 else 0
          #crop_ysize_pos = croped_size[itsc1,1] if itsc1 >= 0 else 0
          #croped_bboxes_i[k] = Bbox3D.crop_bbox_size(croped_bboxes_i[k], 'X', [crop_ysize_neg, crop_ysize_pos])

      # (4) remove too small wall
      min_wall_size_x = 0.2
      sizex_mask = croped_bboxes_i[:,3] > min_wall_size_x
      croped_bboxes_i = croped_bboxes_i[sizex_mask]
      bboxes_splited.append(croped_bboxes_i)

      show = False
      if show and DEBUG and len(points_i) > 0 and obj=='ceiling':
        print(croped_bboxes_i[:,3])
        points = np.concatenate(points_i,0)
        points = points_splited[i]
        points1 = points.copy()
        points1[:,0] += 30
        points = np.concatenate([points, points1], 0)
        bboxes_i[:,0] += 30
        bboxes_i = np.concatenate([bboxes_i, croped_bboxes_i], 0)
        #Bbox3D.draw_points_bboxes(points, bboxes_i, up_axis='Z', is_yx_zb=False)
        Bbox3D.draw_points_bboxes_mesh(points, bboxes_i, up_axis='Z', is_yx_zb=False)
        import pdb; pdb.set_trace()  # XXX BREAKPOINT
        pass
    return bboxes_splited
Beispiel #4
0
def preprocess_doors(doors0, walls, door_thickness=0.18):
    '''
  doors0:[d,7]
  walls:[w,7]
  '''
    door_n = doors0.shape[0]
    if door_n == 0:
        return doors0
    wall_n = walls.shape[0]
    walls_1 = walls.copy()
    # (1) Find the coresponding wall of each door
    # (1.1) Get the four top lines of each door
    door_corners = Bbox3D.bboxes_corners(doors0, 'Z')[:, 0:4, :]  # [d,4,3]
    door_lines0 = np.take(door_corners, Bbox3D._lines_z0_vids,
                          axis=1)  # [d,4,2,3]
    door_lines1 = door_lines0.reshape(-1, 2, 3)
    # (1.2) Get the centroid lines of each wall
    wall_cen_lines = Bbox3D.bboxes_centroid_lines(walls_1, 'X', 'Z')

    # (1.3) Get the intersections_2d between lines and walls
    # A door matches a wall means: there are two top lines of the door, which are
    # intersected with the wall centroid line.
    dw_ints = lines_intersection_2d(door_lines1[:, :, 0:2],
                                    wall_cen_lines[:, :, 0:2], True, True)
    dw_ints = dw_ints.reshape(door_n, 4, wall_n, 2)  # [d,4,w,2]
    tmp0 = (1 - np.isnan(dw_ints)).sum(3)  # [d,4,w]
    door_mask0 = tmp0.sum(1) == 4
    door_matched0 = door_mask0.sum(1).reshape(-1, 1)

    # (1.4) Sometimes on door can match two walls. Remove the confused one by:
    # The right wall should not contain any corners
    dc_in_wall_mask = Bbox3D.points_in_bbox(door_corners.reshape(-1, 3),
                                            walls).reshape(door_n, 4, wall_n)
    dc_in_wall_mask = np.any(dc_in_wall_mask, axis=1)
    bad_match = (door_matched0 > 1) * dc_in_wall_mask
    door_mask = door_mask0 * (1 - bad_match)

    door_ids, wall_ids = np.where(door_mask)
    success_num = door_ids.shape[0]

    # (2) Pick the failed door ids
    wall_num_each_door = door_mask.sum(1)
    fail_match_door_ids = np.where(wall_num_each_door != 1)[0]

    walls_good = walls[wall_ids]
    yaws = limit_period(walls_good[:, -1], 0, np.pi / 2)

    intersections_2d = []
    for i in range(success_num):
        door_idx = door_ids[i]
        cids = np.where(tmp0[door_idx, :, wall_ids[i]])[0]
        ints_i = dw_ints[door_idx, cids, wall_ids[i]]
        intersections_2d.append(ints_i.reshape(1, 2, 2))
    if len(intersections_2d) == 0:
        return np.empty([0, 7])
    intersections_2d = np.concatenate(intersections_2d, 0)

    doors_centroids_2d = np.mean(intersections_2d, 1)
    doors_length = np.linalg.norm(intersections_2d[:, 0] -
                                  intersections_2d[:, 1],
                                  axis=1)
    doors_length -= door_thickness * np.sin(yaws * 2)

    doors_new = doors0[door_ids].copy()
    doors_new[:, 0:2] = doors_centroids_2d
    doors_new[:, 3] = doors_length
    doors_new[:, 4] = door_thickness
    doors_new[:, 6] = walls_good[:, -1]

    if DEBUG and fail_match_door_ids.shape[0] > 0:
        print(f"fail_match_door_ids:{fail_match_door_ids}")
        show_all([doors0[fail_match_door_ids], walls])
        #for dids in fail_d_ids:
        #  fail_w_ids = np.where(door_mask[dids])[0]
        #  show_all([doors0[dids].reshape(-1,7), walls[fail_w_ids].reshape(-1,7)])
        import pdb
        pdb.set_trace()  # XXX BREAKPOINT
        pass

    if DEBUG:
        show_all([doors0, doors_new, walls])
    return doors_new
def gen_pcl(house_fn):
    always_gen_pcl = False
    check_points_out_of_house = False

    parsed_dir = get_pcl_path(house_fn)
    pcl_fn = os.path.join(parsed_dir, 'pcl_camref.ply')
    pcl_intact = check_pcl_intact(parsed_dir)
    if pcl_intact and (not always_gen_pcl):
        print(f'{pcl_fn} intact')
        return True

    # read house scope, abandon images with points out of house scope
    images_intact = check_images_intact(parsed_dir)
    if not images_intact:
        print(f'images not intact, abort generating pcl')
        return

    house_fn = os.path.join(parsed_dir, 'object_bbox/house.txt')
    house_box = np.loadtxt(house_fn)

    base_dir = os.path.dirname(house_fn)
    cam_fn = os.path.join(base_dir, parsed_dir + '/cam')
    #if not os.path.exists(cam_fn):
    #  print(f'{cam_fn} does not exist, abort gen_pcl')
    #  return False
    cam_pos = read_cam_pos(cam_fn)
    depth_fns = glob.glob(
        os.path.join(base_dir, parsed_dir + '/images/*_depth.png'))
    #if cam_pos.shape[0] < MIN_CAM_NUM or len(depth_fns) != cam_pos.shape[0]:
    #  print(f'{cam_pos.shape[0]} cams,  but {len(depth_fns)} images')
    #  return False
    depth_fns.sort()
    print(f'start gen pcl {pcl_fn}\n with {len(depth_fns)} images')

    gen_ply_each_image = False
    if gen_ply_each_image:
        pcl_path = parsed_dir + '/pcls'
        if not os.path.exists(pcl_path):
            os.makedirs(pcl_path)
    pcls_all = []

    max_point_num = 2e7
    cam_num = cam_pos.shape[0] * 1.0
    pre_downsample_num = max(2e5, int(max_point_num / cam_num))
    print(f'pre_downsample_num: {pre_downsample_num}')

    for i, depth_fn in enumerate(depth_fns):
        pcl_i = depth_2_pcl(depth_fn, cam_pos[i])

        if check_points_out_of_house:
            # check if the points are out of house scope
            # do not know why this happens sometims, just use this ineligant way to
            # solve it temperally
            if pcl_i.shape[0] > 1000:
                ids = np.sort(np.random.choice(pcl_i.shape[0], 1000, False))
            else:
                ids = np.arange(pcl_i.shape[0])
            mask = Bbox3D.points_in_bbox(pcl_i[ids, 0:3],
                                         house_box.reshape([1, 7]))
            if not mask.all():
                print(f'some points are out of box in image {i}')
                continue

        if pre_downsample_num < pcl_i.shape[0]:
            pcl_i = random_sample_pcl(pcl_i, pre_downsample_num)
        pcls_all.append(pcl_i)

        if gen_ply_each_image:
            pcd = open3d.PointCloud()
            pcd.points = open3d.Vector3dVector(pcl_i)
            base_name = os.path.basename(depth_fns[i]).replace(
                'depth.png', 'pcl.ply')
            pcl_fn = os.path.join(pcl_path, base_name)
            open3d.write_point_cloud(pcl_fn, pcd)
            open3d.draw_geometries([pcd])
            import pdb
            pdb.set_trace()  # XXX BREAKPOINT
            pass

    pcls_all = np.concatenate(pcls_all, 0)
    if pcls_all.shape[0] > 5e6:
        print(f'{pcls_all.shape[0]} random to 5e6')
        pcls_all = random_sample_pcl(pcls_all, int(5e6))
    org_num = pcls_all.shape[0]
    print(f'org point num: {pcls_all.shape[0]/1000} K')
    if org_num < MIN_POINT_NUM:
        print(
            f'only {org_num} points, del cams and re-generate later\n del {parsed_dir}'
        )
        import shutil
        shutil.rmtree(parsed_dir)
        return False

    #if org_num > 1e7:
    #    pcls_all = random_sample_pcl(pcls_all, int(max(1e7, org_num/10)) )
    #    print(f'random sampling to point num: {pcls_all.shape[0]/1000} K')

    pcd = open3d.PointCloud()
    pcd.points = open3d.Vector3dVector(pcls_all[:, 0:3])
    pcd.colors = open3d.Vector3dVector(pcls_all[:, 3:6])
    pcd = open3d.voxel_down_sample(pcd, voxel_size=0.02)
    new_num = np.asarray(pcd.points).shape[0]
    print(f'new point num: {new_num/1000.0} K')
    open3d.write_point_cloud(pcl_fn, pcd)
    #open3d.draw_geometries([pcd])

    write_summary(parsed_dir, 'points_num', new_num, 'a')
    pcl_size, xyarea = get_pcl_size(pcls_all[:, 0:3])
    write_summary(parsed_dir, 'scene_size', pcl_size, 'a')
    write_summary(parsed_dir, 'xyarea', xyarea, 'a')
    #open3d.draw_geometries([pcd])
    return True
def preprocess_cfr(ceilings_org, walls_org, obj):
    '''
  Z is up, Y is thickness
  '''
    #Bbox3D.draw_bboxes(walls, 'Z', False)
    ceilings = ceilings_org.copy()
    walls = walls_org.copy()
    walls = replace_slant_walls(walls)

    dis_threshold = 0.07

    ceiling_cens = ceilings[:, 0:3]
    ceiling_cens[:, 2] = 0
    ceil_cenlines_x = Bbox3D.bboxes_centroid_lines(ceilings, 'X', 'Z')
    ceil_cenlines_x[:, :, 2] = 0
    #ceil_cenlines_y = Bbox3D.bboxes_centroid_lines(ceilings, 'Y', 'Z')
    wall_cenlines = Bbox3D.bboxes_centroid_lines(walls, 'X', 'Z')
    wall_cenlines[:, :, 2] = 0

    ceilings_shrink = ceilings.copy()
    ceilings_shrink[:, 3:5] -= 0.3

    cn = ceilings.shape[0]

    ## Find edge wall nums
    good_ceiling_ids = []
    for c in range(cn):
        # (0.1) If no any other overlap ceiling, try to keep it
        # Otherwise, delete it when  >3 wall inside ceiling
        tmp = np.delete(ceiling_cens.copy(), c, axis=0)
        any_overlap = Bbox3D.points_in_bbox(tmp, ceilings[c:c + 1]).any()
        if any_overlap:
            wall_corner0_in_ceil = Bbox3D.points_in_bbox(
                wall_cenlines[:, 0, :], ceilings_shrink[c:c + 1])
            wall_corner1_in_ceil = Bbox3D.points_in_bbox(
                wall_cenlines[:, 1, :], ceilings_shrink[c:c + 1])
            wall_inside_ceil = wall_corner0_in_ceil + wall_corner1_in_ceil
            wall_inside_ceil_ids = np.where(wall_inside_ceil)[0]
            nwic = wall_inside_ceil_ids.shape[0]

            if nwic > 3:
                if Debug and 1:
                    print(f'bad ceiling, contain {nwic} walls inside')
                    box_show = np.concatenate(
                        [walls_org, ceilings_org[c:c + 1]], 0)
                    Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)

                    box_show = np.concatenate([
                        walls_org[wall_inside_ceil_ids], ceilings_org[c:c + 1]
                    ], 0)
                    Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)
                import pdb
                pdb.set_trace()  # XXX BREAKPOINT
                continue

        # (1) the central corners of wall are inside of ceiling
        wall_cenlines_auged = line_aug(wall_cenlines)
        cw_cen_dis = ceiling_cens[c].reshape([1, 1, -1]) - wall_cenlines_auged
        cw_cen_dis = np.linalg.norm(cw_cen_dis, axis=2)
        ceil_diag_size = np.linalg.norm(ceilings[c, 3:5])
        on_inside_ceil = (cw_cen_dis - ceil_diag_size / 2 <
                          dis_threshold).sum(1) >= 2

        if Debug and 0:
            #Bbox3D.draw_points_bboxes(wall_cenlines_auged.reshape([-1,3]), walls, 'Z', False)
            inside_ids = np.where(on_inside_ceil)[0]
            box_show = np.concatenate(
                [walls_org[inside_ids], ceilings_org[c:c + 1]], 0)
            Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)

        # (2) along x: wall central line is on x boundaries of ceiling
        dis_cw = vertical_dis_points_lines(ceil_cenlines_x[c], wall_cenlines)
        ceil_y_thickness = ceilings[c, 4]
        mask_x0 = np.abs(dis_cw[0] - dis_cw[1]) < dis_threshold
        mask_x1 = (np.abs(dis_cw - ceil_y_thickness / 2) <
                   dis_threshold).all(0)
        mask_x = mask_x0 * mask_x1 * on_inside_ceil
        wall_on_ceil_boundary_parall_x = np.where(mask_x)[0]
        num_edgew_x = clean_edge_wall_same_side(
            wall_cenlines[wall_on_ceil_boundary_parall_x])

        # (3) along x: wall central line is on x boundaries of ceiling
        ceil_x_thickness = ceilings[c, 3]
        mask_y0 = dis_cw < dis_threshold
        mask_y1 = np.abs(dis_cw - ceil_x_thickness) < dis_threshold
        mask_y = (mask_y0 + mask_y1).all(0)
        mask_y = mask_y * on_inside_ceil
        wall_on_ceil_boundary_parall_y = np.where(mask_y)[0]
        num_edgew_y = clean_edge_wall_same_side(
            wall_cenlines[wall_on_ceil_boundary_parall_y])

        #Bbox3D.point_in_box(wall_cenlines, ceilings[])

        edge_wall_num = num_edgew_x + num_edgew_y

        if edge_wall_num >= 3:
            good_ceiling_ids.append(c)

        if Debug and edge_wall_num < 3 and 0:
            print(f'edge_wall_num: {edge_wall_num}')
            box_show = np.concatenate([walls_org, ceilings_org[c:c + 1]], 0)
            Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)
            #Bbox3D.draw_points_bboxes(ceil_cenlines_x[c], box_show, 'Z', False)
            #Bbox3D.draw_points_bboxes(ceil_cenlines_x[c], ceilings[c:c+1], 'Z', False)

            edge_walls_x = walls_org[wall_on_ceil_boundary_parall_x]
            box_x = np.concatenate([edge_walls_x, ceilings_org[c:c + 1]], 0)
            #Bbox3D.draw_bboxes_mesh(box_x, 'Z', False)

            edge_walls_y = walls_org[wall_on_ceil_boundary_parall_y]
            box_y = np.concatenate([edge_walls_y, ceilings_org[c:c + 1]], 0)
            #Bbox3D.draw_bboxes_mesh(box_y, 'Z', False)

            walls_inside = walls_org[wall_inside_ceil_ids]
            box_ins = np.concatenate([walls_inside, ceilings_org[c:c + 1]], 0)
            #Bbox3D.draw_bboxes_mesh(box_ins, 'Z', False)

            import pdb
            pdb.set_trace()  # XXX BREAKPOINT
            pass

    good_ceiling_ids = np.array(good_ceiling_ids).reshape([-1])
    new_cn = good_ceiling_ids.shape[0]
    print(f'\n\n{obj} {cn} -> {new_cn}')
    if new_cn == 0:
        new_ceilings = ceilings_org[0:0]
    else:
        new_ceilings = ceilings_org[good_ceiling_ids]
    if Debug and new_cn < cn:
        print(good_ceiling_ids)
        box_show = np.concatenate([walls_org, new_ceilings], 0)
        Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)

        bad_ceil_ids = np.array([
            i for i in range(cn) if i not in good_ceiling_ids
        ]).astype(np.int32)
        if bad_ceil_ids.shape[0] > 0:
            box_show = np.concatenate([walls_org, ceilings_org[bad_ceil_ids]],
                                      0)
            Bbox3D.draw_bboxes_mesh(box_show, 'Z', False)
        import pdb
        pdb.set_trace()  # XXX BREAKPOINT
    return ceilings