510 lines
6.6 MiB
Plaintext
510 lines
6.6 MiB
Plaintext
|
{
|
||
|
"cells": [
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 1,
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "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
|
||
|
"text/plain": [
|
||
|
"<Figure size 1500x1000 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
},
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABJ4AAAJvCAYAAAA6DRtsAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOz9ebgsWVXmj3/2EBGZec655966NTJVYTEJ3Uo7AIqIDFJSCILYCiKTEw8ogg9qoygUSAuICD6g6EM/AiI2CCqijKLYtlOjjzZtizRTMdd8p3NOZkbE3nv9/lh7R+S5VWhJU/Dz2/Hq5dbNkyczhr137PWud73LiIgwYcKECRMmTJgwYcKECRMmTJgwYcIXGPZLfQATJkyYMGHChAkTJkyYMGHChAkT/r+JiXiaMGHChAkTJkyYMGHChAkTJkyYcItgIp4mTJgwYcKECRMmTJgwYcKECRMm3CKYiKcJEyZMmDBhwoQJEyZMmDBhwoQJtwgm4mnChAkTJkyYMGHChAkTJkyYMGHCLYKJeJowYcKECRMmTJgwYcKECRMmTJhwi2AiniZMmDBhwoQJEyZMmDBhwoQJEybcIpiIpwkTJkyYMGHChAkTJkyYMGHChAm3CCbiacKECRMmTJgwYcKECRMmTJgwYcItgol4mjBhwoQJEyb8m8All1zCE57whC/1Yfz/NYwxXHHFFV/qw/iC4eMf/zjGGF7zmtd8qQ9lwoQJEyZMmPB5YiKeJkyYMGHChC8SXvOa12CMGf7MZjPudKc78cM//MNcc801X+rD+/8MjDH88A//8Jf6MP6fxT/8wz9gjOF973sfN9xwAy9+8Yv5xm/8Rs477zyOHj3Kve51L974xjd+qQ9zwoQJEyZMmPBFgv9SH8CECRMmTJjw/xqe97zncfvb3571es2f//mf88pXvpK3v/3t/O///b9ZLBZf6sOb8G8Yq9UK77+027u3ve1tnH/++Xzt134tb3vb23jWs57F5Zdfzk//9E/jved3fud3eNSjHsUHPvABnvvc5/6zn3XxxRezWq2oquqLdPQTJkyYMGHChC80JuJpwoQJEyZM+CLjwQ9+MF/zNV8DwPd///dz/PhxfvEXf5Hf//3f59GPfvRN/s7BwQFbW1tfzMOc8G8Qs9nsS30IvP3tb+fBD34wxhjudre78eEPf5iLL754+PlTnvIUHvjAB/KiF72In/iJn/hnx3VRBk6YMGHChAkT/u1iKrWbMGHChAkTvsS4//3vD8CVV14JwBOe8AS2t7f56Ec/yuWXX87Ozg6PecxjACWgnvGMZ3Db296Wpmm4853vzC/8wi8gIjf63N/8zd/kHve4B4vFgmPHjvGN3/iNvPvd7z70nne84x3c5z73YWtri52dHR7ykIfwj//4j4fec/XVV/PEJz6R29zmNjRNw0UXXcS3fdu38fGPf3x4z9/+7d9y2WWXce655zKfz7n97W/P937v9x76nJQSL3vZy7jb3e7GbDbjggsu4ElPehInT5489D4R4fnPfz63uc1tWCwW3O9+97vRMf1r8Kd/+qcYY/jt3/5tnvvc53LrW9+anZ0dvuM7voPTp0/Tti1Pf/rTOf/889ne3uaJT3wibdse+oxXv/rV3P/+9+f888+naRruete78spXvvJG35VS4oorruBWt7rVcOwf+MAHbtKf6tSpUzz96U8f7uUd7nAHXvSiF5FSOvS+q666ig9+8IP0ff8vnuvZHk9XXHEFxhg+9KEP8T3f8z3s7u5y3nnn8TM/8zOICJ/61Kf4tm/7No4cOcKFF17IS17ykht95ic+8Qke9rCHsbW1xfnnn8+P/uiP8q53vQtjDH/6p396o3P6y7/8Sx7ykIcAcPvb3/4Q6VSO8eEPfzht2/Kxj33snz2fm/J4KvPjYx/7GJdddhlbW1vc6la34nnPe96N5sENN9zAYx/7WI4cOcLRo0d5/OMfz/vf//7JN2rChAkTJkz4ImJSPE2YMGHChAlfYnz0ox8F4Pjx48NrIQQuu+wyvuEbvoFf+IVfYLFYICI87GEP473vfS/f933fx93vfnfe9a538eM//uN85jOf4aUvfenw+8997nO54oor+Pqv/3qe97znUdc1/+N//A/+5E/+hAc96EEAvO51r+Pxj388l112GS960YtYLpe88pWv5Bu+4Rv4+7//ey655BIAHvnIR/KP//iPPPWpT+WSSy7h2muv5Y/+6I/45Cc/Ofz7QQ96EOeddx7PfOYzOXr0KB//+Mf53d/93UPn+aQnPYnXvOY1PPGJT+RHfuRHuPLKK3nFK17B3//93/MXf/EXQznVs5/9bJ7//Odz+eWXc/nll/N3f/d3POhBD6Lruv+r6/yCF7yA+XzOM5/5TD7ykY/w8pe/nKqqsNZy8uRJrrjiCv76r/+a17zmNdz+9rfn2c9+9vC7r3zlK7nb3e7Gwx72MLz3/MEf/AFPecpTSCnxQz/0Q8P7fvInf5Kf//mf56EPfSiXXXYZ73//+7nssstYr9eHjmW5XHLf+96Xz3zmMzzpSU/idre7HX/5l3/JT/7kT3LVVVfxspe97NBnvva1r+XKK68c7sm/Ft/1Xd/Fl3/5l/PCF76Qt73tbTz/+c/nnHPO4dd+7de4//3vz4te9CJe//rX82M/9mN87dd+Ld/4jd8IKNF5//vfn6uuuoqnPe1pXHjhhfzWb/0W733ve2/yewohVcbY58LVV18NwLnnnvt5nU+MkW/5lm/hXve6Fz//8z/PO9/5Tp7znOcQQuB5z3seoCTgQx/6UN73vvfx5Cc/mbvc5S78/u//Po9//OM/r++cMGHChAkTJnyekAkTJkyYMGHCFwWvfvWrBZD3vOc9ct1118mnPvUpecMb3iDHjx+X+Xwun/70p0VE5PGPf7wA8sxnPvPQ77/lLW8RQJ7//Ocfev07vuM7xBgjH/nIR0RE5MMf/rBYa+URj3iExBgPvTelJCIie3t7cvToUfmBH/iBQz+/+uqrZXd3d3j95MmTAsiLX/ziz3lev/d7vyeA/M3f/M3nfM9//+//XQB5/etff+j1d77znYdev/baa6Wua3nIQx4yHKuIyE/91E8JII9//OM/53cUAPJDP/RDw7/f+973CiD/7t/9O+m6bnj90Y9+tBhj5MEPfvCh3/+6r/s6ufjiiw+9tlwub/Q9l112mXzZl33Z8O+rr75avPfy8Ic//ND7rrjiihsd+8/+7M/K1taWfOhDHzr03mc+85ninJNPfvKTw2tlPFx55ZU369yf85znDP9+znOeI4D84A/+4PBaCEFuc5vbiDFGXvjCFw6vnzx5Uubz+aHjfMlLXiKAvOUtbxleW61Wcpe73EUAee9733vo+x/72MfKfe9733/2GG+44QY5//zz5T73uc+/eD5XXnmlAPLqV796eK1cj6c+9anDayklechDHiJ1Xct1110nIiK/8zu/I4C87GUvG94XY5T73//+N/rMCRMmTJgwYcIth6nUbsKECRMmTPgi44EPfCDnnXcet73tbXnUox7F9vY2v/d7v8etb33rQ+978pOffOjfb3/723HO8SM/8iOHXn/GM56BiPCOd7wDgLe85S2klHj2s5+NtYcf9cYYAP7oj/6IU6dO8ehHP5rrr79++OOc4573vOegaJnP59R1zZ/+6Z/eqCSu4OjRowD84R/+4ecsB3vTm97E7u4u3/zN33zo+776q7+a7e3t4fve85730HUdT33qU4djBXj605/+uS7nzcbjHve4QybV97znPRGRG5UE3vOe9+RTn/oUIYThtfl8Pvz36dOnuf7667nvfe/Lxz72MU6fPg3AH//xHxNC4ClPecqhz3vqU596o2N505vexH3ucx+OHTt26Ho88IEPJMbIn/3Znw3vfc1rXoOIfN5qJ1AvsQLnHF/zNV+DiPB93/d9w+tHjx7lzne+86Hyt3e+853c+ta35mEPe9jw2mw24wd+4Adu9B0pJd75zncOZXY3hZQSj3nMYzh16hQvf/nLP+/zAQ51LiydDLuu4z3vec9w7FVVHTpWa+0hhdqECRMmTJgw4ZbHVGo3YcK
|
||
|
"text/plain": [
|
||
|
"<Figure size 1500x1000 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
},
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABJ4AAAJvCAYAAAA6DRtsAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOz9ebBsWVnmj3/WsIfMM9x7q6iBKhCwkElbaQdAERFQSkAmIVRUBJwIVAQCMFAUCqQFVAQDbDQwBBqxUWgFlFEUWnFoNKRthy8/pmKQrunWHc7Jk7n3XtPvj3etnXluFVBgFVWX3o9xpG6ePJl7XHu9z3qe51UppcSECRMmTJgwYcKECRMmTJgwYcKECTcy9M29ARMmTJgwYcKECRMmTJgwYcKECRO+PDERTxMmTJgwYcKECRMmTJgwYcKECRNuEkzE04QJEyZMmDBhwoQJEyZMmDBhwoSbBBPxNGHChAkTJkyYMGHChAkTJkyYMOEmwUQ8TZgwYcKECRMmTJgwYcKECRMmTLhJMBFPEyZMmDBhwoQJEyZMmDBhwoQJE24STMTThAkTJkyYMGHChAkTJkyYMGHChJsEE/E0YcKECRMmTJgwYcKECRMmTJgw4SbBRDxNmDBhwoQJEyZMmDBhwoQJEyZMuEkwEU8TJkyYMGHChLMCt7/97Xn84x9/c2/GLRpKKS677LKbezNuNHziE59AKcVrXvOam3tTJkyYMGHChAlfJCbiacKECRMmTPgS4TWveQ1KqfGnbVvudKc78dM//dNcddVVN/fmfdlAKcVP//RP39yb8f8s/vmf/xmlFB/4wAcAeNrTnsbXf/3Xc8455zCfz7nrXe/KZZddxmKxuJm3dMKECRMmTJjwpYC9uTdgwoQJEyZM+H8Nz3/+87nDHe5A13W8//3v55WvfCVvf/vb+Zd/+Rfm8/nNvXkTzmKsViusvXmnd29729s4//zz+aZv+iYA/v7v/5773Oc+POEJT6BtWz74wQ/yohe9iPe85z385V/+JVp/9nXQ293udqxWK6qq+lJt/oQJEyZMmDDhRsZEPE2YMGHChAlfYjzoQQ/iG7/xGwH4sR/7Mc4991x+/dd/nbe85S085jGPud6/OTg4YGtr60u5mRPOQrRte3NvAm9/+9t50IMehFIKgPe///3Xec8ll1zCM57xDD7wgQ9wr3vd67N+VlEGTpgwYcKECRPOXkxWuwkTJkyYMOFmxv3vf38ALr/8cgAe//jHs729zcc+9jEe/OAHs7Ozww/+4A8CQkA9/elP57a3vS1N03DnO9+ZX/u1XyOldJ3P/b3f+z3ucY97MJ/POXbsGN/2bd/Gu9/97kPvecc73sF97nMftra22NnZ4SEPeQj/+q//eug9V155JU94whO4zW1uQ9M03PrWt+bhD384n/jEJ8b3/MM//AOXXnopt7rVrZjNZtzhDnfgR37kRw59ToyRl73sZXz1V381bdtywQUX8MQnPpGTJ08eel9KiRe84AXc5ja3YT6fc7/73e862/SF4H3vex9KKf7wD/+Q5z3veVx88cXs7Ozw6Ec/mtOnT9P3PU996lM5//zz2d7e5glPeAJ93x/6jFe/+tXc//735/zzz6dpGu52t7vxyle+8jrfFWPksssu46KLLhq3/d/+7d+uN5/q1KlTPPWpTx3P5R3veEde/OIXE2M89L4rrriCD33oQzjnPu++npnxdNlll6GU4sMf/jA/9EM/xJEjRzjvvPP4xV/8RVJKfPrTn+bhD384u7u7XHjhhbzkJS+5zmd+8pOf5GEPexhbW1ucf/75PO1pT+Nd73oXSine9773XWef/uZv/oaHPOQhn3M7b3/724/v/1y4voyncn98/OMf59JLL2Vra4uLLrqI5z//+de5D6699loe+9jHsru7y9GjR3nc4x7HP/3TP025URMmTJgwYcKXEJPiacKECRMmTLiZ8bGPfQyAc889d3zNe8+ll17Kt37rt/Jrv/ZrzOdzUko87GEP473vfS8/+qM/yt3vfnfe9a538cxnPpPPfOYzvPSlLx3//nnPex6XXXYZ3/It38Lzn/986rrmf/2v/8Vf/MVf8MAHPhCA173udTzucY/j0ksv5cUvfjHL5ZJXvvKVfOu3fisf/OAHR3LgUY96FP/6r//Kk5/8ZG5/+9tz9dVX82d/9md86lOfGv/9wAc+kPPOO49nPetZHD16lE984hP80R/90aH9fOITn8hrXvManvCEJ/AzP/MzXH755bziFa/ggx/8IH/913892qme85zn8IIXvIAHP/jBPPjBD+Yf//EfeeADH8gwDP+h4/zCF76Q2WzGs571LD760Y/y8pe/nKqq0Fpz8uRJLrvsMv7u7/6O17zmNdzhDnfgOc95zvi3r3zlK/nqr/5qHvawh2Gt5U/+5E/4yZ/8SWKM/NRP/dT4vp/7uZ/jV37lV3joQx/KpZdeyj/90z9x6aWX0nXdoW1ZLpfc97735TOf+QxPfOIT+Yqv+Ar+5m/+hp/7uZ/jiiuu4GUve9mhz3zta1/L5ZdfPp6TLxTf933fx13velde9KIX8ba3vY0XvOAFnHPOOfz2b/8297///Xnxi1/M61//ep7xjGfwTd/0TXzbt30bIETn/e9/f6644gqe8pSncOGFF/L7v//7vPe9773e7ymEVLnGCrz3nDp1imEY+Jd/+Rd+4Rd+gZ2dHe5xj3t8UfsTQuC7vuu7uNe97sWv/Mqv8M53vpPnPve5eO95/vOfDwgJ+NCHPpQPfOADPOlJT+Iud7kLb3nLW3jc4x73RX3nhAkTJkyYMOGLRJowYcKECRMmfEnw6le/OgHpPe95T7rmmmvSpz/96fSGN7whnXvuuWk2m6V///d/Tyml9LjHPS4B6VnPetahv3/zm9+cgPSCF7zg0OuPfvSjk1IqffSjH00ppfSRj3wkaa3TIx/5yBRCOPTeGGNKKaX9/f109OjR9OM//uOHfn/llVemI0eOjK+fPHkyAelXf/VXP+t+/fEf/3EC0t///d9/1vf81V/9VQLS61//+kOvv/Od7zz0+tVXX53quk4PechDxm1NKaWf//mfT0B63OMe91m/owBIP/VTPzX++73vfW8C0td8zdekYRjG1x/zmMckpVR60IMedOjvv/mbvznd7na3O/Tacrm8zvdceuml6Su/8ivHf1955ZXJWpse8YhHHHrfZZdddp1t/6Vf+qW0tbWVPvzhDx9677Oe9axkjEmf+tSnxtfK9XD55ZffoH1/7nOfO/77uc99bgLST/zET4yvee/TbW5zm6SUSi960YvG10+ePJlms9mh7XzJS16SgPTmN795fG21WqW73OUuCUjvfe97D33/Yx/72HTf+973Otv1t3/7twkYf+585ztf52+vD5dffnkC0qtf/erxtXI8nvzkJ4+vxRjTQx7ykFTXdbrmmmtSSin9j//xPxKQXvayl43vCyGk+9///tf5zAkTJkyYMGHCTYfJajdhwoQJEyZ8ifEd3/EdnHfeedz2trfl+7//+9ne3uaP//iPufjiiw+970lPetKhf7/97W/HGMPP/MzPHHr96U9/Oikl3vGOdwDw5je/mRgjz3nOc64T3Fxyd/7sz/6MU6dO8ZjHPIbjx4+PP8YY7nnPe46KltlsRl3XvO9977uOJa7g6NGjAPzpn/7pZ7WDvfGNb+TIkSN853d+56Hv+4Zv+Aa2t7fH73vPe97DMAw8+clPHrcV4KlPfepnO5w3GD/8wz98KKT6nve8Jyml61gC73nPe/LpT38a7/342mw2G//79OnTHD9+nPve9758/OMf5/Tp0wD8+Z//Od57fvInf/LQ5z35yU++zra88Y1v5D73uQ/Hjh07dDy+4zu+gxACf/mXfzm+9zWveQ0ppS9a7QSSJVZgjOEbv/EbSSnxoz/6o+PrR48e5c53vjMf//jHx9fe+c53cvHFF/Owhz1sfK1tW378x3/8Ot8RY+Sd73zn9drs7na3u/Fnf/ZnvPnNb+Znf/Zn2dra+g93tdvsXFg6GQ7DwHve855x26uqOrStWutDCrUJEyZMmDBhwk2PyWo
|
||
|
"text/plain": [
|
||
|
"<Figure size 1500x1000 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
}
|
||
|
],
|
||
|
"source": [
|
||
|
"import numpy as np\n",
|
||
|
"import cv2\n",
|
||
|
"from matplotlib import pyplot as plt\n",
|
||
|
"\n",
|
||
|
"def order_points(pts):\n",
|
||
|
" rect = np.zeros((4, 2), dtype=\"float32\")\n",
|
||
|
" s = pts.sum(axis=1)\n",
|
||
|
" rect[0] = pts[np.argmin(s)]\n",
|
||
|
" rect[2] = pts[np.argmax(s)]\n",
|
||
|
" diff = np.diff(pts, axis=1)\n",
|
||
|
" rect[1] = pts[np.argmin(diff)]\n",
|
||
|
" rect[3] = pts[np.argmax(diff)]\n",
|
||
|
" return rect\n",
|
||
|
"\n",
|
||
|
"def get_transform(image, pts, padding=0.1):\n",
|
||
|
" rect = order_points(pts)\n",
|
||
|
" (tl, tr, br, bl) = rect\n",
|
||
|
" widthA = np.linalg.norm(br - bl)\n",
|
||
|
" widthB = np.linalg.norm(tr - tl)\n",
|
||
|
" maxWidth = max(int(widthA), int(widthB))\n",
|
||
|
" heightA = np.linalg.norm(tr - br)\n",
|
||
|
" heightB = np.linalg.norm(tl - bl)\n",
|
||
|
" maxHeight = max(int(heightA), int(heightB))\n",
|
||
|
" center = np.mean(rect, axis=0)\n",
|
||
|
" vectors = rect - center\n",
|
||
|
" rect_shrinked = rect - vectors * padding\n",
|
||
|
" rect_shrinked[:, 0] = np.clip(rect_shrinked[:, 0], 0, image.shape[1] - 1)\n",
|
||
|
" rect_shrinked[:, 1] = np.clip(rect_shrinked[:, 1], 0, image.shape[0] - 1)\n",
|
||
|
" dst = np.array([\n",
|
||
|
" [0, 0],\n",
|
||
|
" [maxWidth - 1, 0],\n",
|
||
|
" [maxWidth - 1, maxHeight - 1],\n",
|
||
|
" [0, maxHeight - 1]\n",
|
||
|
" ], dtype=\"float32\")\n",
|
||
|
" M = cv2.getPerspectiveTransform(rect_shrinked, dst)\n",
|
||
|
" warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))\n",
|
||
|
" return warped\n",
|
||
|
"\n",
|
||
|
"def get_transformed_plate(image, bbox, padding=0.1):\n",
|
||
|
" plate = get_transform(image, bbox, padding)\n",
|
||
|
" return plate\n",
|
||
|
"\n",
|
||
|
"def process_and_display_plate(image_path, padding=0.1):\n",
|
||
|
" image = cv2.imread(image_path)\n",
|
||
|
" if image is None:\n",
|
||
|
" print(f\"Failed to load image at path: {image_path}\")\n",
|
||
|
" return\n",
|
||
|
" output_image = image.copy()\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",
|
||
|
" plate_found = False\n",
|
||
|
" plate_image = None\n",
|
||
|
" plate_box = None\n",
|
||
|
" for cnt in contours:\n",
|
||
|
" x, y, w, h = cv2.boundingRect(cnt)\n",
|
||
|
" area = w * h\n",
|
||
|
" aspectRatio = float(w) / h\n",
|
||
|
" if aspectRatio >= 3 and area > 600:\n",
|
||
|
" approx = cv2.approxPolyDP(cnt, 0.05 * cv2.arcLength(cnt, True), True)\n",
|
||
|
" if len(approx) <= 4 and x > 15:\n",
|
||
|
" rect = cv2.minAreaRect(cnt)\n",
|
||
|
" box = cv2.boxPoints(rect)\n",
|
||
|
" box = np.intp(box)\n",
|
||
|
" plate_image = get_transformed_plate(image, box, padding)\n",
|
||
|
" plate_found = True\n",
|
||
|
" plate_box = box\n",
|
||
|
" break\n",
|
||
|
" if plate_found:\n",
|
||
|
" cv2.polylines(output_image, [plate_box], isClosed=True, color=(0, 255, 0), thickness=2)\n",
|
||
|
" height, width = image.shape[:2]\n",
|
||
|
" black_space_width = width // 2\n",
|
||
|
" black_space = np.zeros((height, black_space_width, 3), dtype=np.uint8)\n",
|
||
|
" if plate_found and plate_image is not None:\n",
|
||
|
" plate_height, plate_width = plate_image.shape[:2]\n",
|
||
|
" scale_factor = min(black_space_width / (plate_width + 200), height / plate_height)\n",
|
||
|
" new_plate_width = int(plate_width * scale_factor)\n",
|
||
|
" new_plate_height = int(plate_height * scale_factor)\n",
|
||
|
" resized_plate = cv2.resize(plate_image, (new_plate_width, new_plate_height), interpolation=cv2.INTER_AREA)\n",
|
||
|
" y_offset = (height - new_plate_height) // 2\n",
|
||
|
" black_space[y_offset:y_offset + new_plate_height, 0:new_plate_width] = resized_plate\n",
|
||
|
" placeholder_text = \"Text plate:\"\n",
|
||
|
" decoded_text = \"__________\"\n",
|
||
|
" font = cv2.FONT_HERSHEY_SIMPLEX\n",
|
||
|
" font_scale = 1\n",
|
||
|
" font_color = (255, 255, 255)\n",
|
||
|
" thickness = 2\n",
|
||
|
" line_type = cv2.LINE_AA\n",
|
||
|
" cv2.putText(black_space, placeholder_text, (new_plate_width + 20, y_offset + new_plate_height // 2), \n",
|
||
|
" font, font_scale, font_color, thickness, line_type)\n",
|
||
|
" cv2.putText(black_space, decoded_text, (new_plate_width + 20, y_offset + new_plate_height // 2 + 50), \n",
|
||
|
" font, font_scale, font_color, thickness, line_type)\n",
|
||
|
" else:\n",
|
||
|
" placeholder_text = \"Not found.\"\n",
|
||
|
" font = cv2.FONT_HERSHEY_SIMPLEX\n",
|
||
|
" font_scale = 1\n",
|
||
|
" font_color = (0, 0, 255)\n",
|
||
|
" thickness = 2\n",
|
||
|
" line_type = cv2.LINE_AA\n",
|
||
|
" text_size, _ = cv2.getTextSize(placeholder_text, font, font_scale, thickness)\n",
|
||
|
" text_x = (black_space_width - text_size[0]) // 2\n",
|
||
|
" text_y = height // 2\n",
|
||
|
" cv2.putText(black_space, placeholder_text, (text_x, text_y), \n",
|
||
|
" font, font_scale, font_color, thickness, line_type)\n",
|
||
|
" combined_image = np.hstack((output_image, black_space))\n",
|
||
|
" combined_image_rgb = cv2.cvtColor(combined_image, cv2.COLOR_BGR2RGB)\n",
|
||
|
" plt.figure(figsize=(15, 10))\n",
|
||
|
" plt.imshow(combined_image_rgb)\n",
|
||
|
" plt.axis('off')\n",
|
||
|
" plt.title(f'Processed Image: {image_path}')\n",
|
||
|
" plt.show()\n",
|
||
|
"\n",
|
||
|
"images = ['img/1.jpg', 'img/2.jpg', 'img/3.jpg']\n",
|
||
|
"\n",
|
||
|
"for img_path in images:\n",
|
||
|
" process_and_display_plate(img_path, padding=0.1)\n"
|
||
|
]
|
||
|
},
|
||
|
{
|
||
|
"cell_type": "code",
|
||
|
"execution_count": 6,
|
||
|
"metadata": {},
|
||
|
"outputs": [
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "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fOIT2d/f56d/+qd52MMexh//8R9zn/vch8suu+zY9n75l3+ZV73qVcdeu/Od7/xR90VEeNSjHsUb3/hGvumbvon73Oc+vP71r+e7vuu7uO666/jxH/9xAM6ePctms+FJT3oSD3nIQ/jWb/1W/u7v/o7nP//5/NEf/RF/9Ed/RNu243bPnz/PIx7xCL7qq76KxzzmMfyP//E/eNKTnkTTNKN65pYc3/Dd73rXu/hP/+k/8RVf8RU89alP5U//9E959rOfzdve9jZe85rXjETI933f9/H93//9PPShD+VJT3oS73jHO3jBC17An/zJn/D7v//7hBB4xjOewTd/8zcfu4bf8i3fwgMe8ICPe+2++Zu/mTe96U385m/+Jve85z0/6vsuXLhwE+JywOMe9zje/OY3f8Km6DlnHv7wh/MFX/AF/Nf/+l95+ctfzrd/+7ezXC55xjOewdd93dfxFV/xFbzwhS/kcY97HPe///25053uBMB1113Hgx/8YIwxPP3pT2e5XPKSl7zk2LU7ite+9rVcfvnlowrxtsItGS+Hh4c85CEP4frrr+c7v/M7ud3tbscv/MIv3ISonDBhwoQJEyZUyIQJEyZMmDBhxEtf+lIB5Ld+67fkwx/+sLz//e+XV7ziFXLmzBmZz+fygQ98QERE/vN//s+yXC7lb/7mb459/ru/+7vFOSfve9/7Pup3fNu3fZvs7OyMv7/+9a8XQH7jN37j2Ps+4zM+Qx74wAfe5POf93mfJw9+8IPH39/97ncLIC996UvH1x74wAfKve51r1t93O9+97tv8reUknRdd+y18+fPyxVXXCHf+I3feLPbe+Yznym3Zpnx6le/WgB51rOedez1f/tv/60YY+Sd73znse1+4Rd+oaSUbrL/z33uc8fXHvjABwogP/qjPzq+1nWd3Oc+95HLL79c+r6/Vcf3+Mc/XgB5whOecLPH+mu/9msiInLDDTdI0zTyxV/8xZJzHt/3vOc9TwD5mZ/5mZsc/81dw6MA5JnPfKaIiDz96U8X55y8+tWv/pjvExF52tOeJpdffrnc9773vclYGs7Px8PN7dtwLn7wB39wfO38+fMyn8/FGCOveMUrxtff/va332S/vuM7vkOMMfIXf/EX42tnz56VSy655GbH4QMe8AB5/OMf/1H3cblcfsy/fyTe+MY3CiBvfOMbx9du6Xj50R/9UQGOnf/1ei33uMc9brLNCRMmTJgwYYLIVGo3YcKECRMm3Awe+tCHctlll3GHO9yBr/mar2FnZ4dXvepVo6/Q//yf/5MHPOABnD59mhtvvHH8eehDH0rOmd/93d89tr2LFy/yoQ99iN/+7d/mNa95DV/wBV9w7LuuuuoqXv7yl4+vve1tb+Otb30rX//1X3+Tfev7/qMqQ44i5zzuV9/3n+ipwDk3mjCXUjh37hwpJT77sz+bP//zP/+Et3sUr33ta3HO8e///b8/9vpTn/pURITf+I3fOPb6f/gP/+GYOuyxj30sV1xxBa95zWuOvc97z7XXXjv+3jQN1157LTfccAN/9md/9gkd33d913cd+/0pT3kKzrnxu3/rt36Lvu958pOfjLXbpdYTn/hETpw4cZN9vDV43vOexw/90A/xnOc8hy//8i//mO+97rrreO5zn8v3fM/33KwH0pve9KZPWO0
|
||
|
"text/plain": [
|
||
|
"<Figure size 1500x1000 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
},
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "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djz3vOc9ef7znw/AkSNHePazn/0Fn9Nv/uZvDv/9p3/6p7z61a/ml3/5l4eAvvhZnTx5kte85jU88YlP5GlPexrb29u89rWv5bLLLuMv//Ivuf/9789555235/N+93d/lze96U17Xrv00ks/67GICI973ON417vexQ/8wA9w//vfn3e84x0897nP5frrr+eXf/mXAbjttttYrVY84xnP4OEPfzhPf/rT+cQnPsErXvEK3ve+9/G+972PpmmGzz127BiPfvSj+Y7v+A6e+MQn8t/+23/jGc94BnVdD+qZszm/8t2f/OQn+fEf/3G+/du/nec85zn81V/9FS95yUv48Ic/zFve8paBLHrRi17ET/3UT/GIRzyCZzzjGXzsYx/j137t13j/+9/Pn/3Zn1FVFc9//vP5wR/8wT338Id+6Id48IMf/Hnv3Q/+4A/y7ne/m//xP/4H9773vT/r+44fP34acVnw5Cc/mT/5kz/5ok3RY4w86lGP4hu+4Rv4T//pP3H11Vfz7/7dv2Nzc5PnP//5fM/3fA/f/u3fzitf+Uqe/OQn88AHPpA73/nOAFx//fU87GEPwxjD8573PDY3N3nNa16z596t461vfSvnn3/+oEL8bLjpppvY2Nj4osthz2a87O7u8vCHP5wbb7yRZz7zmdzudrfjt37rt04jKidMmDBhwoQJGTJhwoQJEyZMGHDVVVcJIH/0R38kt956q3zmM5+RN7zhDXLOOefIfD6X6667TkRE/uN//I+yubkpf//3f7/n93/sx35MnHNy7bXXftbv+OEf/mHZ2toa/v2Od7xDAHnb2962531f+ZVfKQ95yENO+/0HPehB8rCHPWz49zXXXCOAXHXVVcNrD3nIQ+Q+97nPF3ze11xzzWk/CyFI27Z7Xjt27JhccMEF8v3f//1n/LwXvvCF8oVsM9785jcLID/zMz+z5/V/82/+jRhj5B/+4R/2fO43fuM3SgjhtON/+ctfPrz2kIc8RAD5xV/8xeG1tm3l/ve/v5x//vnSdd0XdH5PecpTBJCnPvWpZzzXP/iDPxARkVtuuUXqupZv+qZvkhjj8L4rr7xSAHnd61532vmf6R6uA5AXvvCFIiLyvOc9T5xz8uY3v/lzvk9E5IorrpDzzz9fHvCAB5w2lsr1+Xw407GVa/GzP/uzw2vHjh2T+Xwuxhh5wxveMLz+0Y9+9LTj+pEf+RExxshf//VfD6/ddtttcvjw4TOOwwc/+MHylKc85XMe58c//nGZzWbypCc96fOe07ve9S4B5F3vetfw2tmOl1/8xV8UYM/1Xy6Xcs973vO0z5wwYcKECRMmiEyldhMmTJgwYcIZ8IhHPILzzjuPO9zhDnzXd30XW1tbvOlNbxp8hf77f//vPPjBD+bQoUMcOXJk+POIRzyCGCPvec979nzeiRMnuPnmm/njP/5j3vKWt/AN3/ANe77roosu4uqrrx5e+/CHP8zf/u3f8r3f+72nHVvXdZ9VGbKOGONwXF3XfbGXAuccdV0Dasp89OhRQgh8zdd8DR/84Ae/6M9dx1vf+lacc/zoj/7ontef85znICK87W1v2/P6v//3/36POuxJT3oSF1xwAW95y1v2vM97z+WXXz78u65rLr/8cm655RY+8IEPfFHn99znPnfPv5/97GfjnBu++4/
|
||
|
"text/plain": [
|
||
|
"<Figure size 1500x1000 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
},
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "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
|
||
|
"text/plain": [
|
||
|
"<Figure size 1500x1000 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
},
|
||
|
{
|
||
|
"data": {
|
||
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABJ4AAAIqCAYAAABlmICzAAAAOXRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjkuMiwgaHR0cHM6Ly9tYXRwbG90bGliLm9yZy8hTgPZAAAACXBIWXMAAA9hAAAPYQGoP6dpAAEAAElEQVR4nOz9ebwlV1X3AX/3UHXOuff2kM5EyEQgJEJ4MRCCgsQEBBkF8RFEkRDEGIPoE0QiERFEBBVQDGEQw6QyKUJQCKOCIjIPr/AoSIAQAiQh6fR07z2nau+93j/W2nW76SQ08QnxfazFp0n3uefWqVO1a++9fuv3+y0nIsIYY4wxxhhjjDHGGGOMMcYYY4wxxhhj/F8Of2ufwBhjjDHGGGOMMcYYY4wxxhhjjDHGGP9vxgg8jTHGGGOMMcYYY4wxxhhjjDHGGGOMcYvECDyNMcYYY4wxxhhjjDHGGGOMMcYYY4xxi8QIPI0xxhhjjDHGGGOMMcYYY4wxxhhjjHGLxAg8jTHGGGOMMcYYY4wxxhhjjDHGGGOMcYvECDyNMcYYY4wxxhhjjDHGGGOMMcYYY4xxi8QIPI0xxhhjjDHGGGOMMcYYY4wxxhhjjHGLxAg8jTHGGGOMMcYYY4wxxhhjjDHGGGOMcYvECDyNMcYYY4wxxhg3GaUUrr32Wr7yla983z87pcQ111zDFVdc8X3/7DHGGGOMMcYYY4wx/usxAk9jjDHGGGOMMcZ+cdVVV3Heeedx7LHH0rYthx56KHe+853ZtWvXLf7ZX/rSlzj77LM54ogjaNuWww8/nHvd616IyC3+2bdGXH755TjneO1rX3trn8oY/43ijDPO4Iwzzri1T2OMMcYYY4wx/ssxAk9jjDHGGGN8X+O1r30tzrnhz3Q65YQTTuDJT34yV1999a19emMAl112GaeeeipvetObOOecc3jHO97B+973Pv7hH/6B5eXlW/SzP/rRj3LPe96Tf/zHf+TpT38673nPe3jf+97HJZdcgnPuFv3s/+nx93//93jvueqqq26R4/d9z53vfGecc7zwhS/c7+elFP7oj/6I4447jul0yl3velfe+MY37ve+j3/84zzpSU/ilFNOoWmamxwXO3fu5Pzzz+eOd7wjs9mMY489lic+8Yn7Mei++MUv8pSnPIV73/veTKdTnHNcfvnlN3jM+XzO85//fO585zuztLTEkUceyaMe9Sj+z//5P/u87zvnur3/3FLXeIwxxhhjjDH+O0a8tU9gjDHGGGOM/5nxnOc8h+OOO475fM6//Mu/8PKXv5xLL72Uz3/+8ywtLd3ap/c/Os455xzatuWjH/0oRx555Pftc7uu4wlPeAInnHAC733ve9myZcv37bNvzTj22GNZX1+naZpb9Tze+c53csopp3Cb29zmFjn+S17ykpuUTD7jGc/gD/7gDzj77LM59dRTefvb387P/dzP4ZzjMY95zPC+Sy+9lIsvvpi73vWu3P72t+c///M/b/B4pRQe8IAH8O///u886UlP4oQTTuCyyy7jZS97Ge95z3v4j//4DzZt2gTARz7yES688ELufOc7c6c73YnPfvazN3qej33sY/m7v/s7zj77bO5+97vzzW9+k5e+9KXc61734nOf+xzHHnvsPu+vc93esXXr1u9yteC9733vd33PGGOMMcYYY/z/RcgYY4wxxhhjfB/jNa95jQDyiU98Yp/Xf/3Xf10AecMb3nArndkYIiKf/OQnBZD3vve93/fPfstb3iLOOfniF7/4ff/sMUSOPvpoedaznnWjP19fX5ec88069tVXXy1btmyR5zznOQLIC17wgn1+fuWVV0rTNPIrv/Irw2ulFDnttNPkqKOOkpTS8PpVV10la2trIiLyK7/yK3Jj29kPf/jDAshFF120z+uvfvWrBZC3vvWtw2vXXXed7Nq1S0REXvCCFwggX/3qV/c75pVXXimA/MZv/MY+r//jP/6jAPLHf/zHw2s3NteNMcYYY4wxxv+0GKV2Y4wxxhhj/LeI+93vfgB89atfHV7bsWMH5513HkcffTSTyYTjjz+eP/zDP6SUMrzni1/8Ive73/24zW1uw2Qy4eijj+aXf/mX2b59OwB79uxheXmZ//2///d+n3nllVcSQuD5z3/+Pq+fccYZNyiP2duD54wzzuAud7nLTX6nG5PZ1D/Vv6XrOn7nd36HU045hS1btrC8vMxpp53GBz7wgeFY1Qfopv6cddZZN3k+q6urPPWpTx2u54knnsgLX/jCfbyTPvrRjzKdTvnyl7/MSSedxGQy4Ta3uQ3nnHPOcE2/8xp86lOf4t73vjez2YzjjjuOV7ziFfu870C+X/3s4447jr/927/lDne4A23bcswxx3D++eezvr6+3/d52cteNpzjbW97W37lV36FHTt27HN+3+2a7X2vnv3sZw//TinxkIc8hG3btvHv//7vN/o+gBe84AX73M8aV1xxBV/4whdu8F7sHTfk8XTWWWexsrLCFVdcwcMe9jBWVlY48sgjeelLXwrA5z73Oe53v/uxvLzMscceyxve8Ib9jvtv//ZvnH766cxmM4466iie+9zn8prXvOYGZWSf+9zn+PrXv85DH/pQAD74wQ/inONNb3oTv/3bv82RRx7J0tISu3bt4tnPfvYNytuqtOyGJGpPf/rTOfHEE/n5n//5G7wGb3/72+n7nic96UnDa845zj33XK688ko+8pGPDK8ffvjhzGazG72eNaof2eGHH77P60cccQTAPsfYtm3bwH66qdi9e/cBH/M7fy/n/F2Pv3d8p8dTvSdvfvOb+a3f+i1uc5vbsLy8zMMf/nC+/vWv7/f7L33pS7n97W/PbDbjnve8Jx/60IdG36gxxhhjjDFulRildmOMMcYYY/y3iC9/+csAHHzwwQCsra1x+umn841vfINzzjmHY445hn/913/lggsu4Fvf+hYvfvGLAQVTjjrqKH7iJ36CzZs38/nPf56XvvSlfOMb3+Dv//7vWVlZ4ZGPfCRvfvOb+eM//mNCCMNnvvGNb0REeOxjH7vf+fzAD/wAz3jGMwC49tprecpTnvI9f6e//Mu/HP7+oQ99iFe+8pX8yZ/8CYcccgiwkbzu2rWLiy++mJ/92Z/l7LPPZvfu3bzqVa/igQ98IB//+Mc5+eSTOfTQQ/c53lvf+lbe9ra37fPaHe5whxs9FxHh4Q9/OB/4wAd44hOfyMknn8x73vMenva0p/GNb3yDP/mTPwHguuuuYz6fc+6553K/+92PX/7lX+bLX/4yL33pS/nYxz7Gxz72MSaTyXDc66+/noc85CE8+tGP5md/9mf567/+a84991zatuUXfuEXDvj71c/+yle+wm/91m/xUz/1Uzz1qU/lk5/8JC94wQv4/Oc/zzvf+c4B8Hj2s5/N7/7u73L/+9+fc889ly9+8Yu8/OUv5xOf+AQf/vCHaZqGZzzjGfziL/7iPvfwl37plzjttNO+6737xV/8RT74wQ/yvve9jzvf+c43+r4dO3bsB1zWOPPMM/mnf/qnm22KnnPmwQ9+MD/6oz/KH/3RH/H617+eJz/5ySwvL/OMZzyDxz72sfzUT/0Ur3jFKzjzzDO5173uNUi6vvGNb3Df+94X5xwXXHABy8vLXHzxxfvcu73j0ksv5bDDDuMe97jHPq//3u/9Hm3b8hu/8RssFgvatv2ev8fHP/5xXve61/Ev//IvN+rH9JnPfIbl5WXudKc77fP6Pe95z+Hn97nPfb6nz73HPe7B8vIyz3zmM9m2bRsnnngil112Geeffz6nnnoq97///b/n73KHO9yBo446ihe96EWceOKJ3O1ud+Ob3/wm559/Pscdd9w+ksAa973vfdmzZw9t2/LABz6QF73oRdzxjnf8nj+7xu///u/jnOM3f/M3ueaaa3jxi1/M/e9/fz772c8OwNfLX/5ynvzkJ3PaaafxlKc8hcsvv5yf/Mmf5KCDDuKoo4662Z89xhhjjDHGGDcrbl3C1RhjjDHGGP/TospP3v/+98u3v/1t+frXvy5vetOb5OCDD5bZbCZXXnmliIj83u/9niw
|
||
|
"text/plain": [
|
||
|
"<Figure size 1500x1000 with 1 Axes>"
|
||
|
]
|
||
|
},
|
||
|
"metadata": {},
|
||
|
"output_type": "display_data"
|
||
|
}
|
||
|
],
|
||
|
"source": [
|
||
|
"import numpy as np\n",
|
||
|
"import cv2\n",
|
||
|
"from matplotlib import pyplot as plt\n",
|
||
|
"import torch\n",
|
||
|
"import torch.nn as nn\n",
|
||
|
"from torchvision import transforms\n",
|
||
|
"\n",
|
||
|
"# Конфигурация устройства\n",
|
||
|
"DEVICE = torch.device('cuda' if torch.cuda.is_available() else 'cpu')\n",
|
||
|
"\n",
|
||
|
"# Классы символов для модели OCR\n",
|
||
|
"CLASSES = \"ABEKMHOPCTYX0123456789\"\n",
|
||
|
"\n",
|
||
|
"# Трансформации для входных изображений\n",
|
||
|
"transform = transforms.Compose([\n",
|
||
|
" transforms.ToPILImage(),\n",
|
||
|
" transforms.Grayscale(),\n",
|
||
|
" transforms.Resize((28, 28)),\n",
|
||
|
" transforms.ToTensor(),\n",
|
||
|
" transforms.Normalize((0.25,), (0.25,))\n",
|
||
|
"])\n",
|
||
|
"\n",
|
||
|
"# Определение модели CNN\n",
|
||
|
"class ImprovedCNN(nn.Module):\n",
|
||
|
" def __init__(self, num_classes):\n",
|
||
|
" super(ImprovedCNN, self).__init__()\n",
|
||
|
" self.conv_layers = nn.Sequential(\n",
|
||
|
" nn.Conv2d(1, 32, kernel_size=3, padding=1),\n",
|
||
|
" nn.SiLU(),\n",
|
||
|
" nn.BatchNorm2d(32),\n",
|
||
|
" nn.MaxPool2d(kernel_size=2, stride=2),\n",
|
||
|
"\n",
|
||
|
" nn.Conv2d(32, 64, kernel_size=3, padding=1),\n",
|
||
|
" nn.SiLU(),\n",
|
||
|
" nn.BatchNorm2d(64),\n",
|
||
|
" nn.MaxPool2d(kernel_size=2, stride=2),\n",
|
||
|
"\n",
|
||
|
" nn.Conv2d(64, 128, kernel_size=3, padding=1),\n",
|
||
|
" nn.SiLU(),\n",
|
||
|
" nn.BatchNorm2d(128),\n",
|
||
|
" nn.AdaptiveAvgPool2d((1, 1)) # Глобальный Average Pooling\n",
|
||
|
" )\n",
|
||
|
" self.fc_layers = nn.Sequential(\n",
|
||
|
" nn.Linear(128, 256),\n",
|
||
|
" nn.SiLU(),\n",
|
||
|
" nn.Linear(256, num_classes)\n",
|
||
|
" )\n",
|
||
|
"\n",
|
||
|
" def forward(self, x):\n",
|
||
|
" x = self.conv_layers(x)\n",
|
||
|
" x = x.view(x.size(0), -1) # Разворачиваем перед линейным слоем\n",
|
||
|
" x = self.fc_layers(x)\n",
|
||
|
" return x\n",
|
||
|
"# Загрузка предобученной модели\n",
|
||
|
"model = ImprovedCNN(len(CLASSES)).to(DEVICE)\n",
|
||
|
"SAVE_PATH = \"best_model_9.pth\"\n",
|
||
|
"try:\n",
|
||
|
" model.load_state_dict(torch.load(SAVE_PATH, map_location=DEVICE))\n",
|
||
|
" model.eval()\n",
|
||
|
"except Exception as e:\n",
|
||
|
" print(f\"Ошибка при загрузке модели: {e}\")\n",
|
||
|
"\n",
|
||
|
"# Функции для распознавания номера\n",
|
||
|
"def extract_symbols(image, masks):\n",
|
||
|
" img_h, img_w = image.shape\n",
|
||
|
" symbols = []\n",
|
||
|
" for bbox in masks:\n",
|
||
|
" x_frac, y_frac, w_frac, h_frac = bbox\n",
|
||
|
" x = int(x_frac * img_w)\n",
|
||
|
" y = int(y_frac * img_h)\n",
|
||
|
" w = int(w_frac * img_w)\n",
|
||
|
" h = int(h_frac * img_h)\n",
|
||
|
" x_end = min(x + w, img_w)\n",
|
||
|
" y_end = min(y + h, img_h)\n",
|
||
|
" symbol = image[y:y_end, x:x_end]\n",
|
||
|
" if symbol.size == 0:\n",
|
||
|
" continue\n",
|
||
|
" symbols.append(symbol)\n",
|
||
|
" return symbols\n",
|
||
|
"\n",
|
||
|
"def is_symbol_present(symbol, threshold=250):\n",
|
||
|
" return np.mean(symbol) < threshold\n",
|
||
|
"\n",
|
||
|
"expected_types_8 = ['letter', 'digit', 'digit', 'digit', 'letter', 'letter', 'digit', 'digit']\n",
|
||
|
"expected_types_9 = ['letter', 'digit', 'digit', 'digit', 'letter', 'letter', 'digit', 'digit', 'digit']\n",
|
||
|
"\n",
|
||
|
"mask_8 = [\n",
|
||
|
" (0.03, 0.05, 0.12, 0.9),\n",
|
||
|
" (0.16, 0.05, 0.12, 0.9),\n",
|
||
|
" (0.28, 0.05, 0.12, 0.9),\n",
|
||
|
" (0.40, 0.05, 0.12, 0.9),\n",
|
||
|
" (0.52, 0.05, 0.12, 0.9),\n",
|
||
|
" (0.64, 0.05, 0.12, 0.9),\n",
|
||
|
" (0.79, 0.07, 0.10, 0.60),\n",
|
||
|
" (0.89, 0.07, 0.10, 0.60),\n",
|
||
|
"]\n",
|
||
|
"\n",
|
||
|
"mask_9 = [\n",
|
||
|
" (0.05, 0.2, 0.1, 0.6),\n",
|
||
|
" (0.17, 0.2, 0.1, 0.6),\n",
|
||
|
" (0.29, 0.2, 0.1, 0.6),\n",
|
||
|
" (0.41, 0.2, 0.1, 0.6),\n",
|
||
|
" (0.53, 0.2, 0.1, 0.6),\n",
|
||
|
" (0.65, 0.2, 0.1, 0.6),\n",
|
||
|
" (0.77, 0.2, 0.1, 0.6),\n",
|
||
|
" (0.89, 0.2, 0.1, 0.6),\n",
|
||
|
" (1.01, 0.2, 0.1, 0.6)\n",
|
||
|
"]\n",
|
||
|
"\n",
|
||
|
"digits = set(['0', '1', '2', '3', '4', '5', '6', '7', '8', '9'])\n",
|
||
|
"letters = set([c for c in CLASSES if c.isalpha()])\n",
|
||
|
"\n",
|
||
|
"def process_plate(plate):\n",
|
||
|
" gray_plate = cv2.cvtColor(plate, cv2.COLOR_BGR2GRAY)\n",
|
||
|
" resized_plate = cv2.resize(gray_plate, None, fx=2, fy=2, interpolation=cv2.INTER_CUBIC)\n",
|
||
|
" blurred = cv2.GaussianBlur(resized_plate, (3, 3), 0)\n",
|
||
|
" symbols_8 = extract_symbols(blurred, mask_8)\n",
|
||
|
" symbols_9 = extract_symbols(blurred, mask_9)\n",
|
||
|
" if len(symbols_9) == 9 and is_symbol_present(symbols_9[-1]):\n",
|
||
|
" characters = symbols_9\n",
|
||
|
" expected_types = expected_types_9\n",
|
||
|
" else:\n",
|
||
|
" characters = symbols_8\n",
|
||
|
" expected_types = expected_types_8\n",
|
||
|
" return blurred, characters, expected_types\n",
|
||
|
"\n",
|
||
|
"def recognize_plate(plate_image):\n",
|
||
|
" if plate_image is None:\n",
|
||
|
" return None\n",
|
||
|
" processed_plate, characters, expected_types = process_plate(plate_image)\n",
|
||
|
" predictions = []\n",
|
||
|
" for idx, (symbol_img, expected_type) in enumerate(zip(characters, expected_types)):\n",
|
||
|
" input_tensor = transform(symbol_img).unsqueeze(0).to(DEVICE)\n",
|
||
|
" with torch.no_grad():\n",
|
||
|
" outputs = model(input_tensor)\n",
|
||
|
" probabilities = torch.nn.functional.softmax(outputs, dim=1)\n",
|
||
|
" indices = torch.argmax(probabilities, dim=1)\n",
|
||
|
" predicted_label = CLASSES[indices[0]]\n",
|
||
|
" if expected_type == 'letter':\n",
|
||
|
" if predicted_label == '0':\n",
|
||
|
" predicted_label = 'O'\n",
|
||
|
" elif predicted_label == '8':\n",
|
||
|
" predicted_label = 'B'\n",
|
||
|
" if predicted_label not in letters:\n",
|
||
|
" continue\n",
|
||
|
" elif expected_type == 'digit':\n",
|
||
|
" if predicted_label == 'O':\n",
|
||
|
" predicted_label = '0'\n",
|
||
|
" elif predicted_label == 'B':\n",
|
||
|
" predicted_label = '8'\n",
|
||
|
" if predicted_label not in digits:\n",
|
||
|
" continue\n",
|
||
|
" predictions.append(predicted_label)\n",
|
||
|
" plate_number = ''.join(predictions)\n",
|
||
|
" return plate_number\n",
|
||
|
"\n",
|
||
|
"# Функции для обнаружения номера\n",
|
||
|
"def order_points(pts):\n",
|
||
|
" rect = np.zeros((4, 2), dtype=\"float32\")\n",
|
||
|
" s = pts.sum(axis=1)\n",
|
||
|
" rect[0] = pts[np.argmin(s)]\n",
|
||
|
" rect[2] = pts[np.argmax(s)]\n",
|
||
|
" diff = np.diff(pts, axis=1)\n",
|
||
|
" rect[1] = pts[np.argmin(diff)]\n",
|
||
|
" rect[3] = pts[np.argmax(diff)]\n",
|
||
|
" return rect\n",
|
||
|
"\n",
|
||
|
"def get_transform(image, pts, padding=0.1):\n",
|
||
|
" rect = order_points(pts)\n",
|
||
|
" (tl, tr, br, bl) = rect\n",
|
||
|
" widthA = np.linalg.norm(br - bl)\n",
|
||
|
" widthB = np.linalg.norm(tr - tl)\n",
|
||
|
" maxWidth = max(int(widthA), int(widthB))\n",
|
||
|
" heightA = np.linalg.norm(tr - br)\n",
|
||
|
" heightB = np.linalg.norm(tl - bl)\n",
|
||
|
" maxHeight = max(int(heightA), int(heightB))\n",
|
||
|
" center = np.mean(rect, axis=0)\n",
|
||
|
" vectors = rect - center\n",
|
||
|
" rect_shrinked = rect - vectors * padding\n",
|
||
|
" rect_shrinked[:, 0] = np.clip(rect_shrinked[:, 0], 0, image.shape[1] - 1)\n",
|
||
|
" rect_shrinked[:, 1] = np.clip(rect_shrinked[:, 1], 0, image.shape[0] - 1)\n",
|
||
|
" dst = np.array([\n",
|
||
|
" [0, 0],\n",
|
||
|
" [maxWidth - 1, 0],\n",
|
||
|
" [maxWidth - 1, maxHeight - 1],\n",
|
||
|
" [0, maxHeight - 1]\n",
|
||
|
" ], dtype=\"float32\")\n",
|
||
|
" M = cv2.getPerspectiveTransform(rect_shrinked, dst)\n",
|
||
|
" warped = cv2.warpPerspective(image, M, (maxWidth, maxHeight))\n",
|
||
|
" return warped\n",
|
||
|
"\n",
|
||
|
"def get_transformed_plate(image, bbox, padding=0.1):\n",
|
||
|
" plate = get_transform(image, bbox, padding)\n",
|
||
|
" return plate\n",
|
||
|
"\n",
|
||
|
"def process_and_display_plate(image_path, padding=0.1):\n",
|
||
|
" image = cv2.imread(image_path)\n",
|
||
|
" if image is None:\n",
|
||
|
" print(f\"Failed to load image at path: {image_path}\")\n",
|
||
|
" return\n",
|
||
|
" output_image = image.copy()\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",
|
||
|
" plate_found = False\n",
|
||
|
" plate_image = None\n",
|
||
|
" plate_box = None\n",
|
||
|
" for cnt in contours:\n",
|
||
|
" x, y, w, h = cv2.boundingRect(cnt)\n",
|
||
|
" area = w * h\n",
|
||
|
" aspectRatio = float(w) / h\n",
|
||
|
" if aspectRatio >= 3 and area > 600:\n",
|
||
|
" approx = cv2.approxPolyDP(cnt, 0.05 * cv2.arcLength(cnt, True), True)\n",
|
||
|
" if len(approx) <= 4 and x > 15:\n",
|
||
|
" rect = cv2.minAreaRect(cnt)\n",
|
||
|
" box = cv2.boxPoints(rect)\n",
|
||
|
" box = np.intp(box)\n",
|
||
|
" plate_image = get_transformed_plate(image, box, padding)\n",
|
||
|
" plate_found = True\n",
|
||
|
" plate_box = box\n",
|
||
|
" break\n",
|
||
|
" if plate_found:\n",
|
||
|
" cv2.polylines(output_image, [plate_box], isClosed=True, color=(0, 255, 0), thickness=2)\n",
|
||
|
" # Распознавание номера\n",
|
||
|
" plate_number = recognize_plate(plate_image)\n",
|
||
|
" else:\n",
|
||
|
" plate_number = None\n",
|
||
|
"\n",
|
||
|
" height, width = image.shape[:2]\n",
|
||
|
" black_space_width = width // 2\n",
|
||
|
" black_space = np.zeros((height, black_space_width, 3), dtype=np.uint8)\n",
|
||
|
" if plate_found and plate_image is not None:\n",
|
||
|
" plate_height, plate_width = plate_image.shape[:2]\n",
|
||
|
" scale_factor = min(black_space_width / (plate_width + 200), height / plate_height)\n",
|
||
|
" new_plate_width = int(plate_width * scale_factor)\n",
|
||
|
" new_plate_height = int(plate_height * scale_factor)\n",
|
||
|
" resized_plate = cv2.resize(plate_image, (new_plate_width, new_plate_height), interpolation=cv2.INTER_AREA)\n",
|
||
|
" y_offset = (height - new_plate_height) // 2\n",
|
||
|
" black_space[y_offset:y_offset + new_plate_height, 0:new_plate_width] = resized_plate\n",
|
||
|
" # Вывод распознанного номера\n",
|
||
|
" if plate_number:\n",
|
||
|
" placeholder_text = \"Number plate:\"\n",
|
||
|
" decoded_text = plate_number\n",
|
||
|
" else:\n",
|
||
|
" placeholder_text = \"Not found\"\n",
|
||
|
" decoded_text = \"\"\n",
|
||
|
" font = cv2.FONT_HERSHEY_SIMPLEX\n",
|
||
|
" font_scale = 1\n",
|
||
|
" font_color = (255, 255, 255)\n",
|
||
|
" thickness = 2\n",
|
||
|
" line_type = cv2.LINE_AA\n",
|
||
|
" cv2.putText(black_space, placeholder_text, (new_plate_width + 20, y_offset + new_plate_height // 2), \n",
|
||
|
" font, font_scale, font_color, thickness, line_type)\n",
|
||
|
" cv2.putText(black_space, decoded_text, (new_plate_width + 20, y_offset + new_plate_height // 2 + 50), \n",
|
||
|
" font, font_scale, font_color, thickness, line_type)\n",
|
||
|
" else:\n",
|
||
|
" placeholder_text = \"Not found\"\n",
|
||
|
" font = cv2.FONT_HERSHEY_SIMPLEX\n",
|
||
|
" font_scale = 1\n",
|
||
|
" font_color = (0, 0, 255)\n",
|
||
|
" thickness = 2\n",
|
||
|
" line_type = cv2.LINE_AA\n",
|
||
|
" text_size, _ = cv2.getTextSize(placeholder_text, font, font_scale, thickness)\n",
|
||
|
" text_x = (black_space_width - text_size[0]) // 2\n",
|
||
|
" text_y = height // 2\n",
|
||
|
" cv2.putText(black_space, placeholder_text, (text_x, text_y), \n",
|
||
|
" font, font_scale, font_color, thickness, line_type)\n",
|
||
|
" combined_image = np.hstack((output_image, black_space))\n",
|
||
|
" combined_image_rgb = cv2.cvtColor(combined_image, cv2.COLOR_BGR2RGB)\n",
|
||
|
" plt.figure(figsize=(15, 10))\n",
|
||
|
" plt.imshow(combined_image_rgb)\n",
|
||
|
" plt.axis('off')\n",
|
||
|
" plt.title(f'Результат обработки: {image_path}')\n",
|
||
|
" plt.show()\n",
|
||
|
"\n",
|
||
|
"# Обработка изображений\n",
|
||
|
"images = ['img/1.jpg', 'img/2.jpg', 'img/3.jpg', 'img/ru4018185.jpg']\n",
|
||
|
"\n",
|
||
|
"for img_path in images:\n",
|
||
|
" process_and_display_plate(img_path, padding=0.1)\n"
|
||
|
]
|
||
|
}
|
||
|
],
|
||
|
"metadata": {
|
||
|
"kernelspec": {
|
||
|
"display_name": ".venv",
|
||
|
"language": "python",
|
||
|
"name": "python3"
|
||
|
},
|
||
|
"language_info": {
|
||
|
"codemirror_mode": {
|
||
|
"name": "ipython",
|
||
|
"version": 3
|
||
|
},
|
||
|
"file_extension": ".py",
|
||
|
"mimetype": "text/x-python",
|
||
|
"name": "python",
|
||
|
"nbconvert_exporter": "python",
|
||
|
"pygments_lexer": "ipython3",
|
||
|
"version": "3.9.13"
|
||
|
}
|
||
|
},
|
||
|
"nbformat": 4,
|
||
|
"nbformat_minor": 2
|
||
|
}
|