├── .gitignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── _config.yml ├── applications ├── __init__.py └── yolo │ ├── __init__.py │ ├── darknet.py │ ├── utils.py │ └── yolo.py ├── camera_cal ├── calibration1.jpg ├── calibration10.jpg ├── calibration11.jpg ├── calibration12.jpg ├── calibration13.jpg ├── calibration14.jpg ├── calibration15.jpg ├── calibration16.jpg ├── calibration17.jpg ├── calibration18.jpg ├── calibration19.jpg ├── calibration2.jpg ├── calibration20.jpg ├── calibration3.jpg ├── calibration4.jpg ├── calibration5.jpg ├── calibration6.jpg ├── calibration7.jpg ├── calibration8.jpg └── calibration9.jpg ├── extras └── yolo_opencv.py ├── files ├── cmtx.npy ├── coco ├── coco.names ├── dmtx.npy ├── yolov3-tiny.cfg └── yolov3.cfg ├── helpers ├── __init__.py ├── birdseye.py └── download_weights.py ├── main.py ├── models ├── __init__.py ├── calibrator.py ├── lane_detector.py └── thresholder.py ├── outputs └── output_sample.png ├── test_images ├── img.jpg └── road.jpg └── test_videos └── challenge_video.mp4 /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | 6 | # C extensions 7 | *.so 8 | 9 | # Distribution / packaging 10 | .Python 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | wheels/ 23 | pip-wheel-metadata/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | db.sqlite3-journal 63 | 64 | # Flask stuff: 65 | instance/ 66 | .webassets-cache 67 | 68 | # Scrapy stuff: 69 | .scrapy 70 | 71 | # Sphinx documentation 72 | docs/_build/ 73 | 74 | # PyBuilder 75 | target/ 76 | 77 | # Jupyter Notebook 78 | .ipynb_checkpoints 79 | 80 | # IPython 81 | profile_default/ 82 | ipython_config.py 83 | 84 | # pyenv 85 | .python-version 86 | 87 | # pipenv 88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 91 | # install all needed dependencies. 92 | #Pipfile.lock 93 | 94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 95 | __pypackages__/ 96 | 97 | # Celery stuff 98 | celerybeat-schedule 99 | celerybeat.pid 100 | 101 | # SageMath parsed files 102 | *.sage.py 103 | 104 | # Environments 105 | .env 106 | .venv 107 | env/ 108 | venv/ 109 | ENV/ 110 | env.bak/ 111 | venv.bak/ 112 | 113 | # Spyder project settings 114 | .spyderproject 115 | .spyproject 116 | 117 | # Rope project settings 118 | .ropeproject 119 | 120 | # mkdocs documentation 121 | /site 122 | 123 | # mypy 124 | .mypy_cache/ 125 | .dmypy.json 126 | dmypy.json 127 | 128 | # Pyre type checker 129 | .pyre/ 130 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at ajeetkharell@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Amit Kharel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # autodrive 2 | A python implementation for Advanced Driving Assistant. (Now with GPU Support) [[Watch Demo](https://www.youtube.com/watch?v=rAp2Q2FrCTE)] 3 | 4 | ![autodrive output sample](https://github.com/ajeetkharel/autodrive/blob/v1.0/outputs/output_sample.png) 5 | 6 | ## About the model 7 | This model can: 8 | * Detect lane lines and fit a curve on them with a polynomial mask. 9 | * Detect objects like: cars, buses, persons, traffic lights, etc 10 | 11 | ### How to run the model? 12 | 13 | * Firstly, clone the repository and change the directory to it. 14 | * Run the following command inside ``` autodrive ``` directory in the terminal/command prompt. 15 | ``` 16 | python main.py 17 | ``` 18 | NOTE: This is a default run type for this model which uses the input video stored in [test_videos](https://github.com/ajeetkharel/autodrive/tree/v1.0/test_videos) folder. 19 | 20 | #### To test the algorithm on your own video type the following command: 21 | ``` 22 | python main.py --input {path to the video} 23 | ``` 24 | #### And, if you want to store the model's output on the video then specify the output video name as: 25 | ``` 26 | python main.py --output {a file name.mkv} 27 | ``` 28 | NOTE: The output video file's extension must be ``` .mkv```. 29 | 30 | ## New Features 31 | * Added GPU support to run object detection using cuda support for getting higher FPS 32 | ###Usage: 33 | ``` 34 | python main.py -g # -g for GPU 35 | ``` 36 | NOTE: The -g inclusion while running the model will allow the model to use GPU for faster inference and higher FPS 37 | 38 | * Added support for using TINY YOLO Model to perform even more faster detection with smaller model. 39 | ###Usage: 40 | ``` 41 | python main.py -t # -t for TINY 42 | ``` 43 | NOTE: The -t inclusion while running the model will tell the model to use TINY Yolo model for running object detection. 44 | 45 | ## Extra features of the model 46 | You can also make changes inside the code to add more features in the models' output.Like: 47 | 48 | * To add labels in the top of every bounding box, pass a argument named ``` labels=True ``` in [pipeline](https://github.com/ajeetkharel/autodrive/blob/v1.0/main.py#L46) function of [main.py](https://github.com/ajeetkharel/autodrive/blob/v1.0/main.py) 49 | ``` 50 | if not lane_only: 51 | self.lane_mask_final = self.object_detector.draw_boxes(self.frame, self.lane_mask_final, labels=True) 52 | ``` 53 | 54 | * If you only want to mask and detect the lanes and dont want to detect objects, you can make ```lane_only``` argument in [main.py](https://github.com/ajeetkharel/autodrive/blob/v1.0/main.py) to ```True``` 55 | 56 | ``` 57 | if __name__=='__main__': 58 | ... 59 | #run the pipeline on specified video 60 | pipeline.run_video(input_video, output_video, lane_only=True) 61 | ... 62 | ``` 63 | 64 | ## Authors 65 | 66 | * **Amit Kharel** - *Overall works* - [ajeetkharel](https://github.com/ajeetkharel) 67 | 68 | ## License 69 | 70 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details 71 | 72 | [![Kofi](https://images-wixmp-ed30a86b8c4ca887773594c2.wixmp.com/f/b5278db5-d08d-44ae-8a27-e3a827c411c3/dcgc1gs-0e20e8ea-65aa-4224-b802-18d4346b8efe.png?token=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJ1cm46YXBwOiIsImlzcyI6InVybjphcHA6Iiwib2JqIjpbW3sicGF0aCI6IlwvZlwvYjUyNzhkYjUtZDA4ZC00NGFlLThhMjctZTNhODI3YzQxMWMzXC9kY2djMWdzLTBlMjBlOGVhLTY1YWEtNDIyNC1iODAyLTE4ZDQzNDZiOGVmZS5wbmcifV1dLCJhdWQiOlsidXJuOnNlcnZpY2U6ZmlsZS5kb3dubG9hZCJdfQ.dzvTPy1MvnZdNJLcv7FGClYdpW9rYptsJGpnzzvL8iQ)](https://www.buymeacoffee.com/amitkharel) 73 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-cayman -------------------------------------------------------------------------------- /applications/__init__.py: -------------------------------------------------------------------------------- 1 | #There's nothing here whoooshhhh -------------------------------------------------------------------------------- /applications/yolo/__init__.py: -------------------------------------------------------------------------------- 1 | from .yolo import YOLO -------------------------------------------------------------------------------- /applications/yolo/darknet.py: -------------------------------------------------------------------------------- 1 | import torch 2 | import torch.nn as nn 3 | import numpy as np 4 | import cv2 5 | 6 | 7 | class YoloLayer(nn.Module): 8 | def __init__(self, anchor_mask=[], num_classes=0, anchors=[], num_anchors=1, device = None): 9 | super(YoloLayer, self).__init__() 10 | self.anchor_mask = anchor_mask 11 | self.num_classes = num_classes 12 | self.anchors = anchors 13 | self.num_anchors = num_anchors 14 | self.anchor_step = len(anchors)/num_anchors 15 | self.coord_scale = 1 16 | self.noobject_scale = 1 17 | self.object_scale = 5 18 | self.class_scale = 1 19 | self.thresh = 0.6 20 | self.stride = 32 21 | self.seen = 0 22 | self.device = device 23 | 24 | def forward(self, output, nms_thresh): 25 | self.thresh = nms_thresh 26 | masked_anchors = [] 27 | 28 | for m in self.anchor_mask: 29 | masked_anchors += self.anchors[m*self.anchor_step:(m+1)*self.anchor_step] 30 | 31 | masked_anchors = [anchor/self.stride for anchor in masked_anchors] 32 | boxes = get_region_boxes(output.data, self.thresh, self.num_classes, masked_anchors, len(self.anchor_mask), self.device) 33 | 34 | return boxes 35 | 36 | 37 | class Upsample(nn.Module): 38 | def __init__(self, stride=2): 39 | super(Upsample, self).__init__() 40 | self.stride = stride 41 | def forward(self, x): 42 | stride = self.stride 43 | assert(x.data.dim() == 4) 44 | B = x.data.size(0) 45 | C = x.data.size(1) 46 | H = x.data.size(2) 47 | W = x.data.size(3) 48 | ws = stride 49 | hs = stride 50 | x = x.view(B, C, H, 1, W, 1).expand(B, C, H, stride, W, stride).contiguous().view(B, C, H*stride, W*stride) 51 | return x 52 | 53 | 54 | #for route and shortcut 55 | class EmptyModule(nn.Module): 56 | def __init__(self): 57 | super(EmptyModule, self).__init__() 58 | 59 | def forward(self, x): 60 | return x 61 | 62 | # support route shortcut 63 | class Darknet(nn.Module): 64 | def __init__(self, cfgfile, use_cuda=False): 65 | super(Darknet, self).__init__() 66 | self.blocks = parse_cfg(cfgfile) 67 | self.device = torch.device('cpu') 68 | if use_cuda: 69 | self.device = torch.device('cuda') 70 | self.models = self.create_network(self.blocks) # merge conv, bn,leaky 71 | self.loss = self.models[len(self.models)-1] 72 | 73 | self.width = int(self.blocks[0]['width']) 74 | self.height = int(self.blocks[0]['height']) 75 | 76 | self.header = torch.IntTensor([0,0,0,0]) 77 | self.seen = 0 78 | 79 | def forward(self, x, nms_thresh): 80 | ind = -2 81 | x = x.to(self.device) 82 | self.loss = None 83 | outputs = dict() 84 | out_boxes = [] 85 | 86 | for block in self.blocks: 87 | ind = ind + 1 88 | if block['type'] == 'net': 89 | continue 90 | elif block['type'] in ['convolutional', 'upsample']: 91 | x = self.models[ind](x.to(self.device)) 92 | outputs[ind] = x 93 | elif block['type'] == 'route': 94 | layers = block['layers'].split(',') 95 | layers = [int(i) if int(i) > 0 else int(i)+ind for i in layers] 96 | if len(layers) == 1: 97 | x = outputs[layers[0]] 98 | outputs[ind] = x 99 | elif len(layers) == 2: 100 | x1 = outputs[layers[0]] 101 | x2 = outputs[layers[1]] 102 | x = torch.cat((x1,x2),1) 103 | outputs[ind] = x 104 | elif block['type'] == 'shortcut': 105 | from_layer = int(block['from']) 106 | activation = block['activation'] 107 | from_layer = from_layer if from_layer > 0 else from_layer + ind 108 | x1 = outputs[from_layer] 109 | x2 = outputs[ind-1] 110 | x = x1 + x2 111 | outputs[ind] = x 112 | elif block['type'] == 'yolo': 113 | boxes = self.models[ind](x.to(self.device), nms_thresh) 114 | out_boxes.append(boxes) 115 | else: 116 | print('unknown type %s' % (block['type'])) 117 | 118 | return out_boxes 119 | 120 | 121 | def print_network(self): 122 | print_cfg(self.blocks) 123 | 124 | def create_network(self, blocks): 125 | models = nn.ModuleList().to(self.device) 126 | 127 | prev_filters = 3 128 | out_filters =[] 129 | prev_stride = 1 130 | out_strides = [] 131 | conv_id = 0 132 | for block in blocks: 133 | if block['type'] == 'net': 134 | prev_filters = int(block['channels']) 135 | continue 136 | elif block['type'] == 'convolutional': 137 | conv_id = conv_id + 1 138 | batch_normalize = int(block['batch_normalize']) 139 | filters = int(block['filters']) 140 | kernel_size = int(block['size']) 141 | stride = int(block['stride']) 142 | is_pad = int(block['pad']) 143 | pad = (kernel_size-1)//2 if is_pad else 0 144 | activation = block['activation'] 145 | model = nn.Sequential() 146 | if batch_normalize: 147 | model.add_module('conv{0}'.format(conv_id), nn.Conv2d(prev_filters, filters, kernel_size, stride, pad, bias=False)) 148 | model.add_module('bn{0}'.format(conv_id), nn.BatchNorm2d(filters)) 149 | else: 150 | model.add_module('conv{0}'.format(conv_id), nn.Conv2d(prev_filters, filters, kernel_size, stride, pad)) 151 | if activation == 'leaky': 152 | model.add_module('leaky{0}'.format(conv_id), nn.LeakyReLU(0.1, inplace=True)) 153 | prev_filters = filters 154 | out_filters.append(prev_filters) 155 | prev_stride = stride * prev_stride 156 | out_strides.append(prev_stride) 157 | models.append(model.to(self.device)) 158 | elif block['type'] == 'upsample': 159 | stride = int(block['stride']) 160 | out_filters.append(prev_filters) 161 | prev_stride = prev_stride // stride 162 | out_strides.append(prev_stride) 163 | models.append(Upsample(stride).to(self.device)) 164 | elif block['type'] == 'route': 165 | layers = block['layers'].split(',') 166 | ind = len(models) 167 | layers = [int(i) if int(i) > 0 else int(i)+ind for i in layers] 168 | if len(layers) == 1: 169 | prev_filters = out_filters[layers[0]] 170 | prev_stride = out_strides[layers[0]] 171 | elif len(layers) == 2: 172 | assert(layers[0] == ind - 1) 173 | prev_filters = out_filters[layers[0]] + out_filters[layers[1]] 174 | prev_stride = out_strides[layers[0]] 175 | out_filters.append(prev_filters) 176 | out_strides.append(prev_stride) 177 | models.append(EmptyModule().to(self.device)) 178 | elif block['type'] == 'shortcut': 179 | ind = len(models) 180 | prev_filters = out_filters[ind-1] 181 | out_filters.append(prev_filters) 182 | prev_stride = out_strides[ind-1] 183 | out_strides.append(prev_stride) 184 | models.append(EmptyModule().to(self.device)) 185 | elif block['type'] == 'yolo': 186 | yolo_layer = YoloLayer(self.device).to(self.device) 187 | anchors = block['anchors'].split(',') 188 | anchor_mask = block['mask'].split(',') 189 | yolo_layer.anchor_mask = [int(i) for i in anchor_mask] 190 | yolo_layer.anchors = [float(i) for i in anchors] 191 | yolo_layer.num_classes = int(block['classes']) 192 | yolo_layer.num_anchors = int(block['num']) 193 | yolo_layer.anchor_step = len(yolo_layer.anchors)//yolo_layer.num_anchors 194 | yolo_layer.stride = prev_stride 195 | out_filters.append(prev_filters) 196 | out_strides.append(prev_stride) 197 | models.append(yolo_layer.to(self.device)) 198 | else: 199 | print('unknown type %s' % (block['type'])) 200 | 201 | return models.to(self.device) 202 | 203 | def load_weights(self, weightfile): 204 | # print() 205 | fp = open(weightfile, 'rb') 206 | header = np.fromfile(fp, count=5, dtype=np.int32) 207 | self.header = torch.from_numpy(header) 208 | self.seen = self.header[3] 209 | buf = np.fromfile(fp, dtype = np.float32) 210 | fp.close() 211 | 212 | start = 0 213 | ind = -2 214 | counter = 3 215 | for block in self.blocks: 216 | if start >= buf.size: 217 | break 218 | ind = ind + 1 219 | if block['type'] == 'net': 220 | continue 221 | elif block['type'] == 'convolutional': 222 | model = self.models[ind] 223 | batch_normalize = int(block['batch_normalize']) 224 | if batch_normalize: 225 | start = load_conv_bn(buf, start, model[0], model[1]) 226 | else: 227 | start = load_conv(buf, start, model[0]) 228 | elif block['type'] == 'upsample': 229 | pass 230 | elif block['type'] == 'route': 231 | pass 232 | elif block['type'] == 'shortcut': 233 | pass 234 | elif block['type'] == 'yolo': 235 | pass 236 | else: 237 | print('unknown type %s' % (block['type'])) 238 | 239 | percent_comp = (counter / len(self.blocks)) * 100 240 | 241 | # print('Loading weights. Please Wait...{:.2f}% Complete'.format(percent_comp), end = '\r', flush = True) 242 | 243 | counter += 1 244 | 245 | 246 | 247 | def convert2cpu(gpu_matrix): 248 | return torch.FloatTensor(gpu_matrix.size()).copy_(gpu_matrix) 249 | 250 | 251 | def convert2cpu_long(gpu_matrix): 252 | return torch.LongTensor(gpu_matrix.size()).copy_(gpu_matrix) 253 | 254 | 255 | def get_region_boxes(output, conf_thresh, num_classes, anchors, num_anchors, only_objectness = 1, validation = False, use_cuda=False): 256 | device = torch.device('cpu') 257 | if use_cuda: 258 | device = torch.device('cuda') 259 | 260 | anchor_step = len(anchors)//num_anchors 261 | if output.dim() == 3: 262 | output = output.unsqueeze(0) 263 | batch = output.size(0) 264 | assert(output.size(1) == (5+num_classes)*num_anchors) 265 | h = output.size(2) 266 | w = output.size(3) 267 | 268 | all_boxes = [] 269 | output = output.view(batch*num_anchors, 5+num_classes, h*w).transpose(0,1).contiguous().view(5+num_classes, batch*num_anchors*h*w).to(device) 270 | 271 | grid_x = torch.linspace(0, w-1, w).repeat(h,1).repeat(batch*num_anchors, 1, 1).view(batch*num_anchors*h*w).type_as(output).to(device) 272 | grid_y = torch.linspace(0, h-1, h).repeat(w,1).t().repeat(batch*num_anchors, 1, 1).view(batch*num_anchors*h*w).type_as(output).to(device) 273 | xs = torch.sigmoid(output[0]) + grid_x 274 | ys = torch.sigmoid(output[1]) + grid_y 275 | 276 | anchor_w = torch.Tensor(anchors).view(num_anchors, anchor_step).index_select(1, torch.LongTensor([0])) 277 | anchor_h = torch.Tensor(anchors).view(num_anchors, anchor_step).index_select(1, torch.LongTensor([1])) 278 | anchor_w = anchor_w.repeat(batch, 1).repeat(1, 1, h*w).view(batch*num_anchors*h*w).type_as(output).to(device) 279 | anchor_h = anchor_h.repeat(batch, 1).repeat(1, 1, h*w).view(batch*num_anchors*h*w).type_as(output).to(device) 280 | ws = torch.exp(output[2]) * anchor_w 281 | hs = torch.exp(output[3]) * anchor_h 282 | 283 | det_confs = torch.sigmoid(output[4]) 284 | cls_confs = torch.nn.Softmax(dim=1)(output[5:5+num_classes].transpose(0,1)).detach() 285 | cls_max_confs, cls_max_ids = torch.max(cls_confs, 1) 286 | cls_max_confs = cls_max_confs.view(-1) 287 | cls_max_ids = cls_max_ids.view(-1) 288 | 289 | 290 | sz_hw = h*w 291 | sz_hwa = sz_hw*num_anchors 292 | det_confs = convert2cpu(det_confs) 293 | cls_max_confs = convert2cpu(cls_max_confs) 294 | cls_max_ids = convert2cpu_long(cls_max_ids) 295 | xs = convert2cpu(xs) 296 | ys = convert2cpu(ys) 297 | ws = convert2cpu(ws) 298 | hs = convert2cpu(hs) 299 | if validation: 300 | cls_confs = convert2cpu(cls_confs.view(-1, num_classes)) 301 | 302 | for b in range(batch): 303 | boxes = [] 304 | for cy in range(h): 305 | for cx in range(w): 306 | for i in range(num_anchors): 307 | ind = b*sz_hwa + i*sz_hw + cy*w + cx 308 | det_conf = det_confs[ind] 309 | if only_objectness: 310 | conf = det_confs[ind] 311 | else: 312 | conf = det_confs[ind] * cls_max_confs[ind] 313 | 314 | if conf > conf_thresh: 315 | bcx = xs[ind] 316 | bcy = ys[ind] 317 | bw = ws[ind] 318 | bh = hs[ind] 319 | cls_max_conf = cls_max_confs[ind] 320 | cls_max_id = cls_max_ids[ind] 321 | box = [bcx/w, bcy/h, bw/w, bh/h, det_conf, cls_max_conf, cls_max_id] 322 | if (not only_objectness) and validation: 323 | for c in range(num_classes): 324 | tmp_conf = cls_confs[ind][c] 325 | if c != cls_max_id and det_confs[ind]*tmp_conf > conf_thresh: 326 | box.append(tmp_conf) 327 | box.append(c) 328 | boxes.append(box) 329 | all_boxes.append(boxes) 330 | 331 | return all_boxes 332 | 333 | 334 | def parse_cfg(cfgfile): 335 | blocks = [] 336 | fp = open(cfgfile, 'r') 337 | block = None 338 | line = fp.readline() 339 | while line != '': 340 | line = line.rstrip() 341 | if line == '' or line[0] == '#': 342 | line = fp.readline() 343 | continue 344 | elif line[0] == '[': 345 | if block: 346 | blocks.append(block) 347 | block = dict() 348 | block['type'] = line.lstrip('[').rstrip(']') 349 | # set default value 350 | if block['type'] == 'convolutional': 351 | block['batch_normalize'] = 0 352 | else: 353 | key,value = line.split('=') 354 | key = key.strip() 355 | if key == 'type': 356 | key = '_type' 357 | value = value.strip() 358 | block[key] = value 359 | line = fp.readline() 360 | 361 | if block: 362 | blocks.append(block) 363 | fp.close() 364 | return blocks 365 | 366 | 367 | def print_cfg(blocks): 368 | print('layer filters size input output'); 369 | prev_width = 416 370 | prev_height = 416 371 | prev_filters = 3 372 | out_filters =[] 373 | out_widths =[] 374 | out_heights =[] 375 | ind = -2 376 | for block in blocks: 377 | ind = ind + 1 378 | if block['type'] == 'net': 379 | prev_width = int(block['width']) 380 | prev_height = int(block['height']) 381 | continue 382 | elif block['type'] == 'convolutional': 383 | filters = int(block['filters']) 384 | kernel_size = int(block['size']) 385 | stride = int(block['stride']) 386 | is_pad = int(block['pad']) 387 | pad = (kernel_size-1)//2 if is_pad else 0 388 | width = (prev_width + 2*pad - kernel_size)//stride + 1 389 | height = (prev_height + 2*pad - kernel_size)//stride + 1 390 | print('%5d %-6s %4d %d x %d / %d %3d x %3d x%4d -> %3d x %3d x%4d' % (ind, 'conv', filters, kernel_size, kernel_size, stride, prev_width, prev_height, prev_filters, width, height, filters)) 391 | prev_width = width 392 | prev_height = height 393 | prev_filters = filters 394 | out_widths.append(prev_width) 395 | out_heights.append(prev_height) 396 | out_filters.append(prev_filters) 397 | elif block['type'] == 'upsample': 398 | stride = int(block['stride']) 399 | filters = prev_filters 400 | width = prev_width*stride 401 | height = prev_height*stride 402 | print('%5d %-6s * %d %3d x %3d x%4d -> %3d x %3d x%4d' % (ind, 'upsample', stride, prev_width, prev_height, prev_filters, width, height, filters)) 403 | prev_width = width 404 | prev_height = height 405 | prev_filters = filters 406 | out_widths.append(prev_width) 407 | out_heights.append(prev_height) 408 | out_filters.append(prev_filters) 409 | elif block['type'] == 'route': 410 | layers = block['layers'].split(',') 411 | layers = [int(i) if int(i) > 0 else int(i)+ind for i in layers] 412 | if len(layers) == 1: 413 | print('%5d %-6s %d' % (ind, 'route', layers[0])) 414 | prev_width = out_widths[layers[0]] 415 | prev_height = out_heights[layers[0]] 416 | prev_filters = out_filters[layers[0]] 417 | elif len(layers) == 2: 418 | print('%5d %-6s %d %d' % (ind, 'route', layers[0], layers[1])) 419 | prev_width = out_widths[layers[0]] 420 | prev_height = out_heights[layers[0]] 421 | assert(prev_width == out_widths[layers[1]]) 422 | assert(prev_height == out_heights[layers[1]]) 423 | prev_filters = out_filters[layers[0]] + out_filters[layers[1]] 424 | out_widths.append(prev_width) 425 | out_heights.append(prev_height) 426 | out_filters.append(prev_filters) 427 | elif block['type'] in ['region', 'yolo']: 428 | print('%5d %-6s' % (ind, 'detection')) 429 | out_widths.append(prev_width) 430 | out_heights.append(prev_height) 431 | out_filters.append(prev_filters) 432 | elif block['type'] == 'shortcut': 433 | from_id = int(block['from']) 434 | from_id = from_id if from_id > 0 else from_id+ind 435 | print('%5d %-6s %d' % (ind, 'shortcut', from_id)) 436 | prev_width = out_widths[from_id] 437 | prev_height = out_heights[from_id] 438 | prev_filters = out_filters[from_id] 439 | out_widths.append(prev_width) 440 | out_heights.append(prev_height) 441 | out_filters.append(prev_filters) 442 | else: 443 | print('unknown type %s' % (block['type'])) 444 | 445 | 446 | def load_conv(buf, start, conv_model): 447 | num_w = conv_model.weight.numel() 448 | num_b = conv_model.bias.numel() 449 | conv_model.bias.data.copy_(torch.from_numpy(buf[start:start+num_b])); start = start + num_b 450 | conv_model.weight.data.copy_(torch.from_numpy(buf[start:start+num_w]).view_as(conv_model.weight.data)); start = start + num_w 451 | return start 452 | 453 | 454 | def load_conv_bn(buf, start, conv_model, bn_model): 455 | num_w = conv_model.weight.numel() 456 | num_b = bn_model.bias.numel() 457 | bn_model.bias.data.copy_(torch.from_numpy(buf[start:start+num_b])); start = start + num_b 458 | bn_model.weight.data.copy_(torch.from_numpy(buf[start:start+num_b])); start = start + num_b 459 | bn_model.running_mean.copy_(torch.from_numpy(buf[start:start+num_b])); start = start + num_b 460 | bn_model.running_var.copy_(torch.from_numpy(buf[start:start+num_b])); start = start + num_b 461 | conv_model.weight.data.copy_(torch.from_numpy(buf[start:start+num_w]).view_as(conv_model.weight.data)); start = start + num_w 462 | return start -------------------------------------------------------------------------------- /applications/yolo/utils.py: -------------------------------------------------------------------------------- 1 | import time 2 | import torch 3 | import numpy as np 4 | import cv2 5 | 6 | 7 | 8 | def boxes_iou(box1, box2): 9 | 10 | # Get the Width and Height of each bounding box 11 | width_box1 = box1[2] 12 | height_box1 = box1[3] 13 | width_box2 = box2[2] 14 | height_box2 = box2[3] 15 | 16 | # Calculate the area of the each bounding box 17 | area_box1 = width_box1 * height_box1 18 | area_box2 = width_box2 * height_box2 19 | 20 | # Find the vertical edges of the union of the two bounding boxes 21 | mx = min(box1[0] - width_box1/2.0, box2[0] - width_box2/2.0) 22 | Mx = max(box1[0] + width_box1/2.0, box2[0] + width_box2/2.0) 23 | 24 | # Calculate the width of the union of the two bounding boxes 25 | union_width = Mx - mx 26 | 27 | # Find the horizontal edges of the union of the two bounding boxes 28 | my = min(box1[1] - height_box1/2.0, box2[1] - height_box2/2.0) 29 | My = max(box1[1] + height_box1/2.0, box2[1] + height_box2/2.0) 30 | 31 | # Calculate the height of the union of the two bounding boxes 32 | union_height = My - my 33 | 34 | # Calculate the width and height of the area of intersection of the two bounding boxes 35 | intersection_width = width_box1 + width_box2 - union_width 36 | intersection_height = height_box1 + height_box2 - union_height 37 | 38 | # If the the boxes don't overlap then their IOU is zero 39 | if intersection_width <= 0 or intersection_height <= 0: 40 | return 0.0 41 | 42 | # Calculate the area of intersection of the two bounding boxes 43 | intersection_area = intersection_width * intersection_height 44 | 45 | # Calculate the area of the union of the two bounding boxes 46 | union_area = area_box1 + area_box2 - intersection_area 47 | 48 | # Calculate the IOU 49 | iou = intersection_area/union_area 50 | 51 | return iou 52 | 53 | 54 | def nms(boxes, iou_thresh): 55 | 56 | # If there are no bounding boxes do nothing 57 | if len(boxes) == 0: 58 | return boxes 59 | 60 | # Create a PyTorch Tensor to keep track of the detection confidence 61 | # of each predicted bounding box 62 | det_confs = torch.zeros(len(boxes)) 63 | 64 | # Get the detection confidence of each predicted bounding box 65 | for i in range(len(boxes)): 66 | det_confs[i] = boxes[i][4] 67 | 68 | # Sort the indices of the bounding boxes by detection confidence value in descending order. 69 | # We ignore the first returned element since we are only interested in the sorted indices 70 | _,sortIds = torch.sort(det_confs, descending = True) 71 | 72 | # Create an empty list to hold the best bounding boxes after 73 | # Non-Maximal Suppression (NMS) is performed 74 | best_boxes = [] 75 | 76 | # Perform Non-Maximal Suppression 77 | for i in range(len(boxes)): 78 | 79 | # Get the bounding box with the highest detection confidence first 80 | box_i = boxes[sortIds[i]] 81 | 82 | # Check that the detection confidence is not zero 83 | if box_i[4] > 0: 84 | 85 | # Save the bounding box 86 | best_boxes.append(box_i) 87 | 88 | # Go through the rest of the bounding boxes in the list and calculate their IOU with 89 | # respect to the previous selected box_i. 90 | for j in range(i + 1, len(boxes)): 91 | box_j = boxes[sortIds[j]] 92 | 93 | # If the IOU of box_i and box_j is higher than the given IOU threshold set 94 | # box_j's detection confidence to zero. 95 | if boxes_iou(box_i, box_j) > iou_thresh: 96 | box_j[4] = 0 97 | 98 | return best_boxes 99 | 100 | 101 | def detect_objects(model, img, iou_thresh, nms_thresh): 102 | 103 | # Start the time. This is done to calculate how long the detection takes. 104 | # Set the model to evaluation mode. 105 | model.eval() 106 | 107 | # Convert the image from a NumPy ndarray to a PyTorch Tensor of the correct shape. 108 | # The image is transposed, then converted to a FloatTensor of dtype float32, then 109 | # Normalized to values between 0 and 1, and finally unsqueezed to have the correct 110 | # shape of 1 x 3 x 416 x 416 111 | img = torch.from_numpy(img.transpose(2,0,1)).float().div(255.0).unsqueeze(0) 112 | 113 | # Feed the image to the neural network with the corresponding NMS threshold. 114 | # The first step in NMS is to remove all bounding boxes that have a very low 115 | # probability of detection. All predicted bounding boxes with a value less than 116 | # the given NMS threshold will be removed. 117 | list_boxes = model(img, nms_thresh) 118 | 119 | # Make a new list with all the bounding boxes returned by the neural network 120 | boxes = list_boxes[0][0] + list_boxes[1][0] + list_boxes[2][0] 121 | 122 | # Perform the second step of NMS on the bounding boxes returned by the neural network. 123 | # In this step, we only keep the best bounding boxes by eliminating all the bounding boxes 124 | # whose IOU value is higher than the given IOU threshold 125 | boxes = nms(boxes, iou_thresh) 126 | 127 | # Stop the time. 128 | 129 | # Print the time it took to detect objects 130 | # print('\n\nIt took {:.3f}'.format(finish - start), 'seconds to detect the objects in the image.\n') 131 | 132 | # Print the number of objects detected 133 | # print('Number of Objects Detected:', len(boxes), '\n') 134 | 135 | return boxes 136 | 137 | 138 | def load_class_names(namesfile): 139 | 140 | # Create an empty list to hold the object classes 141 | class_names = [] 142 | 143 | # Open the file containing the COCO object classes in read-only mode 144 | with open(namesfile, 'r') as fp: 145 | 146 | # The coco.names file contains only one object class per line. 147 | # Read the file line by line and save all the lines in a list. 148 | lines = fp.readlines() 149 | 150 | # Get the object class names 151 | for line in lines: 152 | 153 | # Make a copy of each line with any trailing whitespace removed 154 | line = line.rstrip() 155 | 156 | # Save the object class name into class_names 157 | class_names.append(line) 158 | 159 | return class_names 160 | 161 | 162 | def print_objects(boxes, class_names): 163 | print('Objects Found and Confidence Level:\n') 164 | for i in range(len(boxes)): 165 | box = boxes[i] 166 | if len(box) >= 7 and class_names: 167 | cls_conf = box[5] 168 | cls_id = box[6] 169 | print('%i. %s: %f' % (i + 1, class_names[cls_id], cls_conf)) 170 | 171 | 172 | def plot_boxes(img, boxes, class_names, plot_labels, color = None): 173 | 174 | # Define a tensor used to set the colors of the bounding boxes 175 | colors = torch.FloatTensor([[1,0,1],[0,0,1],[0,1,1],[0,1,0],[1,1,0],[1,0,0]]) 176 | 177 | # Define a function to set the colors of the bounding boxes 178 | def get_color(c, x, max_val): 179 | ratio = float(x) / max_val * 5 180 | i = int(np.floor(ratio)) 181 | j = int(np.ceil(ratio)) 182 | 183 | ratio = ratio - i 184 | r = (1 - ratio) * colors[i][c] + ratio * colors[j][c] 185 | 186 | return int(r * 255) 187 | 188 | # Get the width and height of the image 189 | width = img.shape[1] 190 | height = img.shape[0] 191 | 192 | # Plot the bounding boxes and corresponding labels on top of the image 193 | for i in range(len(boxes)): 194 | 195 | # Get the ith bounding box 196 | box = boxes[i] 197 | 198 | # Get the (x,y) pixel coordinates of the lower-left and lower-right corners 199 | # of the bounding box relative to the size of the image. 200 | x1 = int(np.around((box[0] - box[2]/2.0) * width)) 201 | y1 = int(np.around((box[1] - box[3]/2.0) * height)) 202 | x2 = int(np.around((box[0] + box[2]/2.0) * width)) 203 | y2 = int(np.around((box[1] + box[3]/2.0) * height)) 204 | 205 | # Set the default rgb value to red 206 | rgb = (1, 0, 0) 207 | 208 | # Use the same color to plot the bounding boxes of the same object class 209 | if len(box) >= 7 and class_names: 210 | cls_conf = box[5] 211 | cls_id = box[6] 212 | classes = len(class_names) 213 | offset = cls_id * 123457 % classes 214 | red = get_color(2, offset, classes) / 255 215 | green = get_color(1, offset, classes) / 255 216 | blue = get_color(0, offset, classes) / 255 217 | 218 | # If a color is given then set rgb to the given color instead 219 | if color is None: 220 | rgb = (red, green, blue) 221 | rgb = tuple([int(a*255) for a in rgb]) 222 | else: 223 | rgb = color 224 | 225 | # Calculate the width and height of the bounding box relative to the size of the image. 226 | width_x = x2 - x1 227 | width_y = y1 - y2 228 | # Set the postion and size of the bounding box. (x1, y2) is the pixel coordinate of the 229 | # lower-left corner of the bounding box relative to the size of the image. 230 | cv2.rectangle(img, (x1,y1), (x2, y2), rgb, 2) 231 | 232 | # # If plot_labels = True then plot the corresponding label 233 | if plot_labels: 234 | 235 | # Create a string with the object class name and the corresponding object class probability 236 | conf_tx = class_names[cls_id] 237 | 238 | # Define x and y offsets for the labels 239 | lxc = (img.shape[1] * 0.266) / 100 240 | lyc = (img.shape[0] * 1.180) / 100 241 | 242 | (label_width, label_height), _ = cv2.getTextSize(conf_tx, cv2.FONT_HERSHEY_PLAIN, 1, 2) 243 | cv2.rectangle(img, (int(x1), int(y1 - lyc - label_height)), (int(x1 + width_x), int(y1-1)), rgb, -1) 244 | cv2.putText(img, conf_tx, (x1+int(width/2)-int(label_width/2), y1-label_height+2), cv2.FONT_HERSHEY_PLAIN, 1, (255,255,255), 2) 245 | return img 246 | -------------------------------------------------------------------------------- /applications/yolo/yolo.py: -------------------------------------------------------------------------------- 1 | import cv2 2 | import torch 3 | 4 | from applications.yolo.darknet import Darknet 5 | from applications.yolo.utils import * 6 | from helpers.download_weights import download 7 | 8 | class YOLO(): 9 | def __init__(self, use_tiny=False, download_file=False, use_cuda=False): 10 | 11 | device = torch.device('cpu') 12 | if use_cuda: 13 | device = torch.device('cuda') 14 | 15 | if use_tiny: 16 | cfg_file = 'files/yolov3-tiny.cfg' 17 | if download_file: 18 | download(use_tiny) 19 | # Set the location and name of the pre-trained weights file 20 | weight_file = 'files/yolov3-tiny.weights' 21 | else: 22 | cfg_file = 'files/yolov3.cfg' 23 | if download_file: 24 | download(use_tiny) 25 | # Set the location and name of the pre-trained weights file 26 | weight_file = 'files/yolov3.weights' 27 | 28 | # Set the location and name of the COCO object classes file 29 | namesfile = 'files/coco' 30 | 31 | # Load the network architecture 32 | self.m = Darknet(cfg_file, use_cuda).to(device) 33 | 34 | # Load the pre-trained weights 35 | self.m.load_weights(weight_file) 36 | 37 | # Load the COCO object classes 38 | self.class_names = load_class_names(namesfile) 39 | 40 | # Set the NMS threshold 41 | self.nms_thresh = 0.7 42 | # Set the IOU threshold 43 | self.iou_thresh = 0.3 44 | 45 | def draw_boxes(self, image, draw_im, labels = False): 46 | original_image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB) 47 | # We resize the image to the input width and height of the first layer of the network. 48 | resized_image = cv2.resize(original_image, (self.m.width, self.m.height)) 49 | # Detect objects in the image 50 | boxes = detect_objects(self.m, resized_image, self.iou_thresh, self.nms_thresh) 51 | #Plot the image with bounding boxes and corresponding object class labels 52 | box_img = plot_boxes(draw_im, boxes, self.class_names, labels) 53 | return box_img -------------------------------------------------------------------------------- /camera_cal/calibration1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration1.jpg -------------------------------------------------------------------------------- /camera_cal/calibration10.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration10.jpg -------------------------------------------------------------------------------- /camera_cal/calibration11.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration11.jpg -------------------------------------------------------------------------------- /camera_cal/calibration12.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration12.jpg -------------------------------------------------------------------------------- /camera_cal/calibration13.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration13.jpg -------------------------------------------------------------------------------- /camera_cal/calibration14.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration14.jpg -------------------------------------------------------------------------------- /camera_cal/calibration15.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration15.jpg -------------------------------------------------------------------------------- /camera_cal/calibration16.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration16.jpg -------------------------------------------------------------------------------- /camera_cal/calibration17.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration17.jpg -------------------------------------------------------------------------------- /camera_cal/calibration18.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration18.jpg -------------------------------------------------------------------------------- /camera_cal/calibration19.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration19.jpg -------------------------------------------------------------------------------- /camera_cal/calibration2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration2.jpg -------------------------------------------------------------------------------- /camera_cal/calibration20.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration20.jpg -------------------------------------------------------------------------------- /camera_cal/calibration3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration3.jpg -------------------------------------------------------------------------------- /camera_cal/calibration4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration4.jpg -------------------------------------------------------------------------------- /camera_cal/calibration5.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration5.jpg -------------------------------------------------------------------------------- /camera_cal/calibration6.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration6.jpg -------------------------------------------------------------------------------- /camera_cal/calibration7.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration7.jpg -------------------------------------------------------------------------------- /camera_cal/calibration8.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration8.jpg -------------------------------------------------------------------------------- /camera_cal/calibration9.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/camera_cal/calibration9.jpg -------------------------------------------------------------------------------- /extras/yolo_opencv.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import time 4 | import os 5 | import signal 6 | 7 | class YOLOMODEL(): 8 | def __init__(self): 9 | self.confThresh = 0.5 10 | self.nmsThresh = 0.3 11 | 12 | 13 | classPath = 'files/coco' 14 | classPathTiny = 'files/coco.names' 15 | self.LABELS = open(classPath, 'r').read().strip().split('\n') 16 | self.LABELS_TINY = open(classPathTiny, 'r').read().strip().split('\n') 17 | 18 | 19 | np.random.seed(42) 20 | self.COLORS = np.random.randint(0,255, size=(len(self.LABELS), 3), dtype=np.uint8) 21 | 22 | 23 | weightsPath = 'files/yolov3.weights' 24 | cfgPath = 'files/yolov3.cfg' 25 | 26 | self.net = cv2.dnn.readNetFromDarknet(cfgPath, weightsPath) 27 | self.net.setPreferableTarget(cv2.dnn.DNN_TARGET_OPENCL) 28 | 29 | def yolo_forward(self, image): 30 | self.boxes=[] 31 | self.confidences=[] 32 | self.classIDs = [] 33 | 34 | (self.H,self.W) = image.shape[:2] 35 | 36 | ln = self.net.getLayerNames() 37 | ln = [ln[i[0]-1] for i in self.net.getUnconnectedOutLayers()] 38 | 39 | blob = cv2.dnn.blobFromImage(image, 1/255.0, (416, 416), swapRB=True, crop=False) 40 | self.net.setInput(blob) 41 | self.layerOutputs=self.net.forward(ln) 42 | 43 | self.get_idxs() 44 | 45 | def get_idxs(self): 46 | for output in self.layerOutputs: 47 | for detection in output: 48 | scores = detection[5:] 49 | classID = np.argmax(scores) 50 | confidence = scores[classID] 51 | if self.LABELS[classID] in self.LABELS_TINY: 52 | if confidence > self.confThresh: 53 | box = detection[0:4]*np.array([self.W,self.H,self.W,self.H]) 54 | (centerX, centerY, width, height) = box.astype('int') 55 | 56 | x = int(centerX - (width/2)) 57 | y = int(centerY - (height/2)) 58 | 59 | self.boxes.append([x, y, int(width), int(height)]) 60 | self.confidences.append(float(confidence)) 61 | self.classIDs.append(classID) 62 | 63 | self.idxs = cv2.dnn.NMSBoxes(self.boxes, self.confidences, self.confThresh, self.nmsThresh) 64 | 65 | def annotate(self, image): 66 | if len(self.idxs)>0: 67 | for i in self.idxs.flatten(): 68 | (x, y) = (self.boxes[i][0], self.boxes[i][1]) 69 | (w, h) = (self.boxes[i][2], self.boxes[i][3]) 70 | 71 | color = [int(c) for c in self.COLORS[self.classIDs[i]]] 72 | cv2.rectangle(image, (x,y), (x+w, y+h), color, 2) 73 | text = f'{self.LABELS[self.classIDs[i]]}: {round(self.confidences[i], 2)}' 74 | (label_width, label_height), _ = cv2.getTextSize(text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 2) 75 | cv2.putText(image, text, (x+int(w/2)-int(label_width/2), y-label_height+2), cv2.FONT_HERSHEY_SIMPLEX, 0.5, color, 2) 76 | return image -------------------------------------------------------------------------------- /files/cmtx.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/files/cmtx.npy -------------------------------------------------------------------------------- /files/coco: -------------------------------------------------------------------------------- 1 | person 2 | bicycle 3 | car 4 | motorbike 5 | aeroplane 6 | bus 7 | train 8 | truck 9 | boat 10 | traffic light 11 | fire hydrant 12 | stop sign 13 | parking meter 14 | bench 15 | bird 16 | cat 17 | dog 18 | horse 19 | sheep 20 | cow 21 | elephant 22 | bear 23 | zebra 24 | giraffe 25 | backpack 26 | umbrella 27 | handbag 28 | tie 29 | suitcase 30 | frisbee 31 | skis 32 | snowboard 33 | sports ball 34 | kite 35 | baseball bat 36 | baseball glove 37 | skateboard 38 | surfboard 39 | tennis racket 40 | bottle 41 | wine glass 42 | cup 43 | fork 44 | knife 45 | spoon 46 | bowl 47 | banana 48 | apple 49 | sandwich 50 | orange 51 | broccoli 52 | carrot 53 | hot dog 54 | pizza 55 | donut 56 | cake 57 | chair 58 | sofa 59 | pottedplant 60 | bed 61 | diningtable 62 | toilet 63 | tvmonitor 64 | laptop 65 | mouse 66 | remote 67 | keyboard 68 | cell phone 69 | microwave 70 | oven 71 | toaster 72 | sink 73 | refrigerator 74 | book 75 | clock 76 | vase 77 | scissors 78 | teddy bear 79 | hair drier 80 | toothbrush 81 | -------------------------------------------------------------------------------- /files/coco.names: -------------------------------------------------------------------------------- 1 | person 2 | bicycle 3 | car 4 | motorbike 5 | bus 6 | train 7 | truck 8 | traffic light 9 | fire hydrant 10 | stop sign 11 | parking meter -------------------------------------------------------------------------------- /files/dmtx.npy: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/files/dmtx.npy -------------------------------------------------------------------------------- /files/yolov3-tiny.cfg: -------------------------------------------------------------------------------- 1 | [net] 2 | # Testing 3 | batch=1 4 | subdivisions=1 5 | # Training 6 | # batch=64 7 | # subdivisions=2 8 | width=416 9 | height=416 10 | channels=3 11 | momentum=0.9 12 | decay=0.0005 13 | angle=0 14 | saturation = 1.5 15 | exposure = 1.5 16 | hue=.1 17 | 18 | learning_rate=0.001 19 | burn_in=1000 20 | max_batches = 500200 21 | policy=steps 22 | steps=400000,450000 23 | scales=.1,.1 24 | 25 | [convolutional] 26 | batch_normalize=1 27 | filters=16 28 | size=3 29 | stride=1 30 | pad=1 31 | activation=leaky 32 | 33 | [maxpool] 34 | size=2 35 | stride=2 36 | 37 | [convolutional] 38 | batch_normalize=1 39 | filters=32 40 | size=3 41 | stride=1 42 | pad=1 43 | activation=leaky 44 | 45 | [maxpool] 46 | size=2 47 | stride=2 48 | 49 | [convolutional] 50 | batch_normalize=1 51 | filters=64 52 | size=3 53 | stride=1 54 | pad=1 55 | activation=leaky 56 | 57 | [maxpool] 58 | size=2 59 | stride=2 60 | 61 | [convolutional] 62 | batch_normalize=1 63 | filters=128 64 | size=3 65 | stride=1 66 | pad=1 67 | activation=leaky 68 | 69 | [maxpool] 70 | size=2 71 | stride=2 72 | 73 | [convolutional] 74 | batch_normalize=1 75 | filters=256 76 | size=3 77 | stride=1 78 | pad=1 79 | activation=leaky 80 | 81 | [maxpool] 82 | size=2 83 | stride=2 84 | 85 | [convolutional] 86 | batch_normalize=1 87 | filters=512 88 | size=3 89 | stride=1 90 | pad=1 91 | activation=leaky 92 | 93 | [maxpool] 94 | size=2 95 | stride=1 96 | 97 | [convolutional] 98 | batch_normalize=1 99 | filters=1024 100 | size=3 101 | stride=1 102 | pad=1 103 | activation=leaky 104 | 105 | ########### 106 | 107 | [convolutional] 108 | batch_normalize=1 109 | filters=256 110 | size=1 111 | stride=1 112 | pad=1 113 | activation=leaky 114 | 115 | [convolutional] 116 | batch_normalize=1 117 | filters=512 118 | size=3 119 | stride=1 120 | pad=1 121 | activation=leaky 122 | 123 | [convolutional] 124 | size=1 125 | stride=1 126 | pad=1 127 | filters=255 128 | activation=linear 129 | 130 | 131 | 132 | [yolo] 133 | mask = 3,4,5 134 | anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319 135 | classes=80 136 | num=6 137 | jitter=.3 138 | ignore_thresh = .7 139 | truth_thresh = 1 140 | random=1 141 | 142 | [route] 143 | layers = -4 144 | 145 | [convolutional] 146 | batch_normalize=1 147 | filters=128 148 | size=1 149 | stride=1 150 | pad=1 151 | activation=leaky 152 | 153 | [upsample] 154 | stride=2 155 | 156 | [route] 157 | layers = -1, 8 158 | 159 | [convolutional] 160 | batch_normalize=1 161 | filters=256 162 | size=3 163 | stride=1 164 | pad=1 165 | activation=leaky 166 | 167 | [convolutional] 168 | size=1 169 | stride=1 170 | pad=1 171 | filters=255 172 | activation=linear 173 | 174 | [yolo] 175 | mask = 0,1,2 176 | anchors = 10,14, 23,27, 37,58, 81,82, 135,169, 344,319 177 | classes=80 178 | num=6 179 | jitter=.3 180 | ignore_thresh = .7 181 | truth_thresh = 1 182 | random=1 183 | -------------------------------------------------------------------------------- /files/yolov3.cfg: -------------------------------------------------------------------------------- 1 | [net] 2 | # Testing 3 | # batch=1 4 | # subdivisions=1 5 | # Training 6 | batch=64 7 | subdivisions=16 8 | width=608 9 | height=608 10 | channels=3 11 | momentum=0.9 12 | decay=0.0005 13 | angle=0 14 | saturation = 1.5 15 | exposure = 1.5 16 | hue=.1 17 | 18 | learning_rate=0.001 19 | burn_in=1000 20 | max_batches = 500200 21 | policy=steps 22 | steps=400000,450000 23 | scales=.1,.1 24 | 25 | [convolutional] 26 | batch_normalize=1 27 | filters=32 28 | size=3 29 | stride=1 30 | pad=1 31 | activation=leaky 32 | 33 | # Downsample 34 | 35 | [convolutional] 36 | batch_normalize=1 37 | filters=64 38 | size=3 39 | stride=2 40 | pad=1 41 | activation=leaky 42 | 43 | [convolutional] 44 | batch_normalize=1 45 | filters=32 46 | size=1 47 | stride=1 48 | pad=1 49 | activation=leaky 50 | 51 | [convolutional] 52 | batch_normalize=1 53 | filters=64 54 | size=3 55 | stride=1 56 | pad=1 57 | activation=leaky 58 | 59 | [shortcut] 60 | from=-3 61 | activation=linear 62 | 63 | # Downsample 64 | 65 | [convolutional] 66 | batch_normalize=1 67 | filters=128 68 | size=3 69 | stride=2 70 | pad=1 71 | activation=leaky 72 | 73 | [convolutional] 74 | batch_normalize=1 75 | filters=64 76 | size=1 77 | stride=1 78 | pad=1 79 | activation=leaky 80 | 81 | [convolutional] 82 | batch_normalize=1 83 | filters=128 84 | size=3 85 | stride=1 86 | pad=1 87 | activation=leaky 88 | 89 | [shortcut] 90 | from=-3 91 | activation=linear 92 | 93 | [convolutional] 94 | batch_normalize=1 95 | filters=64 96 | size=1 97 | stride=1 98 | pad=1 99 | activation=leaky 100 | 101 | [convolutional] 102 | batch_normalize=1 103 | filters=128 104 | size=3 105 | stride=1 106 | pad=1 107 | activation=leaky 108 | 109 | [shortcut] 110 | from=-3 111 | activation=linear 112 | 113 | # Downsample 114 | 115 | [convolutional] 116 | batch_normalize=1 117 | filters=256 118 | size=3 119 | stride=2 120 | pad=1 121 | activation=leaky 122 | 123 | [convolutional] 124 | batch_normalize=1 125 | filters=128 126 | size=1 127 | stride=1 128 | pad=1 129 | activation=leaky 130 | 131 | [convolutional] 132 | batch_normalize=1 133 | filters=256 134 | size=3 135 | stride=1 136 | pad=1 137 | activation=leaky 138 | 139 | [shortcut] 140 | from=-3 141 | activation=linear 142 | 143 | [convolutional] 144 | batch_normalize=1 145 | filters=128 146 | size=1 147 | stride=1 148 | pad=1 149 | activation=leaky 150 | 151 | [convolutional] 152 | batch_normalize=1 153 | filters=256 154 | size=3 155 | stride=1 156 | pad=1 157 | activation=leaky 158 | 159 | [shortcut] 160 | from=-3 161 | activation=linear 162 | 163 | [convolutional] 164 | batch_normalize=1 165 | filters=128 166 | size=1 167 | stride=1 168 | pad=1 169 | activation=leaky 170 | 171 | [convolutional] 172 | batch_normalize=1 173 | filters=256 174 | size=3 175 | stride=1 176 | pad=1 177 | activation=leaky 178 | 179 | [shortcut] 180 | from=-3 181 | activation=linear 182 | 183 | [convolutional] 184 | batch_normalize=1 185 | filters=128 186 | size=1 187 | stride=1 188 | pad=1 189 | activation=leaky 190 | 191 | [convolutional] 192 | batch_normalize=1 193 | filters=256 194 | size=3 195 | stride=1 196 | pad=1 197 | activation=leaky 198 | 199 | [shortcut] 200 | from=-3 201 | activation=linear 202 | 203 | 204 | [convolutional] 205 | batch_normalize=1 206 | filters=128 207 | size=1 208 | stride=1 209 | pad=1 210 | activation=leaky 211 | 212 | [convolutional] 213 | batch_normalize=1 214 | filters=256 215 | size=3 216 | stride=1 217 | pad=1 218 | activation=leaky 219 | 220 | [shortcut] 221 | from=-3 222 | activation=linear 223 | 224 | [convolutional] 225 | batch_normalize=1 226 | filters=128 227 | size=1 228 | stride=1 229 | pad=1 230 | activation=leaky 231 | 232 | [convolutional] 233 | batch_normalize=1 234 | filters=256 235 | size=3 236 | stride=1 237 | pad=1 238 | activation=leaky 239 | 240 | [shortcut] 241 | from=-3 242 | activation=linear 243 | 244 | [convolutional] 245 | batch_normalize=1 246 | filters=128 247 | size=1 248 | stride=1 249 | pad=1 250 | activation=leaky 251 | 252 | [convolutional] 253 | batch_normalize=1 254 | filters=256 255 | size=3 256 | stride=1 257 | pad=1 258 | activation=leaky 259 | 260 | [shortcut] 261 | from=-3 262 | activation=linear 263 | 264 | [convolutional] 265 | batch_normalize=1 266 | filters=128 267 | size=1 268 | stride=1 269 | pad=1 270 | activation=leaky 271 | 272 | [convolutional] 273 | batch_normalize=1 274 | filters=256 275 | size=3 276 | stride=1 277 | pad=1 278 | activation=leaky 279 | 280 | [shortcut] 281 | from=-3 282 | activation=linear 283 | 284 | # Downsample 285 | 286 | [convolutional] 287 | batch_normalize=1 288 | filters=512 289 | size=3 290 | stride=2 291 | pad=1 292 | activation=leaky 293 | 294 | [convolutional] 295 | batch_normalize=1 296 | filters=256 297 | size=1 298 | stride=1 299 | pad=1 300 | activation=leaky 301 | 302 | [convolutional] 303 | batch_normalize=1 304 | filters=512 305 | size=3 306 | stride=1 307 | pad=1 308 | activation=leaky 309 | 310 | [shortcut] 311 | from=-3 312 | activation=linear 313 | 314 | 315 | [convolutional] 316 | batch_normalize=1 317 | filters=256 318 | size=1 319 | stride=1 320 | pad=1 321 | activation=leaky 322 | 323 | [convolutional] 324 | batch_normalize=1 325 | filters=512 326 | size=3 327 | stride=1 328 | pad=1 329 | activation=leaky 330 | 331 | [shortcut] 332 | from=-3 333 | activation=linear 334 | 335 | 336 | [convolutional] 337 | batch_normalize=1 338 | filters=256 339 | size=1 340 | stride=1 341 | pad=1 342 | activation=leaky 343 | 344 | [convolutional] 345 | batch_normalize=1 346 | filters=512 347 | size=3 348 | stride=1 349 | pad=1 350 | activation=leaky 351 | 352 | [shortcut] 353 | from=-3 354 | activation=linear 355 | 356 | 357 | [convolutional] 358 | batch_normalize=1 359 | filters=256 360 | size=1 361 | stride=1 362 | pad=1 363 | activation=leaky 364 | 365 | [convolutional] 366 | batch_normalize=1 367 | filters=512 368 | size=3 369 | stride=1 370 | pad=1 371 | activation=leaky 372 | 373 | [shortcut] 374 | from=-3 375 | activation=linear 376 | 377 | [convolutional] 378 | batch_normalize=1 379 | filters=256 380 | size=1 381 | stride=1 382 | pad=1 383 | activation=leaky 384 | 385 | [convolutional] 386 | batch_normalize=1 387 | filters=512 388 | size=3 389 | stride=1 390 | pad=1 391 | activation=leaky 392 | 393 | [shortcut] 394 | from=-3 395 | activation=linear 396 | 397 | 398 | [convolutional] 399 | batch_normalize=1 400 | filters=256 401 | size=1 402 | stride=1 403 | pad=1 404 | activation=leaky 405 | 406 | [convolutional] 407 | batch_normalize=1 408 | filters=512 409 | size=3 410 | stride=1 411 | pad=1 412 | activation=leaky 413 | 414 | [shortcut] 415 | from=-3 416 | activation=linear 417 | 418 | 419 | [convolutional] 420 | batch_normalize=1 421 | filters=256 422 | size=1 423 | stride=1 424 | pad=1 425 | activation=leaky 426 | 427 | [convolutional] 428 | batch_normalize=1 429 | filters=512 430 | size=3 431 | stride=1 432 | pad=1 433 | activation=leaky 434 | 435 | [shortcut] 436 | from=-3 437 | activation=linear 438 | 439 | [convolutional] 440 | batch_normalize=1 441 | filters=256 442 | size=1 443 | stride=1 444 | pad=1 445 | activation=leaky 446 | 447 | [convolutional] 448 | batch_normalize=1 449 | filters=512 450 | size=3 451 | stride=1 452 | pad=1 453 | activation=leaky 454 | 455 | [shortcut] 456 | from=-3 457 | activation=linear 458 | 459 | # Downsample 460 | 461 | [convolutional] 462 | batch_normalize=1 463 | filters=1024 464 | size=3 465 | stride=2 466 | pad=1 467 | activation=leaky 468 | 469 | [convolutional] 470 | batch_normalize=1 471 | filters=512 472 | size=1 473 | stride=1 474 | pad=1 475 | activation=leaky 476 | 477 | [convolutional] 478 | batch_normalize=1 479 | filters=1024 480 | size=3 481 | stride=1 482 | pad=1 483 | activation=leaky 484 | 485 | [shortcut] 486 | from=-3 487 | activation=linear 488 | 489 | [convolutional] 490 | batch_normalize=1 491 | filters=512 492 | size=1 493 | stride=1 494 | pad=1 495 | activation=leaky 496 | 497 | [convolutional] 498 | batch_normalize=1 499 | filters=1024 500 | size=3 501 | stride=1 502 | pad=1 503 | activation=leaky 504 | 505 | [shortcut] 506 | from=-3 507 | activation=linear 508 | 509 | [convolutional] 510 | batch_normalize=1 511 | filters=512 512 | size=1 513 | stride=1 514 | pad=1 515 | activation=leaky 516 | 517 | [convolutional] 518 | batch_normalize=1 519 | filters=1024 520 | size=3 521 | stride=1 522 | pad=1 523 | activation=leaky 524 | 525 | [shortcut] 526 | from=-3 527 | activation=linear 528 | 529 | [convolutional] 530 | batch_normalize=1 531 | filters=512 532 | size=1 533 | stride=1 534 | pad=1 535 | activation=leaky 536 | 537 | [convolutional] 538 | batch_normalize=1 539 | filters=1024 540 | size=3 541 | stride=1 542 | pad=1 543 | activation=leaky 544 | 545 | [shortcut] 546 | from=-3 547 | activation=linear 548 | 549 | ###################### 550 | 551 | [convolutional] 552 | batch_normalize=1 553 | filters=512 554 | size=1 555 | stride=1 556 | pad=1 557 | activation=leaky 558 | 559 | [convolutional] 560 | batch_normalize=1 561 | size=3 562 | stride=1 563 | pad=1 564 | filters=1024 565 | activation=leaky 566 | 567 | [convolutional] 568 | batch_normalize=1 569 | filters=512 570 | size=1 571 | stride=1 572 | pad=1 573 | activation=leaky 574 | 575 | [convolutional] 576 | batch_normalize=1 577 | size=3 578 | stride=1 579 | pad=1 580 | filters=1024 581 | activation=leaky 582 | 583 | [convolutional] 584 | batch_normalize=1 585 | filters=512 586 | size=1 587 | stride=1 588 | pad=1 589 | activation=leaky 590 | 591 | [convolutional] 592 | batch_normalize=1 593 | size=3 594 | stride=1 595 | pad=1 596 | filters=1024 597 | activation=leaky 598 | 599 | [convolutional] 600 | size=1 601 | stride=1 602 | pad=1 603 | filters=255 604 | activation=linear 605 | 606 | 607 | [yolo] 608 | mask = 6,7,8 609 | anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 610 | classes=80 611 | num=9 612 | jitter=.3 613 | ignore_thresh = .7 614 | truth_thresh = 1 615 | random=1 616 | 617 | 618 | [route] 619 | layers = -4 620 | 621 | [convolutional] 622 | batch_normalize=1 623 | filters=256 624 | size=1 625 | stride=1 626 | pad=1 627 | activation=leaky 628 | 629 | [upsample] 630 | stride=2 631 | 632 | [route] 633 | layers = -1, 61 634 | 635 | 636 | 637 | [convolutional] 638 | batch_normalize=1 639 | filters=256 640 | size=1 641 | stride=1 642 | pad=1 643 | activation=leaky 644 | 645 | [convolutional] 646 | batch_normalize=1 647 | size=3 648 | stride=1 649 | pad=1 650 | filters=512 651 | activation=leaky 652 | 653 | [convolutional] 654 | batch_normalize=1 655 | filters=256 656 | size=1 657 | stride=1 658 | pad=1 659 | activation=leaky 660 | 661 | [convolutional] 662 | batch_normalize=1 663 | size=3 664 | stride=1 665 | pad=1 666 | filters=512 667 | activation=leaky 668 | 669 | [convolutional] 670 | batch_normalize=1 671 | filters=256 672 | size=1 673 | stride=1 674 | pad=1 675 | activation=leaky 676 | 677 | [convolutional] 678 | batch_normalize=1 679 | size=3 680 | stride=1 681 | pad=1 682 | filters=512 683 | activation=leaky 684 | 685 | [convolutional] 686 | size=1 687 | stride=1 688 | pad=1 689 | filters=255 690 | activation=linear 691 | 692 | 693 | [yolo] 694 | mask = 3,4,5 695 | anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 696 | classes=80 697 | num=9 698 | jitter=.3 699 | ignore_thresh = .7 700 | truth_thresh = 1 701 | random=1 702 | 703 | 704 | 705 | [route] 706 | layers = -4 707 | 708 | [convolutional] 709 | batch_normalize=1 710 | filters=128 711 | size=1 712 | stride=1 713 | pad=1 714 | activation=leaky 715 | 716 | [upsample] 717 | stride=2 718 | 719 | [route] 720 | layers = -1, 36 721 | 722 | 723 | 724 | [convolutional] 725 | batch_normalize=1 726 | filters=128 727 | size=1 728 | stride=1 729 | pad=1 730 | activation=leaky 731 | 732 | [convolutional] 733 | batch_normalize=1 734 | size=3 735 | stride=1 736 | pad=1 737 | filters=256 738 | activation=leaky 739 | 740 | [convolutional] 741 | batch_normalize=1 742 | filters=128 743 | size=1 744 | stride=1 745 | pad=1 746 | activation=leaky 747 | 748 | [convolutional] 749 | batch_normalize=1 750 | size=3 751 | stride=1 752 | pad=1 753 | filters=256 754 | activation=leaky 755 | 756 | [convolutional] 757 | batch_normalize=1 758 | filters=128 759 | size=1 760 | stride=1 761 | pad=1 762 | activation=leaky 763 | 764 | [convolutional] 765 | batch_normalize=1 766 | size=3 767 | stride=1 768 | pad=1 769 | filters=256 770 | activation=leaky 771 | 772 | [convolutional] 773 | size=1 774 | stride=1 775 | pad=1 776 | filters=255 777 | activation=linear 778 | 779 | 780 | [yolo] 781 | mask = 0,1,2 782 | anchors = 10,13, 16,30, 33,23, 30,61, 62,45, 59,119, 116,90, 156,198, 373,326 783 | classes=80 784 | num=9 785 | jitter=.3 786 | ignore_thresh = .7 787 | truth_thresh = 1 788 | random=1 789 | 790 | -------------------------------------------------------------------------------- /helpers/__init__.py: -------------------------------------------------------------------------------- 1 | #r/whoooshh -------------------------------------------------------------------------------- /helpers/birdseye.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | 4 | src = np.float32([ 5 | [280, 720], 6 | [555, 480], 7 | [740, 480], 8 | [1120, 720] 9 | ]) 10 | 11 | dst = np.float32([ 12 | [280, 720], 13 | [280, 0], 14 | [1100, 0], 15 | [1100, 720] 16 | ]) 17 | 18 | M = cv2.getPerspectiveTransform(src, dst) 19 | Minv = cv2.getPerspectiveTransform(dst, src) 20 | 21 | def transform(image): 22 | return cv2.warpPerspective(image, M, image.shape[:2][::-1], flags=cv2.INTER_LINEAR) 23 | 24 | def inverse_transform(image): 25 | return cv2.warpPerspective(image, Minv, image.shape[:2][::-1], flags=cv2.INTER_LINEAR) -------------------------------------------------------------------------------- /helpers/download_weights.py: -------------------------------------------------------------------------------- 1 | #Taken from: https://stackoverflow.com/a/53877507/13558274 2 | 3 | import urllib.request 4 | from tqdm import tqdm 5 | 6 | class DownloadProgressBar(tqdm): 7 | def update_to(self, b=1, bsize=1, tsize=None): 8 | if tsize is not None: 9 | self.total = tsize 10 | self.update(b * bsize - self.n) 11 | 12 | 13 | def download(tiny=False): 14 | 15 | print("Yolo weights not found in 'files/yolo*.weights'... downloading now...\n") 16 | 17 | output_path = 'files/yolov3.weights' 18 | url = 'https://pjreddie.com/media/files/yolov3.weights' 19 | if tiny: 20 | output_path = 'files/yolov3-tiny.weights' 21 | url = 'https://pjreddie.com/media/files/yolov3-tiny.weights' 22 | with DownloadProgressBar(unit='B', unit_scale=True, 23 | miniters=1, desc=url.split('/')[-1]) as t: 24 | urllib.request.urlretrieve(url, filename=output_path, reporthook=t.update_to) -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | import argparse 4 | import matplotlib.pyplot as plt 5 | import json 6 | import os 7 | import signal 8 | import time 9 | 10 | from models import Thresholder, LaneDetector, Calibrator 11 | from helpers import birdseye 12 | from applications.yolo import YOLO 13 | 14 | class Pipeline(): 15 | def __init__(self, views = ['default'], use_tiny=False, download_file=False, use_cuda = False): 16 | self.calibrator = Calibrator('camera_cal', 8, 6) 17 | self.calibrator.load_camera_matrices('files/cmtx.npy', 'files/dmtx.npy') 18 | # self.calibrator.calibrate() 19 | 20 | self.thresholder = Thresholder() 21 | self.views = views 22 | 23 | self.lane_detector = LaneDetector() 24 | 25 | self.object_detector = YOLO(use_tiny, download_file, use_cuda=use_cuda) 26 | 27 | def get_view(self, key): 28 | view_map = { 29 | 'default':None, 30 | 'init': self.frame, 31 | 'thresh':self.thresh, 32 | 'warped': self.warped, 33 | } 34 | return view_map.get(key, 'init') 35 | 36 | def detect_trafficlight(self, box): 37 | ax = 10 38 | ay = 10 39 | 40 | masked = np.zeros_like(self.frame) 41 | masked[(box[1] - ay):(box[1]+box[3]+ay), (box[0] - ay):(box[0]+box[2]+ay)] = 1 42 | 43 | final = cv2.addWeighted(self.frame, self.frame, mask=masked) 44 | self.frame=final 45 | 46 | def pipeline(self, lane_only): 47 | undistorted = self.calibrator.undistort(self.frame) 48 | self.thresh = self.thresholder.get_thresh(undistorted) 49 | self.warped = birdseye.transform(self.thresh) 50 | lane_fit_img = self.lane_detector.fit_lanes(self.warped, self.lane_detector.left_fit, self.lane_detector.right_fit, self.lane_detector.left_fitx, self.lane_detector.right_fitx, verbose=False) 51 | left_curv, right_curv = self.lane_detector.measure_curvature_pixels(self.lane_detector.left_fit, self.lane_detector.right_fit) 52 | self.lane_mask = birdseye.inverse_transform(lane_fit_img) 53 | self.lane_mask_final = cv2.addWeighted(self.frame, 0.8, self.lane_mask, 0.2, 0) 54 | 55 | if not lane_only: 56 | self.lane_mask_final = self.object_detector.draw_boxes(self.frame, self.lane_mask_final) 57 | 58 | final_image = self.extract_views() 59 | 60 | curr_time = time.time() 61 | diff = curr_time - self.start_time 62 | self.start_time = curr_time 63 | 64 | fps = int(1/diff) 65 | 66 | cv2.putText(final_image,f"Left curvature: {left_curv}", (15, 220), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (125, 40, 32), 2) 67 | cv2.putText(final_image,f"Right curvature: {right_curv}", (15, 250), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (255,0,0), 2) 68 | cv2.putText(final_image,f"FPS: {fps}", (final_image.shape[1]-150, 220), cv2.FONT_HERSHEY_DUPLEX, 0.6, (0,0,0), 2) 69 | 70 | return final_image 71 | 72 | def run_video(self, input_video, output_video, lane_only = True): 73 | 74 | #video for performing detection 75 | cap = cv2.VideoCapture(input_video) 76 | 77 | #for using the fit from initial lane in others wihout performing sliding windows again and again 78 | if output_video is not None: 79 | fourcc = cv2.VideoWriter_fourcc(*'XVID') 80 | out = cv2.VideoWriter(f'outputs/{output_video}', fourcc, cap.get(cv2.CAP_PROP_FPS), (1120, 780)) 81 | 82 | self.start_time = time.time() 83 | while True: 84 | ret, self.frame = cap.read() 85 | if ret: 86 | final_image = self.pipeline(lane_only) 87 | cv2.imshow('Lane', final_image) 88 | if cv2.waitKey(1) & 0xFF==ord('q'): 89 | break 90 | if output_video is not None: 91 | out.write(final_image) 92 | else: 93 | break 94 | cap.release() 95 | cv2.destroyAllWindows() 96 | 97 | def run_image(self): 98 | # self.frame = cv2.imread('test_images/img.jpg') 99 | self.frame = cv2.imread('test_images/road.jpg') 100 | self.start_time = time.time() 101 | final_image = self.pipeline(False) 102 | cv2.imshow("img", final_image) 103 | cv2.waitKey() 104 | cv2.destroyAllWindows() 105 | 106 | def extract_views(self): 107 | if len(self.views) > 4: 108 | print('Not more than 4 views are allowed. Reducing the number to 4.') 109 | 110 | view_images = map(self.get_view, self.views[:4]) 111 | final_image = np.zeros((780, 1120, 3), dtype=np.uint8) 112 | for i, image in enumerate(view_images): 113 | # print(image.shape) 114 | try: 115 | image = cv2.cvtColor(image, cv2.COLOR_GRAY2BGR) 116 | except: 117 | pass 118 | image = cv2.resize(image, (280, 140)) 119 | final_image[0:140, i*280:i*280+280, :] = np.uint8(image/np.max(image)*255) 120 | lane_mask_final = cv2.resize(self.lane_mask_final, (1120, 600)) 121 | final_image[180:, :] = np.uint8(lane_mask_final) 122 | return final_image 123 | 124 | if __name__=='__main__': 125 | 126 | input_video = 'test_videos/challenge_video.mp4' 127 | output_video = None 128 | 129 | ap = argparse.ArgumentParser() 130 | ap.add_argument('-t', action='store_true', required=False, help='Use tiny yolo model') 131 | ap.add_argument('--input', required=False, type=str, help='Input video for the autodrive model') 132 | ap.add_argument('--output', required=False, type=str, help='Name of output video to store the output of autodrive model') 133 | ap.add_argument('-g', required=False, action='store_true', help='Add gpu support to perform object detection') 134 | args = vars(ap.parse_args()) 135 | 136 | #whether to use yolo tiny model or not 137 | use_tiny = args['t'] 138 | use_cuda = args['g'] 139 | 140 | if args['input'] is not None: 141 | input_video = args['input'].strip("'") 142 | if args['output'] is not None: 143 | output_video = args['output'].strip("'") 144 | 145 | #if weights present then don't download, otherwise download 146 | download_file = True 147 | if use_tiny: 148 | if os.path.isfile('files/yolov3-tiny.weights'): 149 | download_file = False 150 | else: 151 | if os.path.isfile('files/yolov3.weights'): 152 | download_file = False 153 | 154 | 155 | pipeline = Pipeline(views=['thresh', 'warped'], use_tiny=use_tiny, download_file=download_file, use_cuda=use_cuda) 156 | 157 | #run the pipeline on a single image 158 | # pipeline.run_image() 159 | 160 | #run the pipeline on specified video 161 | pipeline.run_video(input_video, output_video, lane_only=False) 162 | 163 | #kill the application 164 | os.kill(os.getpid(), signal.SIGTERM) -------------------------------------------------------------------------------- /models/__init__.py: -------------------------------------------------------------------------------- 1 | from .calibrator import Calibrator 2 | from .lane_detector import LaneDetector 3 | from .thresholder import Thresholder -------------------------------------------------------------------------------- /models/calibrator.py: -------------------------------------------------------------------------------- 1 | import glob 2 | import os 3 | import numpy as np 4 | import cv2 5 | import matplotlib.pyplot as plt 6 | 7 | class Calibrator(): 8 | def __init__(self, loc, nx, ny): 9 | self.chessboards = glob.glob(loc+'/*.jpg') 10 | self.nx = nx 11 | self.ny = ny 12 | 13 | def calibrate(self, verbose=False, save=True): 14 | if verbose: 15 | print('Initializing calibration....') 16 | img_points = [] 17 | obj_points = [] 18 | 19 | objp = np.zeros((self.nx*self.ny, 3), dtype=np.float32) 20 | objp[:,:2] = np.mgrid[0:self.nx, 0:self.ny].T.reshape(-1,2) 21 | 22 | if verbose: 23 | print('Processing camera matrices...\n') 24 | 25 | for chessboard in self.chessboards: 26 | img = cv2.imread(chessboard, 0) 27 | ret, corners = cv2.findChessboardCorners(img, (self.nx, self.ny), None) 28 | if ret: 29 | img_points.append(corners) 30 | obj_points.append(objp) 31 | 32 | if verbose: 33 | c_image = cv2.drawChessboardCorners(img, (self.nx, self.ny), corners, ret) 34 | plt.imshow(c_image, cmap='gray') 35 | plt.show() 36 | 37 | ret, self.cmtx, self.dmtx, rvecs, tvecs = cv2.calibrateCamera(obj_points, img_points, img.shape, None, None) 38 | if ret: 39 | print('Camera calibrated successfully. Camera matrices are stored as "cmtx" and "dmtx"') 40 | else: 41 | print("Calibration failed. Make sure you have correctly included the location containing chess-board images.") 42 | 43 | 44 | if save: 45 | self.save_camera_matrices() 46 | 47 | 48 | def load_camera_matrices(self, cmtx, dmtx): 49 | self.cmtx = np.load(cmtx) 50 | self.dmtx = np.load(dmtx) 51 | 52 | def undistort(self, image): 53 | try: 54 | return cv2.undistort(image, self.cmtx, self.dmtx, None, self.cmtx) 55 | except e: 56 | print('Please load the camera matrices first or calibrate the camera.\ 57 | \nUsage: calibrator.calibrate()\tcalibrator.load_camera_matrices(cmtx_loc, dmtx_loc)') 58 | 59 | def save_camera_matrices(self): 60 | os.makedirs("files", exist_ok=True) 61 | 62 | np.save('files/cmtx', self.cmtx) 63 | np.save('files/dmtx', self.dmtx) 64 | print(f'Saved camera matrices as: "cmtx.npy" & "dmtx.npy" in {os.getcwd()+"/files"}') -------------------------------------------------------------------------------- /models/lane_detector.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | 4 | class LaneDetector(): 5 | def __init__(self): 6 | self.left_fit = None 7 | self.right_fit = None 8 | self.left_fitx = None 9 | self.right_fitx = None 10 | 11 | def __extract_lanes(self, warped, verbose=False): 12 | hist = np.sum(warped[350:], axis=0) 13 | midpoint = warped.shape[1]//2 14 | 15 | left_start = np.argmax(hist[:midpoint]) 16 | right_start = np.argmax(hist[midpoint:])+midpoint 17 | 18 | nonzero = warped.nonzero() 19 | self.nonzeroy = np.array(nonzero[0]) 20 | self.nonzerox = np.array(nonzero[1]) 21 | 22 | 23 | self.margin = 100 24 | n_windows = 9 25 | window_height = int(warped.shape[0]/n_windows) 26 | min_pix = 50 27 | 28 | self.left_lane_inds = [] 29 | self.right_lane_inds = [] 30 | 31 | 32 | for i in range(n_windows): 33 | left_bottomleft_x = left_start - self.margin 34 | left_bottomleft_y = warped.shape[0] - i * window_height 35 | 36 | left_topright_x = left_start + self.margin 37 | left_topright_y = left_bottomleft_y - window_height 38 | 39 | right_bottomleft_x = right_start - self.margin 40 | right_bottomleft_y = warped.shape[0] - i * window_height 41 | 42 | right_topright_x = right_start + self.margin 43 | right_topright_y = right_bottomleft_y - window_height 44 | 45 | if verbose: 46 | cv2.rectangle(self.out_img, (left_bottomleft_x, left_topright_y), (left_topright_x, left_bottomleft_y),(0,255,0), thickness=2) 47 | cv2.rectangle(self.out_img, (right_bottomleft_x, right_topright_y), (right_topright_x, right_bottomleft_y),(0,255,0), thickness=2) 48 | 49 | 50 | left_nonzero = (((self.nonzerox >= left_bottomleft_x) & (self.nonzerox <= left_topright_x)) & ((self.nonzeroy <= left_bottomleft_y) & (self.nonzeroy >= left_topright_y))).nonzero()[0] 51 | right_nonzero = (((self.nonzerox >= right_bottomleft_x) & (self.nonzerox <= right_topright_x)) & ((self.nonzeroy <= right_bottomleft_y) & (self.nonzeroy >= right_topright_y))).nonzero()[0] 52 | 53 | self.left_lane_inds.append(left_nonzero) 54 | self.right_lane_inds.append(right_nonzero) 55 | 56 | if len(left_nonzero) > min_pix: 57 | left_start = np.int(np.mean(self.nonzerox[left_nonzero])) 58 | 59 | if len(right_nonzero) > min_pix: 60 | right_start = np.int(np.mean(self.nonzerox[right_nonzero])) 61 | 62 | self.left_lane_inds = np.concatenate(self.left_lane_inds) 63 | self.right_lane_inds = np.concatenate(self.right_lane_inds) 64 | 65 | self.leftx = self.nonzerox[self.left_lane_inds] 66 | self.lefty = self.nonzeroy[self.left_lane_inds] 67 | 68 | self.rightx = self.nonzerox[self.right_lane_inds] 69 | self.righty = self.nonzeroy[self.right_lane_inds] 70 | 71 | if verbose: 72 | self.out_img[self.lefty, self.leftx] = [255, 0, 0] 73 | self.out_img[self.righty, self.rightx] = [0, 0, 255] 74 | 75 | def fit_lanes(self, warped, left_fit, right_fit, p_left_fitx, p_right_fitx, verbose=False): 76 | #for visualization 77 | self.out_img = np.dstack((warped, warped, warped)) 78 | 79 | if np.array(left_fit).any() != None: 80 | found = self.search_around_poly(warped, left_fit, right_fit) 81 | if found == False: 82 | self.__extract_lanes(warped, verbose=verbose) 83 | else: 84 | self.__extract_lanes(warped, verbose=verbose) 85 | 86 | if 0 in [len(self.leftx), len(self.lefty), len(self.rightx), len(self.righty)]: 87 | self.left_fitx, self.right_fitx = p_left_fitx, p_right_fitx 88 | self.ploty = np.linspace(0, warped.shape[0]-1, warped.shape[0]) 89 | else: 90 | self.left_fitx, self.right_fitx, self.ploty = self.fit_poly(warped.shape, self.leftx, self.lefty, self.rightx, self.righty) 91 | 92 | # print(self.left_fitx.shape) 93 | # print(self.ploty.shape) 94 | pts_left = np.array([np.transpose(np.vstack([self.left_fitx, self.ploty]))]) 95 | pts_right = np.array([np.flipud(np.transpose(np.vstack([self.right_fitx, self.ploty])))]) 96 | pts = np.hstack((pts_left, pts_right)) 97 | self.out_img = cv2.fillPoly(self.out_img, np.int_([pts]), (0,255, 0)) 98 | 99 | return self.out_img 100 | 101 | def fit_poly(self, img_shape, leftx, lefty, rightx, righty): 102 | ### TO-DO: Fit a second order polynomial to each with np.polyfit() ### 103 | try: 104 | self.left_fit = np.polyfit(lefty, leftx, 2) 105 | self.right_fit = np.polyfit(righty, rightx, 2) 106 | except TypeError as e: 107 | print("error") 108 | pass 109 | 110 | # Generate x and y values for plotting 111 | self.ploty = np.linspace(0, img_shape[0]-1, img_shape[0]) 112 | ### TO-DO: Calc both polynomials using self.ploty, left_fit and right_fit ### 113 | try: 114 | left_fitx = self.left_fit[0]*self.ploty**2 + self.left_fit[1]*self.ploty + self.left_fit[2] 115 | right_fitx = self.right_fit[0]*self.ploty**2 + self.right_fit[1]*self.ploty + self.right_fit[2] 116 | except TypeError: 117 | # Avoids an error if `left` and `right_fit` are still none or incorrect 118 | print('The function failed to fit a line!') 119 | left_fitx = 1*self.ploty**2 + 1*self.ploty 120 | right_fitx = 1*self.ploty**2 + 1*self.ploty 121 | 122 | return left_fitx, right_fitx, self.ploty 123 | 124 | def search_around_poly(self, binary_warped, left_fit, right_fit): 125 | # HYPERPARAMETER 126 | # Choose the width of the margin around the previous polynomial to search 127 | self.margin = 100 128 | 129 | # Grab activated pixels 130 | nonzero = binary_warped.nonzero() 131 | self.nonzeroy = np.array(nonzero[0]) 132 | self.nonzerox = np.array(nonzero[1]) 133 | 134 | 135 | self.left_lane_inds = ((self.nonzerox > (left_fit[0]*(self.nonzeroy**2) + left_fit[1]*self.nonzeroy + 136 | left_fit[2] - self.margin)) & (self.nonzerox < (left_fit[0]*(self.nonzeroy**2) + 137 | left_fit[1]*self.nonzeroy + left_fit[2] + self.margin))) 138 | self.right_lane_inds = ((self.nonzerox > (right_fit[0]*(self.nonzeroy**2) + right_fit[1]*self.nonzeroy + 139 | right_fit[2] - self.margin)) & (self.nonzerox < (right_fit[0]*(self.nonzeroy**2) + 140 | right_fit[1]*self.nonzeroy + right_fit[2] + self.margin))) 141 | 142 | # Again, extract left and right line pixel positions 143 | self.leftx = self.nonzerox[self.left_lane_inds] 144 | self.lefty = self.nonzeroy[self.left_lane_inds] 145 | self.rightx = self.nonzerox[self.right_lane_inds] 146 | self.righty = self.nonzeroy[self.right_lane_inds] 147 | 148 | if 0 in [len(self.leftx), len(self.lefty), len(self.rightx), len(self.righty)]: 149 | found = False 150 | else: 151 | found = True 152 | return found 153 | 154 | def measure_curvature_pixels(self, left_fit_cr, right_fit_cr): 155 | ''' 156 | Calculates the curvature of polynomial functions in pixels. 157 | ''' 158 | ym_per_pix = 30/500 # meters per pixel in y dimension 159 | xm_per_pix = 3.7/800 # meters per pixel in x dimension 160 | 161 | # We'll choose the maximum y-value, corresponding to the bottom of the image 162 | y_eval = np.max(self.ploty) 163 | 164 | # Calculation of R_curve (radius of curvature) 165 | left_curverad = ((1 + (2*left_fit_cr[0]*y_eval*ym_per_pix + left_fit_cr[1])**2)**1.5) / np.absolute(2*left_fit_cr[0]) 166 | right_curverad = ((1 + (2*right_fit_cr[0]*y_eval*ym_per_pix + right_fit_cr[1])**2)**1.5) / np.absolute(2*right_fit_cr[0]) 167 | 168 | return left_curverad, right_curverad -------------------------------------------------------------------------------- /models/thresholder.py: -------------------------------------------------------------------------------- 1 | import numpy as np 2 | import cv2 3 | class Thresholder(): 4 | def __init__(self): 5 | pass 6 | 7 | def sat_value(self, lane): 8 | hsv = cv2.cvtColor(lane, cv2.COLOR_BGR2HSV) 9 | hls = cv2.cvtColor(lane, cv2.COLOR_BGR2HLS) 10 | 11 | combined = np.zeros(lane.shape[:2]) 12 | s = hls[:,:,-1] 13 | v = hsv[:,:,-1] 14 | combined[((s >= 70 )) & ((v >= 150))] = 1 15 | 16 | return combined 17 | 18 | def binary_thresh(self, g, th=190, t1=50, t2=255): 19 | img = cv2.cvtColor(g, cv2.COLOR_BGR2GRAY) 20 | _, mask = cv2.threshold(img, thresh=th, maxval=255, type=cv2.THRESH_BINARY) 21 | grayb = np.zeros_like(img) 22 | 23 | grayb[(mask > t1) & (mask <= t2)] = 1 24 | return grayb 25 | 26 | def white_mask(self, lane, sensitivity = 50): 27 | hsv = cv2.cvtColor(lane, cv2.COLOR_BGR2HSV) 28 | 29 | # define range of white color in HSV 30 | # change it according to your need ! 31 | hMin = 0;sMin = 0;vMin = 180 32 | hMax = 179;sMax = 29;vMax = 255 33 | lower_white = np.array([hMin, sMin, vMin]) 34 | upper_white = np.array([hMax, sMax, vMax]) 35 | 36 | # Threshold the HSV image to get only white colors 37 | mask = cv2.inRange(hsv, lower_white, upper_white) 38 | # Bitwise-AND mask and original image 39 | res = cv2.bitwise_and(lane,lane, mask= mask) 40 | res = cv2.cvtColor(res, cv2.COLOR_BGR2GRAY) 41 | return res 42 | 43 | def combine(self, thresh1, thresh2): 44 | combined_binary = np.zeros_like(thresh1) 45 | combined_binary[(thresh1==1) | (thresh2==1)] = 1 46 | return combined_binary 47 | 48 | def get_thresh(self, img): 49 | 50 | hls_dark = cv2.cvtColor(img, cv2.COLOR_BGR2HLS) 51 | hsv_dark = cv2.cvtColor(img, cv2.COLOR_BGR2HSV) 52 | 53 | #for left lane 54 | satval = np.zeros(img.shape[:2]) 55 | s = hls_dark[:,:,-1] 56 | v = hsv_dark[:,:,-1] 57 | satval[((s >= 70 )) & ((v >= 150))] = 1 58 | 59 | #for right lane 60 | binary = self.binary_thresh(img) 61 | white = self.white_mask(img) 62 | 63 | combined = np.zeros_like(binary) 64 | combined[(satval==1) | ((binary==1) | (white == 1))] = 1 65 | return combined 66 | # return final_mask -------------------------------------------------------------------------------- /outputs/output_sample.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/outputs/output_sample.png -------------------------------------------------------------------------------- /test_images/img.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/test_images/img.jpg -------------------------------------------------------------------------------- /test_images/road.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/test_images/road.jpg -------------------------------------------------------------------------------- /test_videos/challenge_video.mp4: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ajeetkharel/autodrive/3102392869a2528bc72b843a4847727873b70b7b/test_videos/challenge_video.mp4 --------------------------------------------------------------------------------