2529 lines
1.1 MiB
Plaintext
2529 lines
1.1 MiB
Plaintext
|
{
|
|||
|
"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": "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/CVGxNlRPPjggzjrrLNwwAEH4MYbb8S6detw55134s0338QHH3yAoqIifyB18803o3379rjyyiuRlZWFBx54AMcccwxeeuklHHHEEYFwJ0yYgKKiIkyePBkrV67Evffeiy+//BKvvvqqPwh78MEHkZeXh8svvxx5eXlYuHAhrrvuOpSXl2PatGkA4N/76quvRs+ePTFlyhREo1HcfffdOOyww/Duu+9ijz32AAAsXboUAwYMQEFBASZOnIiMjAzcf//9GDRoEF577TUcdNBBOOKII/Dwww/78RQvq2uvvdY/duihhzrT6sUXX8TZZ5+NCRMm4KqrrkqYrtdddx2i0WjS+dCc688//3y0b98e3377Lf7617/imGOOwerVq5GTkwMAOPfcczFr1iz8+te/xhVXXIF33nkHN954I5YvX46nnnoKwNZ0nTBhAvr374+bbroJxcXFuOuuu/DGG2/ggw8+QOvWrf17NjQ04LjjjsPBBx+MW265BfPnz8ekSZNQX1+PqVOn+uclU9Y7d+6MefPmYdCgQZg4cWJcQfXKK6/ESy+9hFdffRWdO3f2j//5z3/Gn/70J4waNQrnnnsuiouLMX36dBxxxBF+md1e6uvrA2VCM2bMGCxYsAAXX3wx9t13X6SlpWHGjBlYvHjxdt9Xk0w+CllZWfjnP/+JadOmISMjAwDwzTff4OWXX0ZWVlbg3MbGRowYMQJvvPEGzj//fPTu3RuffPIJbr/9dnz66acxXp+vvfYaHn/8cVxyySWIRCK45557cNxxx+F///ufU+hrigULFmDo0KHo1q0bJk+ejOrqakyfPh2HHXYYFi9eHCP83XvvvYH2NBzeOpebTPsVj/Lychx00EEYM2YMMjIyMH/+fFx11VVIT0/HFVdc0ezneumll/DFF1/grLPOQvv27bF06VLMmDEDS5cuxdtvvx13YuCjjz7CyJEj8atf/Qp33323f/zdd9/FW2+9hVNPPRUdO3bEmjVrcO+992LQoEFYtmyZX98JIYSQZuERQgj5wVFZWekVFxfHfFasWOEB8E466aSYa1555RUPgPfKK684w6yqqgp8r62t9fr27esdddRRgeMAvPHjx8dcP2zYMK9z587+99WrV3sAvGnTpsV9jkmTJnlNdTW1tbVe27Ztvb59+3rV1dX+8X//+98eAO+6664L3C8zM9P79NNP/fOKi4u9Vq1aef379/ePzZw50wPg9e/f36utrfWP33LLLR4Ab968ef4xmy6e53njxo3zcnJyvGg06nne1rRt3bq1V1JS4p/36aefehkZGd7JJ5/sHxs5cqSXmZnpff755/6xb7/91svPz/eOOOIIZxoMHDjQGzhwoPO3MWPG+On+3nvveXl5ed4pp5ziNTQ0xD3P8zxvyZIlXjgc9oYOHeoB8FavXu0Mf0dc/8QTT3gAvPfee8/zPM/78MMPPQDeueeeGzjv97//vQfAW7hwoed5W/Npr732CuSDpPcVV1wRiB8A7+KLL/aPNTY2esOGDfMyMzO94uJi/3iyZd3zPO/xxx/3QqGQ97e//c3zvGBePPDAA14oFPKeeOKJwDVr1qzx0tLSvD//+c+B45988omXnp4eOD5w4ECvT58+MfedNm1aTLp27tzZGzNmjP/9nnvu8SKRiHfkkUcG8qa6utoLh8P
|
|||
|
"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": "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
|
|||
|
"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": "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
|
|||
|
"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": "iVBORw0KGgoAAAANSUhEUgAAA90AAAC5CAYAAAA1Q1xXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAClsUlEQVR4nOydd5gUVfb+387dk3Nq0gAzY8CwYl5MuKsiUQQWTKAEERBBRFhAkih5BQGVtIAiBkARMCz6NWdds6zTM8AA05Nz7li/P/hVO/fUTIdhMufzPDwPdyrdqrr3Vled+55XJUmSBIZhGIZhGIZhGIZhmh11W1eAYRiGYRiGYRiGYTor/NLNMAzDMAzDMAzDMC0Ev3QzDMMwDMMwDMMwTAvBL90MwzAMwzAMwzAM00LwSzfDMAzDMAzDMAzDtBD80s0wDMMwDMMwDMMwLQS/dDMMwzAMwzAMwzBMC8Ev3QzDMAzDMAzDMAzTQvBLN8MwDMMwTAemuroap0+fRmlpaasfu7KyEllZWaiurm71YzMMw3QU+KWbYdoRTz/9NNxuNwDA7XZj+fLlbVyjzsvhw4fx008/ecoHDhzA77//3nYVYhiGCYC9e/fi5ptvRmhoKEJCQtCtWzesWrWqxY8rSRK2bNmCq6++GkFBQQgLC0NycjJ2797d4sfuaFRVVWHdunWecllZGTZt2tR2Ferk7N69G1lZWZ7yzp07YbVa265CDFMPfuk+h9m5cydUKpXwLy4uDjfddBPefffdtq7eOcmuXbuwZs0aZGdnY+3atdi1a1dbV6nT8uuvv+KRRx5BRkYGvv76a0yePBmVlZVtXS2GYc5Bfv/9d9xzzz0wm80wGAxISkrC3Xff3eiHwLlz52LUqFEIDQ3F1q1b8f777+ODDz7AlClTWryud911FyZPnozzzz8fL730kufYw4cPb/FjdzRMJhMWLFiAl19+GadPn8bixYtx6NChtq5Wp+Wzzz7D448/jqysLPznP//B1KlToVbzqw7TPlBJkiS1dSWYtmHnzp24//77sXTpUiQnJ0OSJOTn52Pnzp34/fffcejQIQwaNKitq3lO8dprr+G+++6D3W6HwWDA7t27MWLEiLauVqeksLAQ1157LTIzMwEAw4cPx/79+9u4VgzDnGu88cYbGDNmDKKiojB+/HgkJycjKysL27dvR3FxMV599VXccccdnvU/+eQT3HjjjVi+fDnmzp3bqnV98cUXMW7cOOzevRt33XVXqx67o7J27Vo8/vjjcLvdCAsLw9tvv41+/fq1dbU6JX/88QduvPFG5OfnAwAeffRRrF27to1rxTBn4Jfucxj5pfu7777D5Zdf7vl7aWkp4uPjMXLkSLz88sttWMNzk4KCAmRmZiIlJQWxsbFtXZ1Ojc1mw2+//YagoCCcf/75bV0dhmHOMY4dO4aLL74Y3bp1w6effiqM+UVFRbjuuutw+vRp/PLLL+jZsycAYPDgwSgpKcEXX3zR6vW96KKLcPHFF/NvgwDJzs7G6dOncf755yMiIqKtq9Opqa6uxm+//YaYmBj06tWrravDMB54zgWjICIiAiaTCVqt1vO3rKwsqFQqrFmzptHtFi9eDJVKJfxNpVJh2rRpePnll5GWlgaj0Yi+ffvi008/VWxvtVrxwAMPID4+HgaDARdeeCH+/e9/C+t8/vnn6NevH2JiYmA0GtGzZ0/MmTMHdXV1nnVKSkrw2GOP4aKLLkJISAjCwsIwYMAA/Pzzz8K+Pv74Y6hUKuzbt09Rl5CQEIwbN85Tlqfif//998J6RUVFUKlUWLx4seI6FBUVNXqtevTo0eD+s7KyEBcXh2uvvRbR0dG4+OKLoVKpsHPnzkb3FWj9AODHH3/EgAEDEBYWhpCQENx88834+uuvG9ynXq9HYWGhsOyrr77ySBLoMb/55hvcdtttCA8PR1BQEG644QbFj0P5Gv3xxx8YNWoUwsLCEB0djUceeUS4l8CfbYgyaNAg9OjRQ/hbdXU1Zs2aha5du8JgMCAtLQ1r1qwB/bYoXxODwYC+ffvi/PPPx+rVq6FSqXDjjTcqjlWf4uJiDBgwAF26dIHBYEBiYiLuvvtunDx50rOOt/7Sp08f4Rh2ux0LFy5E3759ER4ejuDgYFx33XX46KOPhO3kfdZvC5WVlejbty+Sk5ORm5vrtd4Mw7Q/Vq9ejZqaGmzZskXxkTUmJgabN29GdXW1oNX++uuv0adPH4wePRpRUVEwmUy44oorcODAAWF7+Rn32muvYd68eUhISEBwcDCGDBmC06dPC+t+9tlnGDlyJLp16waDwYCuXbti5syZqK2t9awjv8x07doVAwcORFhYGIKDg3HjjTfis88+U5zb8ePHMXLkSERFRSEoKAhXX3013n77bUX9vP2Tn10nT57ElClTkJaWBpPJhOjoaIwcOVLQ7wLN86z+/vvvFWPtuHHjEBISojjH+jS2fwDo0qULrrnmGmi1WiQkJEClUuHjjz/2ur9A6gcAH374Ia677joEBwcjIiICQ4cOxf/+978G9xkXFweHwyEse+WVVzzXnR7z3Xff9ew7NDQUAwcOVEgf5Gt0/Phx3HrrrQgODkZSUhKWLl0qPIMDeT4CZwIR48ePR3x8PIxGIy655BKF9K7+8zE4OBhXXXUVevXqhalTp0KlUgm/txoiPT0d/fv3R0JCgqf9T548GSUlJZ51AvnNGOjv0PptIScnBz169MDll1+Oqqoqr/VmOhZa36swnZ3y8nIUFRVBkiQUFBRgw4YNqKqqwj333NMs+//kk0/w2muvYfr06TAYDHjuuedw22234dtvv0WfPn0AAPn5+bj66qs9L1ixsbF49913MX78eFRUVGDGjBkAzrxknH/++Rg1ahSCgoLw1VdfYdWqVaipqcGGDRsAnHnQHzhwACNHjkRycjLy8/OxefNm3HDDDTh69CiSkpKa5bxampdeegm//vprs+/3999/x3XXXYewsDA8/vjj0Ol02Lx5M2688UZ88sknuOqqq4T1NRoNdu/ejZkzZ3r+tmPHDhiNRsUL8ocffogBAwagb9++WLRoEdRqNXbs2IH+/fvjs88+w5VXXimsP2rUKPTo0QPLly/H119/jWeffRalpaV48cUXAz4vSZIwZMgQfPTRRxg/fjwuvfRS/Oc//8Hs2bNhtVrxzDPPNLptWVmZ30nr7HY7QkND8cgjjyA6OhrHjh3Dhg0b8MsvvzTpflVUVGDbtm0YM2YMJk6ciMrKSmzfvh233norvv32W1x66aUNbudwOHDnnXfi1KlT+OKLL5CYmBjwsRmGaVsOHTqEHj164Lrrrmtw+fXXX48ePXoIL6vFxcXYsmULQkJCMH36dMTGxmL37t0YPnw4Xn75ZYwZM0bYx1NPPQWVSoU5c+agoKAA69atw9/+9jf89NNPMJlMAM4kZaupqcFDDz2E6OhofPvtt9iwYQOys7Oxd+9ez3EBYOXKlUhISMDs2bNhNBqxdetW/O1vf8P777+P66+/HsCZZ/q1116LmpoaTJ8+HdHR0di1axeGDBmCffv24Y477vBowmW2bNmC//3vf8JYffHFFwMAvvvuO3z55ZcYPXo0unTpgqysLDz//PO48cYbcfToUQQFBZ3trWgV1q5d65n63Jx88MEHGDBgAHr27InFixejtrYWGzZswF//+lf88MMPig/UlZWVOHz4sCBbaOy5/tJLL2Hs2LG49dZbsXLlStTU1OD5559Hv3798OOPPwr7drlcuO2223D11Vdj1apVeO+997Bo0SI4nU4sXbo04POqra3FjTfeiMzMTEybNg3JycnYu3cvxo0bh7KyMjzyyCONbpuZmYmtW7f6dZzq6mp06dIFgwcPRlhYGH777Tds2rQJVqu1SRr8pv4OLS8vx4ABA6DT6fDOO+/4/NDDdDAk5pxlx44dEgDFP4PBIO3cuVNY98SJExIAafXq1Y3ub9GiRRJtUvI+v//+e8/fTp48KRmNRumOO+7w/G38+PFSYmKiVFRUJGw/evRoKTw8XKqpqWn0uLfffrvUp08fT7murk5yuVyK+hsMBmnp0qW
|
|||
|
"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": "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
|
|||
|
"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": "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
|
|||
|
"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": "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VFVVSXFxsRx33HGybt06de5rr70mJ598sgwfPlySyaQMGzZMfvSjH0lzc3P2nM6XmWHDhsnRRx8tZWVlUlxcLFOmTJHXXnstULfPP/9cTj75ZOnbt68UFRXJAQccIM8++2ygfGH/OteuNWvWyAUXXCBjx46VwsJCqayslJNPPlnpd0W+mrX6nXfeCcy1M2bMkJKSkkAd/fSUv4jI0KFD5cADD5R4PC5VVVUSiUTk5ZdfDs0vl/KJiPzud7+TQw45RIqLi6WiokKOP/54+fTTT7vNc8CAAdLe3q6O/fa3v822O+/5/PPPZ/MuLS2Vo48+OiB96Gyjzz//XKZOnSrFxcUyePBgue6669QanMv6KPLlHyLOOeccGThwoKRSKdlrr70C0jv/+lhcXCz777+/jB49Wi688EKJRCLqeas7/vznP8u3vvUtqaqqysb/+eefL9u3b8+ek8szY67Pof5Y2LBhg4wcOVL22Wcf2blzZ2i5jb8t4u5TjL936urqpKamRjzPky1btsicOXNk586d8oMf/OAryf+VV16RRx55RC655BJJJpNy5513ypFHHilvvfWWTJw4UURENm/eLAcccED2Bat///7y/PPPyznnnCP19fVy2WWXiciXLxnjxo2TU045RYqKimTp0qVy0003SVNTk8yZM0dEvlzon3zySTn55JOlurpaNm/eLHfffbccdthh8sknn8jgwYO/knrlmwcffFA+/PDDrzzfjz/+WA455BApKyuTyy+/XBKJhNx9990yZcoUeeWVV2T//fdX58diMVmwYIH86Ec/yv5s7ty5kkqlAi/Iv/vd72TatGkyefJkufrqqyUajcrcuXPlW9/6lrz22muy3377qfNPOeUUGTlypMyePVveeOMN+b//9//Kjh075IEHHsi5Xp7nyXHHHScvvfSSnHPOObL33nvLCy+8IDNnzpT169fLr3/96x6vra2t7fWmdW1tbVJaWiqXXnqpVFZWysqVK2XOnDnywQcf7FJ/1dfXy3333SennXaanHvuudLQ0CD333+/TJ06Vd566y3Ze++9u72uvb1dvv/978vatWvl9ddfl0GDBuV8b8Mwvl6efvppGTlypBxyyCHdHj/00ENl5MiR6mV127Ztcs8990hJSYlccskl0r9/f1mwYIF873vfk4ULF8ppp52m8vjFL34hkUhEfvrTn8qWLVvk1ltvlW9/+9vy3nvvSWFhoYh8uSlbU1OT/PM//7NUVlbKW2+9JXPmzJEvvvhCHn300ex9RURuvPFGqaqqkpkzZ0oqlZJ7771Xvv3tb8uLL74ohx56qIh8uaYfdNBB0tTUJJdccolUVlbK/Pnz5bjjjpPHHntMTjzxxKwmvJN77rlHPv30UzVX77nnniIi8vbbb8sf/vAHOfXUU2Xo0KGyevVqueuuu2TKlCnyySefSFFR0f+0K/4i3HLLLdlPn79KlixZItOmTZNRo0bJNddcI83NzTJnzhz55je/KX/6058Cv6BuaGiQZ555RskWelrXH3zwQZk+fbpMnTpVbrzxRmlqapK77rpLDj74YHn33XdV3ul0Wo488kg54IAD5KabbpJFixbJ1VdfLR0dHXLdddflXK/m5maZMmWKrFixQi666CKprq6WRx99VGbMmCG1tbVy6aWX9njtihUr5N577+3
|
|||
|
"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": "iVBORw0KGgoAAAANSUhEUgAAA94AAACICAYAAAARUjNcAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOx9d4DcxPn2MzMqu9ds3AtuGDDFphmI6RCqMZjeSeiBEAL4R0wgkNASSoBAPkIzxZgaugmETugxvRdTHAzGuNdru5Jm5vtjiqTdPd+dfecCepLFd1ppNBqN5vS85XmJlFIiQ4YMGTJkyJAhQ4YMGTJkyNApoKu6AxkyZMiQIUOGDBkyZMiQIcOPGRnxzpAhQ4YMGTJkyJAhQ4YMGToRGfHOkCFDhgwZMmTIkCFDhgwZOhEZ8c6QIUOGDBkyZMiQIUOGDBk6ERnxzpAhQ4YMGTJkyJAhQ4YMGToRGfHOkCFDhgwZMmTIkCFDhgwZOhEZ8c6QIUOGDBkyZMiQIUOGDBk6ERnxzpAhQ4YMGTJkyJAhQ4YMGToRGfHOkCFDhh8pGhsbMWPGDCxatGiln7u+vh7Tp09HY2PjSj93hgwZMrSGbH3MkCHDykZGvDOsMC699FIIIQAAQghcdtllq7hHP1488cQT+OCDD+zvkydPxqeffrrqOpRhtcODDz6IXXfdFbW1taipqcHAgQPx17/+tdPPK6XEhAkTMGrUKFRVVaGurg5DhgzB3Xff3ennXtPQ0NCAa6+91v6+ePFiXH/99auuQxkAZPflp4BsfVz9kT2Hqyey+9IxWOOI9x133AFCSOrTq1cv7LLLLnjqqadWdfd+kpg0aRKuuuoqfP/997j66qsxadKkVd2lHy0+/vhjnHHGGfjqq6/wxhtv4JRTTkF9ff2q7laGTsKnn36Ko48+Gv3794fv++jXrx+OOuqoFo0t55xzDg499FDU1tbilltuwXPPPYfnn38ep556aqf39cgjj8Qpp5yCDTfcEHfddZc994EHHtjp517TkM/ncf755+Oee+7BjBkzcOGFF+Lxxx8v2++LL77AuHHjsO222yKXy4EQgunTp6/8Dv9E0Nb7kmH1QLY+/jjR1ufwkUcewWGHHYZ11lkHVVVVGDZsGM466ywsXrx45Xf6J4BsfewgyDUMEydOlADkxRdfLO+66y555513yiuvvFJuvPHGEoB8/PHHV3UXf3L45z//KT3PkwCk7/vywQcfXNVd+tFi7ty5ct1115UAJAB54IEHruouZegkPPzww9LzPNmnTx953nnnyVtvvVWef/75sm/fvtLzPPnII4+k9n/ppZckAHnZZZet9L5OmjRJEkLkPffcs9LPvabiqquukpRSCUDW1dXJV199tWyfiRMnSkqpHD58uNxss80kAPnNN9+s/M7+hNCW+5Jh1SNbH3/caMtz2L17dzlixAj5xz/+Ud5yyy3y9NNPl57nyQ022EA2NTWtgl7/+JGtjysOIqWUq4byLx/uuOMOHHfccXj77bex5ZZb2u2LFi1C7969ccghh+Cee+5ZhT38aWLu3Ln4+uuvsd5666Fnz56rujs/ahSLRXzyySeoqqrChhtuuKq7k6ETMG3aNGyyySYYOHAgXnnlldQzNX/+fOywww6YMWMGPvroI6yzzjoAgH333RcLFy7E66+/vtL7O2LECGyyySbZ2ttOfP/995gxYwY23HBDdO3atez7hQsXwnVd1NbW4qqrrsL48ePxzTffYPDgwSu9rz8ltHZfMqxaZOvjTwOtPYcvvfQSdt5559S2O++8E8cccwxuueUWnHjiiSunoz8xZOvjimGNCzVvCV27dkU+n4fjOHbb9OnTQQjBVVdd1eJxF154IQghqW2EEJx22mm45557MGzYMORyOYwcORKvvPJK2fEzZ87E8ccfj969e8P3fWy88ca4/fbbU/u89tpr2H777dGjRw/kcjmss846+P3vf49CoWD3WbhwIX73u99hxIgRqKmpQV1dHUaPHo0PP/ww1dZLL70EQggeeuihsr7U1NTg2GOPtb+bsPx33nkntd/8+fNBCMGFF15YNg7z589vcawGDx5csf3p06ejV69e2HbbbdG9e3dssskmIITgjjvuaLGt9vYPAN5//32MHj0adXV1qKmpwa677oo33nijYpue52HevHmp76ZMmWLTE0rP+eabb2KvvfZCly5dUFVVhZ122qnsD7QZo6lTp+LQQw9FXV0dunfvjjPOOCN1L4F4DpVin332KXtpbmxsxFlnnYUBAwbA930MGzYMV111FUptYmZMfN/HyJEjseGGG+LKK68EIaTsj08pFixYgNGjR2PttdeG7/vo27cvjjrqKHz77bd2n2U9L8OHD0+dIwgC/OlPf8LIkSPRpUsXVFdXY4cddsCLL76YOs60mZwL9fX1GDlyJIYMGYJZs2Yts98/VVx55ZVoamrChAkTygxZPXr0wM0334zGxsZUbuIbb7yB4cOH4/DDD0e3bt2Qz+ex1VZbYfLkyanjzRpy//334w9/+AP69OmD6upqjB07FjNmzEjt++qrr+KQQw7BwIED4fs+BgwYgHHjxqG5udnu09jYiE8++QQDBgzAmDFjUFdXh+rqauy888549dVXy67tf//7Hw455BB069YNVVVVGDVqFP7973+X9W9ZH7M2fPvttzj11FMxbNgw5PN5dO/eHYccckhZOHZHrIXvvPNO2Vw+9thjUVNTU3aNSbTUPgCsvfba2GabbeA4Dvr06QNCCF566SW7b7du3VBbW7vM9peFwYMHY5999inbftppp5X93YuiCJdccgmGDh0K3/cxePBg/OEPf0CxWCxrkxCCM888s6zdPffcE4SQsnMWi0VccMEFWHfdde08Ovvss8vabuvf3vbcIwD4z3/+gx122AHV1dXo2rUr9ttvP3z++ecV2wRavy+VcOONN2LTTTe16+Gmm26K2267LbVPS/PloYceKjtHW54902bp35S7774blFJcfvnly+zzmopsfczWRwAV33sOOOAAACh7vishWx8VsvVx5cJpfZfVE0uWLMH8+fMhpcTcuXNx3XXXoaGhAUcffXSHtP/yyy/j/vvvx+mnnw7f93HDDTdgr732wltvvYXhw4cDAObMmYNRo0bZh6Fnz5546qmncMIJJ2Dp0qX2wauvr8eGG26IQw89FFVVVZgyZQr++te/oqmpCddddx0AtdhOnjwZhxxyCIYMGYI5c+bg5ptvxk477YTPPvsM/fr165Dr6mzcdddd+Pjjjzu83U8//RQ77LAD6urqcPbZZ8N1Xdx8883Yeeed8fLLL+NnP/tZan/GGO6++26MGzfObps4cSJyuVwZSf7Pf/6D0aNHY+TIkbjgggtAKcXEiRPx85//HK+++iq23nrr1P6HHnooBg8ejMsuuwxvvPEG/t//+39YtGgR7rzzznZfl5QSY8eOxYsvvogTTjgBm222GZ555hmMHz8eM2fOxDXXXNPisYsXL26zkF0QBKitrcUZZ5yB7t27Y9q0abjuuuvw0UcfLdf9Wrp0KW699VYcccQROOmkk1BfX4/bbrsNe+65J9566y1sttlmFY8LwxAHHXQQvvvuO7z++uvo27dvu8/9U8Djjz+OwYMHY4cddqj4/Y477ojBgwenXsgWLFiACRMmoKamBqeffjp69uyJu+++GwceeCDuueceHHHEEak2/vKXv4AQgt///veYO3curr32Wuy222744IMPkM/nASghoqamJvz6179G9+7d8dZbb+G6667D999/jwcffNCeFwCuuOIK9OnTB+PHj0cul8Mtt9yC3XbbDc899xx23HFHAGrN3HbbbdHU1ITTTz8d3bt3x6RJkzB27Fg89NBDOOCAA2wOpMGECRPw+eefp56FTTbZBADw9ttv47///S8OP/xwrL322pg+fTpuvPFG7Lzzzvjss89QVVW1ordipeDqq6/GnDlzVmkfTjzxREyaNAkHH3wwzjrrLLz55pu47LLL8Pnnn+PRRx9N7ZvL5XDPPffgyiuvhOu6AJQX5IUXXkAul0vtK4TA2LFj8dprr+FXv/o
|
|||
|
"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": "iVBORw0KGgoAAAANSUhEUgAAA94AAACKCAYAAABcmpJXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOxdd5wURdp+qjrNzAZyRoKAiqByYkREOQMYUM/AmT4RFfUwYj7vPNOZTj311BPRk0NBPTF7dyoGMCAoHmZEEQURkLDAsmlmuqvq+6OqOszM7s4uC4j2w2+YmZ7uqurq6tp+6n3f5yVCCIEYMWLEiBEjRowYMWLEiBEjxmYB3doNiBEjRowYMWLEiBEjRowYMX7OiIl3jBgxYsSIESNGjBgxYsSIsRkRE+8YMWLEiBEjRowYMWLEiBFjMyIm3jFixIgRI0aMGDFixIgRI8ZmREy8Y8SIESNGjBgxYsSIESNGjM2ImHjHiBEjRowYMWLEiBEjRowYmxEx8Y4RI0aMGDFixIgRI0aMGDE2I2LiHSNGjBgxYsSIESNGjBgxYmxGxMQ7RowYMX6mqKmpwbJly7B+/fotXndVVRWWLFmCmpqaLV53jBgxYjSGeH6MESPGlkZMvGNsMm6++WZwzgEAnHPccsstW7lFP1/8+9//xscff+x/f/755/HFF19svQbF+Mlh+vTpOOigg1BWVobS0lL06NEDf/nLXzZ7vUIITJo0Cfvssw9SqRTKy8vRu3dvTJ06dbPXva2huroad999t/99w4YNuP/++7deg2IAiK/LLwHx/PjTR3wf/jQRX5eWwTZHvP/5z3+CEBJ5dezYEcOHD8fLL7+8tZv3i8SUKVNwxx134IcffsCdd96JKVOmbO0m/Wzx2Wef4aKLLsKiRYswd+5cnHvuuaiqqtrazYqxmfDFF1/g1FNPRbdu3eA4Drp27YpTTjml3sWWq666CqNHj0ZZWRkeeughvPbaa3j99dcxfvz4zd7Wk08+Geeeey769++Pxx57zK/72GOP3ex1b2tIJpP44x//iGnTpmHZsmW47rrr8NJLL+Xt99VXX2HChAkYMmQIEokECCFYsmTJlm/wLwTFXpcYPw3E8+PPE8Xeh88++yx++9vfYvvtt0cqlcKOO+6ISy+9FBs2bNjyjf4FIJ4fWwhiG8PkyZMFAHHDDTeIxx57TDz66KPi9ttvFwMGDBAAxEsvvbS1m/iLw5NPPils2xYAhOM4Yvr06Vu7ST9brF69WvTt21cAEADEscceu7WbFGMz4ZlnnhG2bYvOnTuLP/zhD+Lhhx8Wf/zjH0WXLl2Ebdvi2Wefjew/a9YsAUDccsstW7ytU6ZMEYQQMW3atC1e97aKO+64Q1BKBQBRXl4u3nnnnbx9Jk+eLCilYuDAgWLQoEECgPjuu++2fGN/QSjmusTY+ojnx583irkP27VrJ3bZZRdxzTXXiIceekhceOGFwrZtsdNOO4na2tqt0OqfP+L5cdNBhBBi61D+5uGf//wnxo4di3nz5mGPPfbwt69fvx6dOnXCCSecgGnTpm3FFv4ysXr1anzzzTfo168fOnTosLWb87NGJpPB559/jlQqhf79+2/t5sTYDFi8eDF23XVX9OjRA2+//Xbknlq7di32339/LFu2DJ9++im23357AMCoUaOwbt06zJ49e4u3d5dddsGuu+4az71NxA8//IBly5ahf//+aN26dd7v69atg2VZKCsrwx133IHLL78c3333HXr16rXF2/pLQmPXJcbWRTw//jLQ2H04a9YsHHjggZFtjz76KMaMGYOHHnoIZ5111pZp6C8M8fy4adjmXM3rQ+vWrZFMJmGapr9tyZIlIITgjjvuqPe46667DoSQyDZCCM4//3xMmzYNO+64IxKJBAYPHoy333477/jly5fjjDPOQKdOneA4DgYMGIBHHnkkss+7776LoUOHon379kgkEth+++1x5ZVXIp1O+/usW7cOl112GXbZZReUlpaivLwchx12GD755JNIWbNmzQIhBE8//XReW0pLS3H66af737Vb/ocffhjZb+3atSCE4Lrrrsvrh7Vr19bbV7169SpY/pIlS9CxY0cMGTIE7dq1w6677gpCCP75z3/WW1ZT2wcAH330EQ477DCUl5ejtLQUBx10EObOnVuwTNu2sWbNmshvc+bM8cMTcut8//33MXLkSLRq1QqpVAoHHHBA3h9o3UcLFy7E6NGjUV5ejnbt2uGiiy6KXEsgGEO5OPLII/MemmtqanDppZdiu+22g+M42HHHHXHHHXcgd01M94njOBg8eDD69++P22+/HYSQvD8+uaioqMBhhx2G7t27w3EcdOnSBaeccgqWLl3q79PQ/TJw4MBIHdlsFn/6058wePBgtGrVCiUlJdh///0xc+bMyHG6zPBYqKqqwuDBg9G7d2+sXLmywXb/UnH77bejtrYWkyZNylvIat++PR588EHU1NREYhPnzp2LgQMH4sQTT0Tbtm2RTCax55574vnnn48cr+eQf/3rX7j66qvRuXNnlJSU4KijjsKyZcsi+77zzjs44YQT0KNHDziOg+222w4TJkxAXV2dv09NTQ0+//xzbLfddjjiiCNQXl6OkpISHHjggXjnnXfyzu3bb7/FCSecgLZt2yKVSmGfffbBf/7zn7z2NfTSc8PSpUsxfvx47Ljjjkgmk2jXrh1OOOGEPHfslpgLP/zww7yxfPrpp6O0tDTvHMOor3wA6N69O/bdd1+YponOnTuDEIJZs2b5+7Zt2xZlZWUNlt8QevXqhSOPPDJv+/nnn5/3d8/zPNx4443o06cPHMdBr169cPXVVyOTyeSVSQjBxRdfnFfuiBEjQAjJqzOTyeDaa69F3759/XF0xRVX5JVd7N/eplwjAHjzzTex//77o6SkBK1bt8bRRx+NL7/8smCZQOPXpRAeeOAB7Lbbbv58uNtuu+Ef//hHZJ/6xsvTTz+dV0cx954uM/dvytSpU0Epxa233tpgm7dVxPNjPD8CKPjc85vf/AYA8u7vQojnR4l4ftyyMBvf5aeJyspKrF27FkIIrF69Gvfeey+qq6tx6qmntkj5b731Fv71r3/hwgsvhOM4+Pvf/46RI0figw8+wMCBAwEAq1atwj777OPfDB06dMDLL7+MM888Exs3bvRvvKqqKvTv3x+jR49GKpXCnDlz8Je//AW1tbW49957AcjJ9vnnn8cJJ5yA3r17Y9WqVXjwwQdxwAEHYMGCBejatWuLnNfmxmOPPYbPPvusxcv94osvsP/++6O8vBxXXHEFLMvCgw8+iAMPPBBvvfUW9t5778j+hmFg6tSpmDBhgr9t8uTJSCQSeST5zTffxGGHHYbBgwfj2muvBaUUkydPxq9//Wu888472GuvvSL7jx49Gr169cItt9yCuXPn4m9/+xvWr1+PRx99tMnnJYTAUUcdhZkzZ+LMM8/EoEGD8Oqrr+Lyyy/H8uXLcdddd9V77IYNG4oWsstmsygrK8NFF12Edu3aYfHixbj33nvx6aefNut6bdy4EQ8//DBOOukkjBs3DlVVVfjHP/6BESNG4IMPPsCgQYMKHue6Lo477jh8//33mD17Nrp06dLkun8JeOmll9CrVy/sv//+BX8fNmwYevXqFXkgq6iowKRJk1BaWooLL7wQHTp0wNSpU3Hsscdi2rRpOOmkkyJl3HTTTSCE4Morr8Tq1atx99134+CDD8bHH3+MZDIJQAoR1dbW4ne/+x3atWuHDz74APfeey9++OEHTJ8+3a8XAG677TZ07twZl19+ORKJBB566CEcfPDBeO211zBs2DAAcs4cMmQIamtrceGFF6Jdu3aYMmUKjjrqKDz99NP4zW9+48dAakyaNAlffvll5F7YddddAQDz5s3De++9hxNPPBHdu3fHkiVL8MADD+DAAw/EggULkEqlNvVSbBHceeedWLVq1VZtw1lnnYUpU6bg+OOPx6WXXor3338ft9xyC7788ks899xzkX0TiQSmTZuG22+/HZZlAZBWkDfeeAOJRCKyL+ccRx11FN59912cffbZ6N+/Pz777DP
|
|||
|
"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": "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
|
|||
|
"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": "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
|
|||
|
"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": "iVBORw0KGgoAAAANSUhEUgAAAxoAAADxCAYAAABMK8GOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAdjElEQVR4nO3de3BU5f3H8c+imKsEk+USKDcBIdxKB5GL3ByhCHIZKihoxwQZQIMyOC0KWAvUnyWItlCq1lBFkJRiiAPU2opUrjUtIFWkoFhLkFJEQgxgAoMNz+8PJsecbBI27JPs7f2acZycnN3znOd59rDffL/POR5jjBEAAAAAWNQg2A0AAAAAEHkINAAAAABYR6ABAAAAwDoCDQAAAADWEWgAAAAAsI5AAwAAAIB1BBoAAAAArCPQAAAAAGAdgQaAkFFSUqJjx47pq6++qvdjnzt3TgUFBSopKan3Y6NuXLx4UV988YX++9//BrspABCVCDQABFVubq5uv/12XX/99UpMTFTr1q31zDPP1PlxjTHKzs5W3759FR8fr0aNGqldu3Zas2ZNnR8bdWfv3r2699575fV6FRMTo9TUVN11113BbhYARKVrg90AAJHjn//8pxYtWqStW7eqsLBQKSkpuu222zRv3jx17drVZ/85c+Zo8eLFGjt2rFasWCGv1yuPx6Obbrqpztt67733at26dUpPT9fs2bOVlJQkj8ejHj161PmxUTc2btyoe+65R507d9bTTz+t9u3bS5KaNm0a5JYBQHTyGGNMsBsBIPy98cYbmjRpkpKTkzVlyhS1a9dOBQUFevnll3X69Gn9/ve/17hx45z9t2/friFDhmjRokWaM2dOvbZ19erVysjI0Jo1a3TvvffW67FRN4qKitSpUyf1799fubm5uu6664LdJACIegQaAAL22WefqUePHmrdurV27NihJk2aOL8rLCzUwIEDdezYMe3fv1833nijJGn06NEqKirSX//613pvb/fu3dWjRw/l5OTU+7FRN5577jktWLBAn3/+uW644YZgNwcAINZoALBgyZIlKi0tVXZ2tivIkCSv16uXXnpJJSUlrrUXf/vb39StWzdNnDhRycnJiouLU+/evbVhwwbX67dt2yaPx6N169Zp3rx5at68uRISEjRmzBgdO3bMte/OnTs1YcIEtW7dWjExMWrVqpUeffRRnT9/3tmnpKREBw4cUKtWrXTnnXeqUaNGSkhI0JAhQ7Rz506fc/v3v/+tCRMmKDk5WfHx8erbt6/++Mc/+rSvpv8WLFggSTp69KgyMzPVqVMnxcXFKSUlRRMmTFBBQYHrmK+++qo8Ho/27t3r2l5YWOh6P0lasGCBPB6PCgsLXfvu3btXHo9Hr776qmv7u+++q4EDByohIUGNGzfW2LFjdejQIZ/zPn78uB544AE1a9ZMMTEx6tq1q1555RWf/apSuY3S5Tni8Xg0ZMgQZ9uuXbs0YMAAeb1excbG6sYbb9Tjjz+uCxcuuF57pTGQLs+nnj176uc//7latWqlmJgYdezYUVlZWbp06ZJP+x5++GHl5OSoU6dOio2NVa9evbRjxw7Xfv6O10svvaQGDRooNzfX2VZQUODT/4cPH1ZycrJPFq24uFizZs1y2t2hQwctXrzY1e7y93v22Wd9+rtbt26ufi2fk9u2bXPtd+edd1Y5NoGMNQDUhDUaAAL2hz/8QW3bttXAgQOr/P2gQYPUtm1b15fD06dPKzs7W4mJiZo5c6aaNGmiNWvW6Ac/+IFycnI0adIk13s8/fTT8ng8evzxx/Xll19q6dKlGjp0qD744APFxcVJurywvLS0VA899JBSUlK0e/duLV++XP/5z3+cL4GnT5+WJC1evFjNmzfX7NmzFRsbqxUrVmjo0KF65513NGjQIEnSyZMn1b9/f5WWlmrmzJlKSUnRqlWrNGbMGK1fv17jxo1TWlqaXnvtNaed2dnZOnTokH75y18628rXfezZs0fvvfeeJk6cqO985zsqKCjQiy++qCFDhujgwYOKj48PdChqtGXLFo0YMUI33nijFixYoPPnz2v58uW69dZbtW/fPrVt29Y57759+zpfyJs0aaI//elPmjJlis6ePatZs2bV6rjFxcVatGiRz/Zz584pLS1Nd999t+Lj45Wfn69nnnlGpaWlWr58udOWK42BdHlcd+3apV27dumBBx5Qr1699Je//EVz585VQUGBfvOb37iOvX37dq1bt04zZ85UTEyMXnjhBd1xxx3avXu3unXrJsn/8Zo+fbo++eQTpaenq23bturdu7fPuRYVFWnUqFFKS0vTypUrne2lpaUaPHiwjh8/runTp6t169Z67733NHfuXJ04cUJLly6tVV9XZ8eOHXrrrbd8ttseawBwMQAQgOLiYiPJjB07tsb9xowZYySZs2fPGmOMkWQkmW3btjn7lJaWmrS0NNO8eXNz8eJFY4wxW7duNZJMy5YtndcaY8zrr79uJJlly5a5Xl/ZokWLjMfjMUePHjXGGHPkyBEjyVx33XXm8OHDzn6nTp0yKSkpplevXs62WbNmGUlm586dzrZz586Zdu3ambZt25qysjKf46Wnp5s2bdpU2QdVtS8/P99IMqtXr3a2rVy50kgye/bsce176tQpI8nMnz/f2TZ//nwjyZw6dcq17549e4wks3LlSmdbz549TdOmTc3p06edbR9++KFp0KCBuf/++51tU6ZMMampqaawsND1nhMnTjRJSUlVnkdFldv42GOPmaZNm5pevXqZwYMH1/jakSNHmm7dujk/+zsGgwcPNpLMggULXO+XkZFhJJmPPvrI1T5JZu/evc62o0ePmtjYWDNu3Dhnm7/jZYwxZWVlZvTo0SY1NdUcO3bMmWcrV640Fy9eNEOGDDHt2rUzX375pet1Tz31lElISHDNRWOMmTNnjrnmmmvM559/boz5dt4uWbLEp01du3Z19Wv5Z2br1q3Otj59+pgRI0b4jE2gYw0ANaF0CkBAzp07J0m6/vrra9yv/Pdnz551tvXu3VuDBw92fo6Li1NmZqa++OIL7du3z/X6+++/33WM8ePHKzU11fVX2vLMhnS5RKqwsFD9+/eXMUb/+Mc/XO83duxYdezY0fnZ6/UqIyND77//vk6ePClJeuutt3TLLbdowIABzn6JiYmaNm2aCgoKdPDgwRrPubKK7fvmm290+vRpdejQQY0bN/Y5X0k6c+aMCgsLnf+Kioqqfe+ioiLXvmfOnHH9/sSJE/rggw+UkZGh5ORkZ3uPHj00bNgwpx+NMcrLy9Po0aNljHG95/Dhw3XmzJkq21qd48ePa/ny5XryySeVmJhYbdtPnDihDRs2KD8/38koSbUbg2uuuUaPPvqo671/9KMfSZJPqVW/fv3Uq1cv5+fWrVtr7Nixevvtt1VWViapduPVoEEDrV27VikpKRo9erTreSwPPfSQdu/erTfffNOntDA3N1cDBw7UDTfc4OrroUOHqqyszKecq7S01LVfYWGh097qvPHGG9qzZ4+ysrJc222PNQBURukUgICUf/kvDziqU1VA0rlzZ5/90tLSJF2uSe/Tp4+zvWJQIF2us+/QoYOrXv7zzz/XT3/6U23atMnnoX/lX7w9Ho9fx27WrJmOHj3qakPl/Y4ePeqU2fjj/PnzWrRokVauXKnjx4/LVLgXR+XAQJKGDh3q93t36tSpxt8fPXq02v3S0tL09ttvq6SkRCUlJSouLlZ2drays7OrfK8vv/zS73bNnz9fLVq00PTp07V+/foq9+nSpYsT3GVkZGjZsmWudvszBh6PRy1atFCjRo1c+3Xq1EkNGjTwWVdReT5J0k033aTS0lKdOnVKzZs3r/V4lZWVqbCwUF988YUmT54sSXrhhRe0Z88eeTyeKj8jn376qfbv3+8TgJSr3Nfz58/X/PnzffZr1qxZla8vKyvTvHnzdN999/ncuvnUqVNWxxoAKiPQABCQpKQkpaamav/+/TXut3//frVs2dL5Iljxr8U2lJWVadiwYSoqKtLjjz+uzp07KyEhQcePH1dGRoazsNb2cWvjkUc
|
|||
|
"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": "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
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1000x500 with 2 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA2cAAAHqCAYAAACA+jZKAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA7AklEQVR4nO3de1yUZf7/8fcgzoAieAKVBM95gELDNCXPruYptYOta4UZrQesLK202sTaFqvtoKm0Vh5aI10tS93M8NhqWh5L0whdT1se0hSPgcL1+8Mf820cUDRxLuH1fDzmIXPf19zzuW7GuefNdd/XOIwxRgAAAAAAn/LzdQEAAAAAAMIZAAAAAFiBcAYAAAAAFiCcAQAAAIAFCGcAAAAAYAHCGQAAAABYgHAGAAAAABYgnAEAAACABQhnAAAAAGABwhlwBSxfvlwOh0Nz5szxdSkAgBJm165dcjgcmjZtmntZUlKSHA7HFXuOvOPc8uXLr9g2AXizMpzt2LFDAwcOVO3atRUQEKDg4GDFxcVp3LhxOn36dJE979atW5WUlKRdu3YV2XNcrsOHD+uVV15R69atFRoaqvLly+uWW27RrFmzftd2p02bJofD4XELCwtTu3bttHDhwitUvbdTp04pKSmp0G/yeQcFh8OhGTNm5NsmLi5ODodD0dHRl1XTpEmTPA5sV1Pe7+G3P1/sVrNmTUnnDsB5PwPAtaIw73NXKwx88cUXuv322xUREaGAgABVrVpVt912m1atWlWox/fv39+j5uDgYMXExOjVV19VVlZWEVd/ZfnyWAhA8vd1Aef797//rbvvvlsul0v333+/oqOjlZ2drZUrV+qJJ57Qd999p8mTJxfJc2/dulVjxoxR27Ztrfuwu3r1aj3zzDPq2rWrnn32Wfn7++vDDz/UH//4R3fdv8fzzz+vWrVqyRijAwcOaNq0aeratavmz5+v7t27X6Fe/J9Tp065a27btm2hHxcQEKDU1FTde++9Hst37dqlL7/8UgEBAZdd06RJk1S5cmX179//srdxJbRu3Vr//Oc/PZYlJCSoWbNm+vOf/+xeFhQUdLVLA4Ar5vz3uffee09paWleyxs2bFjktfzwww/y8/PToEGDVLVqVR05ckQzZsxQ69at9e9//1u33XbbRbfhcrn0zjvvSJKOHj2qDz/8UCNGjNDatWs1c+bMou6Cl2effVYjR4685McVdCxs3bq1Tp8+LafTeYUqBJAfq8LZzp079cc//lE1atTQ0qVLVa1aNfe6xMREbd++Xf/+9799WGHR+vXXX+V0OuXn5z2gGRUVpYyMDNWoUcO9bMiQIerYsaNeeuklPfnkkypbtuxlP3eXLl3UtGlT9/0HH3xQVapU0QcffFAk4exyde3aVfPmzdOhQ4dUuXJl9/LU1FRVqVJF9erV05EjR3xY4e9Xu3Zt1a5d22PZoEGDVLt2ba9QCgDXqvPfz9asWaO0tDSfvM8lJCQoISHBY9mQIUNUu3ZtvfHGG4UKZ/7+/h61DxkyRM2bN9esWbP02muvKTw83Osxxhj9+uuvCgwM/P2dyKcef/8r9zHPz8/vd/0BFEDhWHVa48svv6wTJ07o3Xff9QhmeerWratHH33Uff/s2bN64YUXVKdOHblcLtWsWVNPP/201ykENWvWVPfu3bVy5Uo1a9ZMAQEBql27tt577z13m2nTpunuu++WJLVr1y7f0ykmTZqkqKgouVwuhYeHKzExUUePHvV6rvxGXtq2besxQpR3mt7MmTP17LPP6rrrrlOZMmV07NixfPdNrVq1PIKZdO6UkF69eikrK0v//e9/PdZ9//332rNnT77bKozy5csrMDDQ6409NzdXb7zxhqKiohQQEKAqVapo4MCBXoFo3bp16ty5sypXrqzAwEDVqlVLAwYMkHRulCs0NFSSNGbMGPe+TkpKumhdPXv2lMvl0uzZsz2Wp6amqk+fPipVqpTXY6ZOnar27dsrLCxMLpdLjRo1UkpKikebmjVr6rvvvtOKFSvc9fz293X06FE99thjqlmzplwul6pXr677779fhw4d8to/L774oqpXr66AgAB16NBB27dvv2i/AADeTp48qeHDhysiIkIul0v169fX3//+dxljPNo5HA4NHTpU77//vurXr6+AgADFxsbqiy++uOznLlOmjEJDQ72O84Xl5+fnPo7kXS6R93lk0aJFatq0qQIDA/WPf/xD0rnjzLBhw9x9rVu3rl566SXl5uZ6bPfo0aPq37+/QkJCVL58ecXHx+dbY0HXnM2YMUPNmjVTmTJlVKFCBbVu3Vqff/65u76CjoUFXXM2e/ZsxcbGKjAwUJUrV9a9996rH3/80aNN//79FRQUpB9//FG9evVSUFCQQkNDNWLECOXk5Hi0nTlzpmJjY1WuXDkFBwfrhhtu0Lhx4wqzy4FiwaqRs/nz56t27dpq2bJlodonJCRo+vTpuuuuuzR8+HB99dVXSk5O1rZt2zR37lyPttu3b9ddd92lBx98UPHx8ZoyZYr69++v2NhYRUVFqXXr1nrkkUc0fvx4Pf300+7TKPL+TUpK0pgxY9SxY0cNHjxY6enpSklJ0dq1a7Vq1SqVLl36svr8wgsvyOl0asSIEcrKyrrk0wX2798vSR6jSHl1t2nTptDn6mdmZurQoUMyxujgwYN68803deLECa+/YA4cOFDTpk3TAw88oEceeUQ7d+7UhAkTtHHjRvd+OHjwoDp16qTQ0FCNHDlS5cuX165du/TRRx9JkkJDQ5WSkqLBgwerd+/euuOOOyRJN95440XrLFOmjHr27KkPPvhAgwcPliR98803+u677/TOO+/o22+/9XpMSkqKoqKidPvtt8vf31/z58/XkCFDlJubq8TEREnSG2+8oYcfflhBQUF65plnJElVqlSRJJ04cUKtWrXStm3bNGDAAN100006dOiQ5s2bp//9738e+37s2LHy8/PTiBEjlJmZqZdffln9+vXTV199VajfAwDgHGOMbr/9di1btkwPPvigGjdurEWLFumJJ57Qjz/+qNdff92j/YoVKzRr1iw98sgjcrlcmjRpkm677TZ9/fXXhb4W+dixY8rOztahQ4f03nvvacuWLXr66acvuw87duyQJFWqVMm9LD09XX379tXAgQP10EMPqX79+jp16pTatGmjH3/8UQMHDlRkZKS+/PJLjRo1Svv27dMbb7zh3ic9e/bUypUrNWjQIDVs2FBz585VfHx8oeoZM2aMkpKS1LJlSz3//PNyOp366quvtHTpUnXq1OmCx8L85H0euPnmm5WcnKwDBw5o3LhxWrVqlTZu3Kjy5cu72+bk5Khz585q3ry5/v73v2vx4sV69dVXVadOHffxPC0tTX379lWHDh300ksvSZK2bdumVatWefxxHijWjCUyMzONJNOzZ89Ctd+0aZORZBISEjyWjxgxwkgyS5cudS+rUaOGkWS++OIL97KDBw8al8tlhg8f7l42e/ZsI8ksW7bMY5sHDx40TqfTdOrUyeTk5LiXT5gwwUgyU6ZM8Xiu+Ph4r3rbtGlj2rRp476/bNkyI8nUrl3bnDp1qlB9Pt/hw4dNWFiYadWqldc6SR7PV5CpU6caSV43l8tlpk2b5tH2P//5j5Fk3n//fY/ln332mcfyuXPnGklm7dq1BT7vzz//bCSZ0aNHX7yj5v/21+zZs82CBQuMw+Ewe/bsMcYY88QTT5jatWsbY87t56ioKI/H5rd/O3fu7H5MnqioqHz32XPPPWckmY8++shrXW5urkd9DRs2NFlZWe7148aNM5LM5s2bC9XPgpQtWzbf1xUAFBeJiYnmtx9LPv74YyPJ/PWvf/Vod9dddxmHw2G2b9/uXpZ37Fq3bp172e7du01AQIDp3bt3oWvo3Lmze1tOp9MMHDjQnD59+qKPi4+PN2XLljU///yz+fnnn8327dvN3/72N+NwOMyNN97obpf3eeSzzz7zePwLL7xgypYta3744QeP5SNHjjSlSpVyH+/y9snLL7/sbnP27FnTqlUrI8lMnTrVvXz06NEe+zMjI8P4+fmZ3r17e3y
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1000x500 with 2 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "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
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1000x500 with 2 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "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G06ujnh99dVXKlOmjOrWrZul9nFxcfrwww/VoUMH9e/fXz/++KNGjRqlzZs3a/bs2W5td+zYoQ4dOujxxx9XbGyspk6dqi5duqh69eqqVKmSGjRooKefflpjx47VCy+84DrUIf2/Q4YM0dChQxUTE6Pu3btr69atmjBhgtasWaOVK1cqX7582XrNw4cPl6+vr5555hmdPXv2qkP6b7/9to4dO6aXXnpJ//73vzNts3//flWsWFGxsbFZPm77r7/+kiSlpaVp//79Gj58uPz8/NSxY0dXm7S0NLVt21YrVqzQk08+qYoVK2rjxo166623tG3bNtf5Ub/99ptat26t22+/XcOGDZPT6dSOHTtcx85XrFhRw4YN06BBg/Tkk0+6AkVW+r1UqVKqU6eO/vWvf6lly5aSpHnz5ikpKUkPPvigxo4dm+E577zzjtq2bavOnTsrJSVFM2bM0P3336+5c+eqVatWki4cAhMXF6datWrpySeflCRXsD1w4IBq1aql48eP68knn1SFChW0f/9+zZo1S6dPn3brs169eik0NFSDBw/W7t279fbbb6tnz56aOXNmlvrhUomJiVq1apU2b978jzj3EACyyxijtm3basmSJXr88cdVtWpVzZ8/X88++6z279+vt956y639smXLNHPmTD399NNyOp0aP368WrRooZ9++knR0dFZ2uaJEyeUkpKiP/74Qx999JE2bdrk+pEwO3bu3ClJKlSokGvZ1q1b1alTJ3Xr1k1PPPGEypcvr9OnT6thw4bav3+/unXrphIlSmjVqlUaOHCgDh48qLffftv1ntxzzz1asWKFnnrqKVWsWFGzZ89WbGxsluoZOnSohgwZorp162rYsGHy9fXVjz/+qMWLF6tZs2Z6++231atXLwUGBurFF1+UJBUpUuSy65s+fbq6du2qmjVratSoUTp8+LDeeecdrVy5Uj///LMKFCjgapuamqrmzZurdu3aev311/Xdd9/pjTfeUNmyZdW9e3dJF36k7dSpk5o2baoxY8ZIkjZv3qyVK1e6/dAO3DRMLklKSjKSzD333JOl9hs2bDCSTFxcnNvyZ555xkgyixcvdi0rWbKkkWSWL1/uWnbkyBHjdDpN//79XcsSExONJLNkyRK3dR45csT4+vqaZs2amdTUVNfycePGGUlm6tSpbtuKjY3NUG/Dhg1Nw4YNXfeXLFliJJkyZcqY06dPZ+k1Hzx40AQFBZn333/fGGPMtGnTjCSzZs0at3a7du0ykjKt41KDBw82kjLcChQoYL799lu3th9//LHx8vIy33//vdvyiRMnGklm5cqVxhhj3nrrLSPJHD169LLbXbNmjZFkpk2bloVX7v5ax40bZ4KCglzv2/33328aN25sjLnw/rdq1crtuZe+vykpKSY6Oto0adLEbXlAQECm79mjjz5qvLy8MrzPxhiTlpbmVl9MTIxrmTHG9O3b13h7e5vjx49n6XVeWneJEiXMwIEDjTH/+5tJTEy85nUBQF4THx9vLv6a8cUXXxhJZsSIEW7tOnToYBwOh9mxY4drWfq+au3ata5le/bsMX5+fqZ9+/ZZrqF58+audfn6+ppu3bqZM2fOXPV5sbGxJiAgwBw9etQcPXrU7Nixw4wcOdI4HA5z++23u9qlf/+4dH86fPhwExAQYLZ
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1000x500 with 2 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA30AAAHqCAYAAACwdidrAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA690lEQVR4nO3dd3xUVf7/8fckYSYhIQQhEIKhIyChLQhLV8iXIkVARFlUQBFEUGmyWFYCKMVVVERAkaKLCAQVEGXpRUA0NAXpLE16TygSSM7vDx6ZH0MSSAjJyMnr+XjMg8y9Z+79zJmQe99zbnEYY4wAAAAAAFby8XYBAAAAAICsQ+gDAAAAAIsR+gAAAADAYoQ+AAAAALAYoQ8AAAAALEboAwAAAACLEfoAAAAAwGKEPgAAAACwGKEPAAAAACxG6AP+QqZMmSKHw6F169Z5uxQAADzs27dPDodDU6ZMcU+Ljo6Ww+G4Y+tYvny5HA6Hli9ffseWCcDy0Ldnzx51795dJUuWlL+/v4KDg1WnTh19+OGHunTpUpatd+vWrYqOjta+ffuybB2ZUbx4cTkcjhSP559//raXmfxHP/nh4+OjwoULq0WLFlq7du0drN7T4cOHFR0drU2bNqWrfXKocjgcWrVqVYr5xhhFRETI4XCoRYsWt1XTsGHDNHv27Nt6bWZFR0erePHi7p9T+5xvfDz44IOSpM6dO7t/BgBbpOfvYHaFjJUrV6pVq1aKiIiQv7+/wsLC1LRpU61evTpdr+/cubNHzcHBwapcubLee+89Xb58OYurv7PGjh3rER4BZC0/bxeQVb7//ns99thjcrlcevrppxUZGamEhAStWrVKr7zyin7//Xd9+umnWbLurVu3avDgwXrwwQfdO+B/NVWqVFG/fv08pt13332ZXu64ceMUFBSkpKQkHTx4UBMmTFD9+vX1yy+/qEqVKple/o0OHz6swYMHq3jx4hlavr+/v6ZNm6a6det6TF+xYoX++OMPuVyu265p2LBhateunVq3bn3by7gT2rZtq9KlS7ufnz9/Xj169FCbNm3Utm1b9/RChQp5ozwAyBb/+c9/PJ5/8cUXWrRoUYrp5cuXz/Jadu7cKR8fHz3//PMKCwvTmTNnNHXqVNWvX1/ff/+9mjZtestluFwuffbZZ5Kks2fP6uuvv1b//v0VGxur6dOnZ/VbSOGNN97QwIEDM/y6sWPHqkCBAurcubPH9Pr16+vSpUtyOp13qEIAkqWhb+/evXriiSdUrFgxLV26VIULF3bP69mzp3bv3q3vv//eixVmrT///FNOp1M+PmkP5BYpUkRPPvnkHV93u3btVKBAAffz1q1bKzIyUjExMVkS+m7Xww8/rJiYGI0ePVp+fv//v8G0adNUrVo1nTx50ovV3RmVKlVSpUqV3M9PnjypHj16qFKlSlny2QPAX9GNf+/Wrl2rRYsWeeXvYNeuXdW1a1ePaS+88IJKliypDz74IF2hz8/Pz6P2F154QTVr1tSMGTM0atQohYeHp3iNMUZ//vmnAgICMv8mUqnn+u1oZvn4+Mjf3/+OLQ/ANVYe3vnOO+/o/PnzmjhxokfgS1a6dGm9/PLL7udXr17V0KFDVapUKblcLhUvXlyvvfZaikMlihcvrhYtWmjVqlWqUaOG/P39VbJkSX3xxRfuNlOmTNFjjz0mSXrooYdSPWxk7NixqlChglwul8LDw9WzZ0+dPXs2xbpu/PZLkh588EGPQ/CSj32fPn263njjDRUpUkS5c+dWXFzcLfspISFBFy5cSHP+lStXtH37dh05cuSWy0pLWFiYJKXYIFy+fFmDBg1S6dKl5XK5FBERoQEDBqTo80WLFqlu3boKCQlRUFCQypYtq9dee03Stff+wAMPSJK6dOni7uv0HC7SoUMHnTp1SosWLXJPS0hI0KxZs/SPf/wj1de8++67ql27tvLnz6+AgABVq1ZNs2bN8mjjcDh04cIFff755+56rv8cDx06pGeffVbh4eFyuVwqUaKEevTooYSEhBT907dvX4WGhiowMFBt2rTRiRMnbvm+AAAZd+HCBfXr108RERFyuVwqW7as3n33XRljPNo5HA716tVLX375pcqWLSt/f39Vq1ZNK1euvO11586dW6GhoSn2A9LLx8fHvV+QfFpJ8v7KggULVL16dQUEBOiTTz6RdG10sHfv3u73Wrp0aY0cOVJJSUkeyz179qw6d+6svHnzKiQkRJ06dUq1xrTO6Zs6dapq1Kih3LlzK1++fKpfv74WLlzoru/333/XihUrUpxqkNY5fTExMapWrZoCAgJUoEABPfnkkzp06JBHm86dOysoKEiHDh1S69atFRQUpNDQUPXv31+JiYkebadPn65q1aopT548Cg4OVsWKFfXhhx+mp8uBu5KVI33fffedSpYsqdq1a6erfdeuXfX555+rXbt26tevn37++WcNHz5c27Zt07fffuvRdvfu3WrXrp2effZZderUSZMmTVLnzp1VrVo1VahQQfXr19dLL72k0aNH67XXXnMfLpL8b3R0tAYPHqyoqCj16NFDO3bs0Lhx4xQbG6vVq1crV65ct/Wehw4dKqfTqf79++vy5cu3PCxi6dKlyp07txITE1WsWDH16dPHIwhL1wJK+fLl1alTp3Qfd3/69GlJUlJSkg4dOqShQ4fK399f7du3d7dJSkpSq1attGrVKnXr1k3ly5fX5s2b9f7772vnzp3u8+F+//13tWjRQpUqVdKQIUPkcrm0e/du97kP5cuX15AhQ/Tmm2+qW7duqlevniSl63MvXry4atWqpa+++krNmjWTJM2fP1/nzp3TE088odGjR6d4zYcffqhWrVqpY8eOSkhI0PTp0/XYY49p3rx5at68uaRrhxF17dpVNWrUULdu3SRJpUqVknTtUNQaNWro7Nmz6tatm8qVK6dDhw5p1qxZunjxosdn9uKLLypfvnwaNGiQ9u3bpw8++EC9evXSjBkz0vU5AADSxxijVq1aadmyZXr22WdVpUoVLViwQK+88ooOHTqk999/36P9ihUrNGPGDL300ktyuVwaO3asmjZtql9++UWRkZHpWmdcXJwSEhJ08uRJffHFF9qyZYv7C83bsWfPHklS/vz53dN27NihDh06qHv37nruuedUtmxZXbx4UQ0aNNChQ4fUvXt3FS1aVGvWrNGrr76qI0eO6IMPPnD3ySOPPKJVq1bp+eefV/ny5fXtt9+qU6dO6apn8ODBio6OVu3atTVkyBA5nU79/PPPWrp0qRo3bqwPPvhAL774ooKCgvT6669LuvmpBlOmTFGXLl30wAMPaPjw4Tp27Jg+/PBDrV69Whs3blRISIi7bWJiopo0aaKaNWvq3Xff1eLFi/Xee++pVKlS6tGjh6RrXyh36NBBjRo10siRIyVJ27Zt0+rVq1PsCwHWMJY5d+6ckWQeeeSRdLXftGmTkWS6du3qMb1///5Gklm6dKl7WrFixYwks3LlSve048ePG5fLZfr16+eeFhMTYySZZcuWeSzz+PHjxul0msaNG5vExET39DFjxhhJZtKkSR7r6tSpU4p6GzRoYBo0aOB+vmzZMiPJlCxZ0ly8eDFd77lly5Zm5MiRZvbs2WbixImmXr16RpIZMGCAR7u9e/caSanWcaNBgwYZSSkeISEh5r///a9H2//85z/Gx8fH/Pjjjx7Tx48fbySZ1atXG2OMef/9940kc+LEiTTXGxsbaySZyZMnp+u9T5482UgysbGxZsyYMSZPnjzufnvsscfMQw89ZIy51v/Nmzf3eO2N/ZuQkGAiIyNNw4YNPaYHBgam2mdPP/208fHxMbGxsSnmJSUledQXFRXlnmaMMX369DG+vr7m7Nmz6XqfqTlx4oSRZAYNGnTbywCAu13Pnj3N9bs/s2fPNpLMW2+95dGuXbt2xuFwmN27d7unJW/b1q1b5562f/9+4+/vb9q0aZPuGpo0aeJeltPpNN27dzeXLl265es6depkAgMDzYkTJ8yJEyfM7t27zbBhw4zD4TCVKlVyt0veX7lx+zt06FATGBhodu7c6TF94MC
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1000x500 with 2 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA6cAAAHqCAYAAAD4RTjrAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAA8QElEQVR4nO3dd5hU5fk/4GeWZXcRWEBpolRRQVE0YEEUFIgNsQVNiAVUxIK9xRYFNYom0cSG0SiaKFHR2EiMImLBii2xIJavIEEQC01EUPb8/vDHxHEpCwKvwH1f11ww57znnGfend0zn3lPyWVZlgUAAAAkVJS6AAAAABBOAQAASE44BQAAIDnhFAAAgOSEUwAAAJITTgEAAEhOOAUAACA54RQAAIDkhFMAAACSE06Bxbr11lsjl8vFSy+9lLoUAFhpJk6cGLlcLm699db8tEGDBkUul1tp23jiiScil8vFE088sdLWCesC4XQ1ef/99+OYY46JVq1aRVlZWZSXl0fnzp3jj3/8Y8ybN2+Vbfett96KQYMGxcSJE1fZNn6oOXPmxFlnnRUtW7aM0tLS2GijjaJ3797x5ZdfrtD6Fu1gFj2Kiopiww03jH322Seef/75lVz9/3z00UcxaNCgeO2116rUflH4y+VyMXbs2ErzsyyLpk2bRi6Xi3322WeFarr00kvj/vvvX6Flf6hBgwZFixYtljh/5syZ0bBhw8jlcnHPPfcUzFvUNwB867v7taU9VkcYeuqpp2LfffeNpk2bRllZWTRu3Dj23HPPeOaZZ6q0fL9+/QpqLi8vj/bt28fvf//7mD9//iqufuW6/vrrC0Iu8MMUpy5gXfCPf/wjDjrooCgtLY3DDz882rVrFwsWLIixY8fGmWeeGW+++WbceOONq2Tbb731VgwePDh23XXXpQaFVGbNmhVdu3aN//73vzFgwIBo3bp1fPLJJ/H000/H/PnzY7311lvhdQ8dOjRq1aoVFRUVMXny5LjpppuiS5cu8eKLL8Y222yz8l7E//fRRx/F4MGDo0WLFsu1/rKyshg+fHjsvPPOBdOffPLJ+O9//xulpaUrXNOll14avXv3jv3333+F17GqXHDBBSv8BQTAuuavf/1rwfO//OUvMWrUqErT27Ztu8preeedd6KoqCiOPfbYaNy4ccyYMSNuv/326NKlS/zjH/+IPffcc5nrKC0tjT//+c8R8e2Xlffee2+cccYZMW7cuLjzzjtX9Uuo5Pzzz4+zzz57uZe7/vrro379+tGvX7+C6V26dIl58+ZFSUnJSqoQ1g3C6Sr2wQcfxC9+8Yto3rx5PP7447Hhhhvm5w0cODDee++9+Mc//pGwwlXrq6++ipKSkigqWvwg/TnnnBOTJk2KV155JVq2bJmf/qtf/eoHb7t3795Rv379/PP9998/2rVrFyNGjFgl4XRF7b333jFixIi4+uqro7j4f7+Sw4cPjw4dOsSnn36asLpV44033oihQ4fGBRdcEBdccEHqcgB+9A499NCC588//3yMGjWq0vTVoX///tG/f/+Caccff3y0atUq/vCHP1QpnBYXFxfUfvzxx8cOO+wQd911V1x55ZXRpEmTSstkWRZfffVV1KhR44e/iMXU89198A9VVFQUZWVlK219sK5wWO8qdsUVV8QXX3wRN998c0EwXaR169Zx8skn559/8803cfHFF8cmm2wSpaWl0aJFizj33HMrHebSokWL2GeffWLs2LGx/fbbR1lZWbRq1Sr+8pe/5NvceuutcdBBB0VExG677bbYQ36uv/762HLLLaO0tDSaNGkSAwcOjJkzZ1ba1ve/EYyI2HXXXWPXXXfNP190fsWdd94Z559/fmy00Uax3nrrxezZsxfbNzNnzoxhw4bFgAEDomXLlrFgwYIlHs7z9ddfx9tvvx1Tp05d7PyqaNy4cUREpZ3P/Pnz48ILL4zWrVtHaWlpNG3aNM4666xKtYwaNSp23nnnqFu3btSqVSs233zzOPfcc/OvfbvttouIiCOOOCLf11U51KdPnz7x2WefxahRo/LTFixYEPfcc0/88pe/XOwyv/vd72KnnXaKDTbYIGrUqBEdOnSodGhsLpeLuXPnxm233Zav57s/xylTpsRRRx0VTZo0idLS0mjZsmUcd9xxsWDBgkr9c9ppp0WDBg2iZs2accABB8Qnn3yyzNe1NCeffHIccMABscsuu/yg9QDwP3Pnzo3TTz89mjZtGqWlpbH55pvH7373u8iyrKBdLpeLE044Ie64447YfPPNo6ysLDp06BBPPfXUCm97vfXWiwYNGlT6DFFVRUVF+c8Ui05FWvRZ55FHHomOHTtGjRo14k9/+lNEfPsZ4pRTTsm/1tatW8fll18eFRUVBeudOXNm9OvXL+rUqRN169aNvn37LrbGJZ1zevvtt8f2228f6623XtSrVy+6dOkSjz76aL6+N998M5588sn8fnbRa1jSOacjRoyIDh06RI0aNaJ+/fpx6KGHxpQpUwra9OvXL2rVqhVTpkyJ/fffP2rVqhUNGjSIM844IxYuXFjQ9s4774wOHTpE7dq1o7y8PLbaaqv44x//WJUuhx8lI6er2EMPPRStWrWKnXbaqUrt+/fvH7fddlv07t07Tj/99HjhhRfisssui/Hjx8d9991X0Pa9996L3r17x1FHHRV9+/aNW265Jfr16xcdOnSILbfcMrp06RInnXRSXH311XHuuefmD/VZ9O+gQYNi8ODB0aNHjzjuuONiwoQJMXTo0Bg3blw888wzUb169RV6zRdffHGUlJTEGWecEfPnz1/iIS1jx46Nr776Klq3bh29e/eO+++/PyoqKqJTp05x3XXXFYxuTpkyJdq2bRt9+/at8rkdn3/+eUREVFRUxJQpU+Liiy+OsrKyOPjgg/NtKioqYt99942xY8fGgAEDom3btvH666/HVVddFe+8807+fM0333wz9tlnn9h6663joosuitLS0njvvffy59e0bds2LrroorjgggtiwIAB+dBVlZ97ixYtolOnTvG3v/0t9tprr4iIePjhh2PWrFnxi1/8Iq6++upKy/zxj3+MfffdNw455JBYsGBB3HnnnXHQQQfFyJEjo2fPnhHx7SFg/fv3j+233z4GDBgQERGbbLJJRHx7CPL2228fM2fOjAEDBkSbNm1iypQpcc8998SXX35Z8DM78cQTo169enHhhRfGxIkT4w9/+EOccMIJcdddd1Xp5/B9I0aMiGeffTbGjx//oz4XGmBNkmVZ7LvvvjFmzJg46qijYptttolHHnkkzjzzzJgyZUpcddVVBe2ffPLJuOuuu+Kkk06K0tLSuP7662PPPfeMF198Mdq1a1elbc6ePTsWLFgQn376afzlL3+JN954I/+l7Yp4//33IyJigw02yE+bMGFC9OnTJ4455pg4+uijY/PNN48vv/wyunbtGlOmTIljjjkmmjVrFs8++2ycc845MXXq1PjDH/6Q75P99tsvxo4dG8cee2y0bds27rvvvujbt2+V6hk8eHAMGjQodtppp7jooouipKQkXnjhhXj88cdj9913jz/84Q9x4oknRq1ateK8886LiIhGjRotcX233nprHHHEEbHddtvFZZddFh9//HH88Y9/jGeeeSZeffXVqFu3br7twoULY4899ogddtghfve738Vjjz0Wv//972OTTTaJ4447LiK+/dK8T58+0b1797j88ssjImL8+PHxzDPPFAx8wBolY5WZNWtWFhHZfvvtV6X2r732WhYRWf/+/Qumn3HGGVlEZI8//nh+WvPmzbOIyJ566qn8tOnTp2elpaXZ6aefnp82YsSILCKyMWPGFKxz+vTpWUlJSbb77rtnCxcuzE+/9tprs4jIbrnlloJt9e3bt1K9Xbt2zbp27Zp/PmbMmCwislatWmVffvnlMl/vlVdemUVEtsEGG2Tbb799dscdd2TXX3991qhRo6xevXrZRx99lG/7wQcfZBGx2Dq+78ILL8wiotKjbt262b/+9a+Ctn/961+zoqKi7Omnny6YfsMNN2QRkT3zzDNZlmXZVVddlUVE9sk
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1000x500 with 2 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "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Lqzg4GBVrVpVH3zwQVaGHLjlWDEz+PXXX6ts2bKqX79+lvrHxcXp008/VceOHdW/f3/9+OOPGjZsmLZs2aJZs2a59d25c6c6duyoJ598UrGxsZo4caK6du2qmjVrqkqVKmrUqJGeffZZjRw5Ui+99JLrUJP0/yYkJGjw4MGKiYlRz549tW3bNo0dO1ZJSUlauXKlChQokK33PHToUPn7++v555/XhQsXbuiQiqlTp0qSRxjcv3+/KleurNjY2Cwfy3/8+HFJUlpamvbv36+hQ4cqICBAnTp1cvVJS0tTu3bttGLFCnXv3l2VK1fWxo0b9d5772n79u2u8+1+/fVXtWnTRnfffbeGDBkip9OpnTt3us6nqFy5soYMGaJBgwape/furpCTlc+9dOnSqlevnv75z3+qVatWkqR58+bp1KlTevTRRzVy5EiP13zwwQdq166dunTpopSUFE2fPl0PP/yw5s6dq9atW0u6fAhSXFyc6tSpo+7du0uSypUrJ+nyIa116tTRyZMn1b17d1WqVEn79+/XzJkzde7cObfP7JlnnlFoaKheffVV7d69W++//7569+6tGTNmZOlzuFpiYqJWrVqlLVu2/CnPZQWAPwNjjNq1a6clS5boySefVPXq1TV//ny98MIL2r9/v9577z23/suWLdOMGTP07LPPyul0asyYMWrZsqV++uknRUdHZ2mbp0+fVkpKin7//Xd99tln2rRpk+uPntnx22+/SZKKFSvmatu2bZs6d+6sHj166KmnnlLFihV17tw5NW7cWPv371ePHj0UFRWlVatWaeDAgTp48KDef/9915g88MADWrFihZ5++mlVrlxZs2bNUmxsbJbqGTx4sBISElS/fn0NGTJE/v7++vHHH7V48WI1b95c77//vp555hkFBQXp5ZdflnT5KKbMTJ48Wd26dVPt2rU1bNgwHT58WB988IFWrlyp9evXKyQkxNU3NTVVLVq0UN26dfX222/ru+++0zvvvKNy5cqpZ8+eki7/0blz585q1qyZRowYIUnasmWLVq5c6TZxAOQbJp87deqUkWQeeOCBLPXfsGGDkWTi4uLc2p9//nkjySxevNjVVqpUKSPJLF++3NV25MgR43Q6Tf/+/V1tiYmJRpJZsmSJ2zqPHDli/P39TfPmzU1qaqqrfdSoUUaSmThxotu2YmNjPept3Lixady4sev5kiVLjCRTtmxZc+7cuSy95ytdunTJlCxZ0tSpU8dj2a5du4ykDOu42quvvmokeTxCQkLMv//9b7e+n3/+ufHx8THff/+9W/u4ceOMJLNy5UpjjDHvvfeekWSOHj2a6XaTkpKMJDNp0qTrv1ljzKRJk4wkk5SUZEaNGmUKFy7sGreHH37YNGnSxBhzefxbt27t9tqrxzclJcVER0ebpk2burUHBgZmOGZPPPGE8fHxMUlJSR7L0tLS3OqLiYlxtRljzHPPPWd8fX3NyZMns/Q+r647KirKDBw40Bjzv5+ZxMTEG14XAOQn8fHx5sqvRl999ZWRZF577TW3fh07djQOh8Ps3LnT1Za+n1uzZo2rbc+ePSYgIMB06NAhyzW0aNHCtS5/f3/To0cPc/78+eu+LjY21gQGBpqjR4+ao0ePmp07d5o33njDOBwOc/fdd7v6pX93uXpfPHToUBMYGGi
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1000x500 with 2 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "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SUlRbGysSpYsqfLly+uuu+7S7t27vdr069dPwcHB2r17t7p166bg4GCFh4dr2LBhysrK8mo7ffp0xcbGqnTp0goJCdF1112nN954Iz9dDhR7F/Snj88//1w1atRQ8+bN89U+MTFR77//vnr27KmhQ4fqu+++0+jRo7Vp0yZ9+umnXm23bt2qnj176t5771Xfvn01efJk9evXT7Gxsapfv75atWqlhx9+WGPHjtWTTz7pTA3I+W9ycrJGjhyp+Ph4DRw4UKmpqZo4caJWr16tlStXqkSJEhfyUh3PPvus3G63hg0bpoyMjLMOgdesWVNVqlTRK6+8otq1a6tx48bas2ePHn/8cVWvXl133nmn03b37t2qW7eu+vbtm+95yb/99pskKTs7W7t379azzz6rwMBA9erVy2mTnZ2trl27asWKFbr//vtVt25dbdiwQa+99pp+/vln5/qeH3/8UV26dFGDBg00atQoeTwebd261ZkbXrduXY0aNUrPPPOM7r//frVs2VKS8vVzr1atmuLi4vSPf/xDnTp1kiTNmzdPaWlpuvPOOzV27Nhcj3njjTfUtWtX9enTR5mZmZo+fbpuv/12zZ07V507d5Z0espIYmKimjZtqvvvv9/pc+n0NMGmTZvqyJEjuv/++1WnTh3t3r1bs2bN0u+//+71M3vooYcUFhamESNGaMeOHXr99dc1aNAgzZgxI18/BwAorowx6tq1q5YsWaJ7771XjRo10vz58/XYY49p9+7deu2117zaL1u2TDNmzNDDDz8sj8ejCRMmqGPHjvr+++8VExOTr+c8evSoMjMzdejQIX3wwQfauHGj80e7gti2bZskqVy5cs621NRU9e7dWwMGDNB9992n2rVr6/fff1fr1q21e/duDRgwQFWrVtU333yj4cOHa+/evXr99dedPrn11lu1YsUKPfDAA6pbt64+/fRT9e3bN1/1jBw5UsnJyWrevLlGjRolt9ut7777TosXL1b79u31+uuv66GHHlJwcLCeeuopSeeetj116lT1799fN9xwg0aPHq39+/frjTfe0MqVK/XDDz+oTJkyTtusrCx16NBBzZo108svv6yvvvpKr7zyimrWrKmBAwdKOv1H0969e6tdu3Z64YUXJEmbNm3SypUrvf7wDVy1TD6lpaUZSebWW2/NV/v169cbSSYxMdFr+7Bhw4wks3jxYmdbVFSUkWSWL1/ubDtw4IDxeDxm6NChzraUlBQjySxZssTrmAcOHDBut9u0b9/eZGVlOdvHjRtnJJnJkyd7PVffvn1z1du6dWvTunVr5/6SJUuMJFOjRg3z+++/5+s1f/fdd6ZmzZpGknOLjY01e/fu9Wq3fft2IynPOmwjRozwOl7OrUyZMuaf//ynV9sPP/zQ+Pn5ma+//tpr+1tvvWUkmZUrVxpjjHnttdeMJHPw4MGzPu/q1auNJDNlypR8vfYpU6YYSWb16tVm3LhxpnTp0k6/3X777aZNmzbGmNP937lzZ6/H2v2bmZlpYmJiTNu2bb22BwUF5dln99xzj/Hz8zOrV6/OtS87O9urvvj4eGebMcY8+uijxt/f3xw5ciRfrzMvBw8eNJLMiBEjCnwMALjckpKSzJkfA2bPnm0kmeeee86rXc+ePY3L5TJbt251tuWci9asWeNs27lzpwk
|
|||
|
"text/plain": [
|
|||
|
"<Figure size 1000x500 with 2 Axes>"
|
|||
|
]
|
|||
|
},
|
|||
|
"metadata": {},
|
|||
|
"output_type": "display_data"
|
|||
|
},
|
|||
|
{
|
|||
|
"name": "stdout",
|
|||
|
"output_type": "stream",
|
|||
|
"text": [
|
|||
|
"\n",
|
|||
|
"Распознанный номер: \n"
|
|||
|
]
|
|||
|
},
|
|||
|
{
|
|||
|
"data": {
|
|||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAxoAAADxCAYAAABMK8GOAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAAdjElEQVR4nO3de3BU5f3H8c+imKsEk+USKDcBIdxKB5GL3ByhCHIZKihoxwQZQIMyOC0KWAvUnyWItlCq1lBFkJRiiAPU2opUrjUtIFWkoFhLkFJEQgxgAoMNz+8PJsecbBI27JPs7f2acZycnN3znOd59rDffL/POR5jjBEAAAAAWNQg2A0AAAAAEHkINAAAAABYR6ABAAAAwDoCDQAAAADWEWgAAAAAsI5AAwAAAIB1BBoAAAAArCPQAAAAAGAdgQaAkFFSUqJjx47pq6++qvdjnzt3TgUFBSopKan3Y6NuXLx4UV988YX++9//BrspABCVCDQABFVubq5uv/12XX/99UpMTFTr1q31zDPP1PlxjTHKzs5W3759FR8fr0aNGqldu3Zas2ZNnR8bdWfv3r2699575fV6FRMTo9TUVN11113BbhYARKVrg90AAJHjn//8pxYtWqStW7eqsLBQKSkpuu222zRv3jx17drVZ/85c+Zo8eLFGjt2rFasWCGv1yuPx6Obbrqpztt67733at26dUpPT9fs2bOVlJQkj8ejHj161PmxUTc2btyoe+65R507d9bTTz+t9u3bS5KaNm0a5JYBQHTyGGNMsBsBIPy98cYbmjRpkpKTkzVlyhS1a9dOBQUFevnll3X69Gn9/ve/17hx45z9t2/friFDhmjRokWaM2dOvbZ19erVysjI0Jo1a3TvvffW67FRN4qKitSpUyf1799fubm5uu6664LdJACIegQaAAL22WefqUePHmrdurV27NihJk2aOL8rLCzUwIEDdezYMe3fv1833nijJGn06NEqKirSX//613pvb/fu3dWjRw/l5OTU+7FRN5577jktWLBAn3/+uW644YZgNwcAINZoALBgyZIlKi0tVXZ2tivIkCSv16uXXnpJJSUlrrUXf/vb39StWzdNnDhRycnJiouLU+/evbVhwwbX67dt2yaPx6N169Zp3rx5at68uRISEjRmzBgdO3bMte/OnTs1YcIEtW7dWjExMWrVqpUeffRRnT9/3tmnpKREBw4cUKtWrXTnnXeqUaNGSkhI0JAhQ7Rz506fc/v3v/+tCRMmKDk5WfHx8erbt6/++Mc/+rSvpv8WLFggSTp69KgyMzPVqVMnxcXFKSUlRRMmTFBBQYHrmK+++qo8Ho/27t3r2l5YWOh6P0lasGCBPB6PCgsLXfvu3btXHo9Hr776qmv7u+++q4EDByohIUGNGzfW2LFjdejQIZ/zPn78uB544AE1a9ZMMTEx6tq1q1555RWf/apSuY3S5Tni8Xg0ZMgQZ9uuXbs0YMAAeb1excbG6sYbb9Tjjz+uCxcuuF57pTGQLs+nnj176uc//7latWqlmJgYdezYUVlZWbp06ZJP+x5++GHl5OSoU6dOio2NVa9evbRjxw7Xfv6O10svvaQGDRooNzfX2VZQUODT/4cPH1ZycrJPFq24uFizZs1y2t2hQwctXrzY1e7y93v22Wd9+rtbt26ufi2fk9u2bXPtd+edd1Y5NoGMNQDUhDUaAAL2hz/8QW3bttXAgQOr/P2gQYPUtm1b15fD06dPKzs7W4mJiZo5c6aaNGmiNWvW6Ac/+IFycnI0adIk13s8/fTT8ng8evzxx/Xll19q6dKlGjp0qD744APFxcVJurywvLS0VA899JBSUlK0e/duLV++XP/5z3+cL4GnT5+WJC1evFjNmzfX7NmzFRsbqxUrVmjo0KF65513NGjQIEnSyZMn1b9/f5WWlmrmzJlKSUnRqlWrNGbMGK1fv17jxo1TWlqaXnvtNaed2dnZOnTokH75y18628rXfezZs0fvvfeeJk6cqO985zsqKCjQiy++qCFDhujgwYOKj48PdChqtGXLFo0YMUI33nijFixYoPPnz2v58uW69dZbtW/fPrVt29Y57759+zpfyJs0aaI//elPmjJlis6ePatZs2bV6rjFxcVatGiRz/Zz584pLS1Nd999t+Lj45Wfn69nnnlGpaWlWr58udOWK42BdHlcd+3apV27dumBBx5Qr1699Je//EVz585VQUGBfvOb37iOvX37dq1bt04zZ85UTEyMXnjhBd1xxx3avXu3unXrJsn/8Zo+fbo++eQTpaenq23bturdu7fPuRYVFWnUqFFKS0vTypUrne2lpaUaPHiwjh8/runTp6t169Z67733NHfuXJ04cUJLly6tVV9XZ8eOHXrrrbd8ttseawBwMQAQgOLiYiPJjB07tsb9xowZYySZs2fPGmOMkWQkmW3btjn7lJaWmrS0NNO8eXNz8eJFY4wxW7duNZJMy5YtndcaY8zrr79uJJlly5a5Xl/ZokWLjMfjMUePHjXGGHPkyBEjyVx33XXm8OHDzn6nTp0yKSkpplevXs62WbNmGUlm586dzrZz586Zdu3ambZt25qysjKf46Wnp5s2bdpU2QdVtS8/P99IMqtXr3a2rVy50kgye/bsce176tQpI8nMnz/f2TZ//nwjyZw6dcq17549e4wks3LlSmdbz549TdOmTc3p06edbR9++KFp0KCBuf/++51tU6ZMMampqaawsND1nhMnTjRJSUlVnkdFldv42GOPmaZNm5pevXqZwYMH1/jakSNHmm7dujk/+zsGgwcPNpLMggULXO+XkZFhJJmPPvrI1T5JZu/evc62o0ePmtjYWDNu3Dhnm7/jZYwxZWVlZvTo0SY1NdUcO3bMmWcrV640Fy9eNEOGDDHt2rUzX375pet1Tz31lElISHDNRWOMmTNnjrnmmmvM559/boz5dt4uWbLEp01du3Z19Wv5Z2br1q3Otj59+pgRI0b4jE2gYw0ANaF0CkBAzp07J0m6/vrra9yv/Pdnz551tvXu3VuDBw92fo6Li1NmZqa++OIL7du3z/X6+++/33WM8ePHKzU11fVX2vLMhnS5RKqwsFD9+/eXMUb/+Mc/XO83duxYdezY0fnZ6/UqIyND77//vk6ePClJeuutt3TLLbdowIABzn6JiYmaNm2aCgoKdPDgwRrPubKK7fvmm290+vRpdejQQY0bN/Y5X0k6c+aMCgsLnf+Kioqqfe+ioiLXvmfOnHH9/sSJE/rggw+UkZGh5ORkZ3uPHj00bNgwpx+NMcrLy9Po0aNljHG95/Dhw3XmzJkq21qd48ePa/ny5XryySeVmJhYbdtPnDihDRs2KD8/38koSbUbg2uuuUaPPvqo671/9KMfSZJPqVW/fv3Uq1cv5+fWrVtr7Nixevvtt1VWViapduPVoEEDrV27VikpKRo9erTreSwPPfSQdu/erTfffNOntDA3N1cDBw7UDTfc4OrroUOHqqyszKecq7S01LVfYWGh097qvPHGG9qzZ4+ysrJc222PNQBURukUgICUf/kvDziqU1VA0rlzZ5/90tLSJF2uSe/Tp4+zvWJQIF2us+/QoYOrXv7zzz/XT3/6U23atMnnoX/lX7w9Ho9fx27WrJmOHj3qakPl/Y4ePeqU2fjj/PnzWrRokVauXKnjx4/LVLgXR+XAQJKGDh3q93t36tSpxt8fPXq02v3S0tL09ttvq6SkRCUlJSouLlZ2drays7OrfK8vv/zS73bNnz9fLVq00PTp07V+/foq9+nSpYsT3GVkZGjZsmWudvszBh6PRy1atFCjRo1c+3Xq1EkNGjTwWVdReT5J0k033aTS0lKdOnVKzZs3r/V4lZWVqbCwUF988YUmT54sSXrhhRe0Z88eeTyeKj8jn376qfbv3+8TgJSr3Nfz58/X/PnzffZr1qxZla8vKyvTvHnzdN999/ncuvnUqVNWxxoAKiPQABCQpKQkpaamav/+/TXut3//frVs2dL5Iljxr8U2lJWVadiwYSoqKtLjjz+uzp07KyEhQcePH1dGRoazsNb2cWvjkUc
|
|||
|
"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": "iVBORw0KGgoAAAANSUhEUgAAAqYAAAGrCAYAAAASDVXcAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAApIUlEQVR4nO3deZQU9b3w4e+wzQDDoEiQRRQEFFQIubijQNyQgGgWicREQK/idYtG5WqMgpjESzAuwQ3RiHGJshhxC4u7IuZoUBEERYWroiKIoIKAMPX+4Tv90vQMDEpeftHnOWfOYaqrq3+9TNWnq6uLoizLsgAAgG2sxrYeAAAARAhTAAASIUwBAEiCMAUAIAnCFACAJAhTAACSIEwBAEiCMAUAIAnClH9769atiw8//DDefvvtbT0UgP/vPv3001i4cGGsXLlyWw8FvjZhyr+l+fPnx8knnxzNmjWLOnXqxI477hgHHHBA+I/MgG+6LMvipptuiv333z/q1asXZWVl0bp167jjjju29dDga/tKYTpnzpz4+c9/Hi1atIji4uJo3rx5HH/88TFnzpytPT4o8Nxzz8W+++4bjz32WFxwwQUxZcqUmDZtWtx3331RVFS0rYcH/BsYO3ZsFBUVVflz8803b+shVulnP/tZnHrqqdGhQ4e4/fbbY9q0afHII4/Ej370o209NPjaam3pFe69997o379/NGrUKE466aRo3bp1LFy4MG655ZaYMGFC3H333fHDH/7wXzFWiLVr18agQYNit912i6lTp0bDhg239ZCAf2PDhw+P1q1bF0w/4IADtsFoNu8vf/lL3HPPPXHHHXfEz372s209HNjqirIt+OzzzTffjE6dOsXOO+8cTz31VHznO9/JXbZ06dI4+OCD45133olZs2bFrrvu+i8ZMN9uEydOjGOPPTbmzZsXu+2227YeDvBvauzYsTFo0KB4/vnnY++9997Ww6m2jh07RqdOneLOO+/c1kOBf4kt+ih/5MiRsWrVqrjpppvyojQionHjxjF69OhYuXJl/OEPf8hNHzZsWBQVFcW8efOiX79+UVZWFjvssEP88pe/jNWrV+cto6ioKIYNG1Zwm0VFRdGjR4/ctCeeeKLKj1+eeeaZvNvd0GeffRZNmzaNoqKieOKJJ3LTe/ToEXvttVfB/b3iiiuiqKgoFi5cmJs2adKk6N27dzRv3jyKi4ujTZs2cdlll8X69esLrr9w4cIqx7nxPFdccUXB9TdU8bHTCy+8kDd96dKllT5uL774YvTq1SvKysqitLQ0Dj300HjuuecKlrt8+fI4++yzo2XLllFcXBxt27aNESNGRHl5+SbHExHRqlWrGDhwYN608ePHR1FRUbRq1So37bXXXotDDjkkmjZtGsXFxdGyZcs49dRTY9myZXnX/fDDD+Okk06KHXfcMUpKSuK73/1u3HbbbXnzPPfcc9G6deuYOHFitGnTJurUqRM777xzDBkyJD7//POC8fXp0yemTp0anTt3jpKSkthjjz3i3nvvzZtv2bJlcd5550XHjh2jtLQ0ysrKolevXvHyyy/nzffwww9HrVq1Cp6rjR//JUuWxK677hrdu3ePtWvX5qavWbMmhg4dGm3bts09DkOGDIk1a9YULO+MM84oeLz79OmT97hWvHbGjh2bN9/pp58eRUVFBc/N13mu4duuvLw8rr766thzzz2jpKQkdtxxxxg8eHB8/PHHefMdffTR0apVqygpKYkmTZpE375945VXXqnWbYwfPz66dOkSdevWjcaNG8fPf/7zWLRoUe7ylStXxuzZs6Nly5bRu3fvKCsri/r160ePHj3i6aefzs1XcVjThj8bbzsqM2/evPjJT34SjRo1ipKSkth7773j/vvvz5unYlu04XZxzpw5sf3220efPn1i3bp1mz1MYuP11mOPPRYHH3xw1K9fP7bbbrs4+uijY+7cuXm3W7FNr/hp0KBB7LvvvnHfffdt9n5VXHfp0qV501944YWCscyaNSsGDhwYu+66a5SUlETTpk3jxBNPjI8++mizt7MlbVKdJrr11lvjkEMOiSZNmkRxcXHsscceccMNNxTcbsW2bmNnnHFGQQdtaWdNmDChYLmlpaUF25eIiL///e+557FBgwbRu3fvr3SI5xZ9lP/AAw9Eq1at4uCDD6708m7dukWrVq3ioYceKrisX79+0apVq7j88svjueeeiz/96U/x8ccfx1/+8pcqb2/58uVx+eWXV3n5WWedFfvss0/etN13373K+f/4xz/G4sWLq7y8OsaOHRulpaXxq1/9KkpLS+Oxxx6LSy65JD755JMYOXJkpdc55ZRTco/ZvffeG3/729++1hg2Z86cOXHwwQdHWVlZDBkyJGrXrh2jR4+OHj16xJNPPhn77bdfRESsWrUqunfvHosWLYrBgwfHzjvvHM8++2xceOGF8f7778fVV1+9Rbe7bt26uOiiiwqmr1y5Mnbaaac46qijoqysLGbPnh3XXXddLFq0KB544IGIiPj888+jR48e8cYbb8QZZ5wRrVu3jvHjx8fAgQNj+fLl8ctf/jIiIj766KN466234te//nX86Ec/inPPPTdeeOGFGDlyZMyePTseeuihvD/E+fPnx09/+tM49dRTY8CAAXHrrbfGscceG5MnT47DDz88IiLeeuutuO++++LYY4+N1q1bx+LFi2P06NHRvXv3ePXVV6N58+YREfGDH/wgrrzyyjjnnHNit912i759+xbc1zVr1sQxxxwTNWvWjL/97W9Rp06diPhyo9a3b9945pln4pRTTokOHTrEK6+8EldddVW8/vrr1Vq5Vscbb7wRY8aMKZi+tZ9r+KZYsWJF7g1+w4YNo1atyjeLgwcPzu1lPeuss2LBggVx7bXXxosvvhjTp0+P2rVr5+Y95ZRTomnTpvHee+/FtddeG4cddlgsWLAg6tWrV+U4Kpa9zz77xOWXXx6LFy+Oa665JqZPnx4vvvhibLfddrk4GjFiRDRt2jTOP//8KCkpiTFjxsRhhx0W06ZNi27dusXee+8dt99+e0REPP3003HTTTdt9nGYM2dOdO3aNVq0aBEXXHBB1K9fP8aNGxfHHHNMTJw4scpD9N5555048sgjo3379jFu3LioVatWdOvWLXf7ERG/+93vIiLytg8HHnhgREQ88sgj0atXr9h1111j2LBh8fnnn8eoUaOia9euMXPmzLw34xGRW+7SpUvj+uuvj2OPPTZmz569yW3/lpg2bVq89dZbMWjQoGjatGnMmTMnbrrpppgzZ04899xz1foeQ3XapDpNdMMNN8See+4Zffv2jVq1asUDDzwQp512WpSXl8fpp5++Ve7v5jqrum6//fYYMGBA9OzZM0aMGBGrVq2KG264IQ466KB48cUXC57HTcqqafny5VlEZEcfffQm5+vbt28WEdknn3ySZVmWDR06NIuIrG/fvnnznXbaaVlEZC+//HJuWkRkQ4cOzf0+ZMiQrEmTJlmXLl2y7t2756Y//vjjWURk48ePr3IcFbdb4cMPP8waNGiQ9erVK4uI7PHHH89d1r1792zPPfcsWMbIkSOziMgWLFiQm7Zq1aqC+QYPHpzVq1cvW716dd70+fPnZxGR3XbbbVWOa8GCBVlEZCNHjqzyvmRZlt16661ZRGTPP/983vQlS5YUPG7HHHNMVqdOnezNN9/MTXvvvfeyBg0aZN26dctNu+yyy7L69etnr7/+et4yL7jggqxmzZrZ22+/vckx7bLLLtmAAQNyv19//fVZcXFx9v3vfz/bZZddNnnd0047LSstLc39fvXVV2cRkd1xxx25aWvXrs0OOOCArLS0NPd6GjBgQBYR2cCBA/OWV/G4PvDAA3nji4hs4sSJuWkrVqzImjVrln3ve9/LTVu9enW2fv36vOUtWLAgKy4uzoYPH17l2F966aUsy/Jft8cff3zWqFGj7LXXXsu7zu23357VqFEje/rpp/Om33jjjVlEZNOnT89Ni4js9NNPL7jd3r175z2uFa+dW2+9NTetX79+2V577ZW1bNky77n5us81fNNUrFM3/Kl
|
|||
|
"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": "iVBORw0KGgoAAAANSUhEUgAAA90AAAC5CAYAAAA1Q1xXAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAABx0ElEQVR4nO29eZRdZZ2v/z1zzVVJpTInJIQpQBABmREREAIIYgsX0CYM126bRqRvNw2XvpfJ1ojoUhqBC8pMtLmAw+XSpIErCEpoUBGQOZCEDGROpYZTwxn27w9+Ker77GS/VZEjaH+etVzLl33O3u9+p31O5TzvJxVFUWRCCCGEEEIIIYR430l/0BUQQgghhBBCCCH+XNGXbiGEEEIIIYQQokboS7cQQgghhBBCCFEj9KVbCCGEEEIIIYSoEfrSLYQQQgghhBBC1Ah96RZCCCGEEEIIIWqEvnQLIYQQQgghhBA1Ql+6hRBCCCGEEEKIGqEv3UIIIYQQoib09vba8uXLbdOmTX/0a3d3d9vSpUutt7f3j35tIYQYjr50CyGCfP3rX7dqtWpmZtVq1ebPn/8B10gIIcSHlXvvvdeOPPJIa25utqamJps+fbp985vfrPl1oyiym2++2Q488EBraGiwlpYWmzlzpt199901v/afGj09Pfbd7353qNzZ2WnXX3/9B1chIf7M0Zdu8aHj9ttvt1Qq5f43fvx4O+KII+yhhx76oKv3n5I77rjDvvWtb9mKFSvs29/+tt1xxx0fdJWEEEL8EXjppZfsC1/4gk2ZMsUKhYJNnjzZPv/5z9tLL7201ddfcsklduqpp1pzc7N9//vft0ceecQeffRRO++882pe1zPOOMO+9KUv2ezZs+2uu+4auvZnP/vZml/7T436+nr7H//jf9iCBQts+fLldsUVV9gDDzzwQVdLiD9bsh90BYTYFldddZXNnDnToiiyNWvW2O23327HHXecPfDAA3bCCSd80NX7T8VVV11lZ555pl188cVWKBT0rwZCCPGfgB//+Md2+umn29ixY+3cc8+1mTNn2tKlS+2WW26x++67z/71X//VTj755KHX/+IXv7Crr77a5s+fb5dccskfta533nmn3XPPPXb33XfbGWec8Ue99p8imUzGrrzySjvzzDOtWq1aS0uLPfjggx90tYT4syUVRVH0QVdCiOHcfvvtdvbZZ9uzzz5r++2339B/37Rpk02YMMFOOeUUW7BgwQdYw/+crF271hYvXmw777yzdXR0fNDVEUIIUUPefPNN22uvvWz69On2xBNPuHV//fr1dthhh9ny5cvthRdesB133NHMzD796U/bxo0b7Ve/+tUfvb5z5syxvfbaS58PRsmKFSts+fLlNnv2bGtra/ugqyPEny36ebn4k6Gtrc3q6+stm33vBxpLly61VCpl3/rWt7b5viuuuMJSqZT7b6lUys4//3xbsGCB7brrrlZXV2f77ruvPfHEE7H3r1y50s455xybMGGCFQoF22OPPezWW291r/nlL39phx56qI0bN87q6upsxx13tIsvvtj6+/uHXrNx40b7h3/4B5szZ441NTVZS0uLzZ07155//nl3rscff9xSqZTdd999sbo0NTXZWWedNVTe8lP8X//61+5169evt1QqZVdccUWsHdavX7/NtpoxY8ZWz7906VIbP368HXzwwdbe3m577bWXpVIpu/3227d5rtHWz8zsueees7lz51pLS4s1NTXZkUceaU8//fRWz5nP523dunXu2KJFi4aUBF7zP/7jP+zYY4+11tZWa2hosMMPPzz2wXBLG7366qt26qmnWktLi7W3t9tXvvIV15dCCPHnzjXXXGPFYtFuvvnm2B9ax40bZzfddJP19vY6V/vpp5+2Pffc00477TQbO3as1dfX28c+9jH76U9/6t6/5Tl3zz332KWXXmoTJ060xsZGO/HEE2358uXutU8++aSdcsopNn36dCsUCjZt2jT7u7/7O+vr6xt6TW9vr/3+97+3adOm2fHHH28tLS3W2Nhon/jEJ+zJJ5+M3dtbb71lp5xyio0dO9YaGhrswAMPdP/Ku6V+Sf/b8vxatmyZnXfeebbrrrtafX29tbe32ymnnGJLly5113w/nte//vWvY8/es846y5qammL3OJxtnd/MbOrUqXbQQQdZNpu1iRMnWiqVsscffzzxfKOpn5nZz3/+czvssMOssbHR2tra7KSTTrJXXnllq+ccP368lUold+xHP/rRULvzmg899NDQuZubm+3444+PqQ9b2uitt96yY445xhobG23y5Ml21VVXmf7tUfyx0M/LxYeWzZs32/r16y2KIlu7dq1dd9111tPTY1/4whfel/P/4he/sHvuuccuuOACKxQKdsMNN9ixxx5rzzzzjO25555mZrZmzRo78MADh76kd3R02EMPPWTnnnuudXV12YUXXmhm7+6QOnv2bDv11FOtoaHBFi1aZN/85jetWCzaddddZ2bvPuR/+tOf2imnnGIzZ860NWvW2E033WSHH364vfzyyzZ58uT35b5qzV133WUvvvji+37el156yQ477DBraWmxf/zHf7RcLmc33XSTfeITn7Bf/OIXdsABB7jXZzIZu/vuu+3v/u7vhv7bbbfdZnV1dbEvyD//+c9t7ty5tu+++9rll19u6XTabrvtNvvkJz9pTz75pO2///7u9aeeeqrNmDHD5s+fb08//bT9y7/8i23atMnuvPPO9/2+hRDiw8gDDzxgM2bMsMMOO2yrxz/+8Y/bjBkz3JfVDRs22M0332xNTU12wQUXWEdHh91999322c9+1hYsWGCnn366O8fXvvY1S6VSdvHFF9vatWvtu9/9rh111FH2u9/9zurr683s3U3ZisWi/c3f/I21t7fbM888Y9ddd52tWLHC7r333qHrmpldffXVNnHiRLvooousrq7Ovv/979tRRx1ljzzyiH384x83s3ef6wcffLAVi0W74IILrL293e644w478cQT7b777rOTTz55yAnfws0332yvvPKKfec73xn6b3vttZeZmT377LP21FNP2WmnnWZTp061pUuX2o033mif+MQn7OWXX7aGhoY/tCv+KHz729+2NWvWvO/nffTRR23u3Lm244472hVXXGF9fX123XXX2SGHHGK//e1vbcaMGe713d3d9n//7/912sK2nu133XWXzZs3z4455hi7+uqrrVgs2o033miHHnqoPffcc+7clUrFjj32WDvwwAPtm9/8pi1cuNAuv/xyK5fLdtVVV73v9y1EjEiIDxm33XZbZGax/xUKhej22293r12yZElkZtE111yzzfNdfvnlEYf6lnP++te/Hvpvy5Yti+rq6qKTTz556L+de+650aRJk6L169e795922mlRa2trVCwWt3nd4447Ltpzzz2Hyv39/VGlUonVv1AoRFddddXQf3vsscciM4vuvffe2DkbGxujefPmDZW3tNWzzz7rXrdu3brIzKLLL7881g7r1q3bZp132GGHrZ5/yZIlQ/cwffr0aO7cuZGZRbfddts2zzXa+n3mM5+J8vl89Oabbw79t1WrVkXNzc3Rxz/+8dg5Tz/99GjOnDlD/723tzdqaWmJzjjjDHfNarUa7bzzztExxxwTVavVodcXi8Vo5syZ0dFHHx1roxNPPNHV97zzzovMLHr++ecT71cIIf4c6OzsjMwsOumkkxJfd+KJJ0ZmFnV1dUVR9N6z9fHHHx96TbFYjGbPnh1NnDgxGhwcjKLovefclClTht4bRVH0v//3/47MLLr22mvd+8n8+fOjVCoVLVu2LIqi9z4L5PP56PXXXx963bp166L29vZo3333HfpvF154YWRm0ZNPPjn037q7u6OZM2dGM2bMiD2noyiK5s2bF+2www5bbYOt1W/RokWRmUV33nnn0H97P57Xzz77bOzZO2/evKixsXGrddvCts6/hbVr10bNzc1Dz/bHHnss8Xyjqd/ee+8djR8/PtqwYcPQf3v++eejdDodnXnmmbFznn766dEJJ5ww9N+XLVsWpdPp6PTTT3fX7O7ujtra2qI
|
|||
|
"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": "iVBORw0KGgoAAAANSUhEUgAAA5IAAAHvCAYAAAAmUuAkAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAACZtElEQVR4nOzdd1yVdf8/8Nd1Fod54DAFVFBURM2NZg6cae5t5irbw8ysLLvTsrupd2lLsxRzW2quzI1bEMWtKCgCykb2OuP6/cGP6yuCylEOh/F6Ph48imu+kfPhc72vzxJEURRBREREREREVEEySwdARERERERENQsTSSIiIiIiIjIJE0kiIiIiIiIyCRNJIiIiIiIiMgkTSSIiIiIiIjIJE0kiIiIiIiIyCRNJIiIiIiIiMgkTSSIiIiIiIjKJwtIBEBHVBQUFBTh48CBOnz6N27dvIzc3F9nZ2WjcuDG++uorS4dHREREZBJBFEXR0kEQET0uHx8f3Lx5E8uXL8eUKVPK7A8JCUHPnj0BAPf7szdlyhSsWLECkydPRnBwcKXEJYoi/ve//+G///0v7ty5U2Z/o0aNcO3aNchk7CBCRERENQefXIiIzOiVV17BzJkzIZfLsXDhQly/fh0GgwGiKEIURURHRzOJJCIiohqHXVuJiMxk3bp1WLp0KVq2bIndu3ejXr16lg6JiIiIqFLwNTgRkZl89dVXUKlU2Lx5M5NIIiIiqlWYSBIRVcCmTZvw4osvomXLlnBycoJarYavry9eeOEFREZGljn+9u3bOHv2LLp06YIzZ86gT58+0Gq1UKlU8Pb2xoQJE3DmzJkH3nPKlCkQBOG+X+WNBQWKW0J79+4NrVYLKysrNGzYEC+88AKuXr1a7vE+Pj4QBAExMTHl7g8ODi73fvfbfj9BQUEQBAEhISEVOv5u+/fvx+jRo+Ht7Q0rKyu4urqiY8eOmDNnDtLS0ioUU2FhIZo2bSr9+92rZLtMJkNUVFS5caxevVo6LigoqNS+kJCQMr8jhUIBLy8v9O7dG+vXr7/vz3flyhU8//zzaNiwIaysrKDVatG7d29s2LDhgf8uJT/v/b58fHzKPS8sLAxjxoyBp6cnVCoV3NzcMHjwYOzZs6fc48v7LNrY2KBJkyaYOnUqLl269MA4iYio9mHXViKiChgzZgysrKwQEBCAXr16Qa/X48KFC1i+fDk2bNiA3bt3o0uXLtLx8fHxAIDQ0FCEhIRALpeja9eu8PT0xPnz57F69Wps2LABy5cvx3PPPffAez/11FPw8/OTvo+KisLRo0fLHCeKIqZMmYI//vgDCoUC3bt3h5ubG06fPo3ly5dj/fr12LhxI/r3719J/ypVY9q0afjhhx8AAG3atEG3bt2QmZmJyMhIfPbZZ+jZs2eZpK483377La5du/bQ40RRxI8//ojvv/++zL6FCxc+9Hx3d3fp31in0yEqKgr79+/H/v37ceHCBcybN6/U8Tt27MCoUaNQUFCAZs2aYcSIEUhOTsbBgwexf/9+7Nq1C7///vsD79m4cWN07dpV+j4nJwcbN24s99ilS5fi1VdfhdFoRNu2bREUFISbN29i+/bt2L59O+bOnYs5c+aUe+7dn8Xs7GyEhoZi2bJlWLt2LcLCwtCyZcuH/vsQEVEtIRIR1QINGzYUAYjLly8vd/+BAwdEAOKD/uxNnjxZBCBOnjy5zL5169aJOTk5pbYZjUbxp59+EgGILVq0EI1GY7n302q14smTJ0ud++OPP4oARCsrKzEyMrLceCZMmCACEIODg0ttX758eblx/vLLLyIA0cXFRYyIiCgV55w5c0QAoqOjo5icnFzqvJJ/uxs3bpQbx/3ud7/t99OjRw8RgHjgwIEKHS+Korho0SIRgOjs7Czu37+/zP7Q0FAxNjb2oTHduHFDtLa2Fhs0aHDfz0HJ9l69eokODg5idnZ2qf3Hjh0TAYi9e/cWAYg9evQotb/kd37vdlEs/vyU/Bx3S0xMFDUajQhA/Pzzz0t9hk6ePCk6OTmJAMRff/213H+f3377TQQgTpkypczPC0Bs2LBhqe3nzp0TFQqFKAiC+Mcff5Ta988//4gqlUoEIO7evbvUvpKycW/50ul04qBBg0QA4rvvvltujEREVDuxaysRUQWMHTsWtra2pbYJgoDXX38dTz75JC5evIjLly+Xe+4nn3yCDh06lNr2xhtvoF+/figsLLxvK5dOpwMAKJXKCsU4f/586X5t2rQpFeecOXPwxBNPICMjA0uXLq3Q9SxNr9dLrXe//vqrtHzL3QIDA1G/fv2HXuvtt99Gfn4+vvvuuwodm5WVVWYJmIULF8LKygqvvPJKxX6Au/Tp0wcAYDQaS21funQpMjMz0b59e8yePbtUl9sOHTpg9uzZAIpbU8tj6mdk4cKF0Ov1GD58OCZOnFhq34ABA/Dyyy8/8H73UigUUmvwvT8bERHVbkwkiYgqKCoqCj/++COmT5+OqVOnYsqUKZgyZQqSkpIAoNyxkgDw/PPPl7v9pZdeAgAcOHCg3P35+fkAACsrq4fGFh8fj+joaADA5MmTy+wXBEGK4373q25OnTqFlJQUuLi4YPjw4Y98nR07dmDr1q14+umnMWLEiIceP2jQIDRq1Ag//vijtOZofHw8Nm7ciHHjxsHV1bXC99br9bhy5YqUoI0dO7bU/pLxouX9zgBg6tSpAIBr167h9u3bZfab8hm5+373G9dacr/Dhw/DYDA88Fo5OTnYuXMn/ve//0Eul2PUqFEVioGIiGoHjpEkInoIg8GAN998E0uWLJESi/JkZWVJ/1/SsuTs7AwHB4dyj2/cuDGA/xtPea+SSWScnJweGuOtW7cqfL+SY+/l6+v70PuUZ8WKFVixYgWA4p/b3t4e/v7+GD16NN58802o1epHuu7NmzcBAM2aNSt3cpyKKCgowLRp02BlZSWNs3wYmUyGN998EzNmzMCuXbvQv39//Pzzz9Dr9Zg2bVqp33N5Dh48WCZeKysrfPrpp1ILY4mS38X9/u0dHR2h1WqRnp6O+Ph4eHp6ltpvymekIvcr+YwUFBQgLS0Nbm5upfY///zzZV6MNGnSBPv37y81RpiIiGo/JpJERA+xcOFCLF68GB4eHvjf//6HLl26wN3dXUqQxo8fj7Vr15ZKMq2trR/7viUP/fcmD+YycuRI2NnZldl+v8l9Stw90YvBYEBcXByOHDmCsLAw/Pvvv9izZ88jJ4KP66uvvsL169fx8ccfo0mTJhU+74UXXsAnn3yCH374AUFBQfj111/x1FNPoV27dg+ddfbuyXaMRiOSkpJw6NAhzJs3D7a2tnj33Xcf50cqpao/I3dPtlNQUICrV68iIiICkydPxt9//43WrVtXSRxERGR5TCSJiB6iZAmGJUuWYMiQIWX2lzcTqLe3N4DiFqPMzExoNJoyx5R0RS059m65ubmIjY2FlZWV1Er0IF5eXtL9srKyym2VvH79eqlj7zV//vxyl4sIDg5+YCLZtWvXMuMJz5w5gy5dumDfvn04fvz4I7VWNWjQAABw9epViKJocjJ6/fp1fP311/Dx8cFHH31k0rkajQaTJ0/Gzz//jLlz5yItLQ3Tpk2r0Ln+/v5l/j1u3bqFjh074r333kP37t3RsWNHAMW/iytXrki/m3tlZmYiPT1dOvZeJctutGjRokKxeXl5ITo6GtevXy93htWSONRqNbRabZn9L774YpluscuWLcPUqVPx3HPP4cKFCxWKg4iIaj6OkSQieoiSB/mGDRuW2Xfx4sVy14P09PSUEsDly5eXe93ffvsNANCrV68y+/bu3Quj0YhOnTpVaCIVb29v6X73JjFA8ZIWJdvLm7SmsrVp00ZKVEq6qJqqQ4cOcHFxQUpKCv7++2+Tz582bRoKCgrw/fffP1IL8VtvvQUA+Prrr+Ht7V2h8ZX34+Xlha5du0IURRw6dEjaXjJRTUnX4HstW7YMQHH30XsTybS0NJw6dQrW1tZo165dheIouV95n5G779etWzcoFBV71zxu3DgAxWXh7jU9iYiodmMiSUT0EM2bNwcA/PT
|
|||
|
"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
|
|||
|
}
|