Пример #1
0
    def __getitem__(self, idx):
        return_dict = {}
        scene_num, center, label = self.pair_dict[idx]
        cloud_0, cloud_1 = [
            x[extract_area(
                x, center, self.context_voxel_size[0].item(
                ), shape='square'), :] for x in self.loaded_clouds[scene_num]
        ]
        z_max = max(cloud_0[:, 2].max(), cloud_1[:, 2].max())
        z_min = min(cloud_0[:, 2].min(), cloud_1[:, 2].min())

        z_voxel_centers = self.voxel_center_heights(z_min, z_max)
        return_dict['voxels'] = {}
        for index, z_voxel_center in enumerate(z_voxel_centers):
            vox_center = torch.cat(
                (center, z_voxel_center.unsqueeze(0).to(center.device)),
                dim=-1)
            context_for_1, voxel_1, context_0_0 = self.get_voxels(
                cloud_1, cloud_0, vox_center)
            context_for_0, voxel_0, context_1_1 = self.get_voxels(
                cloud_0, cloud_1, vox_center)
            return_dict['voxels'][index] = [
                context_for_1, voxel_1, context_0_0, context_for_0, voxel_0,
                context_1_1, z_voxel_center
            ]
        return_dict['cloud_0'] = cloud_0
        return_dict['cloud_1'] = cloud_1

        return return_dict, label
Пример #2
0
    def show_processing(self, frame, name="Press"):
        """
        Displays the inner area (bounding box of the tracking object).

        Parameters
        ----------
        frame : ndarray
            3-channel image.
        name : str
            Name of the window to display the frame.
        """
        main_area = extract_area(frame, self.x_start, self.y_start,
                                 self.x_end - self.x_start,
                                 self.y_end - self.y_start)
        x, y = int(self.bbox[0]), int(self.bbox[1])
        w, h = int(self.bbox[2]), int(self.bbox[3])

        H = main_area.shape[0]
        W = main_area.shape[1]
        p1 = (x, y)
        p2 = (x + w, y + h)
        cv.rectangle(main_area, p1, p2, (255, 0, 0), 1, 1)

        # Resize for better visualization
        main_area = cv.resize(main_area, (W * 3, H * 3))

        cv.imshow(name, main_area)
Пример #3
0
    def process_frame(self, frame):
        """
        Processes a frame to add its average lightness to history.

        Parameters
        ----------
        frame : ndarray
            3-channel image.
        """
        if self.light_history is None:
            raise ValueError('Counter is not initialized')

        area = extract_area(frame, self.x_start, self.y_start, self.width,
                            self.height)

        area = cv.cvtColor(area, cv.COLOR_BGR2HLS)

        light_avg = np.mean(area[:, :, 1])
        self.light_history = np.hstack((self.light_history, light_avg))
Пример #4
0
    def init(self, frame):
        """
        Initializes the average lightness array.

        Parameters
        ----------
        frame : ndarray
            3-channel image
        """

        # Extract the shoot area.
        area = extract_area(frame, self.x_start, self.y_start, self.width,
                            self.height)

        # Change to color space to get the lightness.
        area = cv.cvtColor(area, cv.COLOR_BGR2HLS)

        # Get the average lightness and start the history array.
        light_avg = np.mean(area[:, :, 1])
        self.light_history = np.array(light_avg)
Пример #5
0
    def init(self, frame):
        """
        Initializes the tracker. The object to be tracked is defined by the
        content of the inner area in the frame

        Parameters
        ----------
        frame : ndarray
            3-channel image.
        """

        # Extract the main area.
        main_area = extract_area(frame, self.x_start, self.y_start,
                                 self.x_end - self.x_start,
                                 self.y_end - self.y_start)

        # Initialize the tracker with the main and inner areas.
        self.tracker.init(main_area, self.bbox)

        # Init y value of top-left corner of inner area.
        self.y_pos_history = np.array(int(self.bbox[1]))
Пример #6
0
    def show_processing(self, frame, name="Plates"):
        """
        Displays the lightness of the shoot.

        Parameters
        ----------
        frame : ndarray
            3-channel image.
        name : str
            Name of the window to display the frame.
        """
        shoot = extract_area(frame, self.x_start, self.y_start, self.width,
                             self.height)

        H = shoot.shape[0]
        W = shoot.shape[1]

        shoot = cv.cvtColor(shoot, cv.COLOR_BGR2HLS)

        # Resize for better visualization
        shoot = cv.resize(shoot, (W * 3, H * 3))

        cv.imshow(name, shoot[:, :, 1])
Пример #7
0
    def process_frame(self, frame):
        """
        Processes the frame to update the inner area, that is, finding the
        position of the object defined in the initial frame.

        Parameters
        ----------
        frame : ndarray
            3-channel image.
        """
        if self.y_pos_history is None:
            raise ValueError('Tracker is not initialized')

        main_area = extract_area(frame, self.x_start, self.y_start,
                                 self.x_end - self.x_start,
                                 self.y_end - self.y_start)

        ok, bbox = self.tracker.update(main_area)

        if ok:
            self.bbox = bbox
            # Vertical position of the tracking object in the current frame.
            self.y_pos_history = np.hstack(
                (self.y_pos_history, int(self.bbox[1])))
Пример #8
0
    def __init__(self, directory_path_train,directory_path_test, out_path, clearance=10, preload=False,
                 height_min_dif=0.5, max_height=15.0, device="cpu",n_samples=2048,final_voxel_size=[3., 3., 4.],
                 rotation_augment = True,n_samples_context=2048, context_voxel_size = [3., 3., 4.],
                mode='train',verbose=False,voxel_size_final_downsample=0.07,include_all=False,self_pairs_train=True):

        print(f'Dataset mode: {mode}')
        self.mode = mode
     
        if self.mode =='train':
            directory_path = directory_path_train
        elif self.mode == 'test':
            directory_path = directory_path_test
        else:
            raise Exception('Invalid mode')
        self.verbose = verbose 
        self.include_all = include_all
        self.voxel_size_final_downsample = voxel_size_final_downsample
        self.n_samples_context = n_samples_context
        self.context_voxel_size = torch.tensor(context_voxel_size)
        self.directory_path = directory_path
        self.clearance = clearance
        self.self_pairs_train = self_pairs_train
        self.out_path = out_path
        self.height_min_dif = height_min_dif
        self.max_height = max_height
        self.rotation_augment = rotation_augment
        self.save_name = f'ams_{mode}_save_dict_{clearance}.pt'
        name_insert = self.save_name.split('.')[0]
        self.filtered_scan_path = os.path.join  (
            out_path, f'{name_insert}_filtered_scans.pt')
        if self.mode == 'train':
            self.all_valid_combs_path = os.path.join  (
                out_path, f'{name_insert}_all_valid_combs_{self_pairs_train}.pt')
        else:
            self.all_valid_combs_path = os.path.join  (
                out_path, f'{name_insert}_all_valid_combs.pt')
        self.years = [2019, 2020]
   
        
        self.n_samples = n_samples
        self.final_voxel_size = torch.tensor(final_voxel_size)
        save_path = os.path.join(self.out_path, self.save_name)
        voxel_size_icp = 0.05




        if not preload:

            with open(os.path.join(directory_path, 'args.json')) as f:
                self.args = json.load(f)
            with open(os.path.join(directory_path, 'response.json')) as f:
                self.response = json.load(f)

            print(f"Recreating dataset, saving to: {self.out_path}")
            self.scans = [Scan(x, self.directory_path)
                          for x in self.response['RecordingProperties']]
            self.scans = [
                x for x in self.scans if x.datetime.year in self.years]
            if os.path.isfile(self.filtered_scan_path):
                with open(self.filtered_scan_path, "rb") as fp:
                    self.filtered_scans = pickle.load(fp)
            else:
                self.filtered_scans = filter_scans(self.scans, 3)
                with open(self.filtered_scan_path, "wb") as fp:
                    pickle.dump(self.filtered_scans, fp)

            self.save_dict = {}
            save_id = -1

            for scene_number, scan in enumerate(tqdm(self.filtered_scans)):
                # Gather scans within certain distance of scan center
                relevant_scans = [x for x in self.scans if np.linalg.norm(
                    x.center-scan.center) < 7]

                relevant_times = set([x.datetime for x in relevant_scans])

                # Group by dates
                time_partitions = {time: [
                    x for x in relevant_scans if x.datetime == time] for time in relevant_times}

                # Load and combine clouds from same date
                clouds_per_time = [torch.from_numpy(np.concatenate([load_las(
                    x.path) for x in val])).double().to(device) for key, val in time_partitions.items()]

                # Make xy 0 at center to avoid large values
                center_trans = torch.cat(
                    (torch.from_numpy(scan.center), torch.tensor([0, 0, 0, 0]))).double().to(device)
                
                clouds_per_time = [x-center_trans for x in clouds_per_time]
                # Extract square at center since only those will be used for grid
                clouds_per_time = [x[extract_area(x, center=np.array(
                    [0, 0]), clearance=self.clearance, shape='square'), :] for x in clouds_per_time]
                # Apply registration between each cloud and first in list, store transforms
                # First cloud does not need to be transformed
                

                clouds_per_time = registration_pipeline(
                    clouds_per_time, voxel_size_icp, self.voxel_size_final_downsample)

               
                # Remove below ground and above cutoff
                # Cut off slightly under ground height
        

                ground_cutoff = scan.ground_height - 0.05 
                height_cutoff = ground_cutoff+max_height 
                clouds_per_time = [x[torch.logical_and(
                    x[:, 2] > ground_cutoff, x[:, 2] < height_cutoff), ...] for x in clouds_per_time]

                clouds_per_time = [x.float().cpu() for x in clouds_per_time]

                save_id += 1
                save_entry = {'clouds': clouds_per_time,
                              'ground_height': scan.ground_height}

                self.save_dict[save_id] = save_entry
                if scene_number % 100 == 0 and scene_number != 0:
                    print(f"Progressbackup: {scene_number}!")
                    torch.save(self.save_dict, save_path)

            print(f"Saving to {save_path}!")
            torch.save(self.save_dict, save_path)
        else:
            self.save_dict = torch.load(save_path)


    
            
        if  os.path.isfile(self.all_valid_combs_path):
            self.all_valid_combs = torch.load(self.all_valid_combs_path)
        else:
            self.all_valid_combs = []
            for idx, (save_id,save_entry) in enumerate(tqdm(self.save_dict.items())):
                clouds = save_entry['clouds']     
                clouds = {index:x for index,x in enumerate(clouds) if x.shape[0] > 5000}
                
                if len(clouds) < 2:
                    if self.verbose:
                        print(f'Not enough clouds {idx}, skipping ')
                    continue
                
                cluster_min = torch.stack([torch.min(x,dim=0)[0][:3] for x in clouds.values()]).min(dim=0)[0]
                cluster_max = torch.stack([torch.max(x,dim=0)[0][:3] for x in clouds.values()]).max(dim=0)[0]
                clusters = {}
                for index,x in clouds.items():
                    labels,voxel_centers = voxelize(x[:, :3],start= cluster_min,end=cluster_max,size= self.final_voxel_size)
                    clusters[index] = labels

                

                
                valid_voxels = {}
                for ind,cluster in clusters.items():
                    cluster_indices, counts = cluster.unique(return_counts=True)
                    valid_indices = cluster_indices[counts > self.n_samples_context]
                    valid_voxels[ind] = valid_indices
                common_voxels = []
                for ind_0, ind_1 in combinations(valid_voxels.keys(), 2):
                    if ind_0 == ind_1:
                        continue
                    common_voxels.append([ind_0, ind_1,[x.item() for x in valid_voxels[ind_0] if x in valid_voxels[ind_1]]  ])
                valid_combs = []
                for val in common_voxels:
                    valid_combs.extend([(val[0], val[1], x) for x in val[2]])
                    # Self predict (only on index,since clouds shuffled and 1:1 other to same)
                    if self.mode == 'train' and self.self_pairs_train:
                        valid_combs.extend([(val[0], val[0], x) for x in val[2]])

                if len(valid_combs) < 1:
                    # If not enough recursively give other index from dataset
                    continue
                

                
                
                for draw_ind,draw in enumerate(valid_combs):
                    
                    
                    voxel_center = voxel_centers[draw[2]]
                    cloud_ind_0 = draw[0]
                    voxel_0 = get_voxel(clouds[cloud_ind_0],voxel_center,self.context_voxel_size)
                    if voxel_0.shape[0]>=self.n_samples_context:
                        final_comb = (save_id,draw[0],draw[1],draw[2])
                        self.all_valid_combs.append({'combination':final_comb,'voxel_center':voxel_center})
                    else:
                        print('Invalid')
                        continue
                        
                
                    
                        
                
                n_same =0
                n_dif = 0
                for comb_dict in self.all_valid_combs:
                    if comb_dict['combination'][1] == comb_dict['combination'][2]:
                        n_same+=1
                    else:
                        n_dif +=1
                print(f"n_same/n_dif: {n_same/n_dif}")

                
                torch.save(self.all_valid_combs,self.all_valid_combs_path)
                        
                    

        print('Loaded dataset!')
Пример #9
0
    def __process_frame(self, frame):
        """
        Process a frame to apply thresholding and add the limits to the history
        elements. A limit is valid as long as the white section reaches de end
        of the band.

        Parameters
        ----------
        frame : ndarray
            3-channel image.

        Returns
        -------
        (ndarray, int, int) : Thresheld warped image for visualization, limit of
        the left threshold, limit of the right threshold.
        """

        # Frame in zenital view
        warp = self.warp(frame)
        # Limits to threshold the image in the Light channel
        o_l, o_h = 0, 20

        # Limits to threshold the image in RGB channels to detect orange color
        r_l, r_h, g_l, g_h, b_l, b_h = 230, 255, 115, 170, 50, 85

        # Arrays for thresheld images
        th = []
        gr = []

        # Merge the upper and lower rectangles
        box_left = (self.upper_left_rect[0], self.lower_left_rect[1])
        box_right = (self.upper_right_rect[0], self.lower_right_rect[1])

        # In every rectangle...
        for idx, box in enumerate([box_left, box_right]):
            # Box of interest
            boi = extract_area(warp, box[0][0], box[0][1],
                               box[1][0] - box[0][0], box[1][1] - box[0][1])

            # Thresholding based in color
            b = boi[:, :, 0]
            g = boi[:, :, 1]
            r = boi[:, :, 2]
            bools_color = (r > r_l) & (r < r_h) & (g > g_l) & (g < g_h) & (
                b > b_l) & (b < b_h)
            # binary = np.zeros_like(boi[:,:,0])

            # Thresholding based on Lightness
            boi = cv.cvtColor(boi, cv.COLOR_BGR2HLS)
            # Lightness channel
            boi = boi[:, :, 1]
            # Pixels within the threshold limits
            bools_light = (boi > o_l) & (boi < o_h)
            # Thresheld box
            binary = np.zeros_like(boi)

            bools = bools_color | bools_light

            # Valid pixels as white
            binary[bools == True] = 1

            # Find the  white pixels
            whites = np.argwhere(binary == 1)

            left_nones = []
            right_nones = []

            if whites.shape[0] > 0:
                # fill all space between first and last white pixel
                rows = whites[:, 0]
                rows = np.unique(rows)
                low = rows[0]
                high = rows[-1] + 1
                binary[low:high, :] = 1

                # Find the first white pixel
                # If the box of whites extends to the end of the band, add the
                # first white pixel
                if boi.shape[0] - high < self.offset:
                    if idx == 0:
                        max_left = low

                    elif idx == 1:
                        max_right = low
                # If the box of whites does not extend to the end of the band
                else:
                    if idx == 0 and self.history_left.shape[0] > 0:
                        max_left = self.history_left[-1]

                    elif idx == 0 and self.history_left.shape[0] == 0:
                        max_left = None

                    if idx == 1 and self.history_right.shape[0] > 0:
                        max_right = self.history_right[-1]

                    elif idx == 1 and self.history_right.shape[0] == 0:
                        max_right = None
            else:
                if idx == 0 and self.history_left.shape[0] > 0:
                    max_left = self.history_left[-1]

                elif idx == 0 and self.history_left.shape[0] == 0:
                    max_left = None

                if idx == 1 and self.history_right.shape[0] > 0:
                    max_right = self.history_right[-1]

                elif idx == 1 and self.history_right.shape[0] == 0:
                    max_right = None

            th.append(binary)

            # Grayscale image
            box = extract_area(warp, box[0][0], box[0][1],
                               box[1][0] - box[0][0], box[1][1] - box[0][1])
            box = cv.cvtColor(box, cv.COLOR_BGR2RGB)
            gr.append(box)

        # Left and right thresheld bands
        th_image = np.hstack((th[0], th[1])) * 255
        th_image = cv.resize(th_image,
                             (th_image.shape[1] * 3, th_image.shape[0] * 3))

        # left and right grayscale bands
        gr_image = np.hstack((gr[0], gr[1])) * 255

        # Limit lines drawn in the grayscale bands
        gr_image = cv.cvtColor(gr_image, cv.COLOR_BGR2GRAY)
        upper = self.upper_left_rect[1][1] - self.offset
        lower = self.lower_left_rect[1][1] - self.offset
        gr_image[upper, :] = 0
        gr_image[lower, :] = 0
        gr_image = cv.resize(gr_image,
                             (gr_image.shape[1] * 3, gr_image.shape[0] * 3))

        # Merge thresheld and grayscale images
        gr_image = np.hstack((th_image, gr_image))

        return gr_image.T, max_left, max_right