648 lines
23 KiB
Plaintext
648 lines
23 KiB
Plaintext
{
|
||
"cells": [
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 1,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Используемое устройство: cuda\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"import os\n",
|
||
"import string\n",
|
||
"import torch\n",
|
||
"import torch.nn as nn\n",
|
||
"from torch.utils.data import Dataset, DataLoader\n",
|
||
"from torchvision import transforms\n",
|
||
"from PIL import Image\n",
|
||
"from tqdm import tqdm\n",
|
||
"\n",
|
||
"device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
|
||
"print(f\"Используемое устройство: {device}\")\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 2,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"ALPHABET = '-ABEKMHOPCTYX0123456789'\n",
|
||
"CHAR_TO_IDX = {char: idx + 1 for idx, char in enumerate(ALPHABET)} # 0 будет использоваться для CTC blank\n",
|
||
"IDX_TO_CHAR = {idx + 1: char for idx, char in enumerate(ALPHABET)}\n",
|
||
"\n",
|
||
"NUM_CLASSES = len(ALPHABET) + 1\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 3,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class LicensePlateDataset(Dataset):\n",
|
||
" def __init__(self, root_dir, transform=None):\n",
|
||
" \"\"\"\n",
|
||
" Args:\n",
|
||
" root_dir (string): Путь к директории с данными (train, val, test)\n",
|
||
" transform (callable, optional): Трансформации для изображений\n",
|
||
" \"\"\"\n",
|
||
" self.root_dir = root_dir\n",
|
||
" self.img_dir = os.path.join(root_dir, 'img')\n",
|
||
" self.transform = transform\n",
|
||
" self.images = [img for img in os.listdir(self.img_dir) if img.endswith(('.png', '.jpg', '.jpeg'))]\n",
|
||
" \n",
|
||
" def __len__(self):\n",
|
||
" return len(self.images)\n",
|
||
" \n",
|
||
" def __getitem__(self, idx):\n",
|
||
" img_name = self.images[idx]\n",
|
||
" img_path = os.path.join(self.img_dir, img_name)\n",
|
||
" image = Image.open(img_path).convert('L') \n",
|
||
" if self.transform:\n",
|
||
" image = self.transform(image)\n",
|
||
" \n",
|
||
" label_str = os.path.splitext(img_name)[0].upper()\n",
|
||
" label = [CHAR_TO_IDX[char] for char in label_str if char in CHAR_TO_IDX]\n",
|
||
" label = torch.tensor(label, dtype=torch.long)\n",
|
||
" \n",
|
||
" return image, label\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 6,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"transform = transforms.Compose([\n",
|
||
" transforms.Resize((32, 128)),\n",
|
||
" transforms.ToTensor(),\n",
|
||
" transforms.Normalize((0.5,), (0.5,))\n",
|
||
"])\n",
|
||
"\n",
|
||
"import random\n",
|
||
"import torchvision.transforms.functional as F\n",
|
||
"from torchvision import transforms\n",
|
||
"\n",
|
||
"def random_padding(img):\n",
|
||
" pad_left = random.randint(5, 15)\n",
|
||
" pad_right = random.randint(5, 15)\n",
|
||
" pad_top = random.randint(5, 15)\n",
|
||
" pad_bottom = random.randint(5, 15)\n",
|
||
" return F.pad(img, (pad_left, pad_top, pad_right, pad_bottom), fill=0)\n",
|
||
"\n",
|
||
"# Обновленный transform\n",
|
||
"transform_train = transforms.Compose([\n",
|
||
" transforms.Lambda(random_padding),\n",
|
||
" transforms.Resize((32, 128)), # Изменяем размер изображения\n",
|
||
" transforms.RandomAffine(degrees=10, translate=(0.1, 0.1), scale=(0.9, 1.1)), # Сдвиг, масштабирование, поворот\n",
|
||
" transforms.ColorJitter(brightness=0.2, contrast=0.2), # Изменение яркости и контраста\n",
|
||
" transforms.RandomPerspective(distortion_scale=0.2, p=0.5), # Перспективные искажения\n",
|
||
" transforms.GaussianBlur(kernel_size=3, sigma=(0.1, 2.0)), # Размытие\n",
|
||
" transforms.ToTensor(), # Преобразуем в тензор\n",
|
||
" transforms.Normalize((0.5,), (0.5,)), # Нормализация\n",
|
||
"])\n",
|
||
"\n",
|
||
"def collate_fn(batch):\n",
|
||
" images, labels = zip(*batch)\n",
|
||
" images = torch.stack(images, 0)\n",
|
||
" \n",
|
||
" # Соединяем все метки в один тензор\n",
|
||
" label_lengths = torch.tensor([len(label) for label in labels], dtype=torch.long)\n",
|
||
" labels_concat = torch.cat(labels)\n",
|
||
" \n",
|
||
" return images, labels_concat, label_lengths\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 26,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"train_dataset = LicensePlateDataset(root_dir='dataset-ocr-new/train', transform=transform_train)\n",
|
||
"val_dataset = LicensePlateDataset(root_dir='dataset-ocr-new/val', transform=transform)\n",
|
||
"test_dataset = LicensePlateDataset(root_dir='dataset-ocr-new/test', transform=transform)\n",
|
||
"test_10 = LicensePlateDataset(root_dir=r'dataset-ocr\\fine-tune-val', transform=transform)\n",
|
||
"\n",
|
||
"batch_size = 64\n",
|
||
"\n",
|
||
"train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True, num_workers=0, collate_fn=lambda x: collate_fn(x))\n",
|
||
"val_loader = DataLoader(val_dataset, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=lambda x: collate_fn(x))\n",
|
||
"test_loader = DataLoader(test_dataset, batch_size=batch_size, shuffle=False, num_workers=0, collate_fn=lambda x: collate_fn(x))\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 23,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"class CRNN(nn.Module):\n",
|
||
" def __init__(self, num_classes):\n",
|
||
" super(CRNN, self).__init__()\n",
|
||
" \n",
|
||
" # CNN часть\n",
|
||
" self.cnn = nn.Sequential(\n",
|
||
" nn.Conv2d(1, 64, kernel_size=3, padding=1), # (batch, 64, 32, 128)\n",
|
||
" nn.ReLU(inplace=True),\n",
|
||
" nn.MaxPool2d(2, 2), # (batch, 64, 16, 64)\n",
|
||
" \n",
|
||
" nn.Conv2d(64, 128, kernel_size=3, padding=1), # (batch, 128, 16, 64)\n",
|
||
" nn.ReLU(inplace=True),\n",
|
||
" nn.MaxPool2d(2, 2), # (batch, 128, 8, 32)\n",
|
||
" \n",
|
||
" nn.Conv2d(128, 256, kernel_size=3, padding=1), # (batch, 256, 8, 32)\n",
|
||
" nn.ReLU(inplace=True),\n",
|
||
" nn.BatchNorm2d(256),\n",
|
||
" \n",
|
||
" nn.Conv2d(256, 256, kernel_size=3, padding=1), # (batch, 256, 8, 32)\n",
|
||
" nn.ReLU(inplace=True),\n",
|
||
" nn.MaxPool2d((2,1), (2,1)), # (batch, 256, 4, 32)\n",
|
||
" \n",
|
||
" nn.Conv2d(256, 512, kernel_size=3, padding=1), # (batch, 512, 4, 32)\n",
|
||
" nn.ReLU(inplace=True),\n",
|
||
" nn.BatchNorm2d(512),\n",
|
||
" \n",
|
||
" nn.Conv2d(512, 512, kernel_size=3, padding=1), # (batch, 512, 4, 32)\n",
|
||
" nn.ReLU(inplace=True),\n",
|
||
" nn.MaxPool2d((2,1), (2,1)), # (batch, 512, 2, 32)\n",
|
||
" )\n",
|
||
" \n",
|
||
" # RNN часть\n",
|
||
" self.linear1 = nn.Linear(512 * 2, 256)\n",
|
||
" self.relu = nn.ReLU(inplace=True)\n",
|
||
" self.lstm = nn.LSTM(256, 256, bidirectional=True, batch_first=True)\n",
|
||
" self.linear2 = nn.Linear(512, num_classes)\n",
|
||
" \n",
|
||
" def forward(self, x):\n",
|
||
" # CNN часть\n",
|
||
" conv = self.cnn(x) # (batch, 512, 2, 32)\n",
|
||
" \n",
|
||
" # Перестановка и изменение формы для RNN\n",
|
||
" conv = conv.permute(0, 3, 1, 2) # (batch, width=32, channels=512, height=2)\n",
|
||
" conv = conv.view(conv.size(0), conv.size(1), -1) # (batch, 32, 512*2)\n",
|
||
" \n",
|
||
" # RNN часть\n",
|
||
" out = self.linear1(conv) # (batch, 32, 256)\n",
|
||
" out = self.relu(out) # (batch, 32, 256)\n",
|
||
" out, _ = self.lstm(out) # (batch, 32, 512)\n",
|
||
" out = self.linear2(out) # (batch, 32, num_classes)\n",
|
||
" \n",
|
||
" # Перестановка для CTC loss\n",
|
||
" out = out.permute(1, 0, 2) # (32, batch, num_classes)\n",
|
||
" return out\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 10,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"def train(model, loader, optimizer, criterion, device):\n",
|
||
" model.train()\n",
|
||
" epoch_loss = 0\n",
|
||
" for images, labels, label_lengths in tqdm(loader, desc='Training'):\n",
|
||
" images = images.to(device)\n",
|
||
" labels = labels.to(device)\n",
|
||
" label_lengths = label_lengths.to(device)\n",
|
||
" \n",
|
||
" optimizer.zero_grad()\n",
|
||
" outputs = model(images) # (seq_len, batch, num_classes)\n",
|
||
" \n",
|
||
" # Определяем длину входных последовательностей (последний слой)\n",
|
||
" input_lengths = torch.full(size=(outputs.size(1),), fill_value=outputs.size(0), dtype=torch.long).to(device)\n",
|
||
" \n",
|
||
" loss = criterion(outputs.log_softmax(2), labels, input_lengths, label_lengths)\n",
|
||
" loss.backward()\n",
|
||
" optimizer.step()\n",
|
||
" \n",
|
||
" epoch_loss += loss.item()\n",
|
||
" return epoch_loss / len(loader)\n",
|
||
"\n",
|
||
"def validate(model, loader, criterion, device):\n",
|
||
" model.eval()\n",
|
||
" epoch_loss = 0\n",
|
||
" with torch.no_grad():\n",
|
||
" for images, labels, label_lengths in tqdm(loader, desc='Validation'):\n",
|
||
" images = images.to(device)\n",
|
||
" labels = labels.to(device)\n",
|
||
" label_lengths = label_lengths.to(device)\n",
|
||
" \n",
|
||
" outputs = model(images)\n",
|
||
" \n",
|
||
" input_lengths = torch.full(size=(outputs.size(1),), fill_value=outputs.size(0), dtype=torch.long).to(device)\n",
|
||
" \n",
|
||
" loss = criterion(outputs.log_softmax(2), labels, input_lengths, label_lengths)\n",
|
||
" epoch_loss += loss.item()\n",
|
||
" return epoch_loss / len(loader)\n",
|
||
"\n",
|
||
"def decode_predictions(preds, blank=0):\n",
|
||
" preds = preds.argmax(2) # (seq_len, batch)\n",
|
||
" preds = preds.permute(1, 0) # (batch, seq_len)\n",
|
||
" decoded = []\n",
|
||
" for pred in preds:\n",
|
||
" pred = pred.tolist()\n",
|
||
" decoded_seq = []\n",
|
||
" previous = blank\n",
|
||
" for p in pred:\n",
|
||
" if p != previous and p != blank:\n",
|
||
" decoded_seq.append(IDX_TO_CHAR.get(p, ''))\n",
|
||
" previous = p\n",
|
||
" decoded.append(''.join(decoded_seq))\n",
|
||
" return decoded\n",
|
||
"\n",
|
||
"def evaluate(model, loader, device):\n",
|
||
" model.eval()\n",
|
||
" correct = 0\n",
|
||
" total = 0\n",
|
||
" with torch.no_grad():\n",
|
||
" for images, labels, label_lengths in tqdm(loader, desc='Testing'):\n",
|
||
" images = images.to(device)\n",
|
||
" outputs = model(images)\n",
|
||
" preds = decode_predictions(outputs)\n",
|
||
" \n",
|
||
" batch_size = images.size(0)\n",
|
||
" start = 0\n",
|
||
" for i in range(batch_size):\n",
|
||
" length = label_lengths[i]\n",
|
||
" true_label = ''.join([IDX_TO_CHAR.get(idx.item(), '') for idx in labels[start:start+length]])\n",
|
||
" start += length\n",
|
||
" pred_label = preds[i]\n",
|
||
" if pred_label == true_label:\n",
|
||
" correct += 1\n",
|
||
" total += 1\n",
|
||
" accuracy = correct / total * 100\n",
|
||
" return accuracy\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 27,
|
||
"metadata": {},
|
||
"outputs": [],
|
||
"source": [
|
||
"model = CRNN(num_classes=NUM_CLASSES).to(device)\n",
|
||
"\n",
|
||
"criterion = nn.CTCLoss(blank=0, zero_infinity=True)\n",
|
||
"optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 29,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Epoch 1/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:20<00:00, 7.03it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 16.47it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:05<00:00, 14.37it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0850 | Val Loss: 0.0767 | Accuracy: 96.73%\n",
|
||
"Модель с лучшей потерей сохранена!\n",
|
||
"Модель с лучшей точностью сохранена!\n",
|
||
"Epoch 2/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:20<00:00, 7.07it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 17.92it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 14.78it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0829 | Val Loss: 0.0706 | Accuracy: 97.63%\n",
|
||
"Модель с лучшей потерей сохранена!\n",
|
||
"Модель с лучшей точностью сохранена!\n",
|
||
"Epoch 3/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:20<00:00, 7.09it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 17.20it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 14.51it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0796 | Val Loss: 0.0763 | Accuracy: 96.81%\n",
|
||
"Epoch 4/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:19<00:00, 7.11it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 17.50it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 15.43it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0805 | Val Loss: 0.0732 | Accuracy: 97.72%\n",
|
||
"Модель с лучшей точностью сохранена!\n",
|
||
"Epoch 5/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:19<00:00, 7.17it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 17.76it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 14.62it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0787 | Val Loss: 0.0716 | Accuracy: 97.76%\n",
|
||
"Модель с лучшей точностью сохранена!\n",
|
||
"Epoch 6/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:19<00:00, 7.13it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:03<00:00, 18.01it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 14.58it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0775 | Val Loss: 0.0732 | Accuracy: 97.63%\n",
|
||
"Epoch 7/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:18<00:00, 7.21it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 17.52it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 14.53it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0731 | Val Loss: 0.0746 | Accuracy: 97.58%\n",
|
||
"Epoch 8/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:19<00:00, 7.15it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 17.64it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 14.43it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0745 | Val Loss: 0.0753 | Accuracy: 96.77%\n",
|
||
"Epoch 9/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:19<00:00, 7.19it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 17.87it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:05<00:00, 14.38it/s]\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0734 | Val Loss: 0.0742 | Accuracy: 97.80%\n",
|
||
"Модель с лучшей точностью сохранена!\n",
|
||
"Epoch 10/10\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Training: 100%|██████████| 569/569 [01:19<00:00, 7.15it/s]\n",
|
||
"Validation: 100%|██████████| 72/72 [00:04<00:00, 17.24it/s]\n",
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 14.55it/s]"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Train Loss: 0.0767 | Val Loss: 0.0776 | Accuracy: 96.95%\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"num_epochs = 10\n",
|
||
"best_val_loss = float('inf')\n",
|
||
"best_accuracy = 0.0\n",
|
||
"\n",
|
||
"for epoch in range(1, num_epochs + 1):\n",
|
||
" print(f'Epoch {epoch}/{num_epochs}')\n",
|
||
" \n",
|
||
" train_loss = train(model, train_loader, optimizer, criterion, device)\n",
|
||
" val_loss = validate(model, val_loader, criterion, device)\n",
|
||
" accuracy = evaluate(model, val_loader, device)\n",
|
||
" \n",
|
||
" print(f'Train Loss: {train_loss:.4f} | Val Loss: {val_loss:.4f} | Accuracy: {accuracy:.2f}%')\n",
|
||
" \n",
|
||
" if val_loss < best_val_loss:\n",
|
||
" best_val_loss = val_loss\n",
|
||
" torch.save(model.state_dict(), 'best_loss_model_3.pth')\n",
|
||
" print('Модель с лучшей потерей сохранена!')\n",
|
||
" \n",
|
||
" if accuracy > best_accuracy:\n",
|
||
" best_accuracy = accuracy\n",
|
||
" torch.save(model.state_dict(), 'best_accuracy_model_3.pth')\n",
|
||
" print('Модель с лучшей точностью сохранена!')\n"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 30,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Testing: 100%|██████████| 72/72 [00:04<00:00, 14.55it/s]"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Точность на тестовом наборе: 96.68%\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"#model.load_state_dict(torch.load('models/best_accuracy_model_3.pth'))\n",
|
||
"test_accuracy = evaluate(model, test_loader, device)\n",
|
||
"print(f'Точность на тестовом наборе: {test_accuracy:.2f}%')"
|
||
]
|
||
},
|
||
{
|
||
"cell_type": "code",
|
||
"execution_count": 31,
|
||
"metadata": {},
|
||
"outputs": [
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"Processing Images: 100%|██████████| 8/8 [00:00<00:00, 54.74it/s]"
|
||
]
|
||
},
|
||
{
|
||
"name": "stdout",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"A023TY97.png: A023TY97\n",
|
||
"A413YE97.png: A413YE97\n",
|
||
"B642OT97.png: B642OT97\n",
|
||
"H702TH97.png: H702TH97\n",
|
||
"K263CO97.png: K263CO97\n",
|
||
"O571KT99.png: O571KT99\n",
|
||
"T829MK97.png: T829MK97\n",
|
||
"Y726PA97.png: Y726PA97\n"
|
||
]
|
||
},
|
||
{
|
||
"name": "stderr",
|
||
"output_type": "stream",
|
||
"text": [
|
||
"\n"
|
||
]
|
||
}
|
||
],
|
||
"source": [
|
||
"import os\n",
|
||
"from PIL import Image\n",
|
||
"import torch\n",
|
||
"from torchvision import transforms\n",
|
||
"from tqdm import tqdm \n",
|
||
"\n",
|
||
"def recognize_license_plates(model, folder_path, transform, device):\n",
|
||
" model.eval() \n",
|
||
" images = [img for img in os.listdir(folder_path) if img.endswith(('.png', '.jpg', '.jpeg'))]\n",
|
||
" \n",
|
||
" results = {}\n",
|
||
" \n",
|
||
" for img_name in tqdm(images, desc=\"Processing Images\"):\n",
|
||
" img_path = os.path.join(folder_path, img_name)\n",
|
||
" image = Image.open(img_path).convert('L') \n",
|
||
" \n",
|
||
" image_tensor = transform(image).unsqueeze(0).to(device)\n",
|
||
" \n",
|
||
" with torch.no_grad():\n",
|
||
" output = model(image_tensor) # (seq_len, batch, num_classes)\n",
|
||
" \n",
|
||
" decoded_text = decode_predictions(output)\n",
|
||
" \n",
|
||
" results[img_name] = decoded_text[0]\n",
|
||
" \n",
|
||
" return results\n",
|
||
"\n",
|
||
"folder_path = 'dataset-ocr/fine-tune-train/img_plate_image' # Путь к папке с изображениями\n",
|
||
"#model.load_state_dict(torch.load('best_accuracy_model_2.pth'))\n",
|
||
"#model.to(device)\n",
|
||
"\n",
|
||
"results = recognize_license_plates(model, folder_path, transform, device)\n",
|
||
"\n",
|
||
"for img_name, text in results.items():\n",
|
||
" print(f\"{img_name}: {text}\")\n"
|
||
]
|
||
}
|
||
],
|
||
"metadata": {
|
||
"kernelspec": {
|
||
"display_name": ".venv",
|
||
"language": "python",
|
||
"name": "python3"
|
||
},
|
||
"language_info": {
|
||
"codemirror_mode": {
|
||
"name": "ipython",
|
||
"version": 3
|
||
},
|
||
"file_extension": ".py",
|
||
"mimetype": "text/x-python",
|
||
"name": "python",
|
||
"nbconvert_exporter": "python",
|
||
"pygments_lexer": "ipython3",
|
||
"version": "3.9.13"
|
||
}
|
||
},
|
||
"nbformat": 4,
|
||
"nbformat_minor": 2
|
||
}
|