{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "49811aa9-7ffb-4d09-98f6-2839b37f4e3a",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3\n",
      "548\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "from matplotlib import pyplot as plt\n",
    " \n",
    "image = cv2.imread('img/1.jpg')\n",
    "\n",
    "img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "ret, thresh = cv2.threshold(img_gray, 100, 200, cv2.THRESH_TOZERO_INV)\n",
    "contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "\n",
    "\n",
    "for i in range(len(contours)):\n",
    "    x ,y, w, h = cv2.boundingRect(contours[i])\n",
    "    a=w*h    \n",
    "    aspectRatio = float(w)/h\n",
    "    if  aspectRatio >= 3 and a>600:\n",
    "        approx = cv2.approxPolyDP(contours[i], 0.05* cv2.arcLength(contours[i], True), True)\n",
    "        print(len(approx))\n",
    "        print(x)\n",
    "        if len(approx) <= 4 and x>15  :\n",
    "            width=w\n",
    "            height=h   \n",
    "            start_x=x\n",
    "            start_y=y\n",
    "            end_x=start_x+width\n",
    "            end_y=start_y+height      \n",
    "\n",
    "plate = image[start_y:end_y, start_x:end_x]\n",
    "cv2.imshow('nomer', image)\n",
    "cv2.imshow('nomer2', plate)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "id": "137b37e4-d210-4fcb-a39f-19a68ef903bd",
   "metadata": {},
   "outputs": [],
   "source": [
    "gray = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "_, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)\n",
    "\n",
    "contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "largest_contour = max(contours, key=cv2.contourArea)\n",
    "\n",
    "\n",
    "rect = cv2.minAreaRect(largest_contour)\n",
    "box = cv2.boxPoints(rect)\n",
    "box = np.intp(box)\n",
    "\n",
    "\n",
    "angle = rect[-1]\n",
    "if angle < -45:\n",
    "    angle += 90\n",
    "if (angle < 90):\n",
    "    (h, w) = plate.shape[:2]\n",
    "    center = (w // 2, h // 2)\n",
    "    M = cv2.getRotationMatrix2D(center, angle, 1.0)\n",
    "    rotated = cv2.warpAffine(plate, M, (w, h))\n",
    "else:\n",
    "    angle = 0.5;\n",
    "    (h, w) = plate.shape[:2]\n",
    "    center = (w // 2, h // 2)\n",
    "    M = cv2.getRotationMatrix2D(center, angle, 1.0)\n",
    "    rotated = cv2.warpAffine(plate, M, (w, h))\n",
    "\n",
    "cv2.imshow('nomer2', rotated)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "0ced0394",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "images = ['img/1.jpg', 'img/2.jpg', 'img/3.jpg']\n",
    "\n",
    "processed_plates = []\n",
    "\n",
    "for img_path in images:\n",
    "    image = cv2.imread(img_path)\n",
    "\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    ret, thresh = cv2.threshold(img_gray, 100, 200, cv2.THRESH_TOZERO_INV)\n",
    "    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    for i in range(len(contours)):\n",
    "        x, y, w, h = cv2.boundingRect(contours[i])\n",
    "        a = w * h\n",
    "        aspectRatio = float(w) / h\n",
    "        if aspectRatio >= 3 and a > 600:\n",
    "            approx = cv2.approxPolyDP(contours[i], 0.05 * cv2.arcLength(contours[i], True), True)\n",
    "            if len(approx) <= 4 and x > 15:\n",
    "                width = w\n",
    "                height = h\n",
    "                start_x = x\n",
    "                start_y = y\n",
    "                end_x = start_x + width\n",
    "                end_y = start_y + height\n",
    "                break\n",
    "\n",
    "    plate = image[start_y:end_y, start_x:end_x]\n",
    "\n",
    "    gray = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "    _, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)\n",
    "    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    largest_contour = max(contours, key=cv2.contourArea)\n",
    "    rect = cv2.minAreaRect(largest_contour)\n",
    "    box = cv2.boxPoints(rect)\n",
    "    box = np.intp(box)\n",
    "    angle = rect[-1]\n",
    "\n",
    "    if angle < -45:\n",
    "        angle += 90\n",
    "    if angle < 90:\n",
    "        (h, w) = plate.shape[:2]\n",
    "        center = (w // 2, h // 2)\n",
    "        M = cv2.getRotationMatrix2D(center, angle, 1.0)\n",
    "        rotated = cv2.warpAffine(plate, M, (w, h))\n",
    "    else:\n",
    "        angle = 0.5\n",
    "        (h, w) = plate.shape[:2]\n",
    "        center = (w // 2, h // 2)\n",
    "        M = cv2.getRotationMatrix2D(center, angle, 1.0)\n",
    "        rotated = cv2.warpAffine(plate, M, (w, h))\n",
    "\n",
    "    processed_plates.append(rotated)\n",
    "\n",
    "for i, rotated_plate in enumerate(processed_plates):\n",
    "    cv2.imshow(f'Номер {i+1}', rotated_plate)\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "4cf0eacd",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "images = ['img/1.jpg']\n",
    "\n",
    "for img_path in images:\n",
    "    # Загрузка изображения\n",
    "    image = cv2.imread(img_path)\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    \n",
    "    # Пороговое преобразование для выделения контуров\n",
    "    ret, thresh = cv2.threshold(img_gray, 100, 200, cv2.THRESH_TOZERO_INV)\n",
    "    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    # Поиск контура номерного знака\n",
    "    plate_contour = None\n",
    "    for i in range(len(contours)):\n",
    "        x, y, w, h = cv2.boundingRect(contours[i])\n",
    "        area = w * h\n",
    "        aspect_ratio = float(w) / h\n",
    "        if aspect_ratio >= 3 and area > 600:  # Условие для фильтрации по форме и размеру\n",
    "            approx = cv2.approxPolyDP(contours[i], 0.05 * cv2.arcLength(contours[i], True), True)\n",
    "            if len(approx) <= 4 and x > 15:\n",
    "                plate_contour = contours[i]\n",
    "                start_x, start_y, width, height = x, y, w, h\n",
    "                end_x = start_x + width\n",
    "                end_y = start_y + height\n",
    "                break\n",
    "\n",
    "    # Обрезка области номерного знака\n",
    "    plate = image[start_y:end_y, start_x:end_x]\n",
    "\n",
    "    # Коррекция наклона и поворот номерного знака\n",
    "    gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "    _, plate_thresh = cv2.threshold(gray_plate, 150, 255, cv2.THRESH_BINARY)\n",
    "    contours, _ = cv2.findContours(plate_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    # Корректируем наклон, если требуется\n",
    "    largest_contour = max(contours, key=cv2.contourArea)\n",
    "    rect = cv2.minAreaRect(largest_contour)\n",
    "    box = cv2.boxPoints(rect)\n",
    "    box = np.intp(box)\n",
    "    angle = rect[-1]\n",
    "\n",
    "    if angle < -45:\n",
    "        angle += 90\n",
    "    (h, w) = plate.shape[:2]\n",
    "    center = (w // 2, h // 2)\n",
    "    M = cv2.getRotationMatrix2D(center, angle, 1.0)\n",
    "    rotated_plate = cv2.warpAffine(plate, M, (w, h))\n",
    "\n",
    "    # Обводим номерной знак на исходном изображении\n",
    "    cv2.drawContours(image, [plate_contour], -1, (0, 255, 0), 2)\n",
    "\n",
    "    # Отображение и сохранение результатов\n",
    "    cv2.imshow('Исходное изображение с обведённым номером', image)\n",
    "    cv2.imshow('Изображение номерного знака', rotated_plate)\n",
    "    cv2.imwrite(f'processed_plate_{img_path.split(\"/\")[-1]}', rotated_plate)\n",
    "    cv2.imwrite(f'processed_image_{img_path.split(\"/\")[-1]}', image)\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "7a7f83e8",
   "metadata": {},
   "outputs": [
    {
     "ename": "TesseractNotFoundError",
     "evalue": "tesseract is not installed or it's not in your PATH. See README file for more information.",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31mFileNotFoundError\u001b[0m                         Traceback (most recent call last)",
      "File \u001b[1;32mc:\\Users\\leonk\\Documents\\code\\study\\.venv\\lib\\site-packages\\pytesseract\\pytesseract.py:275\u001b[0m, in \u001b[0;36mrun_tesseract\u001b[1;34m(input_filename, output_filename_base, extension, lang, config, nice, timeout)\u001b[0m\n\u001b[0;32m    274\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 275\u001b[0m     proc \u001b[38;5;241m=\u001b[39m subprocess\u001b[38;5;241m.\u001b[39mPopen(cmd_args, \u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39msubprocess_args())\n\u001b[0;32m    276\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m \u001b[38;5;167;01mOSError\u001b[39;00m \u001b[38;5;28;01mas\u001b[39;00m e:\n",
      "File \u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python39\\lib\\subprocess.py:951\u001b[0m, in \u001b[0;36mPopen.__init__\u001b[1;34m(self, args, bufsize, executable, stdin, stdout, stderr, preexec_fn, close_fds, shell, cwd, env, universal_newlines, startupinfo, creationflags, restore_signals, start_new_session, pass_fds, user, group, extra_groups, encoding, errors, text, umask)\u001b[0m\n\u001b[0;32m    948\u001b[0m             \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstderr \u001b[38;5;241m=\u001b[39m io\u001b[38;5;241m.\u001b[39mTextIOWrapper(\u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mstderr,\n\u001b[0;32m    949\u001b[0m                     encoding\u001b[38;5;241m=\u001b[39mencoding, errors\u001b[38;5;241m=\u001b[39merrors)\n\u001b[1;32m--> 951\u001b[0m     \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_execute_child\u001b[49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mexecutable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mpreexec_fn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mclose_fds\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    952\u001b[0m \u001b[43m                        \u001b[49m\u001b[43mpass_fds\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcwd\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    953\u001b[0m \u001b[43m                        \u001b[49m\u001b[43mstartupinfo\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcreationflags\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mshell\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    954\u001b[0m \u001b[43m                        \u001b[49m\u001b[43mp2cread\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mp2cwrite\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    955\u001b[0m \u001b[43m                        \u001b[49m\u001b[43mc2pread\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mc2pwrite\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    956\u001b[0m \u001b[43m                        \u001b[49m\u001b[43merrread\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merrwrite\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    957\u001b[0m \u001b[43m                        \u001b[49m\u001b[43mrestore_signals\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    958\u001b[0m \u001b[43m                        \u001b[49m\u001b[43mgid\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mgids\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43muid\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mumask\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    959\u001b[0m \u001b[43m                        \u001b[49m\u001b[43mstart_new_session\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m    960\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m:\n\u001b[0;32m    961\u001b[0m     \u001b[38;5;66;03m# Cleanup if the child failed starting.\u001b[39;00m\n",
      "File \u001b[1;32m~\\AppData\\Local\\Programs\\Python\\Python39\\lib\\subprocess.py:1420\u001b[0m, in \u001b[0;36mPopen._execute_child\u001b[1;34m(self, args, executable, preexec_fn, close_fds, pass_fds, cwd, env, startupinfo, creationflags, shell, p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite, unused_restore_signals, unused_gid, unused_gids, unused_uid, unused_umask, unused_start_new_session)\u001b[0m\n\u001b[0;32m   1419\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m-> 1420\u001b[0m     hp, ht, pid, tid \u001b[38;5;241m=\u001b[39m \u001b[43m_winapi\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCreateProcess\u001b[49m\u001b[43m(\u001b[49m\u001b[43mexecutable\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43margs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1421\u001b[0m \u001b[43m                             \u001b[49m\u001b[38;5;66;43;03m# no special security\u001b[39;49;00m\n\u001b[0;32m   1422\u001b[0m \u001b[43m                             \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m   1423\u001b[0m \u001b[43m                             \u001b[49m\u001b[38;5;28;43mint\u001b[39;49m\u001b[43m(\u001b[49m\u001b[38;5;129;43;01mnot\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43mclose_fds\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1424\u001b[0m \u001b[43m                             \u001b[49m\u001b[43mcreationflags\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1425\u001b[0m \u001b[43m                             \u001b[49m\u001b[43menv\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1426\u001b[0m \u001b[43m                             \u001b[49m\u001b[43mcwd\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m   1427\u001b[0m \u001b[43m                             \u001b[49m\u001b[43mstartupinfo\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m   1428\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m   1429\u001b[0m     \u001b[38;5;66;03m# Child is launched. Close the parent's copy of those pipe\u001b[39;00m\n\u001b[0;32m   1430\u001b[0m     \u001b[38;5;66;03m# handles that only the child should have open.  You need\u001b[39;00m\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m   1433\u001b[0m     \u001b[38;5;66;03m# pipe will not close when the child process exits and the\u001b[39;00m\n\u001b[0;32m   1434\u001b[0m     \u001b[38;5;66;03m# ReadFile will hang.\u001b[39;00m\n",
      "\u001b[1;31mFileNotFoundError\u001b[0m: [WinError 2] Не удается найти указанный файл",
      "\nDuring handling of the above exception, another exception occurred:\n",
      "\u001b[1;31mTesseractNotFoundError\u001b[0m                    Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[4], line 54\u001b[0m\n\u001b[0;32m     51\u001b[0m rotated_plate \u001b[38;5;241m=\u001b[39m cv2\u001b[38;5;241m.\u001b[39mwarpAffine(plate, M, (w, h))\n\u001b[0;32m     53\u001b[0m \u001b[38;5;66;03m# Распознавание текста с номерного знака\u001b[39;00m\n\u001b[1;32m---> 54\u001b[0m plate_text \u001b[38;5;241m=\u001b[39m \u001b[43mpytesseract\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mimage_to_string\u001b[49m\u001b[43m(\u001b[49m\u001b[43mrotated_plate\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mconfig\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43m--psm 8\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m     55\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mРаспознанный номер:\u001b[39m\u001b[38;5;124m\"\u001b[39m, plate_text)\n\u001b[0;32m     57\u001b[0m \u001b[38;5;66;03m# Обводим номер на исходном изображении\u001b[39;00m\n",
      "File \u001b[1;32mc:\\Users\\leonk\\Documents\\code\\study\\.venv\\lib\\site-packages\\pytesseract\\pytesseract.py:486\u001b[0m, in \u001b[0;36mimage_to_string\u001b[1;34m(image, lang, config, nice, output_type, timeout)\u001b[0m\n\u001b[0;32m    481\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    482\u001b[0m \u001b[38;5;124;03mReturns the result of a Tesseract OCR run on the provided image to string\u001b[39;00m\n\u001b[0;32m    483\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    484\u001b[0m args \u001b[38;5;241m=\u001b[39m [image, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtxt\u001b[39m\u001b[38;5;124m'\u001b[39m, lang, config, nice, timeout]\n\u001b[1;32m--> 486\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[43m{\u001b[49m\n\u001b[0;32m    487\u001b[0m \u001b[43m    \u001b[49m\u001b[43mOutput\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mBYTES\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_and_get_output\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43margs\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;241;43m+\u001b[39;49m\u001b[43m \u001b[49m\u001b[43m[\u001b[49m\u001b[38;5;28;43;01mTrue\u001b[39;49;00m\u001b[43m]\u001b[49m\u001b[43m)\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    488\u001b[0m \u001b[43m    \u001b[49m\u001b[43mOutput\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mDICT\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43m{\u001b[49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[38;5;124;43mtext\u001b[39;49m\u001b[38;5;124;43m'\u001b[39;49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_and_get_output\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m}\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    489\u001b[0m \u001b[43m    \u001b[49m\u001b[43mOutput\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mSTRING\u001b[49m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mlambda\u001b[39;49;00m\u001b[43m:\u001b[49m\u001b[43m \u001b[49m\u001b[43mrun_and_get_output\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m    490\u001b[0m \u001b[43m\u001b[49m\u001b[43m}\u001b[49m\u001b[43m[\u001b[49m\u001b[43moutput_type\u001b[49m\u001b[43m]\u001b[49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\n",
      "File \u001b[1;32mc:\\Users\\leonk\\Documents\\code\\study\\.venv\\lib\\site-packages\\pytesseract\\pytesseract.py:489\u001b[0m, in \u001b[0;36mimage_to_string.<locals>.<lambda>\u001b[1;34m()\u001b[0m\n\u001b[0;32m    481\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    482\u001b[0m \u001b[38;5;124;03mReturns the result of a Tesseract OCR run on the provided image to string\u001b[39;00m\n\u001b[0;32m    483\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m    484\u001b[0m args \u001b[38;5;241m=\u001b[39m [image, \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtxt\u001b[39m\u001b[38;5;124m'\u001b[39m, lang, config, nice, timeout]\n\u001b[0;32m    486\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m {\n\u001b[0;32m    487\u001b[0m     Output\u001b[38;5;241m.\u001b[39mBYTES: \u001b[38;5;28;01mlambda\u001b[39;00m: run_and_get_output(\u001b[38;5;241m*\u001b[39m(args \u001b[38;5;241m+\u001b[39m [\u001b[38;5;28;01mTrue\u001b[39;00m])),\n\u001b[0;32m    488\u001b[0m     Output\u001b[38;5;241m.\u001b[39mDICT: \u001b[38;5;28;01mlambda\u001b[39;00m: {\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtext\u001b[39m\u001b[38;5;124m'\u001b[39m: run_and_get_output(\u001b[38;5;241m*\u001b[39margs)},\n\u001b[1;32m--> 489\u001b[0m     Output\u001b[38;5;241m.\u001b[39mSTRING: \u001b[38;5;28;01mlambda\u001b[39;00m: \u001b[43mrun_and_get_output\u001b[49m\u001b[43m(\u001b[49m\u001b[38;5;241;43m*\u001b[39;49m\u001b[43margs\u001b[49m\u001b[43m)\u001b[49m,\n\u001b[0;32m    490\u001b[0m }[output_type]()\n",
      "File \u001b[1;32mc:\\Users\\leonk\\Documents\\code\\study\\.venv\\lib\\site-packages\\pytesseract\\pytesseract.py:352\u001b[0m, in \u001b[0;36mrun_and_get_output\u001b[1;34m(image, extension, lang, config, nice, timeout, return_bytes)\u001b[0m\n\u001b[0;32m    341\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m save(image) \u001b[38;5;28;01mas\u001b[39;00m (temp_name, input_filename):\n\u001b[0;32m    342\u001b[0m     kwargs \u001b[38;5;241m=\u001b[39m {\n\u001b[0;32m    343\u001b[0m         \u001b[38;5;124m'\u001b[39m\u001b[38;5;124minput_filename\u001b[39m\u001b[38;5;124m'\u001b[39m: input_filename,\n\u001b[0;32m    344\u001b[0m         \u001b[38;5;124m'\u001b[39m\u001b[38;5;124moutput_filename_base\u001b[39m\u001b[38;5;124m'\u001b[39m: temp_name,\n\u001b[1;32m   (...)\u001b[0m\n\u001b[0;32m    349\u001b[0m         \u001b[38;5;124m'\u001b[39m\u001b[38;5;124mtimeout\u001b[39m\u001b[38;5;124m'\u001b[39m: timeout,\n\u001b[0;32m    350\u001b[0m     }\n\u001b[1;32m--> 352\u001b[0m     run_tesseract(\u001b[38;5;241m*\u001b[39m\u001b[38;5;241m*\u001b[39mkwargs)\n\u001b[0;32m    353\u001b[0m     \u001b[38;5;28;01mreturn\u001b[39;00m _read_output(\n\u001b[0;32m    354\u001b[0m         \u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m{\u001b[39;00mkwargs[\u001b[38;5;124m'\u001b[39m\u001b[38;5;124moutput_filename_base\u001b[39m\u001b[38;5;124m'\u001b[39m]\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mextsep\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;132;01m{\u001b[39;00mextension\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m\"\u001b[39m,\n\u001b[0;32m    355\u001b[0m         return_bytes,\n\u001b[0;32m    356\u001b[0m     )\n",
      "File \u001b[1;32mc:\\Users\\leonk\\Documents\\code\\study\\.venv\\lib\\site-packages\\pytesseract\\pytesseract.py:280\u001b[0m, in \u001b[0;36mrun_tesseract\u001b[1;34m(input_filename, output_filename_base, extension, lang, config, nice, timeout)\u001b[0m\n\u001b[0;32m    278\u001b[0m         \u001b[38;5;28;01mraise\u001b[39;00m\n\u001b[0;32m    279\u001b[0m     \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m--> 280\u001b[0m         \u001b[38;5;28;01mraise\u001b[39;00m TesseractNotFoundError()\n\u001b[0;32m    282\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m timeout_manager(proc, timeout) \u001b[38;5;28;01mas\u001b[39;00m error_string:\n\u001b[0;32m    283\u001b[0m     \u001b[38;5;28;01mif\u001b[39;00m proc\u001b[38;5;241m.\u001b[39mreturncode:\n",
      "\u001b[1;31mTesseractNotFoundError\u001b[0m: tesseract is not installed or it's not in your PATH. See README file for more information."
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import pytesseract\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "# Путь к Tesseract OCR, если требуется\n",
    "# pytesseract.pytesseract.tesseract_cmd = r'C:\\Program Files\\Tesseract-OCR\\tesseract.exe'\n",
    "\n",
    "images = ['img/1.jpg', 'img/2.jpg', 'img/3.jpg']\n",
    "\n",
    "for img_path in images:\n",
    "    image = cv2.imread(img_path)\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    ret, thresh = cv2.threshold(img_gray, 100, 200, cv2.THRESH_TOZERO_INV)\n",
    "    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    # Поиск контура номерного знака\n",
    "    plate_contour = None\n",
    "    for i in range(len(contours)):\n",
    "        x, y, w, h = cv2.boundingRect(contours[i])\n",
    "        a = w * h\n",
    "        aspectRatio = float(w) / h\n",
    "        if aspectRatio >= 3 and a > 600:\n",
    "            approx = cv2.approxPolyDP(contours[i], 0.05 * cv2.arcLength(contours[i], True), True)\n",
    "            if len(approx) <= 4 and x > 15:\n",
    "                plate_contour = contours[i]\n",
    "                start_x, start_y, width, height = x, y, w, h\n",
    "                end_x = start_x + width\n",
    "                end_y = start_y + height\n",
    "                break\n",
    "\n",
    "    # Извлечение области номерного знака\n",
    "    plate = image[start_y:end_y, start_x:end_x]\n",
    "\n",
    "    # Коррекция наклона и поворот номерного знака\n",
    "    gray = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "    _, thresh = cv2.threshold(gray, 150, 255, cv2.THRESH_BINARY)\n",
    "    contours, _ = cv2.findContours(thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    largest_contour = max(contours, key=cv2.contourArea)\n",
    "    rect = cv2.minAreaRect(largest_contour)\n",
    "    box = cv2.boxPoints(rect)\n",
    "    box = np.intp(box)\n",
    "    angle = rect[-1]\n",
    "\n",
    "    if angle < -45:\n",
    "        angle += 90\n",
    "    (h, w) = plate.shape[:2]\n",
    "    center = (w // 2, h // 2)\n",
    "    M = cv2.getRotationMatrix2D(center, angle, 1.0)\n",
    "    rotated_plate = cv2.warpAffine(plate, M, (w, h))\n",
    "\n",
    "    # Распознавание текста с номерного знака\n",
    "    plate_text = pytesseract.image_to_string(rotated_plate, config='--psm 8')\n",
    "    print(\"Распознанный номер:\", plate_text)\n",
    "\n",
    "    # Обводим номер на исходном изображении\n",
    "    cv2.drawContours(image, [plate_contour], -1, (0, 255, 0), 2)\n",
    "    cv2.putText(image, plate_text.strip(), (start_x, start_y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)\n",
    "\n",
    "    # Показ результатов\n",
    "    cv2.imshow('Исходное изображение с обведённым номером', image)\n",
    "    cv2.imshow('Изображение номерного знака', rotated_plate)\n",
    "\n",
    "    # Сохранение результатов\n",
    "    cv2.imwrite(f'processed_plate_{img_path.split(\"/\")[-1]}', rotated_plate)\n",
    "    cv2.imwrite(f'processed_image_{img_path.split(\"/\")[-1]}', image)\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "id": "9e3f2a58",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Номерной знак успешно извлечён и сохранён как 'extracted_plate.jpg'\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "# Загрузка изображения\n",
    "image = cv2.imread('img/1.jpg')\n",
    "img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "# Улучшение контраста с использованием порогового преобразования\n",
    "_, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "\n",
    "# Применение морфологических операций (закрытие) для объединения разрывов\n",
    "kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n",
    "closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)\n",
    "\n",
    "# Поиск контуров после морфологической обработки\n",
    "contours, hierarchy = cv2.findContours(closed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "# Поиск контура номерного знака\n",
    "plate_contour = None\n",
    "for contour in contours:\n",
    "    # Прямоугольник вокруг контура\n",
    "    x, y, w, h = cv2.boundingRect(contour)\n",
    "    \n",
    "    # Фильтры по соотношению сторон и площади\n",
    "    area = w * h\n",
    "    aspect_ratio = float(w) / h\n",
    "    if 2.5 < aspect_ratio < 5.5 and 1500 < area < 15000:  # Уточнённые условия\n",
    "        approx = cv2.approxPolyDP(contour, 0.02 * cv2.arcLength(contour, True), True)\n",
    "        if len(approx) == 4:  # Проверяем, что контур прямоугольный\n",
    "            plate_contour = approx\n",
    "            start_x, start_y, width, height = x, y, w, h\n",
    "            end_x = start_x + width\n",
    "            end_y = start_y + height\n",
    "            break\n",
    "\n",
    "# Проверяем, нашли ли контур номерного знака\n",
    "if plate_contour is not None:\n",
    "    # Обрезаем и сохраняем область номерного знака\n",
    "    plate = image[start_y:end_y, start_x:end_x]\n",
    "    cv2.imwrite('extracted_plate.jpg', plate)\n",
    "    print(\"Номерной знак успешно извлечён и сохранён как 'extracted_plate.jpg'\")\n",
    "else:\n",
    "    print(\"Не удалось найти номерной знак на изображении.\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "id": "a9d5e8d5",
   "metadata": {},
   "outputs": [],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "\n",
    "# Список изображений для обработки\n",
    "images = ['img/1.jpg']\n",
    "\n",
    "for img_path in images:\n",
    "    # Загрузка изображения\n",
    "    image = cv2.imread(img_path)\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    \n",
    "    # Улучшение контраста с использованием порогового преобразования\n",
    "    _, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "\n",
    "    # Применение морфологических операций (закрытие) для объединения разрывов\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n",
    "    closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)\n",
    "\n",
    "    # Поиск контуров после морфологической обработки\n",
    "    contours, hierarchy = cv2.findContours(closed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    # Поиск контура номерного знака\n",
    "    plate_contour = None\n",
    "    for contour in contours:\n",
    "        # Прямоугольник вокруг контура\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        \n",
    "        # Фильтры по соотношению сторон и площади\n",
    "        area = w * h\n",
    "        aspect_ratio = float(w) / h\n",
    "        if 2.5 < aspect_ratio < 5.5 and 1500 < area < 15000:  # Уточнённые условия\n",
    "            approx = cv2.approxPolyDP(contour, 0.02 * cv2.arcLength(contour, True), True)\n",
    "            if len(approx) == 4:  # Проверяем, что контур прямоугольный\n",
    "                plate_contour = approx\n",
    "                start_x, start_y, width, height = x, y, w, h\n",
    "                end_x = start_x + width\n",
    "                end_y = start_y + height\n",
    "                break\n",
    "\n",
    "    # Проверяем, нашли ли контур номерного знака\n",
    "    if plate_contour is not None:\n",
    "        # Обрезаем область номерного знака\n",
    "        plate = image[start_y:end_y, start_x:end_x]\n",
    "\n",
    "        # Коррекция наклона номерного знака\n",
    "        gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "        _, plate_thresh = cv2.threshold(gray_plate, 150, 255, cv2.THRESH_BINARY)\n",
    "        contours, _ = cv2.findContours(plate_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "        if contours:\n",
    "            largest_contour = max(contours, key=cv2.contourArea)\n",
    "            rect = cv2.minAreaRect(largest_contour)\n",
    "            box = cv2.boxPoints(rect)\n",
    "            box = np.intp(box)\n",
    "            angle = rect[-1]\n",
    "\n",
    "            if angle < -45:\n",
    "                angle += 90\n",
    "            (h, w) = plate.shape[:2]\n",
    "            center = (w // 2, h // 2)\n",
    "            M = cv2.getRotationMatrix2D(center, angle, 1.0)\n",
    "            rotated_plate = cv2.warpAffine(plate, M, (w, h))\n",
    "        else:\n",
    "            rotated_plate = plate\n",
    "\n",
    "        # Обводим контур номерного знака на исходном изображении\n",
    "        cv2.drawContours(image, [plate_contour], -1, (0, 255, 0), 2)\n",
    "\n",
    "        # Отображение и сохранение результатов\n",
    "        cv2.imshow('Исходное изображение с обведённым номером', image)\n",
    "        cv2.imshow('Изображение номерного знака', rotated_plate)\n",
    "        cv2.imwrite(f'processed_plate_{img_path.split(\"/\")[-1]}', rotated_plate)\n",
    "        cv2.imwrite(f'processed_image_{img_path.split(\"/\")[-1]}', image)\n",
    "    else:\n",
    "        print(f\"Не удалось найти номерной знак на изображении {img_path}\")\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "id": "b36cc4fc",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер на изображении img/1.jpg: “TBDOMK OL\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import pytesseract\n",
    "\n",
    "# Путь к Tesseract OCR, если требуется\n",
    "pytesseract.pytesseract.tesseract_cmd = r'C:\\Program Files\\Tesseract-OCR\\tesseract.exe'\n",
    "\n",
    "# Список изображений для обработки\n",
    "images = ['img/1.jpg']\n",
    "\n",
    "for img_path in images:\n",
    "    # Загрузка изображения\n",
    "    image = cv2.imread(img_path)\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    \n",
    "    # Улучшение контраста с использованием порогового преобразования\n",
    "    _, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "\n",
    "    # Применение морфологических операций (закрытие) для объединения разрывов\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n",
    "    closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)\n",
    "\n",
    "    # Поиск контуров после морфологической обработки\n",
    "    contours, hierarchy = cv2.findContours(closed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    # Поиск контура номерного знака\n",
    "    plate_contour = None\n",
    "    for contour in contours:\n",
    "        # Прямоугольник вокруг контура\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        \n",
    "        # Фильтры по соотношению сторон и площади\n",
    "        area = w * h\n",
    "        aspect_ratio = float(w) / h\n",
    "        if 2.5 < aspect_ratio < 5.5 and 1500 < area < 15000:  # Уточнённые условия\n",
    "            approx = cv2.approxPolyDP(contour, 0.02 * cv2.arcLength(contour, True), True)\n",
    "            if len(approx) == 4:  # Проверяем, что контур прямоугольный\n",
    "                plate_contour = approx\n",
    "                start_x, start_y, width, height = x, y, w, h\n",
    "                end_x = start_x + width\n",
    "                end_y = start_y + height\n",
    "                break\n",
    "\n",
    "    # Проверяем, нашли ли контур номерного знака\n",
    "    if plate_contour is not None:\n",
    "        # Обрезаем область номерного знака\n",
    "        plate = image[start_y:end_y, start_x:end_x]\n",
    "\n",
    "        # Коррекция наклона номерного знака\n",
    "        gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "        _, plate_thresh = cv2.threshold(gray_plate, 150, 255, cv2.THRESH_BINARY)\n",
    "        contours, _ = cv2.findContours(plate_thresh, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "        if contours:\n",
    "            largest_contour = max(contours, key=cv2.contourArea)\n",
    "            rect = cv2.minAreaRect(largest_contour)\n",
    "            box = cv2.boxPoints(rect)\n",
    "            box = np.intp(box)\n",
    "            angle = rect[-1]\n",
    "\n",
    "            if angle < -45:\n",
    "                angle += 90\n",
    "            (h, w) = plate.shape[:2]\n",
    "            center = (w // 2, h // 2)\n",
    "            M = cv2.getRotationMatrix2D(center, angle, 1.0)\n",
    "            rotated_plate = cv2.warpAffine(plate, M, (w, h))\n",
    "        else:\n",
    "            rotated_plate = plate\n",
    "\n",
    "        # Распознавание текста с помощью Tesseract\n",
    "        plate_text = pytesseract.image_to_string(rotated_plate, config='--psm 8')\n",
    "        print(f\"Распознанный номер на изображении {img_path}: {plate_text.strip()}\")\n",
    "\n",
    "        # Обводим контур номерного знака на исходном изображении\n",
    "        cv2.drawContours(image, [plate_contour], -1, (0, 255, 0), 2)\n",
    "        cv2.putText(image, plate_text.strip(), (start_x, start_y - 10), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)\n",
    "\n",
    "        # Отображение и сохранение результатов\n",
    "        cv2.imshow('Исходное изображение с обведённым номером', image)\n",
    "        cv2.imshow('Изображение номерного знака', rotated_plate)\n",
    "        cv2.imwrite(f'processed_plate_{img_path.split(\"/\")[-1]}', rotated_plate)\n",
    "        cv2.imwrite(f'processed_image_{img_path.split(\"/\")[-1]}', image)\n",
    "    else:\n",
    "        print(f\"Не удалось найти номерной знак на изображении {img_path}\")\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "id": "37f43981",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер на изображении img/1.jpg: RE\n"
     ]
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import pytesseract\n",
    "\n",
    "# Путь к Tesseract OCR, если требуется\n",
    "# pytesseract.pytesseract.tesseract_cmd = r'C:\\Program Files\\Tesseract-OCR\\tesseract.exe'\n",
    "\n",
    "# Список изображений для обработки\n",
    "images = ['img/1.jpg']\n",
    "\n",
    "for img_path in images:\n",
    "    # Загрузка изображения\n",
    "    image = cv2.imread(img_path)\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    \n",
    "    # Улучшение контраста с использованием порогового преобразования\n",
    "    _, thresh = cv2.threshold(img_gray, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)\n",
    "\n",
    "    # Применение морфологических операций (закрытие) для объединения разрывов\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (5, 5))\n",
    "    closed = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel)\n",
    "\n",
    "    # Поиск контуров после морфологической обработки\n",
    "    contours, hierarchy = cv2.findContours(closed, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    # Поиск контура номерного знака\n",
    "    plate_contour = None\n",
    "    for contour in contours:\n",
    "        # Прямоугольник вокруг контура\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        \n",
    "        # Фильтры по соотношению сторон и площади\n",
    "        area = w * h\n",
    "        aspect_ratio = float(w) / h\n",
    "        if 2.5 < aspect_ratio < 5.5 and 1500 < area < 15000:  # Уточнённые условия\n",
    "            approx = cv2.approxPolyDP(contour, 0.02 * cv2.arcLength(contour, True), True)\n",
    "            if len(approx) == 4:  # Проверяем, что контур прямоугольный\n",
    "                plate_contour = approx\n",
    "                start_x, start_y, width, height = x, y, w, h\n",
    "                end_x = start_x + width\n",
    "                end_y = start_y + height\n",
    "                break\n",
    "\n",
    "    # Проверяем, нашли ли контур номерного знака\n",
    "    if plate_contour is not None:\n",
    "        # Обрезаем область номерного знака\n",
    "        plate = image[start_y:end_y, start_x:end_x]\n",
    "\n",
    "        # Дополнительное улучшение для OCR\n",
    "        gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "        # Увеличение контрастности\n",
    "        plate_enhanced = cv2.convertScaleAbs(gray_plate, alpha=1.5, beta=20)\n",
    "        \n",
    "        # Бинаризация и морфология для улучшения качества текста\n",
    "        _, plate_thresh = cv2.threshold(plate_enhanced, 150, 255, cv2.THRESH_BINARY)\n",
    "        plate_cleaned = cv2.morphologyEx(plate_thresh, cv2.MORPH_OPEN, kernel)\n",
    "\n",
    "        # Отправка улучшенного изображения в Tesseract\n",
    "        custom_config = r'--oem 3 --psm 8 -c tessedit_char_whitelist=ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789'\n",
    "        plate_text = pytesseract.image_to_string(plate_cleaned, config=custom_config)\n",
    "        \n",
    "        # Вывод распознанного текста в консоль\n",
    "        print(f\"Распознанный номер на изображении {img_path}: {plate_text.strip()}\")\n",
    "\n",
    "        # Обводим контур номерного знака на исходном изображении (без текста)\n",
    "        cv2.drawContours(image, [plate_contour], -1, (0, 255, 0), 2)\n",
    "\n",
    "        # Отображение и сохранение результатов\n",
    "        cv2.imshow('Исходное изображение с обведённым номером', image)\n",
    "        cv2.imshow('Изображение номерного знака', plate_cleaned)\n",
    "        cv2.imwrite(f'processed_plate_{img_path.split(\"/\")[-1]}', plate_cleaned)\n",
    "        cv2.imwrite(f'processed_image_{img_path.split(\"/\")[-1]}', image)\n",
    "    else:\n",
    "        print(f\"Не удалось найти номерной знак на изображении {img_path}\")\n",
    "\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 34,
   "id": "61c9766c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Результат распознавания на улучшенном изображении:\n",
      "Распознанный номер: TB29MKOP\n"
     ]
    }
   ],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "import pytesseract\n",
    "\n",
    "# Путь к Tesseract OCR, если требуется\n",
    "# pytesseract.pytesseract.tesseract_cmd = r'C:\\Program Files\\Tesseract-OCR\\tesseract.exe'\n",
    "\n",
    "# Загрузка изображения номерного знака\n",
    "plate = cv2.imread('extracted_plate.jpg')\n",
    "\n",
    "# Увеличение изображения для улучшения детализации\n",
    "scale_percent = 300  # Увеличение на 300%\n",
    "width = int(plate.shape[1] * scale_percent / 100)\n",
    "height = int(plate.shape[0] * scale_percent / 100)\n",
    "dim = (width, height)\n",
    "resized_plate = cv2.resize(plate, dim, interpolation=cv2.INTER_CUBIC)\n",
    "cv2.imshow(\"Enhanced Plate\", resized_plate)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n",
    "# Преобразование в оттенки серого\n",
    "gray_plate = cv2.cvtColor(resized_plate, cv2.COLOR_BGR2GRAY)\n",
    "cv2.imshow(\"Enhanced Plate\", gray_plate)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n",
    "# Применение адаптивного порогового преобразования\n",
    "binary_plate = cv2.adaptiveThreshold(gray_plate, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \n",
    "                                     cv2.THRESH_BINARY, 11, 2)\n",
    "\n",
    "# Сохранение и отображение для визуальной проверки\n",
    "cv2.imshow(\"Enhanced Plate\", binary_plate)\n",
    "cv2.waitKey(0)\n",
    "cv2.destroyAllWindows()\n",
    "\n",
    "# Обновлённый вайтлист для распознавания\n",
    "whitelist = 'ABCEHMOPTXYK0123456789'\n",
    "custom_config = f'--oem 3 --psm 8 -c tessedit_char_whitelist={whitelist}'\n",
    "\n",
    "# Распознавание текста на улучшенном изображении\n",
    "print(\"Результат распознавания на улучшенном изображении:\")\n",
    "text = pytesseract.image_to_string(gray_plate, config=custom_config)\n",
    "print(f\"Распознанный номер: {text.strip()}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 43,
   "id": "e0df04cf",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер: \n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 145888 characters]",
      "text/plain": [
       "<Figure size 1500x1000 with 6 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "import pytesseract\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Загрузка изображения номерного знака\n",
    "plate = cv2.imread('extracted_plate.jpg')\n",
    "\n",
    "# 1. Изменение размера изображения\n",
    "resized_plate = cv2.resize(\n",
    "    plate, None, fx=2, fy=2,\n",
    "    interpolation=cv2.INTER_CUBIC)\n",
    "\n",
    "# 2. Конвертация в градации серого\n",
    "gray = cv2.cvtColor(resized_plate, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "# 3. Применение \"чёрной шляпы\" для выделения тёмных символов на светлом фоне\n",
    "rectKern = cv2.getStructuringElement(cv2.MORPH_RECT, (13, 5))\n",
    "blackhat = cv2.morphologyEx(gray, cv2.MORPH_BLACKHAT, rectKern)\n",
    "\n",
    "# 4. Выделение светлых областей с использованием операции закрытия и бинаризации\n",
    "squareKern = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))\n",
    "light = cv2.morphologyEx(gray, cv2.MORPH_CLOSE, squareKern)\n",
    "_, light = cv2.threshold(light, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)\n",
    "\n",
    "# 5. Вычисление градиента Шарра для выделения границ символов\n",
    "gradX = cv2.Sobel(blackhat, ddepth=cv2.CV_32F, dx=1, dy=0, ksize=-1)\n",
    "gradX = np.absolute(gradX)\n",
    "(minVal, maxVal) = (np.min(gradX), np.max(gradX))\n",
    "gradX = 255 * ((gradX - minVal) / (maxVal - minVal))\n",
    "gradX = gradX.astype(\"uint8\")\n",
    "\n",
    "# 6. Сглаживание, закрытие и пороговая обработка\n",
    "gradX = cv2.GaussianBlur(gradX, (5, 5), 0)\n",
    "gradX = cv2.morphologyEx(gradX, cv2.MORPH_CLOSE, rectKern)\n",
    "_, thresh = cv2.threshold(gradX, 0, 255, cv2.THRESH_BINARY | cv2.THRESH_OTSU)\n",
    "\n",
    "# 7. Очистка с помощью эрозии и дилатации\n",
    "thresh = cv2.erode(thresh, None, iterations=2)\n",
    "thresh = cv2.dilate(thresh, None, iterations=2)\n",
    "\n",
    "# 8. Маскирование светлых областей и финальная обработка\n",
    "thresh = cv2.bitwise_and(thresh, thresh, mask=light)\n",
    "thresh = cv2.dilate(thresh, None, iterations=2)\n",
    "thresh = cv2.erode(thresh, None, iterations=1)\n",
    "\n",
    "# Обновлённый вайтлист для Tesseract\n",
    "whitelist = 'ABCEHMOPTXYK0123456789'\n",
    "custom_config = f'--oem 3 --psm 8 -c tessedit_char_whitelist={whitelist}'\n",
    "\n",
    "# Распознавание текста на финальном изображении\n",
    "text = pytesseract.image_to_string(thresh, config=custom_config)\n",
    "print(f\"Распознанный номер: {text.strip()}\")\n",
    "\n",
    "# Отображение всех этапов обработки в Matplotlib\n",
    "fig, axs = plt.subplots(2, 3, figsize=(15, 10))\n",
    "fig.suptitle('Этапы обработки изображения номерного знака')\n",
    "\n",
    "# Отображение оригинального изображения в градациях серого\n",
    "axs[0, 0].imshow(gray, cmap='gray')\n",
    "axs[0, 0].set_title(\"Оригинальное изображение (серое)\")\n",
    "axs[0, 0].axis('off')\n",
    "\n",
    "# Отображение изображения после \"чёрной шляпы\"\n",
    "axs[0, 1].imshow(blackhat, cmap='gray')\n",
    "axs[0, 1].set_title(\"Blackhat\")\n",
    "axs[0, 1].axis('off')\n",
    "\n",
    "# Отображение выделения светлых областей\n",
    "axs[0, 2].imshow(light, cmap='gray')\n",
    "axs[0, 2].set_title(\"Светлые области\")\n",
    "axs[0, 2].axis('off')\n",
    "\n",
    "# Отображение градиента Шарра\n",
    "axs[1, 0].imshow(gradX, cmap='gray')\n",
    "axs[1, 0].set_title(\"Градиент Шарра\")\n",
    "axs[1, 0].axis('off')\n",
    "\n",
    "# Отображение изображения после пороговой обработки\n",
    "axs[1, 1].imshow(thresh, cmap='gray')\n",
    "axs[1, 1].set_title(\"Пороговое изображение\")\n",
    "axs[1, 1].axis('off')\n",
    "\n",
    "# Отображение финального результата\n",
    "axs[1, 2].imshow(thresh, cmap='gray')\n",
    "axs[1, 2].set_title(\"Финальное обработанное изображение\")\n",
    "axs[1, 2].axis('off')\n",
    "\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 82,
   "id": "8ef0267e",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 44172 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер: TB29MKOP\n"
     ]
    }
   ],
   "source": [
    "import re\n",
    "import cv2\n",
    "import numpy as np\n",
    "import pytesseract\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Загрузка изображения номерного знака\n",
    "plate = cv2.imread('extracted_plate.jpg')\n",
    "\n",
    "# Изменение размера для улучшения качества\n",
    "resized_plate = cv2.resize(plate, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)\n",
    "\n",
    "# Преобразование в градации серого\n",
    "gray = cv2.cvtColor(resized_plate, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "# Применение адаптивного порогового преобразования для бинаризации изображения\n",
    "thresh = cv2.adaptiveThreshold(gray, 255, \n",
    "                               cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \n",
    "                               cv2.THRESH_BINARY_INV, 41, 10)\n",
    "\n",
    "# Удаление мелких шумов с помощью морфологического открытия\n",
    "kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))\n",
    "thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)\n",
    "\n",
    "# Удаление рамок номерного знака с помощью удаления крупных контуров по периметру\n",
    "contours, hierarchy = cv2.findContours(thresh.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "\n",
    "# Дополнительная очистка изображения с помощью морфологических операций\n",
    "# Заполнение возможных разрывов в символах\n",
    "thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)\n",
    "\n",
    "# Отображение финального результата\n",
    "plt.imshow(thresh, cmap='gray')\n",
    "plt.title('Обработанное изображение с чёрными буквами на белом фоне')\n",
    "plt.axis('off')\n",
    "plt.show()\n",
    "\n",
    "# Настройки Tesseract для русского и английского языков (если необходимо)\n",
    "whitelist = 'ABCEHMOPTXYK0123456789'\n",
    "custom_config = f'--oem 1 --psm 7 -c tessedit_char_whitelist={whitelist}'\n",
    "\n",
    "# Распознавание текста на финальном изображении\n",
    "text = pytesseract.image_to_string(thresh, config=custom_config)\n",
    "print(f\"Распознанный номер: {text.strip()}\")\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 91,
   "id": "d1a98d92",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 37924 characters]",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер: T829MK97\n"
     ]
    }
   ],
   "source": [
    "import re\n",
    "import cv2\n",
    "import numpy as np\n",
    "import pytesseract\n",
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Загрузка изображения номерного знака\n",
    "plate = cv2.imread('extracted_plate.jpg')\n",
    "\n",
    "# Изменение размера для улучшения качества\n",
    "resized_plate = cv2.resize(plate, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)\n",
    "\n",
    "# Преобразование в градации серого\n",
    "gray = cv2.cvtColor(resized_plate, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "# Применение фильтра размытия для уменьшения шумов\n",
    "gray = cv2.GaussianBlur(gray, (3, 3), 0)\n",
    "\n",
    "# Применение адаптивного порогового преобразования для бинаризации изображения\n",
    "thresh = cv2.adaptiveThreshold(gray, 255, \n",
    "                               cv2.ADAPTIVE_THRESH_GAUSSIAN_C, \n",
    "                               cv2.THRESH_BINARY_INV, 41, 10)\n",
    "\n",
    "# Удаление мелких шумов с помощью морфологического открытия\n",
    "kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))\n",
    "thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)\n",
    "\n",
    "# Дополнительная очистка изображения с помощью морфологических операций\n",
    "thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)\n",
    "\n",
    "# Отображение финального результата\n",
    "plt.imshow(thresh, cmap='gray')\n",
    "plt.title('Обработанное и выровненное изображение')\n",
    "plt.axis('off')\n",
    "plt.show()\n",
    "\n",
    "# Настройки Tesseract для русского и английского языков (если необходимо)\n",
    "whitelist = 'ABCEHMOPTXYK0123456789'\n",
    "custom_config = f'--oem 1 --psm 10 -c tessedit_char_whitelist={whitelist}'\n",
    "\n",
    "# Распознавание текста на финальном изображении\n",
    "text = pytesseract.image_to_string(thresh, config=custom_config)\n",
    "print(f\"Распознанный номер: {text.strip()}\")\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 98,
   "id": "f2b19b5d",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер для img/1.jpg: CT829MK97\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 56752 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер для img/2.jpg: AO2397\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 65104 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер для img/3.jpg: K263C097\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 54736 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import pytesseract\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "# Список входных изображений\n",
    "images = ['img/1.jpg', 'img/2.jpg', 'img/3.jpg']\n",
    "\n",
    "# Настройки для Tesseract\n",
    "whitelist = 'ABCEHMOPTXYK0123456789'\n",
    "custom_config = f'--oem 1 --psm 10 -c tessedit_char_whitelist={whitelist}'\n",
    "\n",
    "# Обработка изображений\n",
    "for img_path in images:\n",
    "    image = cv2.imread(img_path)\n",
    "    if image is None:\n",
    "        print(f\"Не удалось загрузить изображение: {img_path}\")\n",
    "        continue\n",
    "\n",
    "    # Преобразование в градации серого и бинаризация\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    ret, thresh = cv2.threshold(img_gray, 100, 200, cv2.THRESH_TOZERO_INV)\n",
    "\n",
    "    # Поиск контуров\n",
    "    contours, hierarchy = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    plate = None\n",
    "\n",
    "    for i in range(len(contours)):\n",
    "        x, y, w, h = cv2.boundingRect(contours[i])\n",
    "        a = w * h\n",
    "        aspectRatio = float(w) / h\n",
    "        if aspectRatio >= 3 and a > 600:\n",
    "            approx = cv2.approxPolyDP(contours[i], 0.05 * cv2.arcLength(contours[i], True), True)\n",
    "            if len(approx) <= 4 and x > 15:\n",
    "                plate = image[y:y + h, x:x + w]\n",
    "                break\n",
    "\n",
    "    if plate is None:\n",
    "        print(f\"Номерной знак не найден на изображении: {img_path}\")\n",
    "        continue\n",
    "\n",
    "    # Преобразование в градации серого для номерного знака\n",
    "    gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "    # Увеличение размера для улучшения качества\n",
    "    resized_plate = cv2.resize(gray_plate, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)\n",
    "\n",
    "    # Фильтрация и бинаризация\n",
    "    gray = cv2.GaussianBlur(resized_plate, (3, 3), 0)\n",
    "    thresh = cv2.adaptiveThreshold(gray, 255,\n",
    "                                   cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\n",
    "                                   cv2.THRESH_BINARY_INV, 41, 10)\n",
    "\n",
    "    # Морфологические операции для очистки изображения\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))\n",
    "    thresh = cv2.morphologyEx(thresh, cv2.MORPH_OPEN, kernel, iterations=1)\n",
    "    thresh = cv2.morphologyEx(thresh, cv2.MORPH_CLOSE, kernel, iterations=2)\n",
    "\n",
    "    # Распознавание текста\n",
    "    text = pytesseract.image_to_string(thresh, config=custom_config)\n",
    "    print(f\"Распознанный номер для {img_path}: {text.strip()}\")\n",
    "\n",
    "    # Отображение исходного номерного знака и обработанной версии\n",
    "    fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n",
    "    axes[0].imshow(cv2.cvtColor(plate, cv2.COLOR_BGR2RGB))\n",
    "    axes[0].set_title('Вырезанный номерной знак')\n",
    "    axes[0].axis('off')\n",
    "\n",
    "    axes[1].imshow(thresh, cmap='gray')\n",
    "    axes[1].set_title('Обработанный номерной знак')\n",
    "    axes[1].axis('off')\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 173,
   "id": "3ce0582c",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер для img/3.jpg: K2O3COT\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 37928 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import scipy.fftpack\n",
    "import pytesseract\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "# Настройки для Tesseract\n",
    "whitelist = 'ABCEHMOPTXYK0123456789'\n",
    "custom_config = f'--oem 1 --psm 10 -c tessedit_char_whitelist={whitelist}'\n",
    "\n",
    "# Определение функции imclearborder\n",
    "def imclearborder(imgBW, radius):\n",
    "    imgBWcopy = imgBW.copy()\n",
    "    contours, _ = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    imgRows, imgCols = imgBW.shape\n",
    "\n",
    "    contourList = []\n",
    "    for idx in range(len(contours)):\n",
    "        cnt = contours[idx]\n",
    "        for pt in cnt:\n",
    "            rowCnt = pt[0][1]\n",
    "            colCnt = pt[0][0]\n",
    "            check1 = (rowCnt >= 0 and rowCnt < radius) or (rowCnt >= imgRows - radius and rowCnt < imgRows)\n",
    "            check2 = (colCnt >= 0 and colCnt < radius) or (colCnt >= imgCols - radius and colCnt < imgCols)\n",
    "            if check1 or check2:\n",
    "                contourList.append(idx)\n",
    "                break\n",
    "\n",
    "    for idx in contourList:\n",
    "        cv2.drawContours(imgBWcopy, contours, idx, (0, 0, 0), -1)\n",
    "\n",
    "    return imgBWcopy\n",
    "\n",
    "# Определение функции bwareaopen\n",
    "def bwareaopen(imgBW, areaPixels):\n",
    "    imgBWcopy = imgBW.copy()\n",
    "    contours, _ = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    for idx in range(len(contours)):\n",
    "        area = cv2.contourArea(contours[idx])\n",
    "        if 0 < area <= areaPixels:\n",
    "            cv2.drawContours(imgBWcopy, contours, idx, (0, 0, 0), -1)\n",
    "    return imgBWcopy\n",
    "\n",
    "# Гомоморфная фильтрация\n",
    "def homomorphic_filter(image):\n",
    "    rows, cols = image.shape\n",
    "    \n",
    "    imgLog = np.log1p(np.array(image, dtype=\"float\") / 255)\n",
    "    \n",
    "    M, N =  rows + 1, cols + 1\n",
    "    sigma = 10\n",
    "    X, Y = np.meshgrid(np.linspace(0, N - 1, N), np.linspace(0, M - 1, M))\n",
    "    centerX, centerY = np.ceil(N / 2), np.ceil(M / 2)\n",
    "    gaussianNumerator = (X - centerX) ** 2 + (Y - centerY) ** 2\n",
    "\n",
    "    Hlow = np.exp(-gaussianNumerator / (2 * sigma ** 2))\n",
    "    Hhigh = 1 - Hlow\n",
    "    HlowShift = scipy.fftpack.ifftshift(Hlow)\n",
    "    HhighShift = scipy.fftpack.ifftshift(Hhigh)\n",
    "\n",
    "    If = scipy.fftpack.fft2(imgLog, (M, N))\n",
    "    Ioutlow = np.real(scipy.fftpack.ifft2(If * HlowShift, (M, N)))\n",
    "    Iouthigh = np.real(scipy.fftpack.ifft2(If * HhighShift, (M, N)))\n",
    "    \n",
    "    gamma1, gamma2 = 0.3, 1.5\n",
    "    Iout = gamma1 * Ioutlow[:rows, :cols] + gamma2 * Iouthigh[:rows, :cols]\n",
    "    \n",
    "    Ihmf = np.expm1(Iout)\n",
    "    Ihmf = (Ihmf - np.min(Ihmf)) / (np.max(Ihmf) - np.min(Ihmf))\n",
    "    Ihmf2 = np.array(255 * Ihmf, dtype=\"uint8\")\n",
    "    cv2.imshow(\"\", Ihmf2)\n",
    "    cv2.waitKey(0)\n",
    "    cv2.destroyAllWindows()\n",
    "    Ithresh = Ihmf2 < 110\n",
    "    Ithresh = 255 * Ithresh.astype(\"uint8\")\n",
    "    \n",
    "\n",
    "    Iopen = bwareaopen(Ihmf2, 11)\n",
    "    return Iopen\n",
    "\n",
    "# Список входных изображений\n",
    "images = ['img/3.jpg']#, 'img/2.jpg', 'img/3.jpg']\n",
    "\n",
    "# Основной обработчик\n",
    "for img_path in images:\n",
    "    image = cv2.imread(img_path)\n",
    "    if image is None:\n",
    "        print(f\"Не удалось загрузить изображение: {img_path}\")\n",
    "        continue\n",
    "\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    ret, thresh = cv2.threshold(img_gray, 100, 200, cv2.THRESH_TOZERO_INV)\n",
    "    contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    plate = None\n",
    "    for contour in contours:\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        aspectRatio = float(w) / h\n",
    "        if aspectRatio >= 3 and w * h > 600:\n",
    "            plate = image[y:y + h, x:x + w]\n",
    "            break\n",
    "\n",
    "    if plate is None:\n",
    "        print(f\"Номерной знак не найден на изображении: {img_path}\")\n",
    "        continue\n",
    "\n",
    "    gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "    processed_plate = homomorphic_filter(gray_plate)\n",
    "\n",
    "    text = pytesseract.image_to_string(processed_plate, config=custom_config)\n",
    "    print(f\"Распознанный номер для {img_path}: {text.strip()}\")\n",
    "\n",
    "    # Отображение результатов\n",
    "    fig, axes = plt.subplots(1, 2, figsize=(10, 5))\n",
    "    axes[0].imshow(cv2.cvtColor(plate, cv2.COLOR_BGR2RGB))\n",
    "    axes[0].set_title('Вырезанный номерной знак')\n",
    "    axes[0].axis('off')\n",
    "\n",
    "    axes[1].imshow(processed_plate, cmap='gray')\n",
    "    axes[1].set_title('Обработанный номерной знак')\n",
    "    axes[1].axis('off')\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 380,
   "id": "e0e546e4",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Найдено контуров: 15\n",
      "Распознанный номер для img/1.jpg: \n",
      "Распознанный номер для img/1.jpg: 1829MK97\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 91904 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Найдено контуров: 32\n",
      "Распознанный номер для img/2.jpg: \n",
      "Распознанный номер для img/2.jpg: A023y92\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 98880 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Найдено контуров: 24\n",
      "Распознанный номер для img/3.jpg: \n",
      "Распознанный номер для img/3.jpg: K263097\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 81248 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "# Список входных изображений\n",
    "images = ['img/1.jpg', 'img/2.jpg', 'img/3.jpg']\n",
    "\n",
    "# Белый список символов\n",
    "whitelist = 'ABCEHMOPTXyK0123456789'\n",
    "custom_config = f'--oem 1 --psm 7 -c tessedit_char_whitelist={whitelist}'\n",
    "\n",
    "def expand_characters(image, kernel_size=(3, 3), iterations=1):\n",
    "    \"\"\"\n",
    "    Функция для наращивания (расширения) символов на изображении.\n",
    "    \n",
    "    :param image: Входное бинарное изображение (чёрно-белое).\n",
    "    :param kernel_size: Размер ядра для морфологической операции (ширина, высота).\n",
    "    :param iterations: Количество итераций расширения.\n",
    "    :return: Изображение с расширенными символами.\n",
    "    \"\"\"\n",
    "    # Создаём ядро\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, kernel_size)\n",
    "    \n",
    "    # Применяем морфологическое расширение\n",
    "    dilated_image = cv2.dilate(image, kernel, iterations=iterations)\n",
    "    \n",
    "    return dilated_image\n",
    "\n",
    "\n",
    "def imclearborder1(imgBW, radius=1):\n",
    "    \"\"\"\n",
    "    Удаляет только те объекты, которые непосредственно касаются границ изображения.\n",
    "    \n",
    "    :param imgBW: Бинарное изображение (чёрно-белое, 0 и 255).\n",
    "    :param radius: Расстояние от границы, в пределах которого объект считается касающимся.\n",
    "    :return: Изображение с удалёнными объектами, касающимися границ.\n",
    "    \"\"\"\n",
    "    imgBWcopy = imgBW.copy()\n",
    "\n",
    "    # Находим контуры\n",
    "    contours, _ = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    imgRows, imgCols = imgBW.shape\n",
    "\n",
    "    # Список контуров, которые касаются границы\n",
    "    contourList = []\n",
    "\n",
    "    for idx, cnt in enumerate(contours):\n",
    "        for pt in cnt:\n",
    "            rowCnt = pt[0][1]  # y-координата\n",
    "            colCnt = pt[0][0]  # x-координата\n",
    "\n",
    "            # Проверка на касание границы\n",
    "            check1 = (rowCnt >= 0 and rowCnt < radius) or (rowCnt >= imgRows - radius and rowCnt < imgRows)\n",
    "            check2 = (colCnt >= 0 and colCnt < radius) or (colCnt >= imgCols - radius and colCnt < imgCols)\n",
    "            if check1 or check2:\n",
    "                contourList.append(idx)\n",
    "                break\n",
    "\n",
    "    # Удаляем найденные контуры\n",
    "    for idx in contourList:\n",
    "        cv2.drawContours(imgBWcopy, contours, idx, (0, 0, 0), -1)\n",
    "\n",
    "    return imgBWcopy\n",
    "\n",
    "\n",
    "# Функция для создания эталонных шаблонов символов\n",
    "def generate_templates(whitelist):\n",
    "    templates = {}\n",
    "    for char in whitelist:\n",
    "        img = np.zeros((50, 30), dtype=np.uint8)  # Пустое изображение для символа\n",
    "        font = cv2.FONT_HERSHEY_SIMPLEX\n",
    "        cv2.putText(img, char, (5, 40), font, 1.2, 255, 2, cv2.LINE_AA)\n",
    "        templates[char] = img\n",
    "    return templates\n",
    "\n",
    "# Функция для фильтрации маленьких областей (bwareaopen)\n",
    "def bwareaopen(imgBW, areaPixels):\n",
    "    imgBWcopy = imgBW.copy()\n",
    "    contours, _ = cv2.findContours(imgBWcopy, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    for contour in contours:\n",
    "        if cv2.contourArea(contour) < areaPixels:\n",
    "            cv2.drawContours(imgBWcopy, [contour], -1, 0, -1)\n",
    "    return imgBWcopy\n",
    "\n",
    "# Распознавание символов\n",
    "def recognize_characters(image, templates):\n",
    "    contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    recognized_text = \"\"\n",
    "\n",
    "    for contour in sorted(contours, key=lambda x: cv2.boundingRect(x)[0]):  # Сортировка слева направо\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        if w > 10 and h > 20:  # Фильтр по размеру символов\n",
    "            roi = image[y:y+h, x:x+w]\n",
    "            roi_resized = cv2.resize(roi, (30, 50), interpolation=cv2.INTER_CUBIC)\n",
    "\n",
    "            # Сравнение с шаблонами\n",
    "            best_match = None\n",
    "            max_corr = -1\n",
    "            for char, template in templates.items():\n",
    "                res = cv2.matchTemplate(roi_resized, template, cv2.TM_CCOEFF_NORMED)\n",
    "                _, corr, _, _ = cv2.minMaxLoc(res)\n",
    "                if corr > max_corr:\n",
    "                    max_corr = corr\n",
    "                    best_match = char\n",
    "\n",
    "            if best_match is not None and max_corr > 0.5:  # Порог корреляции\n",
    "                recognized_text += best_match\n",
    "\n",
    "    return recognized_text\n",
    "def imclearborder(imgBW, radius=1, area_threshold=50, aspect_ratio_threshold=0.1):\n",
    "    \"\"\"\n",
    "    Удаляет объекты, которые выходят за границу изображения,\n",
    "    сохраняя важные объекты, даже если они касаются границ.\n",
    "\n",
    "    :param imgBW: Бинарное изображение (чёрно-белое, 0 и 255).\n",
    "    :param radius: Радиус касания границы.\n",
    "    :param area_threshold: Минимальная площадь объекта для его сохранения.\n",
    "    :param aspect_ratio_threshold: Минимальное отношение ширины к высоте.\n",
    "    :return: Изображение с удалёнными объектами.\n",
    "    \"\"\"\n",
    "    imgBWcopy = imgBW.copy()\n",
    "\n",
    "    # ШАГ 1: Морфологическая эрозия для разъединения объектов\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2))\n",
    "    eroded = cv2.erode(imgBWcopy, kernel, iterations=1)\n",
    "\n",
    "    # ШАГ 2: Находим контуры\n",
    "    contours, _ = cv2.findContours(eroded, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    imgRows, imgCols = imgBW.shape\n",
    "\n",
    "    print(\"Найдено контуров:\", len(contours))\n",
    "    for cnt_idx, cnt in enumerate(contours):\n",
    "        # Вычисляем ограничивающий прямоугольник\n",
    "        x, y, w, h = cv2.boundingRect(cnt)\n",
    "        area = cv2.contourArea(cnt)\n",
    "\n",
    "        # Проверяем вытянутость объекта\n",
    "        aspect_ratio = w / h if h != 0 else 0\n",
    "\n",
    "        # Определяем точки, касающиеся границы\n",
    "        touches_border = (\n",
    "            x <= radius or y <= radius or \n",
    "            x + w >= imgCols - radius or \n",
    "            y + h >= imgRows - radius\n",
    "        )\n",
    "\n",
    "        # Условие для удаления: объект выходит за границу, мал по площади и не является вытянутым\n",
    "        if touches_border :\n",
    "            cv2.drawContours(imgBWcopy, [cnt], -1, (0, 0, 0), -1)\n",
    "\n",
    "\n",
    "\n",
    "    return imgBWcopy\n",
    "# Генерация шаблонов символов\n",
    "templates = generate_templates(whitelist)\n",
    "\n",
    "# Обработка изображений\n",
    "for img_path in images:\n",
    "    image = cv2.imread(img_path)\n",
    "    if image is None:\n",
    "        print(f\"Не удалось загрузить изображение: {img_path}\")\n",
    "        continue\n",
    "\n",
    "    # Преобразование в градации серого и бинаризация\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    ret, thresh = cv2.threshold(img_gray, 100, 200, cv2.THRESH_TOZERO_INV)\n",
    "\n",
    "    # Поиск контуров\n",
    "    contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    plate = None\n",
    "\n",
    "    for contour in contours:\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        a = w * h\n",
    "        aspectRatio = float(w) / h\n",
    "        if aspectRatio >= 3 and a > 600:\n",
    "            plate = image[y:y + h, x:x + w]\n",
    "            break\n",
    "\n",
    "    if plate is None:\n",
    "        print(f\"Номерной знак не найден на изображении: {img_path}\")\n",
    "        continue\n",
    "\n",
    "    # Преобразование в градации серого для номерного знака\n",
    "    gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "    # Увеличение размера для улучшения качества\n",
    "    resized_plate = cv2.resize(gray_plate, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)\n",
    "\n",
    "    # Фильтрация и бинаризация\n",
    "    gray = cv2.GaussianBlur(resized_plate, (3, 3), 0)\n",
    "    thresh = cv2.adaptiveThreshold(gray, 255,\n",
    "                                   cv2.ADAPTIVE_THRESH_GAUSSIAN_C,\n",
    "                                   cv2.THRESH_BINARY_INV, 41, 10)\n",
    "\n",
    "    # Удаление мелких областей\n",
    "    expanded_image = expand_characters(imclearborder1(imclearborder(thresh, 2), 5), kernel_size=(2, 2), iterations=1)\n",
    "    processed_plate = bwareaopen(expanded_image, 30)\n",
    "\n",
    "    # Распознавание текста с использованием шаблонов\n",
    "    recognized_text = recognize_characters(processed_plate, templates)\n",
    "\n",
    "    print(f\"Распознанный номер для {img_path}: {recognized_text}\")\n",
    "    text = pytesseract.image_to_string(processed_plate, config=custom_config)\n",
    "    print(f\"Распознанный номер для {img_path}: {text.strip()}\")\n",
    "    # Отображение исходного номерного знака и обработанной версии\n",
    "    fig, axes = plt.subplots(1, 3, figsize=(10, 5))\n",
    "    axes[0].imshow(cv2.cvtColor(plate, cv2.COLOR_BGR2RGB))\n",
    "    axes[0].set_title('Вырезанный номерной знак')\n",
    "    axes[0].axis('off')\n",
    "\n",
    "    axes[1].imshow(thresh, cmap='gray')\n",
    "    axes[1].set_title('Обработанный1 номерной знак')\n",
    "    axes[1].axis('off')\n",
    "\n",
    "    axes[2].imshow(processed_plate, cmap='gray')\n",
    "    axes[2].set_title('Обработанный2 номерной знак')\n",
    "    axes[2].axis('off')\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 310,
   "id": "7bf47d00",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Найдено контуров для распознавания: 9\n",
      "Не удалось распознать символ: x=17, y=14, w=22, h=31, корреляция: 0.27\n",
      "Не удалось распознать символ: x=47, y=9, w=23, h=36, корреляция: 0.15\n",
      "Не удалось распознать символ: x=72, y=7, w=25, h=38, корреляция: 0.24\n",
      "Не удалось распознать символ: x=98, y=8, w=24, h=40, корреляция: 0.30\n",
      "Не удалось распознать символ: x=127, y=15, w=24, h=33, корреляция: 0.14\n",
      "Не удалось распознать символ: x=151, y=17, w=27, h=30, корреляция: 0.20\n",
      "Не удалось распознать символ: x=190, y=11, w=18, h=24, корреляция: 0.30\n",
      "Не удалось распознать символ: x=213, y=9, w=14, h=27, корреляция: 0.30\n",
      "Распознанный номер: \n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 21932 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "# Белый список символов (набор символов, которые могут быть в номере)\n",
    "whitelist = 'ABCEHKMOPTXy0123456789'\n",
    "\n",
    "# Генерация шаблонов для каждого символа\n",
    "def generate_templates(whitelist):\n",
    "    templates = {}\n",
    "    for char in whitelist:\n",
    "        img = np.zeros((50, 30), dtype=np.uint8)  # Пустое изображение\n",
    "        font = cv2.FONT_HERSHEY_SIMPLEX\n",
    "        cv2.putText(img, char, (5, 40), font, 1.2, 255, 2, cv2.LINE_AA)  # Рисуем символ\n",
    "        templates[char] = img\n",
    "    return templates\n",
    "\n",
    "def merge_close_contours(image, distance_threshold=10):\n",
    "    contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    merged_contours = []\n",
    "\n",
    "    used = set()  # Множество индексов использованных контуров\n",
    "\n",
    "    for i, cnt1 in enumerate(contours):\n",
    "        if i in used:\n",
    "            continue\n",
    "        new_contour = cnt1.copy()\n",
    "        for j, cnt2 in enumerate(contours):\n",
    "            if i != j and j not in used:\n",
    "                # Вычисляем минимальное расстояние от cnt1 к cnt2\n",
    "                min_distance = float(\"inf\")\n",
    "                for pt1 in cnt1:\n",
    "                    for pt2 in cnt2:\n",
    "                        dist = np.linalg.norm(np.array(pt1[0]) - np.array(pt2[0]))\n",
    "                        if dist < min_distance:\n",
    "                            min_distance = dist\n",
    "                if min_distance < distance_threshold:  # Если расстояние меньше порога\n",
    "                    new_contour = np.vstack((new_contour, cnt2))  # Объединяем контуры\n",
    "                    used.add(j)\n",
    "        used.add(i)\n",
    "        merged_contours.append(new_contour)\n",
    "\n",
    "    # Рисуем новые контуры на чистом изображении\n",
    "    merged_image = np.zeros_like(image)\n",
    "    cv2.drawContours(merged_image, merged_contours, -1, 255, -1)\n",
    "    return merged_image\n",
    "\n",
    "\n",
    "# Распознавание символов через сопоставление шаблонов\n",
    "def recognize_characters(image, templates):\n",
    "    contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    recognized_text = \"\"\n",
    "\n",
    "    # Сортируем контуры слева направо\n",
    "    sorted_contours = sorted(contours, key=lambda x: cv2.boundingRect(x)[0])\n",
    "\n",
    "    print(f\"Найдено контуров для распознавания: {len(sorted_contours)}\")\n",
    "    for contour in sorted_contours:\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        if w > 10 and h > 20:  # Фильтруем слишком маленькие объекты\n",
    "            roi = image[y:y+h, x:x+w]\n",
    "            roi_resized = cv2.resize(roi, (30, 50), interpolation=cv2.INTER_CUBIC)\n",
    "\n",
    "            # Сравниваем ROI с каждым шаблоном\n",
    "            best_match = None\n",
    "            max_corr = -1\n",
    "            for char, template in templates.items():\n",
    "                res = cv2.matchTemplate(roi_resized, template, cv2.TM_CCOEFF_NORMED)\n",
    "                _, corr, _, _ = cv2.minMaxLoc(res)\n",
    "                if corr > max_corr:\n",
    "                    max_corr = corr\n",
    "                    best_match = char\n",
    "\n",
    "            if best_match is not None and max_corr > 0.5:  # Порог корреляции\n",
    "                print(f\"Распознан символ: {best_match}, корреляция: {max_corr:.2f}\")\n",
    "                recognized_text += best_match\n",
    "            else:\n",
    "                print(f\"Не удалось распознать символ: x={x}, y={y}, w={w}, h={h}, корреляция: {max_corr:.2f}\")\n",
    "\n",
    "    return recognized_text\n",
    "\n",
    "def selective_dilation(image, distance_threshold=10, iterations=1):\n",
    "    \"\"\"\n",
    "    Выполняет \"наращивание\" (дилатацию) символов, чтобы соединить близкие контуры.\n",
    "    \n",
    "    :param image: Бинарное изображение.\n",
    "    :param distance_threshold: Максимальное расстояние между контурами для их соединения.\n",
    "    :param iterations: Количество итераций для расширения.\n",
    "    :return: Обработанное изображение.\n",
    "    \"\"\"\n",
    "    # Копируем изображение\n",
    "    image_copy = image.copy()\n",
    "\n",
    "    # Находим контуры\n",
    "    contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    # Создаём маску для временного соединения контуров\n",
    "    temp_mask = np.zeros_like(image)\n",
    "\n",
    "    for i, cnt1 in enumerate(contours):\n",
    "        for j, cnt2 in enumerate(contours):\n",
    "            if i != j:  # Сравниваем контуры только с другими\n",
    "                # Находим минимальное расстояние между точками двух контуров\n",
    "                min_distance = float(\"inf\")\n",
    "                for pt1 in cnt1:\n",
    "                    for pt2 in cnt2:\n",
    "                        dist = np.linalg.norm(np.array(pt1[0]) - np.array(pt2[0]))\n",
    "                        if dist < min_distance:\n",
    "                            min_distance = dist\n",
    "\n",
    "                # Если расстояние меньше порога, соединяем контуры\n",
    "                if min_distance < distance_threshold:\n",
    "                    # Рисуем оба контура на временной маске\n",
    "                    cv2.drawContours(temp_mask, [cnt1], -1, 255, thickness=-1)\n",
    "                    cv2.drawContours(temp_mask, [cnt2], -1, 255, thickness=-1)\n",
    "\n",
    "    # Выполняем дилатацию только на временной маске\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))\n",
    "    dilated_mask = cv2.dilate(temp_mask, kernel, iterations=iterations)\n",
    "\n",
    "    # Объединяем исходное изображение с обработанной маской\n",
    "    result = cv2.bitwise_or(image_copy, dilated_mask)\n",
    "\n",
    "    return result\n",
    "\n",
    "templates = generate_templates(whitelist)\n",
    "processed_image1 = selective_dilation(processed_plate, distance_threshold=7, iterations=1)\n",
    "\n",
    "\n",
    "# Распознавание текста\n",
    "recognized_text = recognize_characters(processed_image1, templates)\n",
    "\n",
    "print(f\"Распознанный номер: {recognized_text}\")\n",
    "\n",
    "# Визуализация результата\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.subplot(1, 2, 1)\n",
    "plt.imshow(processed_image1, cmap='gray')\n",
    "plt.title('После объединения контуров')\n",
    "plt.axis('off')\n",
    "\n",
    "plt.tight_layout()\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 346,
   "id": "bd042f82",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Найдено контуров для распознавания: 9\n",
      "\n",
      "Символ 1 (x=17, y=14, w=22, h=31):\n",
      "\n",
      "Распознанный номер: None\n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 10284 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "# Белый список символов (набор символов, которые могут быть в номере)\n",
    "whitelist = 'ABCEHKMOPTXy0123456789'\n",
    "\n",
    "# Генерация шаблонов для каждого символа\n",
    "def generate_templates(whitelist):\n",
    "    templates = {}\n",
    "    for char in whitelist:\n",
    "        img = np.zeros((50, 30), dtype=np.uint8)  # Пустое изображение для шаблона\n",
    "        font = cv2.FONT_HERSHEY_SIMPLEX\n",
    "        cv2.putText(img, char, (5, 40), font, 1.2, 255, 2, cv2.LINE_AA)  # Рисуем символ\n",
    "        templates[char] = img\n",
    "    return templates\n",
    "\n",
    "# Нормализация ROI (приведение к фиксированным размерам)\n",
    "def normalize_roi(roi):\n",
    "    # Приведение ROI к фиксированным размерам 50x30\n",
    "    resized = cv2.resize(roi, (30, 50), interpolation=cv2.INTER_CUBIC)\n",
    "\n",
    "    # Утоньшение символов\n",
    "    kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))\n",
    "    eroded = cv2.erode(resized, kernel, iterations=1)\n",
    "\n",
    "    return eroded\n",
    "\n",
    "# Сравнение символов через XOR\n",
    "def compare_symbols(roi, template):\n",
    "    # Используем XOR для подсчёта несовпадающих пикселей\n",
    "    diff = cv2.bitwise_xor(roi, template)\n",
    "    total_pixels = roi.size\n",
    "    mismatched_pixels = cv2.countNonZero(diff)\n",
    "    similarity = 1 - (mismatched_pixels / total_pixels)  # Схожесть в диапазоне [0, 1]\n",
    "    return similarity * 100  # Переводим в проценты\n",
    "\n",
    "# Распознавание символов с гибким сравнением\n",
    "def debug_character_recognition(image, templates):\n",
    "    contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    recognized_text = \"\"\n",
    "\n",
    "    # Сортируем контуры слева направо\n",
    "    sorted_contours = sorted(contours, key=lambda x: cv2.boundingRect(x)[0])\n",
    "\n",
    "    print(f\"Найдено контуров для распознавания: {len(sorted_contours)}\")\n",
    "    for idx, contour in enumerate(sorted_contours):\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        if w > 10 and h > 20:  # Фильтруем слишком маленькие объекты\n",
    "            roi = image[y:y+h, x:x+w]\n",
    "            normalized_roi = normalize_roi(roi)\n",
    "\n",
    "            print(f\"\\nСимвол {idx + 1} (x={x}, y={y}, w={w}, h={h}):\")\n",
    "            best_match = None\n",
    "            max_similarity = -1\n",
    "            debug_info = []\n",
    "            return\n",
    "            # Сравниваем ROI с каждым шаблоном\n",
    "            for char, template in templates.items():\n",
    "                similarity = compare_symbols(normalized_roi, template)\n",
    "                debug_info.append((char, similarity))\n",
    "                if similarity > max_similarity:\n",
    "                    max_similarity = similarity\n",
    "                    best_match = char\n",
    "\n",
    "                # Визуализация текущего ROI и шаблона\n",
    "                plt.figure(figsize=(8, 4))\n",
    "                plt.subplot(1, 2, 1)\n",
    "                plt.imshow(normalized_roi, cmap='gray')\n",
    "                plt.title(f'Символ {idx + 1} (ROI, утоньшено)')\n",
    "                plt.axis('off')\n",
    "\n",
    "                plt.subplot(1, 2, 2)\n",
    "                plt.imshow(template, cmap='gray')\n",
    "                plt.title(f'Шаблон: {char}, Схожесть: {similarity:.2f}%')\n",
    "                plt.axis('off')\n",
    "\n",
    "                plt.tight_layout()\n",
    "                plt.show()\n",
    "\n",
    "            # Выводим проценты схожести с каждым символом из шаблона\n",
    "            for char, similarity in debug_info:\n",
    "                #print(f\"  Шаблон: {char}, Схожесть: {similarity:.2f}%\")\n",
    "                pass\n",
    "            if best_match is not None and max_similarity > 50:  # Порог схожести\n",
    "                #print(f\"  -> Распознан символ: {best_match}, Максимальная схожесть: {max_similarity:.2f}%\")\n",
    "                recognized_text += best_match\n",
    "            else:\n",
    "                print(f\"  -> Не удалось распознать символ.\")\n",
    "\n",
    "    return recognized_text\n",
    "\n",
    "\n",
    "# Генерация шаблонов\n",
    "templates = generate_templates(whitelist)\n",
    "\n",
    "\n",
    "\n",
    "# Распознавание текста с улучшенным дебагом\n",
    "recognized_text = debug_character_recognition(processed_image1, templates)\n",
    "\n",
    "print(f\"\\nРаспознанный номер: {recognized_text}\")\n",
    "\n",
    "# Визуализация результата\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.imshow(processed_image1, cmap='gray')\n",
    "plt.title('Обработанное изображение')\n",
    "plt.axis('off')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 383,
   "id": "986222d3",
   "metadata": {},
   "outputs": [],
   "source": [
    "import easyocr\n",
    "import cv2\n",
    "\n",
    "def ocr_image_from_variable(img):\n",
    "    \"\"\"\n",
    "    Выполняет OCR для изображения из переменной.\n",
    "    \n",
    "    :param img: Изображение в формате numpy (например, после загрузки с помощью OpenCV).\n",
    "    :return: Распознанный текст.\n",
    "    \"\"\"\n",
    "    reader = easyocr.Reader(['en'], gpu=True)  # Инициализация easyOCR\n",
    "    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)  # Конвертация в оттенки серого\n",
    "    \n",
    "    # Запуск OCR\n",
    "    results = reader.readtext(gray)\n",
    "    \n",
    "    text = \"\"\n",
    "    for res in results:\n",
    "        if len(results) == 1:\n",
    "            text = res[1]\n",
    "        if len(results) > 1 and len(res[1]) > 6 and res[2] > 0.2:  # Фильтрация текста\n",
    "            text = res[1]\n",
    "    \n",
    "    return text"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 384,
   "id": "c568f0d0",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "Neither CUDA nor MPS are available - defaulting to CPU. Note: This module is much faster with a GPU.\n"
     ]
    },
    {
     "ename": "error",
     "evalue": "OpenCV(4.10.0) d:\\a\\opencv-python\\opencv-python\\opencv\\modules\\imgproc\\src\\color.simd_helpers.hpp:92: error: (-2:Unspecified error) in function '__cdecl cv::impl::`anonymous-namespace'::CvtHelper<struct cv::impl::`anonymous namespace'::Set<3,4,-1>,struct cv::impl::A0x46dff480::Set<1,-1,-1>,struct cv::impl::A0x46dff480::Set<0,2,5>,4>::CvtHelper(const class cv::_InputArray &,const class cv::_OutputArray &,int)'\n> Invalid number of channels in input image:\n>     'VScn::contains(scn)'\n> where\n>     'scn' is 1\n",
     "output_type": "error",
     "traceback": [
      "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[1;31merror\u001b[0m                                     Traceback (most recent call last)",
      "Cell \u001b[1;32mIn[384], line 1\u001b[0m\n\u001b[1;32m----> 1\u001b[0m recognized_text \u001b[38;5;241m=\u001b[39m \u001b[43mocr_image_from_variable\u001b[49m\u001b[43m(\u001b[49m\u001b[43mresized_plate\u001b[49m\u001b[43m)\u001b[49m\n",
      "Cell \u001b[1;32mIn[383], line 12\u001b[0m, in \u001b[0;36mocr_image_from_variable\u001b[1;34m(img)\u001b[0m\n\u001b[0;32m      5\u001b[0m \u001b[38;5;250m\u001b[39m\u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m      6\u001b[0m \u001b[38;5;124;03mВыполняет OCR для изображения из переменной.\u001b[39;00m\n\u001b[0;32m      7\u001b[0m \u001b[38;5;124;03m\u001b[39;00m\n\u001b[0;32m      8\u001b[0m \u001b[38;5;124;03m:param img: Изображение в формате numpy (например, после загрузки с помощью OpenCV).\u001b[39;00m\n\u001b[0;32m      9\u001b[0m \u001b[38;5;124;03m:return: Распознанный текст.\u001b[39;00m\n\u001b[0;32m     10\u001b[0m \u001b[38;5;124;03m\"\"\"\u001b[39;00m\n\u001b[0;32m     11\u001b[0m reader \u001b[38;5;241m=\u001b[39m easyocr\u001b[38;5;241m.\u001b[39mReader([\u001b[38;5;124m'\u001b[39m\u001b[38;5;124men\u001b[39m\u001b[38;5;124m'\u001b[39m], gpu\u001b[38;5;241m=\u001b[39m\u001b[38;5;28;01mTrue\u001b[39;00m)  \u001b[38;5;66;03m# Инициализация easyOCR\u001b[39;00m\n\u001b[1;32m---> 12\u001b[0m gray \u001b[38;5;241m=\u001b[39m \u001b[43mcv2\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mcvtColor\u001b[49m\u001b[43m(\u001b[49m\u001b[43mimg\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcv2\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mCOLOR_BGR2GRAY\u001b[49m\u001b[43m)\u001b[49m  \u001b[38;5;66;03m# Конвертация в оттенки серого\u001b[39;00m\n\u001b[0;32m     14\u001b[0m \u001b[38;5;66;03m# Запуск OCR\u001b[39;00m\n\u001b[0;32m     15\u001b[0m results \u001b[38;5;241m=\u001b[39m reader\u001b[38;5;241m.\u001b[39mreadtext(gray)\n",
      "\u001b[1;31merror\u001b[0m: OpenCV(4.10.0) d:\\a\\opencv-python\\opencv-python\\opencv\\modules\\imgproc\\src\\color.simd_helpers.hpp:92: error: (-2:Unspecified error) in function '__cdecl cv::impl::`anonymous-namespace'::CvtHelper<struct cv::impl::`anonymous namespace'::Set<3,4,-1>,struct cv::impl::A0x46dff480::Set<1,-1,-1>,struct cv::impl::A0x46dff480::Set<0,2,5>,4>::CvtHelper(const class cv::_InputArray &,const class cv::_OutputArray &,int)'\n> Invalid number of channels in input image:\n>     'VScn::contains(scn)'\n> where\n>     'scn' is 1\n"
     ]
    }
   ],
   "source": [
    "recognized_text = ocr_image_from_variable(resized_plate)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 350,
   "id": "0225f69f",
   "metadata": {},
   "outputs": [],
   "source": [
    "import torch\n",
    "import torch.nn as nn\n",
    "import torch.nn.functional as F\n",
    "\n",
    "# Белый список символов (набор символов, которые могут быть в номере)\n",
    "whitelist = 'ABCEHKMOPTXy0123456789'\n",
    "\n",
    "# Архитектура модели CNN\n",
    "def compute_output_size(input_size, kernel_size, stride, padding):\n",
    "        return (input_size - kernel_size + 2 * padding) // stride + 1\n",
    "\n",
    "class CNN(nn.Module):\n",
    "    def __init__(self, num_classes):\n",
    "        super(CNN, self).__init__()\n",
    "        # Сверточные слои\n",
    "        self.conv1 = nn.Conv2d(1, 32, kernel_size=3, stride=1, padding=1)\n",
    "        self.conv2 = nn.Conv2d(32, 64, kernel_size=3, stride=1, padding=1)\n",
    "        self.pool = nn.MaxPool2d(kernel_size=2, stride=2)\n",
    "        \n",
    "        # Вычисляем размеры после сверточных слоев\n",
    "        height = compute_output_size(28, kernel_size=3, stride=1, padding=1)  # conv1\n",
    "        height = compute_output_size(height, kernel_size=3, stride=1, padding=1)  # conv2\n",
    "        height = compute_output_size(height, kernel_size=2, stride=2, padding=0)  # pool\n",
    "        width = height  # Для квадратного входа\n",
    "\n",
    "        # Полносвязные слои\n",
    "        self.fc1 = nn.Linear(64 * height * width, 128)\n",
    "        self.fc2 = nn.Linear(128, num_classes)\n",
    "        self.dropout = nn.Dropout(0.5)\n",
    "\n",
    "    def forward(self, x):\n",
    "        x = F.relu(self.conv1(x))\n",
    "        x = self.pool(F.relu(self.conv2(x)))\n",
    "        x = x.view(x.size(0), -1)\n",
    "        x = F.relu(self.fc1(x))\n",
    "        x = self.dropout(x)\n",
    "        x = self.fc2(x)\n",
    "        return x\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 366,
   "id": "29fdb225",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "C:\\Users\\leonk\\AppData\\Local\\Temp\\ipykernel_22288\\1427365130.py:9: FutureWarning: You are using `torch.load` with `weights_only=False` (the current default value), which uses the default pickle module implicitly. It is possible to construct malicious pickle data which will execute arbitrary code during unpickling (See https://github.com/pytorch/pytorch/blob/main/SECURITY.md#untrusted-models for more details). In a future release, the default value for `weights_only` will be flipped to `True`. This limits the functions that could be executed during unpickling. Arbitrary objects will no longer be allowed to be loaded via this mode unless they are explicitly allowlisted by the user via `torch.serialization.add_safe_globals`. We recommend you start setting `weights_only=True` for any use case where you don't have full control of the loaded file. Please open an issue on GitHub for any issues related to this experimental feature.\n",
      "  model.load_state_dict(torch.load(\"letter_recognition_model1.pth\", map_location=device))\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "CNN(\n",
       "  (conv1): Conv2d(1, 32, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "  (conv2): Conv2d(32, 64, kernel_size=(3, 3), stride=(1, 1), padding=(1, 1))\n",
       "  (pool): MaxPool2d(kernel_size=2, stride=2, padding=0, dilation=1, ceil_mode=False)\n",
       "  (fc1): Linear(in_features=12544, out_features=128, bias=True)\n",
       "  (fc2): Linear(in_features=128, out_features=22, bias=True)\n",
       "  (dropout): Dropout(p=0.5, inplace=False)\n",
       ")"
      ]
     },
     "execution_count": 366,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "# Устройство (CPU или GPU)\n",
    "device = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
    "\n",
    "# Инициализация модели\n",
    "num_classes = len(whitelist)\n",
    "model = CNN(num_classes).to(device)\n",
    "\n",
    "# Загрузка сохранённых весов\n",
    "model.load_state_dict(torch.load(\"letter_recognition_model1.pth\", map_location=device))\n",
    "model.eval()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 375,
   "id": "21d9c521",
   "metadata": {},
   "outputs": [],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "from torchvision import transforms\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "# Преобразование для модели\n",
    "transform = transforms.Compose([\n",
    "    transforms.ToPILImage(),\n",
    "    transforms.Resize((28, 28)),\n",
    "    transforms.ToTensor(),\n",
    "    transforms.Normalize((0.5,), (0.5,))  # Приведение в диапазон [-1, 1]\n",
    "])\n",
    "\n",
    "# Функция для распознавания символов\n",
    "def recognize_characters(image, model, whitelist):\n",
    "    contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    recognized_text = \"\"\n",
    "\n",
    "    # Сортируем контуры слева направо\n",
    "    sorted_contours = sorted(contours, key=lambda x: cv2.boundingRect(x)[0])\n",
    "\n",
    "    for idx, contour in enumerate(sorted_contours):\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        if w > 10 and h > 20:  # Фильтруем слишком маленькие объекты\n",
    "            roi = image[y:y+h, x:x+w]\n",
    "\n",
    "            # Приведение ROI к формату модели (28x28)\n",
    "            roi_resized = transform(roi).unsqueeze(0).to(device)  # Добавляем размер батча\n",
    "\n",
    "            # Предсказание\n",
    "            with torch.no_grad():\n",
    "                output = model(roi_resized)\n",
    "                probabilities = torch.softmax(output, dim=1).cpu().numpy().flatten()\n",
    "\n",
    "                # Топ-3 предсказания\n",
    "                top3_indices = probabilities.argsort()[-3:][::-1]\n",
    "                top3_characters = [whitelist[idx] for idx in top3_indices]\n",
    "                \n",
    "                top3_probs = [probabilities[idx] * 100 for idx in top3_indices]\n",
    "\n",
    "                # Лучшее предсказание\n",
    "                best_character = top3_characters[0]\n",
    "                #recognized_text += best_character\n",
    "\n",
    "                # Визуализация\n",
    "                fig, ax = plt.subplots(1, 2, figsize=(10, 5))\n",
    "                ax[0].imshow(roi, cmap='gray')\n",
    "                ax[0].set_title(f\"Contour {idx + 1}: Best Match '{best_character}'\")\n",
    "                ax[0].axis('off')\n",
    "\n",
    "                bar_positions = range(len(top3_characters))\n",
    "                ax[1].barh(bar_positions, top3_probs, align='center')\n",
    "                ax[1].set_yticks(bar_positions)\n",
    "                ax[1].set_yticklabels(top3_characters)\n",
    "                ax[1].invert_yaxis()  # Ставим сверху лучший результат\n",
    "                ax[1].set_xlabel('Probability (%)')\n",
    "                ax[1].set_title(\"Top-3 Predictions\")\n",
    "\n",
    "                plt.tight_layout()\n",
    "                plt.show()\n",
    "\n",
    "    return recognized_text\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 376,
   "id": "230c39ed",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 19956 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 20340 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 20240 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 20124 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 20324 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 20764 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 20176 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "image/png": "[encoded data, 19476 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Распознанный номер: \n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 10284 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "\n",
    "# Распознавание текста\n",
    "recognized_text = recognize_characters(processed_image1, model, whitelist)\n",
    "\n",
    "# Вывод результата\n",
    "print(f\"\\nРаспознанный номер: {recognized_text}\")\n",
    "\n",
    "# Визуализация результата\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.imshow(processed_image1, cmap='gray')\n",
    "plt.title('Обработанное изображение')\n",
    "plt.axis('off')\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 217,
   "id": "bc797665",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 14236 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import cv2\n",
    "import numpy as np\n",
    "\n",
    "def imclearborder(imgBW, connectivity=8):\n",
    "    \"\"\"\n",
    "    Удаляет только те пиксели объектов, которые непосредственно касаются границ изображения.\n",
    "    \n",
    "    :param imgBW: Бинарное изображение (чёрно-белое, 0 и 255).\n",
    "    :param connectivity: Тип связности (4 или 8). 8-связность по умолчанию.\n",
    "    :return: Изображение с удалёнными пикселями, касающимися границ.\n",
    "    \"\"\"\n",
    "    # Находим связные компоненты\n",
    "    num_labels, labels = cv2.connectedComponents(imgBW, connectivity)\n",
    "\n",
    "    # Создаем маску для граничных пикселей\n",
    "    border_mask = np.zeros_like(labels)\n",
    "\n",
    "    # Помечаем пиксели на границах\n",
    "    border_mask[0, :] = labels[0, :]   # Верхняя граница\n",
    "    border_mask[-1, :] = labels[-1, :]  # Нижняя граница\n",
    "    border_mask[:, 0] = labels[:, 0]   # Левая граница\n",
    "    border_mask[:, -1] = labels[:, -1]  # Правая граница\n",
    "\n",
    "    # Уникальные метки на границах\n",
    "    border_labels = np.unique(border_mask)\n",
    "\n",
    "    # Удаляем только пиксели на границе, не трогая внутренние области\n",
    "    result = imgBW.copy()\n",
    "    for label in border_labels:\n",
    "        if label != 0:  # Пропускаем фон\n",
    "            result[(labels == label) & (border_mask == label)] = 0\n",
    "\n",
    "    return result\n",
    "\n",
    "# Бинарное изображение\n",
    "imgBW = np.array([\n",
    "    [1, 1, 1, 1, 1],\n",
    "    [1, 0, 1, 0, 1],\n",
    "    [1, 0, 1, 0, 1],\n",
    "    [1, 0, 1, 1, 2],\n",
    "    [1, 0, 1, 0, 1],\n",
    "    [1, 0, 1, 0, 1],\n",
    "    [1, 0, 1, 0, 1],\n",
    "    [1, 0, 1, 0, 1],\n",
    "    [1, 0, 0, 0, 1],\n",
    "    [1, 1, 1, 1, 1]\n",
    "], dtype=np.uint8) * 255\n",
    "\n",
    "# Удаление объектов, касающихся границы\n",
    "result = imclearborder(imgBW)\n",
    "\n",
    "# Визуализация\n",
    "import matplotlib.pyplot as plt\n",
    "plt.figure(figsize=(10, 5))\n",
    "plt.subplot(1, 2, 1)\n",
    "plt.imshow(imgBW, cmap='gray')\n",
    "plt.title('Оригинальное изображение')\n",
    "plt.axis('off')\n",
    "\n",
    "plt.subplot(1, 2, 2)\n",
    "plt.imshow(result, cmap='gray')\n",
    "plt.title('Без объектов на границе')\n",
    "plt.axis('off')\n",
    "\n",
    "plt.show()\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 168,
   "id": "c2854f0f",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Распознанный номер для img/1.jpg: \n"
     ]
    },
    {
     "data": {
      "image/png": "[encoded data, 39044 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 2 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import numpy as np\n",
    "import cv2\n",
    "import pytesseract\n",
    "from matplotlib import pyplot as plt\n",
    "\n",
    "# Настройки для распознавания\n",
    "whitelist = 'ABCEHMOPTXyK0123456789'\n",
    "\n",
    "# Функция для создания эталонных шаблонов символов\n",
    "def generate_templates(whitelist):\n",
    "    templates = {}\n",
    "    for char in whitelist:\n",
    "        img = np.zeros((50, 30), dtype=np.uint8)  # Пустое изображение для символа\n",
    "        font = cv2.FONT_HERSHEY_SIMPLEX\n",
    "        cv2.putText(img, char, (5, 40), font, 1.2, 255, 2, cv2.LINE_AA)\n",
    "        templates[char] = img\n",
    "    return templates\n",
    "\n",
    "# Нахождение символов и сопоставление\n",
    "def recognize_characters(image, templates):\n",
    "    contours, _ = cv2.findContours(image, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)\n",
    "    recognized_text = \"\"\n",
    "\n",
    "    for contour in sorted(contours, key=lambda x: cv2.boundingRect(x)[0]):  # Сортировка слева направо\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        if w > 10 and h > 20:  # Фильтр по размеру символов\n",
    "            roi = image[y:y+h, x:x+w]\n",
    "            roi_resized = cv2.resize(roi, (30, 50), interpolation=cv2.INTER_CUBIC)  # Приведение к стандартному размеру\n",
    "\n",
    "            # Сравнение с шаблонами\n",
    "            best_match = None\n",
    "            max_corr = -1\n",
    "            for char, template in templates.items():\n",
    "                res = cv2.matchTemplate(roi_resized, template, cv2.TM_CCOEFF_NORMED)\n",
    "                _, corr, _, _ = cv2.minMaxLoc(res)\n",
    "                if corr > max_corr:\n",
    "                    max_corr = corr\n",
    "                    best_match = char\n",
    "\n",
    "            if best_match is not None and max_corr > 0.5:  # Порог корреляции\n",
    "                recognized_text += best_match\n",
    "\n",
    "    return recognized_text\n",
    "\n",
    "# Основной обработчик\n",
    "def process_plate(image_path):\n",
    "    image = cv2.imread(image_path)\n",
    "    if image is None:\n",
    "        print(f\"Не удалось загрузить изображение: {image_path}\")\n",
    "        return\n",
    "\n",
    "    # Преобразование в градации серого и пороговая обработка\n",
    "    img_gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)\n",
    "    _, thresh = cv2.threshold(img_gray, 100, 200, cv2.THRESH_TOZERO_INV)\n",
    "    contours, _ = cv2.findContours(thresh, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)\n",
    "\n",
    "    # Нахождение области номерного знака\n",
    "    plate = None\n",
    "    for contour in contours:\n",
    "        x, y, w, h = cv2.boundingRect(contour)\n",
    "        aspect_ratio = float(w) / h\n",
    "        if aspect_ratio >= 3 and w * h > 600:\n",
    "            plate = image[y:y + h, x:x + w]\n",
    "            break\n",
    "\n",
    "    if plate is None:\n",
    "        print(f\"Номерной знак не найден на изображении: {image_path}\")\n",
    "        return\n",
    "\n",
    "    # Преобразование вырезанного номера\n",
    "    gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
    "\n",
    "    # Применение гомоморфной фильтрации\n",
    "    processed_plate = homomorphic_filter(gray_plate)\n",
    "\n",
    "    # Нахождение и распознавание символов\n",
    "    templates = generate_templates(whitelist)\n",
    "    recognized_text = recognize_characters(processed_plate, templates)\n",
    "\n",
    "    print(f\"Распознанный номер для {image_path}: {recognized_text}\")\n",
    "\n",
    "    # Визуализация\n",
    "    plt.figure(figsize=(10, 5))\n",
    "    plt.subplot(1, 2, 1)\n",
    "    plt.imshow(cv2.cvtColor(plate, cv2.COLOR_BGR2RGB))\n",
    "    plt.title(\"Вырезанный номер\")\n",
    "    plt.axis(\"off\")\n",
    "\n",
    "    plt.subplot(1, 2, 2)\n",
    "    plt.imshow(processed_plate, cmap=\"gray\")\n",
    "    plt.title(\"Обработанный номер\")\n",
    "    plt.axis(\"off\")\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "# Пример обработки\n",
    "images = ['img/1.jpg']\n",
    "for img_path in images:\n",
    "    process_plate(img_path)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 160,
   "id": "63cc871f",
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "[encoded data, 52660 characters]",
      "text/plain": [
       "<Figure size 1000x500 with 25 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "import matplotlib.pyplot as plt\n",
    "\n",
    "# Функция для отображения шаблонов\n",
    "def display_templates(templates):\n",
    "    n_templates = len(templates)\n",
    "    cols = 5  # Количество столбцов в сетке\n",
    "    rows = (n_templates + cols - 1) // cols  # Количество строк для размещения всех символов\n",
    "\n",
    "    fig, axes = plt.subplots(rows, cols, figsize=(10, 5))\n",
    "    fig.suptitle(\"Шаблоны символов\", fontsize=16)\n",
    "\n",
    "    # Проходим по всем шаблонам\n",
    "    for i, (char, img) in enumerate(templates.items()):\n",
    "        row, col = divmod(i, cols)\n",
    "        axes[row, col].imshow(img, cmap='gray')\n",
    "        axes[row, col].set_title(f\"'{char}'\")\n",
    "        axes[row, col].axis('off')\n",
    "\n",
    "    # Удаляем лишние оси\n",
    "    for j in range(i + 1, rows * cols):\n",
    "        row, col = divmod(j, cols)\n",
    "        axes[row, col].axis('off')\n",
    "\n",
    "    plt.tight_layout()\n",
    "    plt.show()\n",
    "\n",
    "# Пример использования\n",
    "templates = generate_templates(whitelist)  # Генерация шаблонов\n",
    "display_templates(templates)  # Отображение\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": 5
}
