jdeskew 0.2.3__tar.gz → 0.3.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.github/workflows/python-package.yml +1 -1
- {jdeskew-0.2.3 → jdeskew-0.3.0}/PKG-INFO +3 -2
- jdeskew-0.3.0/icip_paper.pdf +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/jdeskew/estimator.py +30 -26
- {jdeskew-0.2.3 → jdeskew-0.3.0}/jdeskew.egg-info/PKG-INFO +3 -2
- {jdeskew-0.2.3 → jdeskew-0.3.0}/jdeskew.egg-info/SOURCES.txt +1 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.circleci/config.yml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.dockerignore +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.flake8 +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.github/workflows/codeql-analysis.yml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.github/workflows/dependency-review.yml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.github/workflows/docker-build-and-push.yml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.github/workflows/greetings.yml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.github/workflows/label.yml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.github/workflows/python-publish.yml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.github/workflows/stale.yml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.gitignore +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/.pre-commit-config.yaml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/Dockerfile +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/LICENSE +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/README.md +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/cog.yaml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/estimator.py +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/jdeskew/__init__.py +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/jdeskew/utility.py +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/jdeskew.egg-info/dependency_links.txt +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/jdeskew.egg-info/requires.txt +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/jdeskew.egg-info/top_level.txt +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/main.py +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/pyproject.toml +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/reproduce.ipynb +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/setup.cfg +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/setup.py +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/tests/test.png +0 -0
- {jdeskew-0.2.3 → jdeskew-0.3.0}/tests/test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: jdeskew
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Document Image Skew Estimation using Adaptive Radial Projection
|
|
5
5
|
Author-email: Luan Pham <phamquiluan@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -38,6 +38,7 @@ Requires-Dist: pytest-xdist; extra == "dev"
|
|
|
38
38
|
Requires-Dist: fastapi; extra == "dev"
|
|
39
39
|
Requires-Dist: uvicorn[standard]; extra == "dev"
|
|
40
40
|
Requires-Dist: python-multipart; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
41
42
|
|
|
42
43
|
# Document Image Skew Estimation
|
|
43
44
|
|
|
Binary file
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Skew Estimator."""
|
|
2
2
|
import cv2
|
|
3
3
|
import numpy as np
|
|
4
|
+
from typing import Optional
|
|
4
5
|
|
|
5
6
|
|
|
6
|
-
def _ensure_gray(image):
|
|
7
|
+
def _ensure_gray(image: np.ndarray) -> np.ndarray:
|
|
7
8
|
try:
|
|
8
9
|
image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
|
|
9
10
|
except cv2.error:
|
|
@@ -11,7 +12,7 @@ def _ensure_gray(image):
|
|
|
11
12
|
return image
|
|
12
13
|
|
|
13
14
|
|
|
14
|
-
def _ensure_optimal_square(image):
|
|
15
|
+
def _ensure_optimal_square(image: np.ndarray) -> np.ndarray:
|
|
15
16
|
assert image is not None, image
|
|
16
17
|
nw = nh = cv2.getOptimalDFTSize(max(image.shape[:2]))
|
|
17
18
|
output_image = cv2.copyMakeBorder(
|
|
@@ -26,7 +27,7 @@ def _ensure_optimal_square(image):
|
|
|
26
27
|
return output_image
|
|
27
28
|
|
|
28
29
|
|
|
29
|
-
def _get_fft_magnitude(image):
|
|
30
|
+
def _get_fft_magnitude(image: np.ndarray) -> np.ndarray:
|
|
30
31
|
gray = _ensure_gray(image)
|
|
31
32
|
opt_gray = _ensure_optimal_square(gray)
|
|
32
33
|
|
|
@@ -35,7 +36,7 @@ def _get_fft_magnitude(image):
|
|
|
35
36
|
~opt_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, -10
|
|
36
37
|
)
|
|
37
38
|
|
|
38
|
-
# perform fft
|
|
39
|
+
# perform fft - using fft2 to ensure square output
|
|
39
40
|
dft = np.fft.fft2(opt_gray)
|
|
40
41
|
shifted_dft = np.fft.fftshift(dft)
|
|
41
42
|
|
|
@@ -44,7 +45,7 @@ def _get_fft_magnitude(image):
|
|
|
44
45
|
return magnitude
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
def _get_angle_radial_projection(m, angle_max=None, num=
|
|
48
|
+
def _get_angle_radial_projection(m: np.ndarray, angle_max: Optional[float] = None, num: Optional[int] = None) -> float:
|
|
48
49
|
"""Get angle via radial projection.
|
|
49
50
|
|
|
50
51
|
Arguments:
|
|
@@ -57,42 +58,45 @@ def _get_angle_radial_projection(m, angle_max=None, num=None, W=None):
|
|
|
57
58
|
r = c = m.shape[0] // 2
|
|
58
59
|
|
|
59
60
|
if angle_max is None:
|
|
60
|
-
|
|
61
|
+
angle_max = 15.0
|
|
61
62
|
|
|
62
63
|
if num is None:
|
|
63
64
|
num = 20
|
|
64
65
|
|
|
65
66
|
tr = np.linspace(-1 * angle_max, angle_max, int(angle_max * num * 2)) / 180 * np.pi
|
|
66
|
-
profile_arr = tr.copy()
|
|
67
67
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
lambda x: m[c + int(x * np.cos(t)), c + int(-1 * x * np.sin(t))]
|
|
71
|
-
)
|
|
72
|
-
_l = _f(range(0, r))
|
|
73
|
-
val_init = np.sum(_l)
|
|
74
|
-
return val_init
|
|
68
|
+
# Pre-allocate array for better performance
|
|
69
|
+
li = np.zeros_like(tr)
|
|
75
70
|
|
|
76
|
-
|
|
77
|
-
|
|
71
|
+
for i, t in enumerate(tr):
|
|
72
|
+
x = np.arange(0, r)
|
|
73
|
+
y = c + np.int32(x * np.cos(t))
|
|
74
|
+
x = c + np.int32(-1 * x * np.sin(t))
|
|
75
|
+
# Use boolean indexing for faster computation
|
|
76
|
+
valid_indices = (y >= 0) & (y < m.shape[0]) & (x >= 0) & (x < m.shape[1])
|
|
77
|
+
li[i] = np.sum(m[y[valid_indices], x[valid_indices]])
|
|
78
78
|
|
|
79
79
|
a = tr[np.argmax(li)] / np.pi * 180
|
|
80
80
|
|
|
81
81
|
if a == -1 * angle_max:
|
|
82
|
-
return 0
|
|
83
|
-
return a
|
|
82
|
+
return 0.0
|
|
83
|
+
return float(a)
|
|
84
84
|
|
|
85
85
|
|
|
86
86
|
def get_angle(
|
|
87
|
-
image: np.ndarray,
|
|
88
|
-
|
|
87
|
+
image: np.ndarray,
|
|
88
|
+
vertical_image_shape: Optional[int] = None,
|
|
89
|
+
angle_max: Optional[float] = None
|
|
90
|
+
) -> float:
|
|
89
91
|
"""Getting angle from a given document image.
|
|
90
92
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
93
|
+
Args:
|
|
94
|
+
image: Input image as numpy array
|
|
95
|
+
vertical_image_shape: Optional resize height for preprocessing
|
|
96
|
+
angle_max: Maximum angle to search for
|
|
97
|
+
|
|
98
|
+
Returns:
|
|
99
|
+
float: Estimated skew angle in degrees
|
|
96
100
|
"""
|
|
97
101
|
assert isinstance(image, np.ndarray), image
|
|
98
102
|
|
|
@@ -100,7 +104,7 @@ def get_angle(
|
|
|
100
104
|
# vertical_image_shape = 512
|
|
101
105
|
|
|
102
106
|
if angle_max is None:
|
|
103
|
-
angle_max = 15
|
|
107
|
+
angle_max = 15.0
|
|
104
108
|
|
|
105
109
|
# resize
|
|
106
110
|
if vertical_image_shape is not None:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: jdeskew
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Document Image Skew Estimation using Adaptive Radial Projection
|
|
5
5
|
Author-email: Luan Pham <phamquiluan@gmail.com>
|
|
6
6
|
License: MIT License
|
|
@@ -38,6 +38,7 @@ Requires-Dist: pytest-xdist; extra == "dev"
|
|
|
38
38
|
Requires-Dist: fastapi; extra == "dev"
|
|
39
39
|
Requires-Dist: uvicorn[standard]; extra == "dev"
|
|
40
40
|
Requires-Dist: python-multipart; extra == "dev"
|
|
41
|
+
Dynamic: license-file
|
|
41
42
|
|
|
42
43
|
# Document Image Skew Estimation
|
|
43
44
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|