Skip to content

Commit

Permalink
Returned to a separate backbone for ocr models
Browse files Browse the repository at this point in the history
Fixed bag with block_cnn in ocr models
  • Loading branch information
dmitroprobachay committed Mar 28, 2022
1 parent abe933d commit 8a915d5
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 185 deletions.
8 changes: 4 additions & 4 deletions .github/workflows/nn-ci-cpu-testing.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@ name: Nomeroff Net CI CPU Testing

on:
push:
branches: [ v3.0, master ]
branches: [ master v3.0 v3.1 ]
pull_request:
branches: [ v3.0, master ]
schedule:
- cron: '0 0 * * *' # Runs at 00:00 UTC every day
branches: [ master v3.0 v3.1 ]
# schedule:
# - cron: '0 0 * * *' # Runs at 00:00 UTC every day

jobs:
cpu-tests:
Expand Down
9 changes: 8 additions & 1 deletion History.md
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
3.0.0 / 2021-11-24
3.1.0 / 2022-03-28
==================
**updates**
* Returned to a separate backbone for ocr models
* Fixed bag with block_cnn in ocr models

3.0.0 / 2022-03-16
==================
**updates**
* Refactored code with Sonarqube
* Added Pipelines
* Restructured code
* Added common backbone for ocr models

2.5.0 / 2021-11-24
==================
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
![Nomeroff Net. Automatic numberplate recognition system](./public/images/nomeroff_net.svg)

Nomeroff Net. Automatic numberplate recognition system. Version 3.0
Nomeroff Net. Automatic numberplate recognition system. Version 3.1
<br /><br />
<blockquote style="border-left-color: #ff0000">
Now there is a war going on in my country, Russian soldiers are shooting at civilians in Ukraine. Enemy aviation launches rockets and drops bombs on residential quarters.
Expand Down
2 changes: 1 addition & 1 deletion nomeroff_net/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@

from nomeroff_net.pipelines import pipeline

__version__ = "3.0.0"
__version__ = "3.1.0"
20 changes: 4 additions & 16 deletions nomeroff_net/data_loaders/text_image_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,12 @@
import json
import torch
import numpy as np
import torch.nn as nn
from tqdm import tqdm
from PIL import Image
from typing import List, Tuple, Generator, Any
from torchvision import transforms
from torchvision.models import resnet18

from nomeroff_net.tools.mcm import modelhub, get_device_torch
from nomeroff_net.tools.mcm import get_device_torch
from nomeroff_net.tools.ocr_tools import is_valid_str

device_torch = get_device_torch()
Expand All @@ -26,6 +24,7 @@ def __init__(self,
img_h: int = 64,
batch_size: int = 1,
max_plate_length: int = 8,
seed: int = 42,
with_aug: bool = False) -> None:

self.dirpath = dirpath
Expand All @@ -43,7 +42,7 @@ def __init__(self,
ann_dirpath = os.path.join(dirpath, 'ann')
cache_postfix = "cache_ocr"
if with_aug:
cache_postfix = f"{cache_postfix}_aug"
cache_postfix = f"{cache_postfix}_aug_{seed}"
cache_dirpath = os.path.join(dirpath, cache_postfix)
os.makedirs(cache_dirpath, exist_ok=True)
self.pathes = [os.path.join(img_dirpath, file_name) for file_name in os.listdir(img_dirpath)]
Expand Down Expand Up @@ -116,25 +115,14 @@ def __getitem__(self, index):
img = self.get_x_from_path(img_path)
return img, text

def prepare_transformers(self, model_name="Resnet18"):
model_info = modelhub.download_model_by_name(model_name)
path_to_model = model_info["path"]

resnet = resnet18(pretrained=False)
modules = list(resnet.children())[:-3]
self.resnet = nn.Sequential(*modules)
self.resnet.load_state_dict(torch.load(path_to_model, map_location=device_torch))
self.resnet = self.resnet.to(device_torch)
def prepare_transformers(self):
self.list_transforms = transforms.Compose([
transforms.ToTensor(),
])

@torch.no_grad()
def transform(self, img) -> torch.Tensor:
x = self.list_transforms(img)
x = x.unsqueeze(0).to(device_torch)
x = self.resnet(x)
x = x.squeeze(0).cpu()
return x

def next_sample(self) -> Tuple:
Expand Down
2 changes: 2 additions & 0 deletions nomeroff_net/data_modules/numberplate_ocr_data_module.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ def __init__(self,
batch_size=32,
max_plate_length=8,
num_workers=0,
seed=42,
with_aug=False):
super().__init__()
self.batch_size = batch_size
Expand All @@ -33,6 +34,7 @@ def __init__(self,
img_h=height,
batch_size=batch_size,
max_plate_length=max_plate_length,
seed=seed,
with_aug=with_aug)

# init validation generator
Expand Down
86 changes: 38 additions & 48 deletions nomeroff_net/nnmodels/ocr_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@
"""
import torch
import torch.nn as nn
from typing import List, Any
import pytorch_lightning as pl

from torch.nn import functional
from typing import List, Any
from torchvision.models import resnet18

from nomeroff_net.tools.ocr_tools import plot_loss, print_prediction

Expand All @@ -21,39 +21,6 @@ def weights_init(m):
m.bias.data.fill_(0)


class BlockCNN(nn.Module):
def __init__(self, in_nc, out_nc, kernel_size, padding, stride=tuple([1])):
super(BlockCNN, self).__init__()
self.in_nc = in_nc
self.out_nc = out_nc
self.kernel_size = kernel_size
self.padding = padding
# layers
self.conv = nn.Conv2d(in_nc, out_nc,
kernel_size=kernel_size,
stride=stride,
padding=padding)
self.bn = nn.BatchNorm2d(out_nc)

def forward(self, batch, use_bn=False, use_relu=False,
use_maxpool=False, maxpool_kernelsize=None):
"""
in:
batch - [batch_size, in_nc, H, W]
out:
batch - [batch_size, out_nc, H', W']
"""
batch = self.conv(batch)
if use_bn:
batch = self.bn(batch)
if use_relu:
batch = functional.relu(batch)
if use_maxpool:
assert maxpool_kernelsize is not None
batch = functional.max_pool2d(batch, kernel_size=maxpool_kernelsize, stride=1)
return batch


class BlockRNN(nn.Module):
def __init__(self, in_size, hidden_size, out_size, bidirectional):
super(BlockRNN, self).__init__()
Expand Down Expand Up @@ -105,8 +72,11 @@ def __init__(self,
self.bidirectional = bidirectional

self.label_converter = label_converter

self.cnn = BlockCNN(256, 256, kernel_size=3, padding=1)

# convolutions
resnet = resnet18(pretrained=True)
modules = list(resnet.children())[:-3]
self.resnet = nn.Sequential(*modules)

# RNN + Linear
self.linear1 = nn.Linear(1024, 512)
Expand Down Expand Up @@ -135,6 +105,9 @@ def forward(self, batch: torch.float64):
torch.Size([32, batch_size, vocab_size]) -- :OUT
"""
batch_size = batch.size(0)

# convolutions
batch = self.resnet(batch)

# make sequences of image features
batch = batch.permute(0, 3, 1, 2)
Expand Down Expand Up @@ -195,26 +168,43 @@ def configure_optimizers(self):
nesterov=True,
weight_decay=self.weight_decay,
momentum=self.momentum)
scheduler = torch.optim.lr_scheduler.ReduceLROnPlateau(optimizer, verbose=True, patience=5)
lr_schedulers = {'scheduler': scheduler, 'monitor': 'val_loss'}
return [optimizer], [lr_schedulers]
return optimizer

def training_step(self, batch, batch_idx):
loss = self.step(batch)
self.log(f'train_loss', loss)
self.train_losses.append(loss.cpu().detach().numpy())
return loss
self.log('train_loss', loss, on_step=True, on_epoch=True, prog_bar=True, logger=True)
tqdm_dict = {
'train_loss': loss,
}
return {
'loss': loss,
'progress_bar': tqdm_dict,
'log': tqdm_dict
}

def validation_step(self, batch, batch_idx):
loss = self.step(batch)
self.log('val_loss', loss)
self.val_losses.append(loss.cpu().detach().numpy())
return loss
self.log('val_loss', loss, on_step=False, on_epoch=True, prog_bar=True, logger=True)
tqdm_dict = {
'val_loss': loss,
}
return {
'val_loss': loss,
'progress_bar': tqdm_dict,
'log': tqdm_dict
}

def test_step(self, batch, batch_idx):
loss = self.step(batch)
self.log('test_loss', loss)
return loss
self.log('test_loss', loss, on_step=False, on_epoch=True, prog_bar=True, logger=True)
tqdm_dict = {
'test_loss': loss,
}
return {
'test_loss': loss,
'progress_bar': tqdm_dict,
'log': tqdm_dict
}


if __name__ == "__main__":
Expand Down
7 changes: 2 additions & 5 deletions nomeroff_net/pipes/number_plate_text_readers/text_detector.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from torch import no_grad

from nomeroff_net import text_detectors
from nomeroff_net.pipes.base.resnet18 import Resnet18
from nomeroff_net.tools.errors import TextDetectorError
from nomeroff_net.tools.image_processing import convert_cv_zones_rgb_to_bgr

Expand Down Expand Up @@ -43,15 +42,13 @@ def __init__(self,
self.detectors_names.append(_label)
i += 1

self.resnet18 = Resnet18()
if load_models:
self.load()

def load(self):
"""
TODO: support reloading
"""
self.resnet18.load()
for i, (detector_class, detector_name) in enumerate(zip(self.detectors, self.detectors_names)):
detector = detector_class()
detector.load(self.prisets[detector_name]['model_path'])
Expand Down Expand Up @@ -100,13 +97,13 @@ def preprocess(self,
labels, lines = self.define_predict_classes(zones, labels, lines)
predicted = self.define_order_detector(zones, labels)
for key in predicted.keys():
predicted[key]["xs"] = self.resnet18.preprocess(predicted[key]["zones"])
predicted[key]["xs"] = self.detectors[int(key)].preprocess(predicted[key]["zones"])
return predicted

@no_grad()
def forward(self, predicted):
for key in predicted.keys():
xs = self.resnet18.forward(predicted[key]["xs"])
xs = predicted[key]["xs"]
predicted[key]["ys"] = self.detectors[int(key)].forward(xs)
return predicted

Expand Down
Loading

0 comments on commit 8a915d5

Please sign in to comment.