Skip to content

Commit b2a06fc

Browse files
author
dmitroprobachay
committed
Merge branch 'v3.4'
# Conflicts: # README.md # nomeroff_net/__init__.py
2 parents a8c2dfe + aae8c96 commit b2a06fc

File tree

16 files changed

+265
-119
lines changed

16 files changed

+265
-119
lines changed

History.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
3.4.0 / 2022-11-21
2+
==================
3+
**updates**
4+
* Update ocr models
5+
* Added support yolov8 models for numberpalte detection
6+
* Update option classification models with efficientnet_v2_s backbone
7+
18
3.3.0 / 2022-11-21
29
==================
310
**updates**

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
![Nomeroff Net. Automatic numberplate recognition system](./public/images/nomeroff_net.svg)
22

3-
Nomeroff Net. Automatic numberplate recognition system. Version 3.3
3+
Nomeroff Net. Automatic numberplate recognition system. Version 3.4
44
<br /><br />
55
<blockquote style="border-left-color: #ff0000">
66
Now there is a war going on in our country, Russian soldiers are shooting at civilians in Ukraine. Enemy aviation launches rockets and drops bombs on residential quarters.

examples/ju/inference/custom-object-detection.ipynb

Lines changed: 54 additions & 29 deletions
Large diffs are not rendered by default.

nomeroff_net/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,4 @@
99
from nomeroff_net.pipelines import pipeline
1010

1111

12-
__version__ = "3.3.0"
12+
__version__ = "3.4.0"

nomeroff_net/nnmodels/numberplate_options_model.py

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import torch.nn as nn
77
from torch.nn import functional
88
from .numberplate_classification_model import ClassificationNet
9-
from torchvision.models import resnet18
9+
from torchvision.models import efficientnet_v2_s
1010
from nomeroff_net.tools.errors import NPOptionsNetError
1111
import contextlib
1212
from nomeroff_net.tools.mcm import get_device_torch
@@ -17,36 +17,63 @@ def dummy_context_mgr():
1717
yield None
1818

1919

20+
class DoubleLinear(torch.nn.Module):
21+
def __init__(self, linear1, linear2):
22+
super(DoubleLinear, self).__init__()
23+
self.linear1 = linear1
24+
self.linear2 = linear2
25+
26+
def forward(self, input):
27+
return self.linear1(input), self.linear2(input)
28+
29+
2030
class NPOptionsNet(ClassificationNet):
2131
def __init__(self,
2232
region_output_size: int,
2333
count_line_output_size: int,
2434
batch_size: int = 1,
25-
learning_rate: float = 0.005,
35+
learning_rate: float = 0.001,
2636
train_regions=True,
27-
train_count_lines=True):
37+
train_count_lines=True,
38+
backbone=None):
2839
super(NPOptionsNet, self).__init__()
2940
self.batch_size = batch_size
3041
self.learning_rate = learning_rate
3142

3243
self.train_regions = train_regions
3344
self.train_count_lines = train_count_lines
3445

35-
resnet = resnet18(pretrained=True)
36-
modules = list(resnet.children())[:-3]
37-
self.resnet = nn.Sequential(*modules)
46+
if backbone is None:
47+
backbone = efficientnet_v2_s
48+
self.model = backbone()
3849

39-
self.dropout_reg = nn.Dropout(0.2)
40-
self.fc1_reg = nn.Linear(256 * 4 * 19, 512)
41-
self.fc2_reg = nn.Linear(512, 256)
42-
self.batch_norm_reg = nn.BatchNorm1d(512)
43-
self.fc3_reg = nn.Linear(256, region_output_size)
44-
45-
self.dropout_line = nn.Dropout(0.2)
46-
self.fc1_line = nn.Linear(256 * 4 * 19, 512)
47-
self.fc2_line = nn.Linear(512, 256)
48-
self.batch_norm_line = nn.BatchNorm1d(512)
49-
self.fc3_line = nn.Linear(256, count_line_output_size)
50+
if 'efficientnet' in str(backbone):
51+
in_features = self.model.classifier[1].in_features
52+
else:
53+
raise NotImplementedError(backbone)
54+
55+
linear_region = nn.Sequential(
56+
nn.Dropout(p=0.2, inplace=False),
57+
nn.Linear(in_features=in_features,
58+
out_features=region_output_size,
59+
bias=True)
60+
)
61+
if not self.train_regions:
62+
for name, param in linear_region.named_parameters():
63+
param.requires_grad = False
64+
linear_line = nn.Sequential(
65+
nn.Dropout(p=0.2, inplace=False),
66+
nn.Linear(in_features=in_features,
67+
out_features=count_line_output_size,
68+
bias=True)
69+
)
70+
if not self.train_count_lines:
71+
for name, param in linear_line.named_parameters():
72+
param.requires_grad = False
73+
if 'efficientnet' in str(backbone):
74+
self.model.classifier = DoubleLinear(linear_region, linear_line)
75+
else:
76+
raise NotImplementedError(backbone)
5077

5178
def training_step(self, batch, batch_idx):
5279
loss, acc, acc_reg, acc_line = self.step(batch)
@@ -104,25 +131,10 @@ def test_step(self, batch, batch_idx):
104131
}
105132

106133
def forward(self, x):
107-
x = self.resnet(x)
108-
109-
with dummy_context_mgr() if self.train_regions else torch.no_grad():
110-
x1 = x.reshape(x.size(0), -1)
111-
x1 = self.dropout_reg(x1)
112-
x1 = functional.relu(self.fc1_reg(x1))
113-
if self.batch_size > 1:
114-
x1 = self.batch_norm_reg(x1)
115-
x1 = functional.relu(self.fc2_reg(x1))
116-
x1 = functional.softmax(self.fc3_reg(x1))
117-
118-
with dummy_context_mgr() if self.train_count_lines else torch.no_grad():
119-
x2 = x.reshape(x.size(0), -1)
120-
x2 = self.dropout_line(x2)
121-
x2 = functional.relu(self.fc1_line(x2))
122-
if self.batch_size > 1:
123-
x2 = self.batch_norm_line(x2)
124-
x2 = functional.relu(self.fc2_line(x2))
125-
x2 = functional.softmax(self.fc3_line(x2))
134+
135+
x1, x2 = self.model(x)
136+
x1 = functional.softmax(x1)
137+
x2 = functional.softmax(x2)
126138

127139
return x1, x2
128140

@@ -151,12 +163,19 @@ def step(self, batch):
151163
return loss, acc, acc_reg, acc_line
152164

153165
def configure_optimizers(self):
154-
optimizer = torch.optim.ASGD(self.parameters(),
155-
lr=self.learning_rate,
156-
lambd=0.0001,
157-
alpha=0.75,
158-
t0=1000000.0,
159-
weight_decay=0)
166+
optimizer = torch.optim.SGD(self.parameters(), lr=self.learning_rate, momentum=0.9)
167+
168+
# # Try this
169+
# from lion_pytorch import Lion
170+
# optimizer = Lion(self.parameters(), lr=self.learning_rate)
171+
172+
# # Old optimizer
173+
# optimizer = torch.optim.ASGD(self.parameters(),
174+
# lr=self.learning_rate,
175+
# lambd=0.0001,
176+
# alpha=0.75,
177+
# t0=1000000.0,
178+
# weight_decay=0)
160179
return optimizer
161180

162181

nomeroff_net/pipelines/multiline_number_plate_detection_and_reading.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def forward(self, inputs: Any, **forward_parameters: Dict) -> Any:
4343
count_lines, confidences, predicted,
4444
zones, image_ids,
4545
images_bboxs, images,
46-
images_points, images_mline_boxes) = self.forward_detection_np(inputs, **forward_parameters)
46+
images_points, images_mline_boxes, preprocessed_np) = self.forward_detection_np(inputs, **forward_parameters)
4747
zones = convert_multiline_images_to_one_line(
4848
image_ids,
4949
images,
@@ -56,4 +56,4 @@ def forward(self, inputs: Any, **forward_parameters: Dict) -> Any:
5656
count_lines, confidences,
5757
zones, image_ids,
5858
images_bboxs, images,
59-
images_points, **forward_parameters)
59+
images_points, preprocessed_np, **forward_parameters)

nomeroff_net/pipelines/number_plate_classification.py

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,14 @@ def preprocess(self, inputs: Any, **preprocess_parameters: Dict) -> Any:
3535
@no_grad()
3636
def forward(self, inputs: Any, **forward_parameters: Dict) -> Any:
3737
model_output = self.detector.forward(inputs)
38-
return unzip([p.cpu().numpy() for p in model_output])
38+
model_output = [p.cpu().numpy() for p in model_output]
39+
model_output = [*model_output, inputs]
40+
return unzip(model_output)
3941

4042
def postprocess(self, inputs: Any, **postprocess_parameters: Dict) -> Any:
41-
confidences, region_ids, count_lines = self.detector.unzip_predicted(unzip(inputs))
43+
unziped_inputs = unzip(inputs)
44+
processed_np = [np for np in unziped_inputs[2]]
45+
confidences, region_ids, count_lines = self.detector.unzip_predicted(unziped_inputs)
4246
count_lines = self.detector.custom_count_lines_id_to_all_count_lines(count_lines)
4347
region_names = self.detector.get_region_labels(region_ids)
44-
return unzip([region_ids, region_names, count_lines, confidences, inputs])
48+
return unzip([region_ids, region_names, count_lines, confidences, inputs, processed_np])

nomeroff_net/pipelines/number_plate_detection_and_reading.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,16 @@ def __init__(self,
2727
default_label: str = "eu_ua_2015",
2828
default_lines_count: int = 1,
2929
number_plate_localization_class: Pipeline = DefaultNumberPlateLocalization,
30+
number_plate_localization_detector=None,
3031
**kwargs):
3132
self.default_label = default_label
3233
self.default_lines_count = default_lines_count
3334
self.number_plate_localization = number_plate_localization_class(
3435
"number_plate_localization",
3536
image_loader=None,
36-
path_to_model=path_to_model)
37+
path_to_model=path_to_model,
38+
detector=number_plate_localization_detector
39+
)
3740
self.number_plate_key_points_detection = NumberPlateKeyPointsDetection(
3841
"number_plate_key_points_detection",
3942
image_loader=None,
@@ -81,22 +84,24 @@ def forward_detection_np(self, inputs: Any, **forward_parameters: Dict):
8184
count_lines = [self.default_lines_count for _ in zones]
8285
confidences = [-1 for _ in zones]
8386
predicted = [-1 for _ in zones]
87+
preprocessed_np = [None for _ in zones]
8488
else:
8589
(region_ids, region_names, count_lines,
86-
confidences, predicted) = unzip(self.number_plate_classification(zones, **forward_parameters))
90+
confidences, predicted, preprocessed_np) = unzip(self.number_plate_classification(zones,
91+
**forward_parameters))
8792
return (region_ids, region_names, count_lines, confidences,
8893
predicted, zones, image_ids, images_bboxs, images,
89-
images_points, images_mline_boxes)
94+
images_points, images_mline_boxes, preprocessed_np)
9095

9196
def forward_recognition_np(self, region_ids, region_names,
9297
count_lines, confidences,
9398
zones, image_ids,
9499
images_bboxs, images,
95-
images_points, **forward_parameters):
100+
images_points, preprocessed_np, **forward_parameters):
96101
number_plate_text_reading_res = unzip(
97102
self.number_plate_text_reading(unzip([zones,
98103
region_names,
99-
count_lines]), **forward_parameters))
104+
count_lines, preprocessed_np]), **forward_parameters))
100105
if len(number_plate_text_reading_res):
101106
texts, _ = number_plate_text_reading_res
102107
else:
@@ -116,12 +121,12 @@ def forward(self, inputs: Any, **forward_parameters: Dict) -> Any:
116121
count_lines, confidences, predicted,
117122
zones, image_ids,
118123
images_bboxs, images,
119-
images_points, images_mline_boxes) = self.forward_detection_np(inputs, **forward_parameters)
124+
images_points, images_mline_boxes, preprocessed_np) = self.forward_detection_np(inputs, **forward_parameters)
120125
return self.forward_recognition_np(region_ids, region_names,
121126
count_lines, confidences,
122127
zones, image_ids,
123128
images_bboxs, images,
124-
images_points, **forward_parameters)
129+
images_points, preprocessed_np, **forward_parameters)
125130

126131
@empty_method
127132
def postprocess(self, inputs: Any, **postprocess_parameters: Dict) -> Any:

nomeroff_net/pipelines/number_plate_localization.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@
33
from nomeroff_net.image_loaders import BaseImageLoader
44
from nomeroff_net.pipelines.base import Pipeline
55
from nomeroff_net.tools import unzip
6-
from nomeroff_net.pipes.number_plate_localizators.yolo_v5_detector import Detector
6+
#from nomeroff_net.pipes.number_plate_localizators.yolo_v5_detector import Detector
7+
from nomeroff_net.pipes.number_plate_localizators.yolo_v8_detector import Detector
78

89

910
class NumberPlateLocalization(Pipeline):
@@ -15,9 +16,12 @@ def __init__(self,
1516
task,
1617
image_loader: Optional[Union[str, BaseImageLoader]],
1718
path_to_model="latest",
19+
detector=None,
1820
**kwargs):
1921
super().__init__(task, image_loader, **kwargs)
20-
self.detector = Detector()
22+
if detector is None:
23+
detector = Detector
24+
self.detector = detector()
2125
self.detector.load(path_to_model)
2226

2327
def sanitize_parameters(self, img_size=None, stride=None, min_accuracy=None, **kwargs):

nomeroff_net/pipelines/number_plate_text_reading.py

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,15 @@
1919
"for_regions": ["eu", "xx_transit", "xx_unknown"],
2020
"model_path": "latest"
2121
},
22-
"ru_shufflenet_v2_x2_0": {
22+
"ru": {
2323
"for_regions": ["ru", "eu_ua_ordlo_lpr", "eu_ua_ordlo_dpr"],
2424
"model_path": "latest"
2525
},
26-
"kz_shufflenet_v2_x2_0": {
26+
"kz": {
2727
"for_regions": ["kz"],
2828
"model_path": "latest"
2929
},
30-
"kg_shufflenet_v2_x2_0": {
30+
"kg": { # "kg_shufflenet_v2_x2_0"
3131
"for_regions": ["kg"],
3232
"model_path": "latest"
3333
},
@@ -39,7 +39,7 @@
3939
"for_regions": ["su"],
4040
"model_path": "latest"
4141
},
42-
"am_shufflenet_v2_x2_0": {
42+
"am": {
4343
"for_regions": ["am"],
4444
"model_path": "latest"
4545
},
@@ -62,11 +62,13 @@ def __init__(self,
6262
default_label: str = "eu_ua_2015",
6363
default_lines_count: int = 1,
6464
class_detector=TextDetector,
65+
need_preprocess=False,
6566
**kwargs):
6667
if presets is None:
6768
presets = DEFAULT_PRESETS
6869
super().__init__(task, image_loader, **kwargs)
6970
self.detector = class_detector(presets, default_label, default_lines_count)
71+
self.need_preprocess = need_preprocess
7072

7173
def sanitize_parameters(self, **kwargs):
7274
return {}, {}, {}
@@ -75,14 +77,17 @@ def __call__(self, images: Any, **kwargs):
7577
return super().__call__(images, **kwargs)
7678

7779
def preprocess(self, inputs: Any, **preprocess_parameters: Dict) -> Any:
78-
images, labels, lines = unzip(inputs)
80+
images, labels, lines, preprocessed_np = unzip(inputs)
7981
images = [self.image_loader.load(item) for item in images]
80-
return unzip([images, labels, lines])
82+
return unzip([images, labels, lines, preprocessed_np])
8183

8284
@no_grad()
8385
def forward(self, inputs: Any, **forward_parameters: Dict) -> Any:
84-
images, labels, lines = unzip(inputs)
85-
model_inputs = self.detector.preprocess(images, labels, lines)
86+
images, labels, lines, preprocessed_np = unzip(inputs)
87+
if self.need_preprocess or all([np is None for np in preprocessed_np]):
88+
model_inputs = self.detector.preprocess(images, labels, lines)
89+
else:
90+
model_inputs = self.detector.preprocess(preprocessed_np, labels, lines, need_preprocess=False)
8691
model_outputs = self.detector.forward(model_inputs)
8792
model_outputs = self.detector.postprocess(model_outputs)
8893
return unzip([images, model_outputs, labels])

nomeroff_net/pipes/number_plate_classificators/options_detector.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ def __init__(self, options: Dict = None) -> None:
7575
options = dict()
7676

7777
# input
78-
self.height = 64
79-
self.width = 295
78+
self.height = 50
79+
self.width = 200
8080
self.color_channels = 3
8181

8282
# outputs 1
@@ -398,9 +398,8 @@ def unzip_predicted(predicted):
398398
confidences.append([region_confidence, count_lines_confidence])
399399
return confidences, region_ids, count_lines
400400

401-
@staticmethod
402-
def preprocess(images):
403-
x = [normalize_img(img) for img in images]
401+
def preprocess(self, images):
402+
x = [normalize_img(img, height=self.height, width=self.width) for img in images]
404403
x = np.moveaxis(np.array(x), 3, 1)
405404
return x
406405

@@ -415,7 +414,7 @@ def predict_with_confidence(self, imgs: List[np.ndarray or List]) -> Tuple:
415414
"""
416415
Predict options(region, count lines) with confidence by numberplate images
417416
"""
418-
xs = [normalize_img(img) for img in imgs]
417+
xs = [normalize_img(img, height=self.height, width=self.width) for img in imgs]
419418
if not bool(xs):
420419
return [], [], [], []
421420
predicted = self._predict(xs)

0 commit comments

Comments
 (0)