loiccordone / object-detection-with-spiking-neural-networks

Repository code for the IJCNN 2022 paper "Object Detection with Spiking Neural Networks on Automotive Event Data"
MIT License
57 stars 12 forks source link

RuntimeError: "coalesce" not implemented for 'Bool' #20

Open ShristiDasBiswas opened 1 year ago

ShristiDasBiswas commented 1 year ago

How do I solve this error? Screenshot_2023-03-21_14-08-01

Here is the code I am using:

import tqdm
import pdb
import torch
from torch.utils.data import Dataset
from torch.utils.data import DataLoader
import argparse
import numpy as np
from numpy.lib.recfunctions import structured_to_unstructured
import sys
sys.path.insert(0, "/home/min/a/sdasbisw/Desktop/PROJECTS/obj_det/")
from prophesee_utils.io.psee_loader import PSEELoader
class GEN1DetectionDataset(Dataset):
    def __init__(self, args, mode="train"):
        self.mode = mode
        self.tbin = args.tbin
        self.C, self.T = 2 * args.tbin, args.T
        self.sample_size = args.sample_size
        self.quantization_size = [args.sample_size//args.T,1,1]
        self.h, self.w = args.image_shape
        self.quantized_w = self.w // self.quantization_size[1]
        self.quantized_h = self.h // self.quantization_size[2]
        save_file_name = f"gen1_{mode}_{self.sample_size//1000}_{self.quantization_size[0]/1000}ms_{self.tbin}tbin.pt"
        save_file = os.path.join(args.path, save_file_name)
        print(save_file)
        if os.path.isfile(save_file):
            print("Loading samples")
            self.samples = torch.load(save_file)
            print("File loaded.")
        else:
            data_dir = os.path.join(args.path, mode)
            self.samples = self.build_dataset(data_dir, save_file)
            torch.save(self.samples, save_file)
            print(f"Done! File saved as {save_file}")

    def __getitem__(self, index):
        sparse_tensor, target = self.samples[index]
        return sparse_tensor.to_dense().permute(0,3,1,2), target

    def __len__(self):
        return len(self.samples)

    def build_dataset(self, path, save_file):
        # Remove duplicates (.npy and .dat)
        files = [os.path.join(path, time_seq_name[:-9]) for time_seq_name in os.listdir(path)
                        if time_seq_name[-3:] == 'npy']

        print('Building the Dataset')
        pbar = tqdm.tqdm(total=len(files), unit='File', unit_scale=True)
        samples = []
        for file_name in files:
            print(f"Processing {file_name}...")
            events_file = file_name + '_td.dat'
            video = PSEELoader(events_file)

            boxes_file = file_name + '_bbox.npy'
            boxes = np.load(boxes_file)
            # Rename 'ts' in 't' if needed (Prophesee GEN1)
            boxes.dtype.names = [dtype if dtype != "ts" else "t" for dtype in boxes.dtype.names]

            boxes_per_ts = np.split(boxes, np.unique(boxes['t'], return_index=True)[1][1:])
            for b in boxes_per_ts:
                sample=self.create_sample(video,b)
                if sample is not None:
                    samples.extend([sample])
            # samples.extend([sample for b in boxes_per_ts if (sample := self.create_sample(video,b)) is not None])
            pbar.update(1)

        pbar.close()
        torch.save(samples, save_file)
        print(f"Done! File saved as {save_file}")
        return samples

    def create_sample(self, video, boxes):
        ts = boxes['t'][0]
        video.seek_time(ts-self.sample_size)
        events = video.load_delta_t(self.sample_size)

        targets = self.create_targets(boxes)

        if targets['boxes'].shape[0] == 0:
            print(f"No boxes at {ts}")
            return None
        elif events.size == 0:
            print(f"No events at {ts}")
            return None
        else:
            return (self.create_data(events), targets)

    def create_targets(self, boxes):
        torch_boxes = torch.from_numpy(structured_to_unstructured(boxes[['x', 'y', 'w', 'h']], dtype=np.float32))

        # keep only last instance of every object per target
        _,unique_indices = np.unique(np.flip(boxes['track_id']), return_index=True) # keep last unique objects
        unique_indices = np.flip(-(unique_indices+1))
        torch_boxes = torch_boxes[[*unique_indices]]

        torch_boxes[:, 2:] += torch_boxes[:, :2] # implicit conversion to xyxy
        torch_boxes[:, 0::2].clamp_(min=0, max=self.w)
        torch_boxes[:, 1::2].clamp_(min=0, max=self.h)

        # valid idx = width and height of GT bbox aren't 0
        valid_idx = (torch_boxes[:,2]-torch_boxes[:,0] != 0) & (torch_boxes[:,3]-torch_boxes[:,1] != 0)
        torch_boxes = torch_boxes[valid_idx, :]

        torch_labels = torch.from_numpy(boxes['class_id']).to(torch.long)
        torch_labels = torch_labels[[*unique_indices]]
        torch_labels = torch_labels[valid_idx]

        return {'boxes': torch_boxes, 'labels': torch_labels}

    def create_data(self, events):
        events['t'] -= events['t'][0]
        feats = torch.nn.functional.one_hot(torch.from_numpy(events['p']).to(torch.long), self.C)

        coords = torch.from_numpy(
            structured_to_unstructured(events[['t', 'y', 'x']], dtype=np.int32))

        # Bin the events on T timesteps
        coords = torch.floor(coords/torch.tensor(self.quantization_size)) 
        coords[:, 1].clamp_(min=0, max=self.quantized_h-1)
        coords[:, 2].clamp_(min=0, max=self.quantized_w-1)

        # TBIN computations
        tbin_size = self.quantization_size[0] / self.tbin

        # get for each ts the corresponding tbin index
        tbin_coords = (events['t'] % self.quantization_size[0]) // tbin_size
        # tbin_index * polarity produces the real tbin index according to polarity (range 0-(tbin*2))
        tbin_feats = ((events['p']+1) * (tbin_coords+1)) - 1

        feats = torch.nn.functional.one_hot(torch.from_numpy(tbin_feats).to(torch.long), 2*self.tbin)

        sparse_tensor = torch.sparse_coo_tensor(
            coords.t().to(torch.int32), 
            feats,
            (self.T, self.quantized_h, self.quantized_w, self.C),
        )
        sparse_tensor = sparse_tensor.coalesce().to(torch.bool)

        return sparse_tensor

def collate_fn(batch):
    samples = [item[0] for item in batch]
    samples = torch.stack(samples, 0)

    targets = [item[1] for item in batch]
    return [samples, targets]

if __name__ == '__main__':
    parser = argparse.ArgumentParser(description='Classify event dataset')
    # Dataset
    parser.add_argument('-dataset', default='gen1', type=str, help='dataset used {GEN1}')
    parser.add_argument('-path', default='/local/a/datasets/gen1', type=str, help='path to dataset location')
    parser.add_argument('-num_classes', default=2, type=int, help='number of classes')

    # Data
    parser.add_argument('-b', default=64, type=int, help='batch size')
    parser.add_argument('-sample_size', default=100000, type=int, help='duration of a sample in µs')
    parser.add_argument('-T', default=5, type=int, help='simulating time-steps')
    parser.add_argument('-tbin', default=2, type=int, help='number of micro time bins')
    parser.add_argument('-image_shape', default=(240,304), type=tuple, help='spatial resolution of events')

    # Training
    parser.add_argument('-epochs', default=50, type=int, help='number of total epochs to run')
    parser.add_argument('-lr', default=1e-3, type=float, help='learning rate used')
    parser.add_argument('-wd', default=1e-4, type=float, help='weight decay used')
    parser.add_argument('-num_workers', default=4, type=int, help='number of workers for dataloaders')
    parser.add_argument('-no_train', action='store_false', help='whether to train the model', dest='train')
    parser.add_argument('-test', action='store_true', help='whether to test the model')
    parser.add_argument('-device', default=0, type=int, help='device')
    parser.add_argument('-precision', default=16, type=int, help='whether to use AMP {16, 32, 64}')
    parser.add_argument('-save_ckpt', action='store_true', help='saves checkpoints')
    parser.add_argument('-comet_api', default=None, type=str, help='api key for Comet Logger')

    # Backbone
    parser.add_argument('-backbone', default='vgg-11', type=str, help='model used {squeezenet-v, vgg-v, mobilenet-v, densenet-v}', dest='model')
    parser.add_argument('-no_bn', action='store_false', help='don\'t use BatchNorm2d', dest='bn')
    parser.add_argument('-pretrained_backbone', default=None, type=str, help='path to pretrained backbone model')
    parser.add_argument('-pretrained', default=None, type=str, help='path to pretrained model')
    parser.add_argument('-extras', type=int, default=[640, 320, 320], nargs=4, help='number of channels for extra layers after the backbone')

    # Priors
    parser.add_argument('-min_ratio', default=0.05, type=float, help='min ratio for priors\' box generation')
    parser.add_argument('-max_ratio', default=0.80, type=float, help='max ratio for priors\' box generation')
    parser.add_argument('-aspect_ratios', default=[[2], [2, 3], [2, 3], [2, 3], [2], [2]], type=int, help='aspect ratios for priors\' box generation')

    # Loss parameters
    parser.add_argument('-box_coder_weights', default=[10.0, 10.0, 5.0, 5.0], type=float, nargs=4, help='weights for the BoxCoder class')
    parser.add_argument('-iou_threshold', default=0.50, type=float, help='intersection over union threshold for the SSDMatcher class')
    parser.add_argument('-score_thresh', default=0.01, type=float, help='score threshold used for postprocessing the detections')
    parser.add_argument('-nms_thresh', default=0.45, type=float, help='NMS threshold used for postprocessing the detections')
    parser.add_argument('-topk_candidates', default=200, type=int, help='number of best detections to keep before NMS')
    parser.add_argument('-detections_per_img', default=100, type=int, help='number of best detections to keep after NMS')

    args = parser.parse_args()
    print(args)

    if args.dataset == "gen1":
        dataset = GEN1DetectionDataset
    else:
        sys.exit(f"{args.dataset} is not a supported dataset.")

    # train_dataset = dataset(args, mode="train")
    # val_dataset = dataset(args, mode="val")   
    test_dataset = dataset(args, mode="test")    
    # train_dataloader = DataLoader(train_dataset, batch_size=args.b, collate_fn=collate_fn, num_workers=args.num_workers, shuffle=True)
    # val_dataloader = DataLoader(val_dataset, batch_size=args.b, collate_fn=collate_fn, num_workers=args.num_workers)
    test_dataloader = DataLoader(test_dataset, batch_size=1, collate_fn=collate_fn, num_workers=args.num_workers)
    for idx, data in enumerate(test_dataloader, 0):
        pdb.set_trace()
        aa, bb = data
ztrbq commented 1 year ago

Hi Shristi, the same error occurred to me when I train the model with object_dectection.py. Have you figured out this error? I'm wondering if you can give me a hand, thanks.

The error occurred in the gen1_od_dataset.py, in get_item() function.

image

ycerend commented 6 months ago

I am getting the same problem, have you found any solutions to this problem?

108360215 commented 5 months ago

我遇到了同樣的問題,你找到解決這個問題的方法了嗎?