From a4e6544db1a11c7f9fb7cc1b0a964e35b765937d Mon Sep 17 00:00:00 2001 From: itqop Date: Wed, 15 Jan 2025 05:14:27 +0300 Subject: [PATCH] Initial commit: Add complete project files to the repository --- __init__.py | 0 config.py | 9 +++ license_plate_recognizer.py | 107 +++++++++++++++++++++++++++++++ main.py | 69 ++++++++++++++++++++ model.py | 56 ++++++++++++++++ models/best_accuracy_model_3.pth | Bin 0 -> 23330538 bytes models/yolo_plate.pt | Bin 0 -> 5453032 bytes requirements.txt | 11 ++++ 8 files changed, 252 insertions(+) create mode 100644 __init__.py create mode 100644 config.py create mode 100644 license_plate_recognizer.py create mode 100644 main.py create mode 100644 model.py create mode 100644 models/best_accuracy_model_3.pth create mode 100644 models/yolo_plate.pt create mode 100644 requirements.txt diff --git a/__init__.py b/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/config.py b/config.py new file mode 100644 index 0000000..2eb390d --- /dev/null +++ b/config.py @@ -0,0 +1,9 @@ +ALPHABET = '-ABEKMHOPCTYX0123456789' +CHAR_TO_IDX = {char: idx + 1 for idx, char in enumerate(ALPHABET)} +IDX_TO_CHAR = {idx + 1: char for idx, char in enumerate(ALPHABET)} + +NUM_CLASSES = len(ALPHABET) + 1 +CTC_BLANK = 0 + +YOLO_WEIGHTS_PATH = "models/yolo_plate.pt" +CRNN_WEIGHTS_PATH = "models/best_accuracy_model_3.pth" \ No newline at end of file diff --git a/license_plate_recognizer.py b/license_plate_recognizer.py new file mode 100644 index 0000000..2313a70 --- /dev/null +++ b/license_plate_recognizer.py @@ -0,0 +1,107 @@ +import torch +from PIL import Image +from ultralytics import YOLO +from torchvision import transforms +from model import CRNN +import cv2 + +from config import CTC_BLANK, IDX_TO_CHAR + +# ------------------------------------------------------------ +# Основной класс, объединяющий YOLO + CRNN +# ------------------------------------------------------------ +class LicensePlateRecognizer: + def __init__(self, + yolo_model_path: str, + crnn_model_path: str, + num_classes: int, + device: str = "cpu"): + """ + yolo_model_path: путь к файлу весов YOLO (напр. "yolo_plate.pt") + crnn_model_path: путь к файлу весов CRNN (напр. "best_accuracy_model_2.pth") + """ + self.yolo_model = YOLO(yolo_model_path) + + self.crnn_model = CRNN(num_classes=num_classes).to(device) + self.crnn_model.load_state_dict(torch.load(crnn_model_path, map_location=device)) + self.crnn_model.eval() + + self.transform = transforms.Compose([ + transforms.Resize((32, 128)), + transforms.ToTensor(), + transforms.Normalize((0.5,), (0.5,)) + ]) + + self.device = device + + def detect_and_recognize_frame(self, frame, padding: int = 5): + """ + Принимает кадр (BGR, np.array) напрямую, + возвращает список словарей: + { + "bbox": (x1, y1, x2, y2), + "text": распознанный_номер + } + """ + results = self.yolo_model.predict(frame) + detections_info = [] + + if not results: + return detections_info + + for result in results: + boxes = result.boxes + if boxes is None or len(boxes) == 0: + continue + + for box in boxes: + x1, y1, x2, y2 = map(int, box.xyxy[0].tolist()) + + x1_padded = max(x1 - padding, 0) + y1_padded = max(y1 - padding, 0) + x2_padded = min(x2 + padding, frame.shape[1]) + y2_padded = min(y2 + padding, frame.shape[0]) + + plate_crop_bgr = frame[y1_padded:y2_padded, x1_padded:x2_padded] + if plate_crop_bgr.size == 0: + continue + + # Конвертация в PIL (grayscale) + plate_gray = cv2.cvtColor(plate_crop_bgr, cv2.COLOR_BGR2GRAY) + plate_pil = Image.fromarray(plate_gray) + + # Подготовка к CRNN + plate_tensor = self.transform(plate_pil).unsqueeze(0).to(self.device) + + with torch.no_grad(): + logits = self.crnn_model(plate_tensor) + decoded_texts = decode_predictions(logits) + recognized_text = decoded_texts[0] if len(decoded_texts) > 0 else "" + + detections_info.append({ + "bbox": (x1, y1, x2, y2), + "text": recognized_text + }) + + return detections_info + + +# ------------------------------------------------------------ +# Расшифровка результатов CRNN +# ------------------------------------------------------------ +def decode_predictions(preds, blank=CTC_BLANK): + # preds: (seq_len, batch, num_classes) + preds = preds.argmax(2) # (seq_len, batch) + preds = preds.permute(1, 0) # (batch, seq_len) + + decoded = [] + for pred in preds: + pred = pred.tolist() + decoded_seq = [] + previous = blank + for p in pred: + if p != previous and p != blank: + decoded_seq.append(IDX_TO_CHAR.get(p, '')) + previous = p + decoded.append(''.join(decoded_seq)) + return decoded \ No newline at end of file diff --git a/main.py b/main.py new file mode 100644 index 0000000..4bd9e0c --- /dev/null +++ b/main.py @@ -0,0 +1,69 @@ + +import cv2 +import numpy as np + +from config import NUM_CLASSES, CRNN_WEIGHTS_PATH, YOLO_WEIGHTS_PATH +from license_plate_recognizer import LicensePlateRecognizer + +# ------------------------------------------------------------ +# Запуск в режиме реального времени (веб-камера) +# ------------------------------------------------------------ +if __name__ == "__main__": + lpr = LicensePlateRecognizer( + yolo_model_path=YOLO_WEIGHTS_PATH, + crnn_model_path=CRNN_WEIGHTS_PATH, + num_classes=NUM_CLASSES, + device="cpu" + ) + + cap = cv2.VideoCapture(0) + + # cap.set(cv2.CAP_PROP_FRAME_WIDTH, 640) + # cap.set(cv2.CAP_PROP_FRAME_HEIGHT, 480) + + while True: + ret, frame = cap.read() + if not ret: + print("Не удалось считать кадр с веб-камеры.") + break + + detections = lpr.detect_and_recognize_frame(frame, padding=5) + + for det in detections: + x1, y1, x2, y2 = det["bbox"] + text = det["text"] + + cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2) + + cv2.putText( + frame, text, (x1, y1 - 10), + cv2.FONT_HERSHEY_SIMPLEX, + 0.7, (0, 255, 0), 2 + ) + + height, width, _ = frame.shape + black_bar_width = 300 + black_bar = np.zeros((height, black_bar_width, 3), dtype=np.uint8) + + y_start = 40 + for i, det in enumerate(detections): + txt = det["text"] + cv2.putText( + black_bar, + f"Plate #{i+1}: {txt}", + (10, y_start), + cv2.FONT_HERSHEY_SIMPLEX, + 0.7, (255, 255, 255), 2 + ) + y_start += 40 + + display_frame = np.hstack((frame, black_bar)) + + cv2.imshow("License Plate Recognition", display_frame) + + key = cv2.waitKey(1) & 0xFF + if key == 27 or key == ord('q'): + break + + cap.release() + cv2.destroyAllWindows() diff --git a/model.py b/model.py new file mode 100644 index 0000000..71fae6a --- /dev/null +++ b/model.py @@ -0,0 +1,56 @@ +import torch.nn as nn + + +# ------------------------------------------------------------ +# CRNN-модель +# ------------------------------------------------------------ +class CRNN(nn.Module): + def __init__(self, num_classes): + super(CRNN, self).__init__() + + # CNN часть + self.cnn = nn.Sequential( + nn.Conv2d(1, 64, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.MaxPool2d(2, 2), + + nn.Conv2d(64, 128, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.MaxPool2d(2, 2), + + nn.Conv2d(128, 256, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.BatchNorm2d(256), + + nn.Conv2d(256, 256, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.MaxPool2d((2,1), (2,1)), + + nn.Conv2d(256, 512, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.BatchNorm2d(512), + + nn.Conv2d(512, 512, kernel_size=3, padding=1), + nn.ReLU(inplace=True), + nn.MaxPool2d((2,1), (2,1)), + ) + + # RNN часть + self.linear1 = nn.Linear(512 * 2, 256) + self.relu = nn.ReLU(inplace=True) + self.lstm = nn.LSTM(256, 256, bidirectional=True, batch_first=True) + self.linear2 = nn.Linear(512, num_classes) + + def forward(self, x): + # x: (batch, 1, 32, 128) — после Resize + conv = self.cnn(x) # (batch, 512, 2, 32) + conv = conv.permute(0, 3, 1, 2) # (batch, width=32, channels=512, height=2) + conv = conv.view(conv.size(0), conv.size(1), -1) # (batch, 32, 512*2) + + out = self.linear1(conv) # (batch, 32, 256) + out = self.relu(out) # (batch, 32, 256) + out, _ = self.lstm(out) # (batch, 32, 512) — bidirectional + out = self.linear2(out) # (batch, 32, num_classes) + + out = out.permute(1, 0, 2) # (32, batch, num_classes) + return out diff --git a/models/best_accuracy_model_3.pth b/models/best_accuracy_model_3.pth new file mode 100644 index 0000000000000000000000000000000000000000..6d91355e8cd6442a5d55f9a4f21c863f145ac8e5 GIT binary patch literal 23330538 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