Compare commits
20 Commits
a772fdad77
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 0d3467c98c | |||
| 671c5085e9 | |||
| 644c61e9fe | |||
| 5a87e37bb2 | |||
| 206c2a6877 | |||
| 7c0ed23362 | |||
| 162d4b60e3 | |||
| 1a5f4e3fd8 | |||
| 2cee123f07 | |||
| 6da4b066ba | |||
| 03be8d68b6 | |||
| 740fbe7406 | |||
| dce15c1a40 | |||
| 95b82cde7e | |||
| 6b31c54a26 | |||
| 12886be170 | |||
| 63f9daa6d7 | |||
| 149dd86132 | |||
| 96ee97f802 | |||
| 410ac16efd |
1
.gitignore
vendored
1
.gitignore
vendored
@@ -13,6 +13,7 @@ output/
|
|||||||
train_data/
|
train_data/
|
||||||
pretrained_models/
|
pretrained_models/
|
||||||
log/
|
log/
|
||||||
|
tags
|
||||||
*.DS_Store
|
*.DS_Store
|
||||||
*.vs
|
*.vs
|
||||||
*.user
|
*.user
|
||||||
|
|||||||
@@ -1,8 +1,41 @@
|
|||||||
Global:
|
Global:
|
||||||
|
model_name: PP-OCRv5_server_rec # To use static model for inference.
|
||||||
|
debug: false
|
||||||
use_gpu: false
|
use_gpu: false
|
||||||
|
epoch_num: 75
|
||||||
|
log_smooth_window: 20
|
||||||
|
print_batch_step: 10
|
||||||
|
save_model_dir: output/PP-OCRv5_server_rec
|
||||||
|
save_epoch_step: 1
|
||||||
|
eval_batch_step: [0, 2000]
|
||||||
|
cal_metric_during_train: true
|
||||||
|
calc_epoch_interval: 1
|
||||||
|
pretrained_model: https://paddle-model-ecology.bj.bcebos.com/paddlex/official_pretrained_model/PP-OCRv5_server_rec_pretrained.pdparams
|
||||||
|
checkpoints:
|
||||||
|
save_inference_dir:
|
||||||
|
use_visualdl: false
|
||||||
|
infer_img: train_data/images/1C4HJXEN3MW645094.jpg
|
||||||
character_dict_path: train_data/dict.txt
|
character_dict_path: train_data/dict.txt
|
||||||
save_model_dir: ./output/
|
max_text_length: &max_text_length 19
|
||||||
pretrained_model: pretrained_models/PP-OCRv5_server_rec_pretrained.pdparams
|
infer_mode: false
|
||||||
|
use_space_char: true
|
||||||
|
distributed: true
|
||||||
|
save_res_path: output/rec/predicts_ppocrv5.txt
|
||||||
|
d2s_train_image_shape: [3, 48, 320]
|
||||||
|
|
||||||
|
|
||||||
|
Optimizer:
|
||||||
|
name: Adam
|
||||||
|
beta1: 0.9
|
||||||
|
beta2: 0.999
|
||||||
|
lr:
|
||||||
|
name: Cosine
|
||||||
|
learning_rate: 0.0005
|
||||||
|
warmup_epoch: 1
|
||||||
|
regularizer:
|
||||||
|
name: L2
|
||||||
|
factor: 3.0e-05
|
||||||
|
|
||||||
|
|
||||||
Architecture:
|
Architecture:
|
||||||
model_type: rec
|
model_type: rec
|
||||||
@@ -26,7 +59,13 @@ Architecture:
|
|||||||
fc_decay: 0.00001
|
fc_decay: 0.00001
|
||||||
- NRTRHead:
|
- NRTRHead:
|
||||||
nrtr_dim: 384
|
nrtr_dim: 384
|
||||||
max_text_length: 17
|
max_text_length: *max_text_length
|
||||||
|
|
||||||
|
Loss:
|
||||||
|
name: MultiLoss
|
||||||
|
loss_config_list:
|
||||||
|
- CTCLoss:
|
||||||
|
- NRTRLoss:
|
||||||
|
|
||||||
PostProcess:
|
PostProcess:
|
||||||
name: CTCLabelDecode
|
name: CTCLabelDecode
|
||||||
@@ -37,18 +76,41 @@ Metric:
|
|||||||
|
|
||||||
Train:
|
Train:
|
||||||
dataset:
|
dataset:
|
||||||
name: SimpleDataSet
|
name: MultiScaleDataSet
|
||||||
data_dir: train_data/
|
ds_width: false
|
||||||
|
data_dir: train_data/images
|
||||||
|
ext_op_transform_idx: 1
|
||||||
label_file_list:
|
label_file_list:
|
||||||
- train_data/train.txt
|
- train_data/train.txt
|
||||||
|
transforms:
|
||||||
|
- DecodeImage:
|
||||||
|
img_mode: BGR
|
||||||
|
channel_first: false
|
||||||
|
- MultiLabelEncode:
|
||||||
|
gtc_encode: NRTRLabelEncode
|
||||||
|
- KeepKeys:
|
||||||
|
keep_keys:
|
||||||
|
- image
|
||||||
|
- label_ctc
|
||||||
|
- label_gtc
|
||||||
|
- length
|
||||||
|
- valid_ratio
|
||||||
|
sampler:
|
||||||
|
name: MultiScaleSampler
|
||||||
|
scales: [[320, 32], [320, 48], [320, 64]]
|
||||||
|
first_bs: &bs 128
|
||||||
|
fix_bs: false
|
||||||
|
divided_factor: [8, 16] # w, h
|
||||||
|
is_training: True
|
||||||
loader:
|
loader:
|
||||||
batch_size_per_card: 16 # Уменьшите для экономии памяти
|
shuffle: true
|
||||||
num_workers: 2
|
batch_size_per_card: *bs
|
||||||
|
drop_last: true
|
||||||
|
num_workers: 16
|
||||||
Eval:
|
Eval:
|
||||||
dataset:
|
dataset:
|
||||||
name: SimpleDataSet
|
name: SimpleDataSet
|
||||||
data_dir: train_data/
|
data_dir: train_data/images
|
||||||
label_file_list:
|
label_file_list:
|
||||||
- train_data/val.txt
|
- train_data/val.txt
|
||||||
transforms:
|
transforms:
|
||||||
@@ -57,7 +119,6 @@ Eval:
|
|||||||
channel_first: false
|
channel_first: false
|
||||||
- MultiLabelEncode:
|
- MultiLabelEncode:
|
||||||
gtc_encode: NRTRLabelEncode
|
gtc_encode: NRTRLabelEncode
|
||||||
max_text_length: 17
|
|
||||||
- RecResizeImg:
|
- RecResizeImg:
|
||||||
image_shape: [3, 48, 320]
|
image_shape: [3, 48, 320]
|
||||||
- KeepKeys:
|
- KeepKeys:
|
||||||
|
|||||||
135
configs/rec_custom_kaggle.yml
Normal file
135
configs/rec_custom_kaggle.yml
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
Global:
|
||||||
|
model_name: PP-OCRv5_server_rec # To use static model for inference.
|
||||||
|
debug: false
|
||||||
|
use_gpu: true
|
||||||
|
epoch_num: 75
|
||||||
|
log_smooth_window: 20
|
||||||
|
print_batch_step: 10
|
||||||
|
save_model_dir: /kaggle/output/PP-OCRv5_server_rec
|
||||||
|
save_epoch_step: 1
|
||||||
|
eval_batch_step: [0, 2000]
|
||||||
|
cal_metric_during_train: true
|
||||||
|
calc_epoch_interval: 1
|
||||||
|
pretrained_model: https://paddle-model-ecology.bj.bcebos.com/paddlex/official_pretrained_model/PP-OCRv5_server_rec_pretrained.pdparams
|
||||||
|
checkpoints:
|
||||||
|
save_inference_dir:
|
||||||
|
use_visualdl: false
|
||||||
|
infer_img: /kaggle/input/custom-ocr-dataset/images/1C4HJXEN3MW645094.jpg
|
||||||
|
character_dict_path: /kaggle/input/custom-ocr-dataset/dict.txt
|
||||||
|
max_text_length: &max_text_length 19
|
||||||
|
infer_mode: false
|
||||||
|
use_space_char: true
|
||||||
|
distributed: true
|
||||||
|
save_res_path: /kaggle/output/rec/predicts_ppocrv5.txt
|
||||||
|
d2s_train_image_shape: [3, 48, 320]
|
||||||
|
|
||||||
|
|
||||||
|
Optimizer:
|
||||||
|
name: Adam
|
||||||
|
beta1: 0.9
|
||||||
|
beta2: 0.999
|
||||||
|
lr:
|
||||||
|
name: Cosine
|
||||||
|
learning_rate: 0.0005
|
||||||
|
warmup_epoch: 1
|
||||||
|
regularizer:
|
||||||
|
name: L2
|
||||||
|
factor: 3.0e-05
|
||||||
|
|
||||||
|
|
||||||
|
Architecture:
|
||||||
|
model_type: rec
|
||||||
|
algorithm: SVTR_HGNet
|
||||||
|
Transform:
|
||||||
|
Backbone:
|
||||||
|
name: PPHGNetV2_B4
|
||||||
|
text_rec: True
|
||||||
|
Head:
|
||||||
|
name: MultiHead
|
||||||
|
head_list:
|
||||||
|
- CTCHead:
|
||||||
|
Neck:
|
||||||
|
name: svtr
|
||||||
|
dims: 120
|
||||||
|
depth: 2
|
||||||
|
hidden_dims: 120
|
||||||
|
kernel_size: [1, 3]
|
||||||
|
use_guide: True
|
||||||
|
Head:
|
||||||
|
fc_decay: 0.00001
|
||||||
|
- NRTRHead:
|
||||||
|
nrtr_dim: 384
|
||||||
|
max_text_length: *max_text_length
|
||||||
|
|
||||||
|
Loss:
|
||||||
|
name: MultiLoss
|
||||||
|
loss_config_list:
|
||||||
|
- CTCLoss:
|
||||||
|
- NRTRLoss:
|
||||||
|
|
||||||
|
PostProcess:
|
||||||
|
name: CTCLabelDecode
|
||||||
|
|
||||||
|
Metric:
|
||||||
|
name: RecMetric
|
||||||
|
main_indicator: acc
|
||||||
|
|
||||||
|
Train:
|
||||||
|
dataset:
|
||||||
|
name: MultiScaleDataSet
|
||||||
|
ds_width: false
|
||||||
|
data_dir: /kaggle/input/custom-ocr-dataset/images
|
||||||
|
ext_op_transform_idx: 1
|
||||||
|
label_file_list:
|
||||||
|
- /kaggle/input/custom-ocr-dataset/train.txt
|
||||||
|
transforms:
|
||||||
|
- DecodeImage:
|
||||||
|
img_mode: BGR
|
||||||
|
channel_first: false
|
||||||
|
- MultiLabelEncode:
|
||||||
|
gtc_encode: NRTRLabelEncode
|
||||||
|
- KeepKeys:
|
||||||
|
keep_keys:
|
||||||
|
- image
|
||||||
|
- label_ctc
|
||||||
|
- label_gtc
|
||||||
|
- length
|
||||||
|
- valid_ratio
|
||||||
|
sampler:
|
||||||
|
name: MultiScaleSampler
|
||||||
|
scales: [[320, 32], [320, 48], [320, 64]]
|
||||||
|
first_bs: &bs 128
|
||||||
|
fix_bs: false
|
||||||
|
divided_factor: [8, 16] # w, h
|
||||||
|
is_training: True
|
||||||
|
loader:
|
||||||
|
shuffle: true
|
||||||
|
batch_size_per_card: *bs
|
||||||
|
drop_last: true
|
||||||
|
num_workers: 16
|
||||||
|
Eval:
|
||||||
|
dataset:
|
||||||
|
name: SimpleDataSet
|
||||||
|
data_dir: /kaggle/input/custom-ocr-dataset/images
|
||||||
|
label_file_list:
|
||||||
|
- /kaggle/input/custom-ocr-dataset/val.txt
|
||||||
|
transforms:
|
||||||
|
- DecodeImage:
|
||||||
|
img_mode: BGR
|
||||||
|
channel_first: false
|
||||||
|
- MultiLabelEncode:
|
||||||
|
gtc_encode: NRTRLabelEncode
|
||||||
|
- RecResizeImg:
|
||||||
|
image_shape: [3, 48, 320]
|
||||||
|
- KeepKeys:
|
||||||
|
keep_keys:
|
||||||
|
- image
|
||||||
|
- label_ctc
|
||||||
|
- label_gtc
|
||||||
|
- length
|
||||||
|
- valid_ratio
|
||||||
|
loader:
|
||||||
|
shuffle: false
|
||||||
|
drop_last: false
|
||||||
|
batch_size_per_card: 128
|
||||||
|
num_workers: 4
|
||||||
@@ -15,8 +15,8 @@ albumentations
|
|||||||
# to be compatible with albumentations
|
# to be compatible with albumentations
|
||||||
albucore
|
albucore
|
||||||
packaging
|
packaging
|
||||||
# paddlepaddle-gpu
|
paddlepaddle-gpu==2.5.2
|
||||||
paddlepaddle
|
# paddlepaddle
|
||||||
paddleocr
|
paddleocr
|
||||||
imgaug
|
imgaug
|
||||||
lmdb
|
lmdb
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
import os
|
|
||||||
import cv2
|
import cv2
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
def rename_files(directory_path: str):
|
def rename_files(directory_path: str):
|
||||||
extensions = set()
|
extensions = set()
|
||||||
@@ -37,8 +39,51 @@ def check_symbols(dir_path: str):
|
|||||||
if any([char not in dict_chars for char in label]):
|
if any([char not in dict_chars for char in label]):
|
||||||
print(filename)
|
print(filename)
|
||||||
|
|
||||||
|
def max_height(dir_path: str):
|
||||||
|
max_height = 0
|
||||||
|
max_filename = ''
|
||||||
|
for filename in os.listdir(dir_path):
|
||||||
|
im = Image.open(os.path.join(dir_path, filename))
|
||||||
|
if im.height > max_height:
|
||||||
|
max_height = im.height
|
||||||
|
max_filename = filename
|
||||||
|
|
||||||
|
print(max_filename, max_height)
|
||||||
|
|
||||||
|
def resize_to_height(dir_path: str, target_height=48):
|
||||||
|
for filename in os.listdir(dir_path):
|
||||||
|
with Image.open(os.path.join(dir_path, filename)) as img:
|
||||||
|
width_percent = target_height / float(img.height)
|
||||||
|
new_width = int(float(img.width) * width_percent)
|
||||||
|
resized_img = img.resize((new_width, target_height), Image.LANZOS)
|
||||||
|
resized_img.save(os.path.join(dir_path, filename))
|
||||||
|
|
||||||
|
def split_dataset(dir_path: str):
|
||||||
|
images_dir = os.path.join(dir_path, "images")
|
||||||
|
filenames = os.listdir(images_dir)
|
||||||
|
dataset_length = len(filenames)
|
||||||
|
random.shuffle(filenames)
|
||||||
|
train_ratio = 0.8
|
||||||
|
val_ratio = 0.2
|
||||||
|
train_files_len = round(dataset_length * train_ratio)
|
||||||
|
val_files_len = round(dataset_length * val_ratio)
|
||||||
|
train_files = filenames[:train_files_len]
|
||||||
|
val_files = filenames[train_files_len:train_files_len+val_files_len]
|
||||||
|
train_file_path = os.path.join(dir_path, "train.txt")
|
||||||
|
val_file_path = os.path.join(dir_path, "val.txt")
|
||||||
|
with open(train_file_path, "w") as train_file:
|
||||||
|
for filename in train_files:
|
||||||
|
label, _ = filename.split(".")
|
||||||
|
train_file.write(f"{filename}\t{label}\n")
|
||||||
|
|
||||||
|
with open(val_file_path, "w") as val_file:
|
||||||
|
for filename in val_files:
|
||||||
|
label, _ = filename.split(".")
|
||||||
|
val_file.write(f"{filename}\t{label}\n")
|
||||||
|
|
||||||
# rename_files("train_data/images/")
|
# rename_files("train_data/images/")
|
||||||
# check_images("train_data/images/")
|
# check_images("train_data/images/")
|
||||||
# check_labels("train_data/images/")
|
# check_labels("train_data/images/")
|
||||||
check_symbols("train_data/")
|
# check_symbols("train_data/")
|
||||||
|
# max_height("train_data/images")
|
||||||
|
split_dataset("train_data/")
|
||||||
|
|||||||
45
scripts/detect_vin.py
Normal file
45
scripts/detect_vin.py
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
"""
|
||||||
|
exec(open("scripts/detect_vin.py").read())
|
||||||
|
"""
|
||||||
|
|
||||||
|
from paddleocr import PaddleOCR
|
||||||
|
import cv2
|
||||||
|
import re
|
||||||
|
|
||||||
|
ocr = PaddleOCR(lang='en', use_textline_orientation=True)
|
||||||
|
|
||||||
|
def cut_vin(ocr, input_image, output_image):
|
||||||
|
"""
|
||||||
|
Вырезать VIN из изображения.
|
||||||
|
"""
|
||||||
|
|
||||||
|
image = cv2.imread(input_image)
|
||||||
|
|
||||||
|
result = ocr.predict(image)
|
||||||
|
processed_image = result[0]["doc_preprocessor_res"]["output_img"]
|
||||||
|
|
||||||
|
vin_pattern = re.compile(r'^[A-HJ-NPR-Z0-9]{17}$')
|
||||||
|
found_vin = None
|
||||||
|
|
||||||
|
for text, bbox in zip(result[0]["rec_texts"], result[0]["rec_boxes"]):
|
||||||
|
if vin_pattern.match(text):
|
||||||
|
found_vin = text
|
||||||
|
break
|
||||||
|
|
||||||
|
if found_vin:
|
||||||
|
x_min, y_min = bbox[0], bbox[1]
|
||||||
|
x_max, y_max = bbox[2], bbox[3]
|
||||||
|
|
||||||
|
vin_region = processed_image[y_min:y_max, x_min:x_max]
|
||||||
|
|
||||||
|
cv2.imwrite(output_image, vin_region)
|
||||||
|
|
||||||
|
return found_vin
|
||||||
|
|
||||||
|
vin = cut_vin(ocr, "input/image.jpg", "output/vin.jpg")
|
||||||
|
|
||||||
|
if vin:
|
||||||
|
print(f"VIN найден: {vin}.")
|
||||||
|
else:
|
||||||
|
print("VIN не обнаружен.")
|
||||||
|
|
||||||
27
scripts/rec_validation.py
Normal file
27
scripts/rec_validation.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import os
|
||||||
|
from paddleocr import TextRecognition
|
||||||
|
|
||||||
|
ocr = TextRecognition(
|
||||||
|
model_name="PP-OCRv5_server_rec"
|
||||||
|
# model_dir="output/PP-OCRv5_server_rec_vin"
|
||||||
|
)
|
||||||
|
|
||||||
|
with open("train_data/val.txt", "r") as label_file:
|
||||||
|
lines = label_file.readlines()
|
||||||
|
total = len(lines)
|
||||||
|
matches, mismatches = 0, 0
|
||||||
|
for idx, line in enumerate(lines, start=1):
|
||||||
|
file_name, label = line.split("\t")
|
||||||
|
label = label.strip()
|
||||||
|
path = os.path.join("train_data", os.path.join("images", file_name))
|
||||||
|
result = ocr.predict(path)
|
||||||
|
print(f"{idx}/{total} ", end="")
|
||||||
|
if result[0]["rec_text"] and result[0]["rec_text"] == label:
|
||||||
|
print(f"match {label}")
|
||||||
|
matches += 1
|
||||||
|
else:
|
||||||
|
print(f"mismatch {label}")
|
||||||
|
mismatches += 1
|
||||||
|
|
||||||
|
print(f"{matches} matches of {total}")
|
||||||
|
print(f"{mismatches} mismatches of {total}")
|
||||||
27
scripts/score_validation.py
Normal file
27
scripts/score_validation.py
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import os
|
||||||
|
from paddleocr import PaddleOCR
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
|
ocr = PaddleOCR(rec_model_dir="output/vin_rec_inference")
|
||||||
|
|
||||||
|
def get_best_accuracy(ocr: PaddleOCR, file_path: str) -> Optional[tuple[str, float]]:
|
||||||
|
result = ocr.predict(file_path)
|
||||||
|
texts_with_scores = [item for item in filter(
|
||||||
|
lambda x: len(x[0]) == 17,
|
||||||
|
zip(result[0]["rec_texts"], result[0]["rec_scores"])
|
||||||
|
)]
|
||||||
|
if texts_with_scores:
|
||||||
|
return max(
|
||||||
|
texts_with_scores,
|
||||||
|
key=lambda y: y[1]
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
with open("train_data/val.txt", "r") as label_file:
|
||||||
|
for line in label_file.readlines():
|
||||||
|
file_name, label = line.split("\t")
|
||||||
|
path = os.path.join("train_data", os.path.join("images", file_name))
|
||||||
|
print(file_name)
|
||||||
|
print(get_best_accuracy(ocr, path))
|
||||||
Reference in New Issue
Block a user