jdeskew 0.2.3__tar.gz → 0.4.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.
Files changed (39) hide show
  1. jdeskew-0.4.0/.codacy.yaml +3 -0
  2. {jdeskew-0.2.3 → jdeskew-0.4.0}/.github/workflows/python-package.yml +1 -1
  3. {jdeskew-0.2.3 → jdeskew-0.4.0}/Dockerfile +2 -0
  4. {jdeskew-0.2.3 → jdeskew-0.4.0}/PKG-INFO +29 -11
  5. {jdeskew-0.2.3 → jdeskew-0.4.0}/README.md +13 -9
  6. {jdeskew-0.2.3 → jdeskew-0.4.0}/estimator.py +1 -0
  7. jdeskew-0.4.0/icip_paper.pdf +0 -0
  8. jdeskew-0.4.0/jdeskew/estimator.py +137 -0
  9. {jdeskew-0.2.3 → jdeskew-0.4.0}/jdeskew/utility.py +22 -6
  10. {jdeskew-0.2.3 → jdeskew-0.4.0}/jdeskew.egg-info/PKG-INFO +29 -11
  11. {jdeskew-0.2.3 → jdeskew-0.4.0}/jdeskew.egg-info/SOURCES.txt +4 -0
  12. jdeskew-0.4.0/jdeskew.egg-info/scm_file_list.json +33 -0
  13. jdeskew-0.4.0/jdeskew.egg-info/scm_version.json +8 -0
  14. {jdeskew-0.2.3 → jdeskew-0.4.0}/main.py +1 -1
  15. {jdeskew-0.2.3 → jdeskew-0.4.0}/pyproject.toml +17 -1
  16. jdeskew-0.2.3/jdeskew/estimator.py +0 -112
  17. {jdeskew-0.2.3 → jdeskew-0.4.0}/.circleci/config.yml +0 -0
  18. {jdeskew-0.2.3 → jdeskew-0.4.0}/.dockerignore +0 -0
  19. {jdeskew-0.2.3 → jdeskew-0.4.0}/.flake8 +0 -0
  20. {jdeskew-0.2.3 → jdeskew-0.4.0}/.github/workflows/codeql-analysis.yml +0 -0
  21. {jdeskew-0.2.3 → jdeskew-0.4.0}/.github/workflows/dependency-review.yml +0 -0
  22. {jdeskew-0.2.3 → jdeskew-0.4.0}/.github/workflows/docker-build-and-push.yml +0 -0
  23. {jdeskew-0.2.3 → jdeskew-0.4.0}/.github/workflows/greetings.yml +0 -0
  24. {jdeskew-0.2.3 → jdeskew-0.4.0}/.github/workflows/label.yml +0 -0
  25. {jdeskew-0.2.3 → jdeskew-0.4.0}/.github/workflows/python-publish.yml +0 -0
  26. {jdeskew-0.2.3 → jdeskew-0.4.0}/.github/workflows/stale.yml +0 -0
  27. {jdeskew-0.2.3 → jdeskew-0.4.0}/.gitignore +0 -0
  28. {jdeskew-0.2.3 → jdeskew-0.4.0}/.pre-commit-config.yaml +0 -0
  29. {jdeskew-0.2.3 → jdeskew-0.4.0}/LICENSE +0 -0
  30. {jdeskew-0.2.3 → jdeskew-0.4.0}/cog.yaml +0 -0
  31. {jdeskew-0.2.3 → jdeskew-0.4.0}/jdeskew/__init__.py +0 -0
  32. {jdeskew-0.2.3 → jdeskew-0.4.0}/jdeskew.egg-info/dependency_links.txt +0 -0
  33. {jdeskew-0.2.3 → jdeskew-0.4.0}/jdeskew.egg-info/requires.txt +0 -0
  34. {jdeskew-0.2.3 → jdeskew-0.4.0}/jdeskew.egg-info/top_level.txt +0 -0
  35. {jdeskew-0.2.3 → jdeskew-0.4.0}/reproduce.ipynb +0 -0
  36. {jdeskew-0.2.3 → jdeskew-0.4.0}/setup.cfg +0 -0
  37. {jdeskew-0.2.3 → jdeskew-0.4.0}/setup.py +0 -0
  38. {jdeskew-0.2.3 → jdeskew-0.4.0}/tests/test.png +0 -0
  39. {jdeskew-0.2.3 → jdeskew-0.4.0}/tests/test.py +0 -0
@@ -0,0 +1,3 @@
1
+ ---
2
+ exclude_paths:
3
+ - "tests/**"
@@ -15,7 +15,7 @@ jobs:
15
15
  strategy:
16
16
  fail-fast: false
17
17
  matrix:
18
- python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"]
18
+ python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"]
19
19
 
20
20
  steps:
21
21
  - uses: actions/checkout@v3
@@ -1,6 +1,7 @@
1
1
  FROM ubuntu:20.04
2
2
  ARG DEBIAN_FRONTEND=noninteractive
3
3
 
4
+ # hadolint ignore=DL3008
4
5
  RUN apt-get update && apt-get install --no-install-recommends -y python3.8 python3.8-dev python3-pip ffmpeg\
5
6
  && apt-get clean \
6
7
  && rm -rf /var/lib/apt/lists/*
@@ -11,6 +12,7 @@ RUN pip install pip==22.0.3
11
12
  WORKDIR /app
12
13
  COPY . /app/
13
14
 
15
+ # hadolint ignore=DL3013
14
16
  RUN pip install .[dev]
15
17
  EXPOSE 80
16
18
  ENTRYPOINT ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: jdeskew
3
- Version: 0.2.3
3
+ Version: 0.4.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
@@ -25,6 +25,19 @@ License: MIT License
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
27
 
28
+ Classifier: Intended Audience :: Developers
29
+ Classifier: Intended Audience :: Science/Research
30
+ Classifier: Operating System :: OS Independent
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3.8
33
+ Classifier: Programming Language :: Python :: 3.9
34
+ Classifier: Programming Language :: Python :: 3.10
35
+ Classifier: Programming Language :: Python :: 3.11
36
+ Classifier: Programming Language :: Python :: 3.12
37
+ Classifier: Programming Language :: Python :: 3.13
38
+ Classifier: Programming Language :: Python :: 3.14
39
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
40
+ Requires-Python: >=3.8
28
41
  Description-Content-Type: text/markdown
29
42
  License-File: LICENSE
30
43
  Requires-Dist: numpy
@@ -38,6 +51,7 @@ Requires-Dist: pytest-xdist; extra == "dev"
38
51
  Requires-Dist: fastapi; extra == "dev"
39
52
  Requires-Dist: uvicorn[standard]; extra == "dev"
40
53
  Requires-Dist: python-multipart; extra == "dev"
54
+ Dynamic: license-file
41
55
 
42
56
  # Document Image Skew Estimation
43
57
 
@@ -46,11 +60,9 @@ Requires-Dist: python-multipart; extra == "dev"
46
60
  [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/25553a5195074e37a01dd3370c55abaa)](https://www.codacy.com/gh/phamquiluan/jdeskew/dashboard?utm_source=github.com&utm_medium=referral&utm_content=phamquiluan/jdeskew&utm_campaign=Badge_Coverage)
47
61
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/25553a5195074e37a01dd3370c55abaa)](https://www.codacy.com/gh/phamquiluan/jdeskew/dashboard?utm_source=github.com&utm_medium=referral&utm_content=phamquiluan/jdeskew&utm_campaign=Badge_Grade)
48
62
  [![Downloads](https://static.pepy.tech/personalized-badge/jdeskew?period=total&units=international_system&left_color=black&right_color=orange&left_text=Downloads)](https://pepy.tech/project/jdeskew)
49
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/dependency-review.yml/badge.svg)
50
63
  ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/python-package.yml/badge.svg)
51
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/docker-build-and-push.yml/badge.svg)
52
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/python-publish.yml/badge.svg)
53
- [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/adaptive-radial-projection-on-fourier/document-image-skew-estimation-on-dise-2021)](https://paperswithcode.com/sota/document-image-skew-estimation-on-dise-2021?p=adaptive-radial-projection-on-fourier)
64
+ [![HF Paper](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Paper-yellow)](https://huggingface.co/papers/2603.05942)
65
+ [![HF Dataset](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Dataset-blue)](https://huggingface.co/datasets/phamquiluan/DISE-2021)
54
66
 
55
67
 
56
68
  ![Cover Image](https://user-images.githubusercontent.com/24642166/165683091-4091bb3c-6625-4180-93b6-86deec9a0750.gif)
@@ -81,6 +93,12 @@ Requires-Dist: python-multipart; extra == "dev"
81
93
  pip install jdeskew
82
94
  ```
83
95
 
96
+ ### conda
97
+
98
+ ```bash
99
+ conda create --name jdeskew -c conda-forge jdeskew
100
+ ```
101
+
84
102
  ## How-to-use
85
103
 
86
104
  ### using python
@@ -131,6 +149,8 @@ Link1: https://ieeexplore.ieee.org/document/9897910
131
149
 
132
150
  Link2: https://www.researchgate.net/publication/364320913_ADAPTIVE_RADIAL_PROJECTION_ON_FOURIER_MAGNITUDE_SPECTRUM_FOR_DOCUMENT_IMAGE_SKEW_ESTIMATION
133
151
 
152
+ Link3: https://huggingface.co/papers/2603.05942
153
+
134
154
 
135
155
  ## Performance Comparison on DISE 2021
136
156
 
@@ -161,7 +181,9 @@ This datasets are built upon three other datasets: DISEC 2013, RVL-CDIP, RDCL 20
161
181
  | DISE 2021 (45 degree) | https://drive.google.com/file/d/1a-a6aOqdsghjeHGLnCLsDs7NoJIus-Pw/view?usp=sharing |
162
182
  | DISE 2021 (15 degree) | https://drive.google.com/file/d/1BLiuu-j28dbuPFi4n3C0KuV6vXGmB0qS/view?usp=sharing |
163
183
 
164
- Can also download from Zenodo: https://zenodo.org/records/12570649
184
+ Can also download from Hugging Face: https://huggingface.co/datasets/phamquiluan/DISE-2021
185
+
186
+ Or from Zenodo: https://zenodo.org/records/12570649
165
187
 
166
188
  ## Reproducibility and Evaluation Code
167
189
 
@@ -180,7 +202,3 @@ L. Pham, H. Hoang, X.T. Mai, T. A. Tran, "Adaptive Radial Projection on Fourier
180
202
  organization={IEEE}
181
203
  }
182
204
  ```
183
-
184
- ## Star History
185
-
186
- [![Star History Chart](https://api.star-history.com/svg?repos=phamquiluan/jdeskew&type=Date)](https://star-history.com/#phamquiluan/jdeskew&Date)
@@ -5,11 +5,9 @@
5
5
  [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/25553a5195074e37a01dd3370c55abaa)](https://www.codacy.com/gh/phamquiluan/jdeskew/dashboard?utm_source=github.com&utm_medium=referral&utm_content=phamquiluan/jdeskew&utm_campaign=Badge_Coverage)
6
6
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/25553a5195074e37a01dd3370c55abaa)](https://www.codacy.com/gh/phamquiluan/jdeskew/dashboard?utm_source=github.com&utm_medium=referral&utm_content=phamquiluan/jdeskew&utm_campaign=Badge_Grade)
7
7
  [![Downloads](https://static.pepy.tech/personalized-badge/jdeskew?period=total&units=international_system&left_color=black&right_color=orange&left_text=Downloads)](https://pepy.tech/project/jdeskew)
8
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/dependency-review.yml/badge.svg)
9
8
  ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/python-package.yml/badge.svg)
10
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/docker-build-and-push.yml/badge.svg)
11
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/python-publish.yml/badge.svg)
12
- [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/adaptive-radial-projection-on-fourier/document-image-skew-estimation-on-dise-2021)](https://paperswithcode.com/sota/document-image-skew-estimation-on-dise-2021?p=adaptive-radial-projection-on-fourier)
9
+ [![HF Paper](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Paper-yellow)](https://huggingface.co/papers/2603.05942)
10
+ [![HF Dataset](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Dataset-blue)](https://huggingface.co/datasets/phamquiluan/DISE-2021)
13
11
 
14
12
 
15
13
  ![Cover Image](https://user-images.githubusercontent.com/24642166/165683091-4091bb3c-6625-4180-93b6-86deec9a0750.gif)
@@ -40,6 +38,12 @@
40
38
  pip install jdeskew
41
39
  ```
42
40
 
41
+ ### conda
42
+
43
+ ```bash
44
+ conda create --name jdeskew -c conda-forge jdeskew
45
+ ```
46
+
43
47
  ## How-to-use
44
48
 
45
49
  ### using python
@@ -90,6 +94,8 @@ Link1: https://ieeexplore.ieee.org/document/9897910
90
94
 
91
95
  Link2: https://www.researchgate.net/publication/364320913_ADAPTIVE_RADIAL_PROJECTION_ON_FOURIER_MAGNITUDE_SPECTRUM_FOR_DOCUMENT_IMAGE_SKEW_ESTIMATION
92
96
 
97
+ Link3: https://huggingface.co/papers/2603.05942
98
+
93
99
 
94
100
  ## Performance Comparison on DISE 2021
95
101
 
@@ -120,7 +126,9 @@ This datasets are built upon three other datasets: DISEC 2013, RVL-CDIP, RDCL 20
120
126
  | DISE 2021 (45 degree) | https://drive.google.com/file/d/1a-a6aOqdsghjeHGLnCLsDs7NoJIus-Pw/view?usp=sharing |
121
127
  | DISE 2021 (15 degree) | https://drive.google.com/file/d/1BLiuu-j28dbuPFi4n3C0KuV6vXGmB0qS/view?usp=sharing |
122
128
 
123
- Can also download from Zenodo: https://zenodo.org/records/12570649
129
+ Can also download from Hugging Face: https://huggingface.co/datasets/phamquiluan/DISE-2021
130
+
131
+ Or from Zenodo: https://zenodo.org/records/12570649
124
132
 
125
133
  ## Reproducibility and Evaluation Code
126
134
 
@@ -139,7 +147,3 @@ L. Pham, H. Hoang, X.T. Mai, T. A. Tran, "Adaptive Radial Projection on Fourier
139
147
  organization={IEEE}
140
148
  }
141
149
  ```
142
-
143
- ## Star History
144
-
145
- [![Star History Chart](https://api.star-history.com/svg?repos=phamquiluan/jdeskew&type=Date)](https://star-history.com/#phamquiluan/jdeskew&Date)
@@ -9,6 +9,7 @@ from jdeskew.estimator import get_angle
9
9
 
10
10
 
11
11
  class Estimator(BasePredictor):
12
+
12
13
  """Cog estimator."""
13
14
 
14
15
  def predict(self, input_path: Path = Input()) -> Dict:
Binary file
@@ -0,0 +1,137 @@
1
+ """Skew Estimator."""
2
+ import cv2
3
+ import numpy as np
4
+ from typing import Optional
5
+
6
+
7
+ def _ensure_gray(image: np.ndarray) -> np.ndarray:
8
+ try:
9
+ image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
10
+ except cv2.error:
11
+ pass
12
+ return image
13
+
14
+
15
+ def _ensure_optimal_square(image: np.ndarray) -> np.ndarray:
16
+ if image is None:
17
+ raise ValueError("image must not be None")
18
+ nw = nh = cv2.getOptimalDFTSize(max(image.shape[:2]))
19
+ output_image = cv2.copyMakeBorder(
20
+ src=image,
21
+ top=0,
22
+ bottom=nh - image.shape[0],
23
+ left=0,
24
+ right=nw - image.shape[1],
25
+ borderType=cv2.BORDER_CONSTANT,
26
+ value=255,
27
+ )
28
+ return output_image
29
+
30
+
31
+ def _get_fft_magnitude(image: np.ndarray) -> np.ndarray:
32
+ gray = _ensure_gray(image)
33
+ opt_gray = _ensure_optimal_square(gray)
34
+
35
+ # thresh
36
+ opt_gray = cv2.adaptiveThreshold(
37
+ ~opt_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, -10
38
+ )
39
+
40
+ # perform fft - using fft2 to ensure square output
41
+ dft = np.fft.fft2(opt_gray)
42
+ shifted_dft = np.fft.fftshift(dft)
43
+
44
+ # get the magnitude (module)
45
+ magnitude = np.abs(shifted_dft)
46
+ return magnitude
47
+
48
+
49
+ def _get_angle_radial_projection(m: np.ndarray, angle_max: Optional[float] = None, num: Optional[int] = None) -> float:
50
+ """
51
+ Get angle via radial projection.
52
+
53
+ Parameters
54
+ ----------
55
+ m : np.ndarray
56
+ Square FFT magnitude spectrum of the image.
57
+ angle_max : float, optional
58
+ Maximum angle to search for, in degrees.
59
+ num : int, optional
60
+ Number of angles to generate between 1 degree.
61
+
62
+ Returns
63
+ -------
64
+ float
65
+ Estimated skew angle in degrees.
66
+
67
+ """
68
+ if m.shape[0] != m.shape[1]:
69
+ raise ValueError(f"expected a square matrix, got shape {m.shape}")
70
+ r = c = m.shape[0] // 2
71
+
72
+ if angle_max is None:
73
+ angle_max = 15.0
74
+
75
+ if num is None:
76
+ num = 20
77
+
78
+ tr = np.linspace(-1 * angle_max, angle_max, int(angle_max * num * 2)) / 180 * np.pi
79
+
80
+ # Pre-allocate array for better performance
81
+ li = np.zeros_like(tr)
82
+
83
+ for i, t in enumerate(tr):
84
+ x = np.arange(0, r)
85
+ y = c + np.int32(x * np.cos(t))
86
+ x = c + np.int32(-1 * x * np.sin(t))
87
+ # Use boolean indexing for faster computation
88
+ valid_indices = (y >= 0) & (y < m.shape[0]) & (x >= 0) & (x < m.shape[1])
89
+ li[i] = np.sum(m[y[valid_indices], x[valid_indices]])
90
+
91
+ a = tr[np.argmax(li)] / np.pi * 180
92
+
93
+ if a == -1 * angle_max:
94
+ return 0.0
95
+ return float(a)
96
+
97
+
98
+ def get_angle(
99
+ image: np.ndarray,
100
+ vertical_image_shape: Optional[int] = None,
101
+ angle_max: Optional[float] = None
102
+ ) -> float:
103
+ """
104
+ Get angle from a given document image.
105
+
106
+ Parameters
107
+ ----------
108
+ image : np.ndarray
109
+ Input image as numpy array.
110
+ vertical_image_shape : int, optional
111
+ Resize height for preprocessing.
112
+ angle_max : float, optional
113
+ Maximum angle to search for, in degrees.
114
+
115
+ Returns
116
+ -------
117
+ float
118
+ Estimated skew angle in degrees.
119
+
120
+ """
121
+ if not isinstance(image, np.ndarray):
122
+ raise TypeError(f"image must be a numpy array, got {type(image)}")
123
+
124
+ # if vertical_image_shape is None:
125
+ # vertical_image_shape = 512
126
+
127
+ if angle_max is None:
128
+ angle_max = 15.0
129
+
130
+ # resize
131
+ if vertical_image_shape is not None:
132
+ ratio = vertical_image_shape / image.shape[0]
133
+ image = cv2.resize(image, None, fx=ratio, fy=ratio)
134
+
135
+ m = _get_fft_magnitude(image)
136
+ a = _get_angle_radial_projection(m, angle_max=angle_max)
137
+ return a
@@ -3,13 +3,29 @@ import cv2
3
3
 
4
4
 
5
5
  def rotate(image, angle, resize=True, border_mode=None, border_value=None, flags=None):
6
- """Rotate input image respect to a given angle.
6
+ """
7
+ Rotate input image with respect to a given angle.
8
+
9
+ Parameters
10
+ ----------
11
+ image : np.ndarray
12
+ Input image.
13
+ angle : float
14
+ Rotation angle in degrees.
15
+ resize : bool
16
+ Resize output to the input image shape.
17
+ border_mode : int, optional
18
+ cv2.BORDER_REPLICATE or cv2.BORDER_CONSTANT.
19
+ border_value : optional
20
+ Border value used when border_mode is cv2.BORDER_CONSTANT.
21
+ flags : int, optional
22
+ OpenCV interpolation flags.
23
+
24
+ Returns
25
+ -------
26
+ np.ndarray
27
+ Rotated image.
7
28
 
8
- Params:
9
- --------
10
- resize (Bool) : resize to input image shape
11
- border_mode : cv2.BORDER_REPLICATE, cv2.BORDER_CONSTANT
12
- border_value : when border_mode == cv2.BORDER_CONSTANT
13
29
  """
14
30
  if border_mode is None:
15
31
  border_mode = cv2.BORDER_CONSTANT
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: jdeskew
3
- Version: 0.2.3
3
+ Version: 0.4.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
@@ -25,6 +25,19 @@ License: MIT License
25
25
  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
26
  SOFTWARE.
27
27
 
28
+ Classifier: Intended Audience :: Developers
29
+ Classifier: Intended Audience :: Science/Research
30
+ Classifier: Operating System :: OS Independent
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: Programming Language :: Python :: 3.8
33
+ Classifier: Programming Language :: Python :: 3.9
34
+ Classifier: Programming Language :: Python :: 3.10
35
+ Classifier: Programming Language :: Python :: 3.11
36
+ Classifier: Programming Language :: Python :: 3.12
37
+ Classifier: Programming Language :: Python :: 3.13
38
+ Classifier: Programming Language :: Python :: 3.14
39
+ Classifier: Topic :: Scientific/Engineering :: Image Processing
40
+ Requires-Python: >=3.8
28
41
  Description-Content-Type: text/markdown
29
42
  License-File: LICENSE
30
43
  Requires-Dist: numpy
@@ -38,6 +51,7 @@ Requires-Dist: pytest-xdist; extra == "dev"
38
51
  Requires-Dist: fastapi; extra == "dev"
39
52
  Requires-Dist: uvicorn[standard]; extra == "dev"
40
53
  Requires-Dist: python-multipart; extra == "dev"
54
+ Dynamic: license-file
41
55
 
42
56
  # Document Image Skew Estimation
43
57
 
@@ -46,11 +60,9 @@ Requires-Dist: python-multipart; extra == "dev"
46
60
  [![Codacy Badge](https://app.codacy.com/project/badge/Coverage/25553a5195074e37a01dd3370c55abaa)](https://www.codacy.com/gh/phamquiluan/jdeskew/dashboard?utm_source=github.com&utm_medium=referral&utm_content=phamquiluan/jdeskew&utm_campaign=Badge_Coverage)
47
61
  [![Codacy Badge](https://app.codacy.com/project/badge/Grade/25553a5195074e37a01dd3370c55abaa)](https://www.codacy.com/gh/phamquiluan/jdeskew/dashboard?utm_source=github.com&utm_medium=referral&utm_content=phamquiluan/jdeskew&utm_campaign=Badge_Grade)
48
62
  [![Downloads](https://static.pepy.tech/personalized-badge/jdeskew?period=total&units=international_system&left_color=black&right_color=orange&left_text=Downloads)](https://pepy.tech/project/jdeskew)
49
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/dependency-review.yml/badge.svg)
50
63
  ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/python-package.yml/badge.svg)
51
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/docker-build-and-push.yml/badge.svg)
52
- ![example workflow](https://github.com/phamquiluan/jdeskew/actions/workflows/python-publish.yml/badge.svg)
53
- [![PWC](https://img.shields.io/endpoint.svg?url=https://paperswithcode.com/badge/adaptive-radial-projection-on-fourier/document-image-skew-estimation-on-dise-2021)](https://paperswithcode.com/sota/document-image-skew-estimation-on-dise-2021?p=adaptive-radial-projection-on-fourier)
64
+ [![HF Paper](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Paper-yellow)](https://huggingface.co/papers/2603.05942)
65
+ [![HF Dataset](https://img.shields.io/badge/%F0%9F%A4%97%20Hugging%20Face-Dataset-blue)](https://huggingface.co/datasets/phamquiluan/DISE-2021)
54
66
 
55
67
 
56
68
  ![Cover Image](https://user-images.githubusercontent.com/24642166/165683091-4091bb3c-6625-4180-93b6-86deec9a0750.gif)
@@ -81,6 +93,12 @@ Requires-Dist: python-multipart; extra == "dev"
81
93
  pip install jdeskew
82
94
  ```
83
95
 
96
+ ### conda
97
+
98
+ ```bash
99
+ conda create --name jdeskew -c conda-forge jdeskew
100
+ ```
101
+
84
102
  ## How-to-use
85
103
 
86
104
  ### using python
@@ -131,6 +149,8 @@ Link1: https://ieeexplore.ieee.org/document/9897910
131
149
 
132
150
  Link2: https://www.researchgate.net/publication/364320913_ADAPTIVE_RADIAL_PROJECTION_ON_FOURIER_MAGNITUDE_SPECTRUM_FOR_DOCUMENT_IMAGE_SKEW_ESTIMATION
133
151
 
152
+ Link3: https://huggingface.co/papers/2603.05942
153
+
134
154
 
135
155
  ## Performance Comparison on DISE 2021
136
156
 
@@ -161,7 +181,9 @@ This datasets are built upon three other datasets: DISEC 2013, RVL-CDIP, RDCL 20
161
181
  | DISE 2021 (45 degree) | https://drive.google.com/file/d/1a-a6aOqdsghjeHGLnCLsDs7NoJIus-Pw/view?usp=sharing |
162
182
  | DISE 2021 (15 degree) | https://drive.google.com/file/d/1BLiuu-j28dbuPFi4n3C0KuV6vXGmB0qS/view?usp=sharing |
163
183
 
164
- Can also download from Zenodo: https://zenodo.org/records/12570649
184
+ Can also download from Hugging Face: https://huggingface.co/datasets/phamquiluan/DISE-2021
185
+
186
+ Or from Zenodo: https://zenodo.org/records/12570649
165
187
 
166
188
  ## Reproducibility and Evaluation Code
167
189
 
@@ -180,7 +202,3 @@ L. Pham, H. Hoang, X.T. Mai, T. A. Tran, "Adaptive Radial Projection on Fourier
180
202
  organization={IEEE}
181
203
  }
182
204
  ```
183
-
184
- ## Star History
185
-
186
- [![Star History Chart](https://api.star-history.com/svg?repos=phamquiluan/jdeskew&type=Date)](https://star-history.com/#phamquiluan/jdeskew&Date)
@@ -1,3 +1,4 @@
1
+ .codacy.yaml
1
2
  .dockerignore
2
3
  .flake8
3
4
  .gitignore
@@ -7,6 +8,7 @@ LICENSE
7
8
  README.md
8
9
  cog.yaml
9
10
  estimator.py
11
+ icip_paper.pdf
10
12
  main.py
11
13
  pyproject.toml
12
14
  reproduce.ipynb
@@ -27,6 +29,8 @@ jdeskew.egg-info/PKG-INFO
27
29
  jdeskew.egg-info/SOURCES.txt
28
30
  jdeskew.egg-info/dependency_links.txt
29
31
  jdeskew.egg-info/requires.txt
32
+ jdeskew.egg-info/scm_file_list.json
33
+ jdeskew.egg-info/scm_version.json
30
34
  jdeskew.egg-info/top_level.txt
31
35
  tests/test.png
32
36
  tests/test.py
@@ -0,0 +1,33 @@
1
+ {
2
+ "files": [
3
+ "main.py",
4
+ ".dockerignore",
5
+ "icip_paper.pdf",
6
+ ".pre-commit-config.yaml",
7
+ "Dockerfile",
8
+ "setup.py",
9
+ "pyproject.toml",
10
+ ".codacy.yaml",
11
+ "LICENSE",
12
+ "reproduce.ipynb",
13
+ "README.md",
14
+ ".flake8",
15
+ ".gitignore",
16
+ "estimator.py",
17
+ "cog.yaml",
18
+ ".github/workflows/dependency-review.yml",
19
+ ".github/workflows/docker-build-and-push.yml",
20
+ ".github/workflows/greetings.yml",
21
+ ".github/workflows/codeql-analysis.yml",
22
+ ".github/workflows/python-publish.yml",
23
+ ".github/workflows/label.yml",
24
+ ".github/workflows/python-package.yml",
25
+ ".github/workflows/stale.yml",
26
+ "tests/test.py",
27
+ "tests/test.png",
28
+ "jdeskew/utility.py",
29
+ "jdeskew/__init__.py",
30
+ "jdeskew/estimator.py",
31
+ ".circleci/config.yml"
32
+ ]
33
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "tag": "0.4.0",
3
+ "distance": 0,
4
+ "node": "g4d690b28c74341a36eb33b9b068cc6a29f9bd93c",
5
+ "dirty": false,
6
+ "branch": "HEAD",
7
+ "node_date": "2026-08-01"
8
+ }
@@ -6,7 +6,7 @@ app = FastAPI()
6
6
 
7
7
  @app.post("/predict")
8
8
  def predict(file: UploadFile):
9
- """simple predict function."""
9
+ """Simple predict function."""
10
10
  # read image
11
11
  import cv2
12
12
  import numpy as np
@@ -11,6 +11,21 @@ name = "jdeskew"
11
11
  description = "Document Image Skew Estimation using Adaptive Radial Projection"
12
12
  authors = [{name = "Luan Pham", email = "phamquiluan@gmail.com"}]
13
13
  license = {file = "LICENSE"}
14
+ requires-python = ">=3.8"
15
+ classifiers = [
16
+ "Intended Audience :: Developers",
17
+ "Intended Audience :: Science/Research",
18
+ "Operating System :: OS Independent",
19
+ "Programming Language :: Python :: 3",
20
+ "Programming Language :: Python :: 3.8",
21
+ "Programming Language :: Python :: 3.9",
22
+ "Programming Language :: Python :: 3.10",
23
+ "Programming Language :: Python :: 3.11",
24
+ "Programming Language :: Python :: 3.12",
25
+ "Programming Language :: Python :: 3.13",
26
+ "Programming Language :: Python :: 3.14",
27
+ "Topic :: Scientific/Engineering :: Image Processing",
28
+ ]
14
29
  dependencies = [
15
30
  "numpy",
16
31
  "opencv-python-headless"
@@ -64,4 +79,5 @@ exclude = '''
64
79
  [tool.pytest.ini_options]
65
80
  minversion = "6.0"
66
81
  addopts = "-ra -q"
67
- testpaths = [ "tests/test.png" ]
82
+ testpaths = [ "tests" ]
83
+ python_files = [ "test.py", "test_*.py" ]
@@ -1,112 +0,0 @@
1
- """Skew Estimator."""
2
- import cv2
3
- import numpy as np
4
-
5
-
6
- def _ensure_gray(image):
7
- try:
8
- image = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
9
- except cv2.error:
10
- pass
11
- return image
12
-
13
-
14
- def _ensure_optimal_square(image):
15
- assert image is not None, image
16
- nw = nh = cv2.getOptimalDFTSize(max(image.shape[:2]))
17
- output_image = cv2.copyMakeBorder(
18
- src=image,
19
- top=0,
20
- bottom=nh - image.shape[0],
21
- left=0,
22
- right=nw - image.shape[1],
23
- borderType=cv2.BORDER_CONSTANT,
24
- value=255,
25
- )
26
- return output_image
27
-
28
-
29
- def _get_fft_magnitude(image):
30
- gray = _ensure_gray(image)
31
- opt_gray = _ensure_optimal_square(gray)
32
-
33
- # thresh
34
- opt_gray = cv2.adaptiveThreshold(
35
- ~opt_gray, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 15, -10
36
- )
37
-
38
- # perform fft
39
- dft = np.fft.fft2(opt_gray)
40
- shifted_dft = np.fft.fftshift(dft)
41
-
42
- # get the magnitude (module)
43
- magnitude = np.abs(shifted_dft)
44
- return magnitude
45
-
46
-
47
- def _get_angle_radial_projection(m, angle_max=None, num=None, W=None):
48
- """Get angle via radial projection.
49
-
50
- Arguments:
51
- ------------
52
- angle_max : float
53
- num : int
54
- number of angles to generate between 1 degree
55
- """
56
- assert m.shape[0] == m.shape[1]
57
- r = c = m.shape[0] // 2
58
-
59
- if angle_max is None:
60
- pass
61
-
62
- if num is None:
63
- num = 20
64
-
65
- tr = np.linspace(-1 * angle_max, angle_max, int(angle_max * num * 2)) / 180 * np.pi
66
- profile_arr = tr.copy()
67
-
68
- def f(t):
69
- _f = np.vectorize(
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
75
-
76
- vf = np.vectorize(f)
77
- li = vf(profile_arr)
78
-
79
- a = tr[np.argmax(li)] / np.pi * 180
80
-
81
- if a == -1 * angle_max:
82
- return 0
83
- return a
84
-
85
-
86
- def get_angle(
87
- image: np.ndarray, vertical_image_shape: int = None, angle_max: float = None
88
- ):
89
- """Getting angle from a given document image.
90
-
91
- image : np.ndarray
92
- vertical_image_shape : int
93
- resize image as preprocessing
94
- angle_max : float
95
- maximum angle to searching
96
- """
97
- assert isinstance(image, np.ndarray), image
98
-
99
- # if vertical_image_shape is None:
100
- # vertical_image_shape = 512
101
-
102
- if angle_max is None:
103
- angle_max = 15
104
-
105
- # resize
106
- if vertical_image_shape is not None:
107
- ratio = vertical_image_shape / image.shape[0]
108
- image = cv2.resize(image, None, fx=ratio, fy=ratio)
109
-
110
- m = _get_fft_magnitude(image)
111
- a = _get_angle_radial_projection(m, angle_max=angle_max)
112
- return a
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