{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [],
   "source": [
    "import os\n",
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.optim as optim\n",
    "from torch.utils.data import DataLoader, Dataset\n",
    "from torchvision import transforms\n",
    "from PIL import Image\n",
    "\n",
    "DATASET_DIR = \"dataset\"\n",
    "SAVE_PATH = \"best_model_2.pth\"\n",
    "BATCH_SIZE = 32\n",
    "EPOCHS = 10\n",
    "LEARNING_RATE = 0.001\n",
    "DEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "\n",
    "CLASSES = \"ABEKMHOPCTYX0123456789\"\n",
    "NUM_CLASSES = len(CLASSES)\n",
    "CLASS_TO_IDX = {char: idx for idx, char in enumerate(CLASSES)}\n",
    "IDX_TO_CLASS = {idx: char for char, idx in CLASS_TO_IDX.items()}"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Weights loaded successfully from best_model_2.pth\n",
      "Model set to evaluation mode\n"
     ]
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "# Определение модели\n",
    "class SimpleCNN(nn.Module):\n",
    "    def __init__(self, num_classes):\n",
    "        super(SimpleCNN, self).__init__()\n",
    "        self.conv_layers = nn.Sequential(\n",
    "            nn.Conv2d(1, 32, kernel_size=3, padding=1),  # Увеличиваем количество фильтров\n",
    "            nn.LeakyReLU(negative_slope=0.1),  # Используем LeakyReLU вместо ReLU\n",
    "            nn.BatchNorm2d(32),  # Нормализация\n",
    "            nn.MaxPool2d(kernel_size=2, stride=2),\n",
    "            \n",
    "            nn.Conv2d(32, 64, kernel_size=3, padding=1),\n",
    "            nn.ELU(),  # Используем ELU вместо ReLU\n",
    "            nn.BatchNorm2d(64),\n",
    "            nn.MaxPool2d(kernel_size=2, stride=2),\n",
    "            \n",
    "            nn.Conv2d(64, 128, kernel_size=3, padding=1),\n",
    "            nn.SiLU(),  # Используем SiLU (Swish) вместо ReLU\n",
    "            nn.BatchNorm2d(128),\n",
    "            nn.MaxPool2d(kernel_size=2, stride=2)\n",
    "        )\n",
    "        self.fc_layers = nn.Sequential(\n",
    "            nn.Linear(128 * 3 * 3, 256),\n",
    "            nn.GELU(),  # Используем GELU для полносвязного слоя\n",
    "            nn.Dropout(0.5),\n",
    "            nn.Linear(256, num_classes)\n",
    "        )\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = self.conv_layers(x)\n",
    "        x = x.view(x.size(0), -1)\n",
    "        x = self.fc_layers(x)\n",
    "        return x\n",
    "\n",
    "# Создание модели и загрузка весов\n",
    "model = SimpleCNN(len(CLASSES)).to(DEVICE)\n",
    "\n",
    "# Загрузка весов модели\n",
    "model.load_state_dict(torch.load(\"best_model_2.pth\", map_location=DEVICE))\n",
    "print(\"Weights loaded successfully from best_model_2.pth\")\n",
    "\n",
    "# Перевод модели в режим оценки\n",
    "model.eval()\n",
    "print(\"Model set to evaluation mode\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 26,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 138244 characters]",
      "text/plain": [
       "<Figure size 1200x600 with 8 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Загрузка изображения в чёрно-белом формате\n",
    "rotated = cv2.imread(\"img/plate-viva.jpg\", cv2.IMREAD_GRAYSCALE)\n",
    "\n",
    "# Увеличиваем изображение для лучшей обработки\n",
    "resized = cv2.resize(rotated, None, fx=4, fy=4, interpolation=cv2.INTER_CUBIC)\n",
    "img_conf = cv2.GaussianBlur(resized, (3, 3), 0)\n",
    "\n",
    "# Настройки вырезания\n",
    "start_x = 25  # начальная координата X\n",
    "start_y = 25  # начальная координата Y\n",
    "symbol_width = 55  # базовая ширина символа\n",
    "symbol_height = 110  # высота символа\n",
    "spacing = 0  # промежуток между символами\n",
    "\n",
    "# Настройки для регулировки ширины и высоты\n",
    "left_adjustment = 4    # Сдвиг с левой стороны (уменьшение ширины: отрицательное, увеличение: положительное)\n",
    "right_adjustment = 4   # Сдвиг с правой стороны (уменьшение ширины: отрицательное, увеличение: положительное)\n",
    "top_adjustment = 10    # Сдвиг сверху (уменьшение высоты: отрицательное, увеличение: положительное)\n",
    "bottom_adjustment = -30  # Сдвиг снизу (уменьшение высоты: отрицательное, увеличение: положительное)\n",
    "\n",
    "# Последовательность символов на номерном знаке: 1 буква, 3 цифры, 2 буквы, 2 цифры\n",
    "symbol_sequence = ['буква', 'цифра', 'цифра', 'цифра', 'буква', 'буква', 'цифра', 'цифра']\n",
    "\n",
    "# Список для сохранения вырезанных символов\n",
    "chars1 = []\n",
    "\n",
    "# Вырезаем каждый символ\n",
    "for i, symbol_type in enumerate(symbol_sequence):\n",
    "    # Координаты текущего символа с учетом регулировки ширины и высоты\n",
    "    crop_start_x = start_x + i * (symbol_width + spacing) + left_adjustment\n",
    "    crop_end_x = start_x + i * (symbol_width + spacing) + symbol_width + right_adjustment\n",
    "\n",
    "    # Если это последние 2 цифры, поднимаем их выше\n",
    "    if i >= len(symbol_sequence) - 2:  # Последние два символа\n",
    "        crop_start_y = start_y + top_adjustment - 11  # Поднимаем символ выше на 10 пикселей\n",
    "        crop_end_y = start_y + symbol_height + bottom_adjustment - 11\n",
    "        crop_start_x -=5\n",
    "        crop_end_x -=5\n",
    "    else:\n",
    "        crop_start_y = start_y + top_adjustment\n",
    "        crop_end_y = start_y + symbol_height + bottom_adjustment\n",
    "\n",
    "    # Проверяем, что координаты в пределах изображения\n",
    "    crop_start_x = max(0, crop_start_x)\n",
    "    crop_end_x = min(img_conf.shape[1], crop_end_x)\n",
    "    crop_start_y = max(0, crop_start_y)\n",
    "    crop_end_y = min(img_conf.shape[0], crop_end_y)\n",
    "\n",
    "    # Вырезаем символ\n",
    "    crop = img_conf[crop_start_y:crop_end_y, crop_start_x:crop_end_x]\n",
    "\n",
    "    # Добавляем символ в список\n",
    "    chars1.append(crop)\n",
    "\n",
    "# Отображаем все символы с помощью matplotlib\n",
    "plt.figure(figsize=(12, 6))\n",
    "for i, char in enumerate(chars1):\n",
    "    plt.subplot(1, len(chars1), i + 1)  # Создаем сетку для отображения символов\n",
    "    plt.imshow(char, cmap='gray')\n",
    "    plt.title(f\"Symbol {i}\")\n",
    "    plt.axis('off')\n",
    "\n",
    "plt.suptitle(\"Extracted Symbols from Plate\")\n",
    "plt.tight_layout()\n",
    "plt.show()\n",
    "\n",
    "# Теперь все символы находятся в списке `chars1`\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 37,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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",
      "text/plain": [
       "<Figure size 1000x200 with 7 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Загрузка изображения номерного знака\n",
    "image_path = \"img/plate3_original.jpg\"  # Замените на путь к вашему изображению\n",
    "plate_image = cv2.imread(image_path)\n",
    "\n",
    "# Преобразование в оттенки серого\n",
    "gray = cv2.cvtColor(plate_image, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "# Бинаризация изображения\n",
    "_, binary = cv2.threshold(gray, 0, 255, cv2.THRESH_BINARY_INV + cv2.THRESH_OTSU)\n",
    "\n",
    "# Определение размеров изображения\n",
    "height, width = binary.shape\n",
    "\n",
    "# Предполагаемые ширины символов (в процентах от ширины изображения)\n",
    "symbol_widths = [0.1, 0.1, 0.1, 0.1, 0.1, 0.1, 0.1]  # 7 символов\n",
    "\n",
    "# Предполагаемые отступы между символами (в процентах от ширины изображения)\n",
    "symbol_gaps = [0.02, 0.02, 0.02, 0.02, 0.02, 0.02]  # 6 промежутков\n",
    "\n",
    "# Вычисление абсолютных позиций символов\n",
    "positions = []\n",
    "current_x = 0\n",
    "for i in range(len(symbol_widths)):\n",
    "    symbol_w = int(symbol_widths[i] * width)\n",
    "    positions.append((current_x, current_x + symbol_w))\n",
    "    current_x += symbol_w\n",
    "    if i < len(symbol_gaps):\n",
    "        current_x += int(symbol_gaps[i] * width)\n",
    "\n",
    "# Извлечение и отображение каждого символа\n",
    "plt.figure(figsize=(10, 2))\n",
    "for idx, (start_x, end_x) in enumerate(positions):\n",
    "    symbol = binary[:, start_x:end_x]\n",
    "    plt.subplot(1, len(positions), idx + 1)\n",
    "    plt.imshow(symbol, cmap='gray')\n",
    "    plt.axis('off')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 27,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 23236 characters]",
      "text/plain": [
       "<Figure size 800x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 25796 characters]",
      "text/plain": [
       "<Figure size 800x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 25212 characters]",
      "text/plain": [
       "<Figure size 800x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 24972 characters]",
      "text/plain": [
       "<Figure size 800x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 23856 characters]",
      "text/plain": [
       "<Figure size 800x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 24508 characters]",
      "text/plain": [
       "<Figure size 800x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 24712 characters]",
      "text/plain": [
       "<Figure size 800x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 23712 characters]",
      "text/plain": [
       "<Figure size 800x400 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torchvision.transforms as transforms\n",
    "import numpy as np\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "transform = transforms.Compose([\n",
    "        transforms.Resize((28, 28)),  # Изменяем размер до 28x28\n",
    "        transforms.ToTensor(), \n",
    "        transforms.ColorJitter(brightness=0.5),      # Преобразуем в тензор\n",
    "        transforms.Normalize(mean=[0.5], std=[0.5])  # Нормализация: среднее 0.5, стандартное отклонение 0.5\n",
    "    ])\n",
    "\n",
    "# Функция для получения топ-3 предсказаний\n",
    "def get_top_predictions(output, top_k=3):\n",
    "    probabilities = torch.softmax(output, dim=1).squeeze()  # Преобразование в вероятности\n",
    "    top_probs, top_indices = torch.topk(probabilities, top_k)\n",
    "    return top_probs.tolist(), [CLASSES[idx] for idx in top_indices.tolist()]\n",
    "\n",
    "from PIL import Image\n",
    "\n",
    "# Функция распознавания символов с выводом графиков\n",
    "def recognize_and_plot_characters(characters, model, device=\"cpu\"):\n",
    "    \"\"\"\n",
    "    Распознает символы из списка изображений characters и строит графики топ-3 предсказаний.\n",
    "\n",
    "    :param characters: Список символов (массивы numpy 28x28).\n",
    "    :param model: Загруженная модель для распознавания.\n",
    "    :param device: Устройство для вычислений (\"cpu\" или \"cuda\").\n",
    "    \"\"\"\n",
    "    for i, char in enumerate(characters):\n",
    "        # Преобразование символа в объект PIL.Image\n",
    "        char_image = Image.fromarray(char)\n",
    "        \n",
    "        # Преобразование символа в тензор\n",
    "        char_tensor = transform(char_image).unsqueeze(0).to(device)  # Добавляем batch размерности\n",
    "        \n",
    "        # Прогон через модель\n",
    "        with torch.no_grad():\n",
    "            output = model(char_tensor)\n",
    "            top_probs, top_classes = get_top_predictions(output)\n",
    "\n",
    "        # Вывод символа и графика топ-3 предсказаний\n",
    "        plt.figure(figsize=(8, 4))\n",
    "\n",
    "        # Символ\n",
    "        plt.subplot(1, 2, 1)\n",
    "        plt.imshow(char, cmap='gray')\n",
    "        plt.axis('off')\n",
    "        plt.title(f\"Символ {i + 1}\")\n",
    "\n",
    "        # Топ-3 предсказания\n",
    "        plt.subplot(1, 2, 2)\n",
    "        plt.barh(top_classes, top_probs, color='blue')\n",
    "        plt.xlabel(\"Вероятность\")\n",
    "        plt.ylabel(\"Класс\")\n",
    "        plt.title(\"Топ-3 предсказания\")\n",
    "        plt.gca().invert_yaxis()  # Инвертируем ось, чтобы лучший класс был сверху\n",
    "\n",
    "        plt.tight_layout()\n",
    "        plt.show()\n",
    "\n",
    "\n",
    "# Использование функции\n",
    "if len(chars1) > 0:\n",
    "    recognize_and_plot_characters(chars1, model, device=DEVICE)\n",
    "else:\n",
    "    print(\"Список characters пуст.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "array([[156, 157, 157, ..., 153, 153, 153],\n",
       "       [156, 157, 158, ..., 155, 155, 155],\n",
       "       [156, 157, 158, ..., 158, 157, 157],\n",
       "       ...,\n",
       "       [174, 174, 175, ..., 183, 183, 183],\n",
       "       [172, 173, 174, ..., 180, 180, 180],\n",
       "       [171, 172, 172, ..., 177, 177, 177]], dtype=uint8)"
      ]
     },
     "execution_count": 13,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "chars1[0]"
   ]
  }
 ],
 "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
}
