Skip to content

Commit

Permalink
Init Repo
Browse files Browse the repository at this point in the history
  • Loading branch information
ansleliu committed Apr 17, 2019
1 parent c567663 commit 46724e3
Show file tree
Hide file tree
Showing 99 changed files with 57,539 additions and 1 deletion.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -102,3 +102,5 @@ venv.bak/

# mypy
.mypy_cache/

\.idea/
41 changes: 40 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,41 @@
# LightNet++
LightNet++: Light-weight Networks for Semantic Image Segmentation (Cityscapes and Mapillary Vistas Dataset)
This repository contains the code (PyTorch-1.0+, **W.I.P.**) for: "**LightNet++: Boosted Light-weighted Networks for Real-time Semantic Segmentation**" by Huijun Liu.
**LightNet++** is an advanced version of **[LightNet](https://github.com/ansleliu/LightNet)**, which purpose to get more concise model design,
smaller models, and better performance.

- **MobileNetV2Plus**: Modified MobileNetV2 (backbone)<sup>[[1,8]](#references)</sup> + DSASPPInPlaceABNBlock<sup>[[2,3]](#references)</sup> +
Parallel Bottleneck Channel-Spatial Attention Block (PBCSABlock)<sup>[[6]](#references)</sup> + UnSharp Masking (USM) + Encoder-Decoder Arch.<sup>[[3]](#references)</sup> +
InplaceABN<sup>[[4]](#references)</sup>.

- **ShuffleNetV2Plus**: Modified ShuffleNetV2 (backbone)<sup>[[1,8]](#references)</sup> + DSASPPInPlaceABNBlock<sup>[[2,3]](#references)</sup> +
Parallel Bottleneck Channel-Spatial Attention Block (PBCSABlock)<sup>[[6]](#references)</sup>+ UnSharp Masking (USM) + Encoder-Decoder Arch.<sup>[[3]](#references)</sup> +
InplaceABN<sup>[[4]](#references)</sup>.


### Dependencies

- [Python3.6](https://www.python.org/downloads/)
- [PyTorch(1.0.1+)](http://pytorch.org)
- [inplace_abn](https://github.com/mapillary/inplace_abn)
- [apex](https://github.com/NVIDIA/apex): Tools for easy mixed precision and distributed training in Pytorch
- [tensorboard](https://www.tensorflow.org/programmers_guide/summaries_and_tensorboard)
- [tensorboardX](https://github.com/lanpa/tensorboard-pytorch)
- [tqdm](https://github.com/tqdm/tqdm)

### Datasets for Autonomous Driving
- [Cityscapes](https://www.cityscapes-dataset.com/)
- [Mapillary Vistas Dataset](https://www.mapillary.com/dataset/vistas)
- [Berkeley Deep Drive (BDD100K)](https://bdd-data.berkeley.edu/)
- [ApolloScape](http://apolloscape.auto/index.html#)


## Results

### Results on Cityscapes (Pixel-level/Semantic Segmentation)

| Model | mIoU (S.S*) |
|---|---|
|**MobileNetV2Plus**|69.0885-72.5255 (**WIP**)|
|**ShuffleNetV2Plus**|71.5314 (**WIP**)|

* S.S.: Single Scale (1024x2048)
Empty file added checkpoint/__init__.py
Empty file.
Binary file added checkpoint/cityscapes_mobilenetv2plus_x1.0.pkl
Binary file not shown.
Binary file added checkpoint/cityscapes_shufflenetv2plus_x0.5.pkl
Binary file not shown.
Binary file added checkpoint/cityscapes_shufflenetv2plus_x1.0.pkl
Binary file not shown.
Binary file added checkpoint/deepdrive_shufflenetv2plus_x1.0.pkl
Binary file not shown.
Binary file added checkpoint/imagenet_shufflenetv2_x0.5.pkl
Binary file not shown.
Binary file added checkpoint/imagenet_shufflenetv2_x1.0.pkl
Binary file not shown.
Empty file added datasets/__init__.py
Empty file.
170 changes: 170 additions & 0 deletions datasets/augmentations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
# LightNet++: Boosted Light-weighted Networks for Real-time Semantic Segmentation
# ---------------------------------------------------------------------------------------------------------------- #
# Data Augmentations for Semantic Segmentation
# Adapted from https://github.com/ZijunDeng/pytorch-semantic-segmentation/blob/master/utils/joint_transforms.py
# ---------------------------------------------------------------------------------------------------------------- #
# Author: Huijun Liu M.Sc.
# Date: 10.10.2018
# ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ #
import math
import random
import numbers

from PIL import Image, ImageOps


class Compose(object):
def __init__(self, augmentations):
self.augmentations = augmentations

def __call__(self, image, mask):
"""
:param image: <PIL.Image> Image to be augmented, mode='RGB'
:param mask: <PIL.Image> Mask to be augmented, mode='L'
:return: image, mask
"""
assert image.size == mask.size, "> The size of Image and Mask mismatch!!!"

for aug in self.augmentations:
image, mask = aug(image, mask)
return image, mask


class RandomCrop(object):
def __init__(self, size, padding=0):
if isinstance(size, numbers.Number):
self.size = (int(size), int(size))
else:
self.size = size
self.padding = padding

def __call__(self, image, mask):
if self.padding > 0:
image = ImageOps.expand(image, border=self.padding, fill=0)
mask = ImageOps.expand(mask, border=self.padding, fill=0)

assert image.size == mask.size, "> The size of Image and Mask mismatch!!!"

w, h = image.size
th, tw = self.size
if w == tw and h == th:
return image, mask
if w < tw or h < th:
return image.resize((tw, th), Image.BILINEAR), mask.resize((tw, th), Image.NEAREST)

x1 = random.randint(0, w - tw)
y1 = random.randint(0, h - th)
return image.crop((x1, y1, x1 + tw, y1 + th)), mask.crop((x1, y1, x1 + tw, y1 + th))


class CenterCrop(object):
def __init__(self, size):
if isinstance(size, numbers.Number):
self.size = (int(size), int(size))
else:
self.size = size

def __call__(self, image, mask):
assert image.size == mask.size, "> The size of Image and Mask mismatch!!!"

w, h = image.size
th, tw = self.size
x1 = int(round((w - tw) / 2.))
y1 = int(round((h - th) / 2.))
return image.crop((x1, y1, x1 + tw, y1 + th)), mask.crop((x1, y1, x1 + tw, y1 + th))


class RandomHorizontallyFlip(object):
def __call__(self, image, mask):
if random.random() < 0.5:
return image.transpose(Image.FLIP_LEFT_RIGHT), mask.transpose(Image.FLIP_LEFT_RIGHT)
return image, mask


class FreeScale(object):
def __init__(self, size):
self.size = tuple(reversed(size)) # size: (h, w)

def __call__(self, image, mask):
assert image.size == mask.size, "> The size of Image and Mask mismatch!!!"

return image.resize(self.size, Image.BILINEAR), mask.resize(self.size, Image.NEAREST)


class Scale(object):
def __init__(self, size): # size: (h, w)
self.size = size

def __call__(self, image, mask):
assert image.size == mask.size, "> The size of Image and Mask mismatch!!!"

w, h = image.size
if (w >= h and w == self.size[1]) or (h >= w and h == self.size[0]):
return image, mask

oh, ow = self.size
return image.resize((ow, oh), Image.BILINEAR), mask.resize((ow, oh), Image.NEAREST)


class RandomScale(object):
def __init__(self, limit):
"""
:param limit: <tuple of float> for example: (0.725, 1.25)
"""
assert isinstance(limit, tuple), "> limit must be a tuple, for example: (0.725, 1.25)"
self.limit = limit

def __call__(self, image, mask):
scale = random.uniform(self.limit[0], self.limit[1])
w = int(scale * image.size[0])
h = int(scale * image.size[1])

return image.resize((w, h), Image.BILINEAR), mask.resize((w, h), Image.NEAREST)


class RandomSizedCrop(object):
def __init__(self, size):
self.size = size

def __call__(self, image, mask):
assert image.size == mask.size, "> The size of Image and Mask mismatch!!!"

for attempt in range(12):
area = image.size[0] * image.size[1]
target_area = random.uniform(0.45, 1.0) * area
aspect_ratio = random.uniform(0.5, 2)

w = int(round(math.sqrt(target_area * aspect_ratio)))
h = int(round(math.sqrt(target_area / aspect_ratio)))

if random.random() < 0.5:
w, h = h, w

if w <= image.size[0] and h <= image.size[1]:
x1 = random.randint(0, image.size[0] - w)
y1 = random.randint(0, image.size[1] - h)

image = image.crop((x1, y1, x1 + w, y1 + h))
mask = mask.crop((x1, y1, x1 + w, y1 + h))
assert (image.size == (w, h))

return image.resize((self.size, self.size), Image.BILINEAR), \
mask.resize((self.size, self.size), Image.NEAREST)

# Fallback
scale = Scale(self.size)
crop = CenterCrop(self.size)
return crop(*scale(image, mask))


class RandomRotate(object):
def __init__(self, degree):
self.degree = degree

def __call__(self, image, mask):
rotate_degree = random.random() * 2 * self.degree - self.degree
return image.rotate(rotate_degree, Image.BILINEAR), mask.rotate(rotate_degree, Image.NEAREST)

Empty file added datasets/cityscapes/__init__.py
Empty file.
Loading

0 comments on commit 46724e3

Please sign in to comment.