Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
__pycache__/
*.pyc
venv/
dataset.zip
dataset/
pretrained_model/
pretrained_model.zip
5 changes: 5 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
repos:
- repo: https://github.com/python/black
rev: stable
hooks:
- id: black
15 changes: 11 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
# Grid-Anchor-based-Image-Cropping-Pytorch
This code includes several extensions we have made to our conference version. Please read the [paper](https://drive.google.com/open?id=1Bd1VaqYVycB7Npv5OdXKl-znKs_APl4n) for details.

## Change Log
### 2020-09-10
- Add an `autocrop.py` driver for batch processing on image folder.
- Overrides `_roi_align.so` and `_roi_align.so` with corresponding CPU version. (I'm using a MAC without nvidia gpu :( ).
- Removed a lot of `*.pyc` files
- Add a `requirements.txt` with correct (old) dependencies.
- Fix formatting issues with black in pre-commit hook

### Requirements
## Requirements
python 2.7, pytorch 0.4.1, numpy, cv2, scipy.

### Usage
## Usage
1. Download the source code, the [dataset](https://drive.google.com/open?id=1X9xK5O9cx4_MvDkWAs5wVuM-mPWINaqa) and the [pretrained model](https://drive.google.com/open?id=1kaNWvfIdtbh2GIPNSWXdxqyS-d2DR1F3).

2. Run ``TrainModel.py`` to train a new model on our dataset or Run ``demo_eval.py`` to test the pretrained model on any images.

3. To change the aspect ratio of generated crops, please change the ``generate_bboxes`` function in ``croppingDataset.py`` (line 115).

### Annotation software
## Annotation software
The executable annotation software can be found [here](https://github.com/lld533/Grid-Anchor-based-Image-Cropping-Pytorch).

### Other implementation
## Other implementation
1. [PyTorch 1.0 or later](https://github.com/lld533/Grid-Anchor-based-Image-Cropping-Pytorch)
2. [Matlab (conference version)](https://github.com/HuiZeng/Grid-Anchor-based-Image-Cropping)
92 changes: 51 additions & 41 deletions ShuffleNetV2.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,49 +6,51 @@
from torch.nn import init
import math


def conv_bn(inp, oup, stride):
return nn.Sequential(
nn.Conv2d(inp, oup, 3, stride, 1, bias=False),
nn.BatchNorm2d(oup),
nn.ReLU(inplace=True)
nn.ReLU(inplace=True),
)


def conv_1x1_bn(inp, oup):
return nn.Sequential(
nn.Conv2d(inp, oup, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup),
nn.ReLU(inplace=True)
nn.ReLU(inplace=True),
)


def channel_shuffle(x, groups):
batchsize, num_channels, height, width = x.data.size()

channels_per_group = num_channels // groups

# reshape
x = x.view(batchsize, groups,
channels_per_group, height, width)
x = x.view(batchsize, groups, channels_per_group, height, width)

x = torch.transpose(x, 1, 2).contiguous()

# flatten
x = x.view(batchsize, -1, height, width)

return x



class InvertedResidual(nn.Module):
def __init__(self, inp, oup, stride, benchmodel):
super(InvertedResidual, self).__init__()
self.benchmodel = benchmodel
self.stride = stride
assert stride in [1, 2]

oup_inc = oup//2
oup_inc = oup // 2

if self.benchmodel == 1:
#assert inp == oup_inc
self.banch2 = nn.Sequential(
# assert inp == oup_inc
self.banch2 = nn.Sequential(
# pw
nn.Conv2d(oup_inc, oup_inc, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup_inc),
Expand All @@ -60,8 +62,8 @@ def __init__(self, inp, oup, stride, benchmodel):
nn.Conv2d(oup_inc, oup_inc, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup_inc),
nn.ReLU(inplace=True),
)
else:
)
else:
self.banch1 = nn.Sequential(
# dw
nn.Conv2d(inp, inp, 3, stride, 1, groups=inp, bias=False),
Expand All @@ -70,8 +72,8 @@ def __init__(self, inp, oup, stride, benchmodel):
nn.Conv2d(inp, oup_inc, 1, 1, 0, bias=False),
nn.BatchNorm2d(oup_inc),
nn.ReLU(inplace=True),
)
)

self.banch2 = nn.Sequential(
# pw
nn.Conv2d(inp, oup_inc, 1, 1, 0, bias=False),
Expand All @@ -85,34 +87,34 @@ def __init__(self, inp, oup, stride, benchmodel):
nn.BatchNorm2d(oup_inc),
nn.ReLU(inplace=True),
)

@staticmethod
def _concat(x, out):
# concatenate along channel axis
return torch.cat((x, out), 1)
return torch.cat((x, out), 1)

def forward(self, x):
if 1==self.benchmodel:
x1 = x[:, :(x.shape[1]//2), :, :]
x2 = x[:, (x.shape[1]//2):, :, :]
if 1 == self.benchmodel:
x1 = x[:, : (x.shape[1] // 2), :, :]
x2 = x[:, (x.shape[1] // 2) :, :, :]
out = self._concat(x1, self.banch2(x2))
elif 2==self.benchmodel:
elif 2 == self.benchmodel:
out = self._concat(self.banch1(x), self.banch2(x))

return channel_shuffle(out, 2)


class ShuffleNetV2(nn.Module):
def __init__(self, n_class=1000, input_size=224, width_mult=1.):
def __init__(self, n_class=1000, input_size=224, width_mult=1.0):
super(ShuffleNetV2, self).__init__()

assert input_size % 32 == 0

self.stage_repeats = [4, 8, 4]
# index 0 is invalid and should never be called.
# only used for indexing convenience.
if width_mult == 0.5:
self.stage_out_channels = [-1, 24, 48, 96, 192, 1024]
self.stage_out_channels = [-1, 24, 48, 96, 192, 1024]
elif width_mult == 1.0:
self.stage_out_channels = [-1, 24, 116, 232, 464, 1024]
elif width_mult == 1.5:
Expand All @@ -122,36 +124,42 @@ def __init__(self, n_class=1000, input_size=224, width_mult=1.):
else:
raise ValueError(
"""{} groups is not supported for
1x1 Grouped Convolutions""".format(num_groups))
1x1 Grouped Convolutions""".format(
num_groups
)
)

# building first layer
input_channel = self.stage_out_channels[1]
self.conv1 = conv_bn(3, input_channel, 2)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)
self.conv1 = conv_bn(3, input_channel, 2)
self.maxpool = nn.MaxPool2d(kernel_size=3, stride=2, padding=1)

self.features = []
# building inverted residual blocks
for idxstage in range(len(self.stage_repeats)):
numrepeat = self.stage_repeats[idxstage]
output_channel = self.stage_out_channels[idxstage+2]
output_channel = self.stage_out_channels[idxstage + 2]
for i in range(numrepeat):
if i == 0:
#inp, oup, stride, benchmodel):
self.features.append(InvertedResidual(input_channel, output_channel, 2, 2))
# inp, oup, stride, benchmodel):
self.features.append(
InvertedResidual(input_channel, output_channel, 2, 2)
)
else:
self.features.append(InvertedResidual(input_channel, output_channel, 1, 1))
self.features.append(
InvertedResidual(input_channel, output_channel, 1, 1)
)
input_channel = output_channel



# make it nn.Sequential
self.features = nn.Sequential(*self.features)

# building last several layers
self.conv_last = conv_1x1_bn(input_channel, self.stage_out_channels[-1])
self.globalpool = nn.Sequential(nn.AvgPool2d(int(input_size/32)))
# building classifier
self.classifier = nn.Sequential(nn.Linear(self.stage_out_channels[-1], n_class))
self.conv_last = conv_1x1_bn(input_channel, self.stage_out_channels[-1])
self.globalpool = nn.Sequential(nn.AvgPool2d(int(input_size / 32)))

# building classifier
self.classifier = nn.Sequential(nn.Linear(self.stage_out_channels[-1], n_class))

def forward(self, x):
x = self.conv1(x)
Expand All @@ -163,10 +171,12 @@ def forward(self, x):
x = self.classifier(x)
return x

def shufflenetv2(width_mult=1.):

def shufflenetv2(width_mult=1.0):
model = ShuffleNetV2(width_mult=width_mult)
return model



if __name__ == "__main__":
"""Testing
"""
Expand Down
Loading