Deskew using Hough Lines

First implementation finished. Working on using it to crop.
Going to try using probabalistic hough lines and a bounding box
to pick out lines within a margin of the correct orientation (vertical)
and put a bounding box around these lines to try and approximate the
receipt.

Signed-off-by: Ethan Wellenreiter <ewellenreiter@gmail.com>
This commit is contained in:
Ethan Wellenreiter 2023-10-07 11:59:28 -04:00
parent f66a757d8b
commit d5e7a2eed2
2 changed files with 188 additions and 51 deletions

View File

@ -2,7 +2,7 @@
"cells": [
{
"cell_type": "code",
"execution_count": 59,
"execution_count": 264,
"metadata": {},
"outputs": [],
"source": [
@ -13,18 +13,20 @@
},
{
"cell_type": "code",
"execution_count": 60,
"execution_count": 265,
"metadata": {},
"outputs": [],
"source": [
"import cv2\n",
"import numpy as np\n",
"import math"
"import math\n",
"\n",
"import scipy.stats as st"
]
},
{
"cell_type": "code",
"execution_count": 61,
"execution_count": 266,
"metadata": {},
"outputs": [],
"source": [
@ -59,40 +61,62 @@
" hp = int((max_wh - w) / 2)\n",
" vp = int((max_wh - h) / 2)\n",
" padding = (hp, vp, hp, vp)\n",
" return cv2.copyMakeBorder(image, vp, vp, hp, hp, cv2.BORDER_CONSTANT, self.fill)"
" return cv2.copyMakeBorder(image, vp, vp, hp, hp, cv2.BORDER_CONSTANT, self.fill)\n",
" \n",
" \n",
" \n",
"def rotate(img, angle):\n",
" rows,cols = img.shape[0], img.shape[1]\n",
" M = cv2.getRotationMatrix2D((cols/2,rows/2),angle,1)\n",
" dst = cv2.warpAffine(img,M,(cols,rows))\n",
" return dst"
]
},
{
"cell_type": "code",
"execution_count": 62,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"img = cv2.imread('./testing_space/final.jpg')"
"def WithinXDegrees(lines, margin):\n",
" for line in lines:\n",
" if ()"
]
},
{
"cell_type": "code",
"execution_count": 63,
"execution_count": 267,
"metadata": {},
"outputs": [],
"source": [
"img = ResizeWithAspectRatio(SquarePad(fill=255)(img), 500)\n",
"img = cv2.imread('./testing_space/final.jpg')\n",
"img = SquarePad(fill=255)(img)\n",
"img = rotate(img, 54)"
]
},
{
"cell_type": "code",
"execution_count": 268,
"metadata": {},
"outputs": [],
"source": [
"resizedimg = ResizeWithAspectRatio(SquarePad(fill=255)(img), 500)\n",
"\n",
"cv2.imshow(\"Detected Lines (in red) - Standard Hough Line Transform\", img)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()\n",
"# cv2.imshow(\"Detected Lines (in red) - Standard Hough Line Transform\", img)\n",
"# cv2.waitKey(0)\n",
"# cv2.destroyAllWindows()\n",
"\n",
"gray = cv2.cvtColor(resizedimg ,cv2.COLOR_BGR2GRAY)\n",
"cdst = resizedimg.copy()\n",
"\n",
"gray = cv2.cvtColor(img ,cv2.COLOR_BGR2GRAY)\n",
"cdst = img.copy()\n",
"\n",
"dst = cv2.Canny(gray, 50, 200, None, 3)\n",
"lines = cv2.HoughLines(dst, 1, np.pi/180, 100, None, 0, 0)\n"
"lines = cv2.HoughLines(dst, 1, np.pi/180, 150, None, 0, 0)\n"
]
},
{
"cell_type": "code",
"execution_count": 64,
"execution_count": 269,
"metadata": {},
"outputs": [],
"source": [
@ -105,31 +129,133 @@
" b = math.sin(theta)\n",
" x0 = a * rho\n",
" y0 = b * rho\n",
" pt1 = (int(x0 + 1000*(-b)), int(y0 + 1000*(a)))\n",
" pt2 = (int(x0 - 1000*(-b)), int(y0 - 1000*(a)))\n",
" unroundedpt1 = (x0 + 1000*(-b), y0 + 1000*(a))\n",
" unroundedpt2 = (x0 - 1000*(-b), y0 - 1000*(a))\n",
" pt1 = (int(unroundedpt1[0]), int(unroundedpt1[1]))\n",
" pt2 = (int(unroundedpt2[0]), int(unroundedpt2[1]))\n",
" v1_theta = math.atan2(pt1[1], pt1[0])\n",
" v2_theta = math.atan2(v2[1], v2[0])\n",
" angles[i] = ((v2_theta - v1_theta) * (180.0 / math.pi)) % 360\n",
" v2_theta = math.atan2(pt2[1], pt2[0])\n",
" angles[i] = abs(math.atan2(unroundedpt2[1] - unroundedpt1[1], unroundedpt2[0] - unroundedpt1[0]))\n",
" cv2.line(cdst, pt1, pt2, (0,0,255), 3, cv2.LINE_AA)"
]
},
{
"cell_type": "code",
"execution_count": 270,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"34.95042550298022\n",
"-55.04957449701978\n"
]
}
],
"source": [
"# print(st.mode(np.around(angles, decimals=1)))\n",
"mode = st.mode(np.around(angles, decimals=2))[0]\n",
"print(np.rad2deg(mode))\n",
"# slope = math.tan(np.deg2rad(mode))\n",
"# print(slope)\n",
"# myy0 = 0\n",
"# p1 = [0,myy0]\n",
"# p2 = [0,myy0]\n",
"# while (math.dist(p1, p2) < 5000):\n",
"# p2[0] += 0.5\n",
"# p2[1] += 0.5*slope*1000\n",
"# p2[1] = int(p2[1])\n",
"# print(p2)\n",
"# cv2.line(cdst, p1, p2, (0,255,0), 3, cv2.LINE_AA)\n",
"rotationangle = np.rad2deg(mode)-90\n",
"print(rotationangle)"
]
},
{
"cell_type": "code",
"execution_count": 271,
"metadata": {},
"outputs": [],
"source": [
"cv2.imshow(\"Detected Lines (in red) - Standard Hough Line Transform\", cdst)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 272,
"metadata": {},
"outputs": [],
"source": [
"cv2.imshow(\"Detected Lines (in red) - Standard Hough Line Transform\", rotate(cdst,rotationangle))\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 273,
"metadata": {},
"outputs": [],
"source": [
"rotatedimg = SquarePad(fill=255)(rotate(img, rotationangle))"
]
},
{
"cell_type": "code",
"execution_count": 274,
"metadata": {},
"outputs": [],
"source": [
"cv2.imshow(\"Rotated Image\", ResizeWithAspectRatio(rotatedimg, 1000))\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": 302,
"metadata": {},
"outputs": [],
"source": [
"resizedrotatedimg = ResizeWithAspectRatio(rotatedimg, 500)\n",
"gray1 = cv2.cvtColor(resizedrotatedimg, cv2.COLOR_BGR2GRAY)\n",
"dst1 = cv2.Canny(gray1, 50, 200, None, 3)\n",
"cdstP = resizedrotatedimg.copy()\n",
"linesP = cv2.HoughLinesP(dst1, 1, np.pi / 180, 50, None, 50, 30)"
]
},
{
"cell_type": "code",
"execution_count": 303,
"metadata": {},
"outputs": [],
"source": [
"if linesP is not None:\n",
" for i in range(0, len(linesP)):\n",
" l = linesP[i][0]\n",
" cv2.line(cdstP, (l[0], l[1]), (l[2], l[3]), (0,0,255), 3, cv2.LINE_AA)"
]
},
{
"cell_type": "code",
"execution_count": 305,
"metadata": {},
"outputs": [],
"source": [
"cv2.imshow(\"Detected Lines (in red) - Standard Hough Line Transform\", cdstP)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": []
},
{
"cell_type": "code",
"execution_count": 65,
"metadata": {},
"outputs": [],
"source": [
"cv2.imshow(\"Detected Lines (in red) - Standard Hough Line Transform\", cdst)\n",
"cv2.waitKey(0)\n",
"cv2.destroyAllWindows()"
]
}
],
"metadata": {

View File

@ -2,9 +2,20 @@
"cells": [
{
"cell_type": "code",
"execution_count": 154,
"execution_count": 1,
"metadata": {},
"outputs": [],
"outputs": [
{
"name": "stderr",
"output_type": "stream",
"text": [
"/usr/local/lib/python3.10/dist-packages/torchvision/datapoints/__init__.py:12: UserWarning: The torchvision.datapoints and torchvision.transforms.v2 namespaces are still Beta. While we do not expect major breaking changes, some APIs may still change according to user feedback. Please submit any feedback you may have in this issue: https://github.com/pytorch/vision/issues/6753, and you can also check out https://github.com/pytorch/vision/issues/7319 to learn more about the APIs that we suspect might involve future changes. You can silence this warning by calling torchvision.disable_beta_transforms_warning().\n",
" warnings.warn(_BETA_TRANSFORMS_WARNING)\n",
"/usr/local/lib/python3.10/dist-packages/torchvision/transforms/v2/__init__.py:54: UserWarning: The torchvision.datapoints and torchvision.transforms.v2 namespaces are still Beta. While we do not expect major breaking changes, some APIs may still change according to user feedback. Please submit any feedback you may have in this issue: https://github.com/pytorch/vision/issues/6753, and you can also check out https://github.com/pytorch/vision/issues/7319 to learn more about the APIs that we suspect might involve future changes. You can silence this warning by calling torchvision.disable_beta_transforms_warning().\n",
" warnings.warn(_BETA_TRANSFORMS_WARNING)\n"
]
}
],
"source": [
"import cv2\n",
"import numpy as np\n",
@ -17,7 +28,7 @@
},
{
"cell_type": "code",
"execution_count": 155,
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
@ -46,7 +57,7 @@
},
{
"cell_type": "code",
"execution_count": 156,
"execution_count": 3,
"metadata": {},
"outputs": [],
"source": [
@ -67,7 +78,7 @@
},
{
"cell_type": "code",
"execution_count": 157,
"execution_count": 4,
"metadata": {},
"outputs": [],
"source": [
@ -96,7 +107,7 @@
},
{
"cell_type": "code",
"execution_count": 158,
"execution_count": 5,
"metadata": {},
"outputs": [],
"source": [
@ -171,7 +182,7 @@
},
{
"cell_type": "code",
"execution_count": 159,
"execution_count": 6,
"metadata": {},
"outputs": [],
"source": [
@ -188,7 +199,7 @@
},
{
"cell_type": "code",
"execution_count": 160,
"execution_count": 7,
"metadata": {},
"outputs": [],
"source": [
@ -198,7 +209,7 @@
},
{
"cell_type": "code",
"execution_count": 161,
"execution_count": 8,
"metadata": {},
"outputs": [],
"source": [
@ -245,7 +256,7 @@
},
{
"cell_type": "code",
"execution_count": 162,
"execution_count": 9,
"metadata": {},
"outputs": [],
"source": [
@ -299,7 +310,7 @@
},
{
"cell_type": "code",
"execution_count": 163,
"execution_count": 10,
"metadata": {},
"outputs": [],
"source": [
@ -326,7 +337,7 @@
},
{
"cell_type": "code",
"execution_count": 164,
"execution_count": 11,
"metadata": {},
"outputs": [],
"source": [
@ -349,7 +360,7 @@
},
{
"cell_type": "code",
"execution_count": 165,
"execution_count": 12,
"metadata": {},
"outputs": [],
"source": [
@ -377,7 +388,7 @@
},
{
"cell_type": "code",
"execution_count": 166,
"execution_count": 13,
"metadata": {},
"outputs": [],
"source": [
@ -386,7 +397,7 @@
},
{
"cell_type": "code",
"execution_count": 169,
"execution_count": 15,
"metadata": {},
"outputs": [
{
@ -395,24 +406,24 @@
"True"
]
},
"execution_count": 169,
"execution_count": 15,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"cropped = morphologyCrop(img)\n",
"rotated = deskew(cropped)\n",
"cropped2 = morphologyCrop(rotated)\n",
"# rotated = deskew(cropped)\n",
"# cropped2 = morphologyCrop(rotated)\n",
"# cropped2 = selectiveSearchCrop(rotated)\n",
"# cropped3 = cannyEdgeCrop(cropped2)\n",
"cv2.imwrite(\"./testing_space/final.jpg\", cropped2)\n",
"cv2.imwrite(\"./testing_space/final.jpg\", cropped)\n",
"# final = rotate(cropped2, 180) # need to implement the code to determine if a doc is upside down"
]
},
{
"cell_type": "code",
"execution_count": 168,
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [