pixrr 0.1.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.
- pixrr-0.1.0/LICENSE +21 -0
- pixrr-0.1.0/PKG-INFO +128 -0
- pixrr-0.1.0/README.md +83 -0
- pixrr-0.1.0/pyproject.toml +40 -0
- pixrr-0.1.0/setup.cfg +4 -0
- pixrr-0.1.0/src/pixrr/__init__.py +40 -0
- pixrr-0.1.0/src/pixrr/edges.py +205 -0
- pixrr-0.1.0/src/pixrr/enhance.py +178 -0
- pixrr-0.1.0/src/pixrr/filters.py +191 -0
- pixrr-0.1.0/src/pixrr/io.py +270 -0
- pixrr-0.1.0/src/pixrr/segmentation.py +171 -0
- pixrr-0.1.0/src/pixrr/threshold.py +70 -0
- pixrr-0.1.0/src/pixrr/utils.py +35 -0
- pixrr-0.1.0/src/pixrr.egg-info/PKG-INFO +128 -0
- pixrr-0.1.0/src/pixrr.egg-info/SOURCES.txt +17 -0
- pixrr-0.1.0/src/pixrr.egg-info/dependency_links.txt +1 -0
- pixrr-0.1.0/src/pixrr.egg-info/requires.txt +5 -0
- pixrr-0.1.0/src/pixrr.egg-info/top_level.txt +1 -0
- pixrr-0.1.0/tests/test_basic.py +20 -0
pixrr-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) <2025> <Hrishikesh Tiwari>
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
pixrr-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pixrr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A lightweight image processing toolkit for Python
|
|
5
|
+
Author-email: Hrishikesh Tiwari <tiwarihrishikesh686@gmail.com>
|
|
6
|
+
License: MIT License
|
|
7
|
+
|
|
8
|
+
Copyright (c) <2025> <Hrishikesh Tiwari>
|
|
9
|
+
|
|
10
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
11
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
12
|
+
in the Software without restriction, including without limitation the rights
|
|
13
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
14
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
15
|
+
furnished to do so, subject to the following conditions:
|
|
16
|
+
|
|
17
|
+
The above copyright notice and this permission notice shall be included in all
|
|
18
|
+
copies or substantial portions of the Software.
|
|
19
|
+
|
|
20
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
21
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
22
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
23
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
24
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
25
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
26
|
+
SOFTWARE.
|
|
27
|
+
|
|
28
|
+
Project-URL: Homepage, https://github.com/Hrishi11572/pixrr
|
|
29
|
+
Project-URL: Documentation, https://github.com/Hrishi11572/pixrr/tree/main/docs
|
|
30
|
+
Keywords: image-processing,computer-vision,education,filters,numpy
|
|
31
|
+
Classifier: Programming Language :: Python :: 3
|
|
32
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
33
|
+
Classifier: Operating System :: OS Independent
|
|
34
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
35
|
+
Classifier: Intended Audience :: Education
|
|
36
|
+
Requires-Python: >=3.9
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
License-File: LICENSE
|
|
39
|
+
Requires-Dist: numpy>=2.3.5
|
|
40
|
+
Requires-Dist: scipy>=1.16.3
|
|
41
|
+
Requires-Dist: matplotlib>=3.10.7
|
|
42
|
+
Requires-Dist: pillow>=12.0.0
|
|
43
|
+
Requires-Dist: numba>=0.63.1
|
|
44
|
+
Dynamic: license-file
|
|
45
|
+
|
|
46
|
+
# **pixrr : A lightweight image processing toolkit for python**
|
|
47
|
+
|
|
48
|
+
`pixrr` is a lightweight, beginner-friendly image processing library built for fast experimentation and teaching.
|
|
49
|
+
It focuses on simplicity, clean function names, and easy-to-understand code, making it useful both for quick image tasks and for pedagogical environments such as introductory image processing courses.
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Features (Planned & Implemented)
|
|
54
|
+
|
|
55
|
+
### **Core Utilities**
|
|
56
|
+
|
|
57
|
+
* Convert color images to grayscale and binary
|
|
58
|
+
* Basic I/O helpers (read, write, display)
|
|
59
|
+
* Plot and analyze histograms
|
|
60
|
+
* Image thresholding
|
|
61
|
+
* Image Cropping and Rotation (planned)
|
|
62
|
+
|
|
63
|
+
### **Filtering & Enhancement**
|
|
64
|
+
|
|
65
|
+
* Convolution using masks
|
|
66
|
+
* Convolution using FFT (planned)
|
|
67
|
+
* Contrast enhancement
|
|
68
|
+
* Histogram equalization
|
|
69
|
+
* Smoothing filters (Gaussian, median, weighted median)
|
|
70
|
+
* Sharpening and Laplacian operators
|
|
71
|
+
|
|
72
|
+
### **Edge and Gradient Detection**
|
|
73
|
+
|
|
74
|
+
* Prewitt and Sobel (horizontal & vertical)
|
|
75
|
+
* Second-order derivatives
|
|
76
|
+
* Canny edge detector (planned)
|
|
77
|
+
|
|
78
|
+
### **Segmentation & Classification**
|
|
79
|
+
|
|
80
|
+
* Otsu thresholding
|
|
81
|
+
* K-means clustering for image segmentation
|
|
82
|
+
* Mean-Shift clustering (planned)
|
|
83
|
+
* EM/Bayesian pixel classification (planned)
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Project Roadmap
|
|
88
|
+
|
|
89
|
+
| Stage | Goal |
|
|
90
|
+
| ------ | ------------------------------------------------------- |
|
|
91
|
+
| Part 1 | Basic conversions, contour extraction, histogram tools |
|
|
92
|
+
| Part 2 | Convolution, enhancement techniques, gradient operators |
|
|
93
|
+
| Part 3 | Automatic segmentation & clustering |
|
|
94
|
+
| Part 4 | Advanced edge detection and EM classification |
|
|
95
|
+
|
|
96
|
+
---
|
|
97
|
+
|
|
98
|
+
## Installation (when it’s live)
|
|
99
|
+
|
|
100
|
+
```bash
|
|
101
|
+
pip install pixrr
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
*(Currently under development. Not on PyPI yet.)*
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## Usage Example
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
import pixrr as pix
|
|
112
|
+
|
|
113
|
+
# Example
|
|
114
|
+
img = pix.handle_image("peda_img/test_images/test2.png")
|
|
115
|
+
grey = pix.convert_to_gray(img)
|
|
116
|
+
# Applying gaussian
|
|
117
|
+
pix.show_image(pix.gaussian_smoothing(grey, kernel_size=3))
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Contribution
|
|
123
|
+
|
|
124
|
+
This library is early-stage but open to improvements, bug reports, and algorithm implementations.
|
|
125
|
+
Ideal for students learning image processing or developers wanting simple utilities without heavy dependencies.
|
|
126
|
+
|
|
127
|
+
---
|
|
128
|
+
|
pixrr-0.1.0/README.md
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
# **pixrr : A lightweight image processing toolkit for python**
|
|
2
|
+
|
|
3
|
+
`pixrr` is a lightweight, beginner-friendly image processing library built for fast experimentation and teaching.
|
|
4
|
+
It focuses on simplicity, clean function names, and easy-to-understand code, making it useful both for quick image tasks and for pedagogical environments such as introductory image processing courses.
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Features (Planned & Implemented)
|
|
9
|
+
|
|
10
|
+
### **Core Utilities**
|
|
11
|
+
|
|
12
|
+
* Convert color images to grayscale and binary
|
|
13
|
+
* Basic I/O helpers (read, write, display)
|
|
14
|
+
* Plot and analyze histograms
|
|
15
|
+
* Image thresholding
|
|
16
|
+
* Image Cropping and Rotation (planned)
|
|
17
|
+
|
|
18
|
+
### **Filtering & Enhancement**
|
|
19
|
+
|
|
20
|
+
* Convolution using masks
|
|
21
|
+
* Convolution using FFT (planned)
|
|
22
|
+
* Contrast enhancement
|
|
23
|
+
* Histogram equalization
|
|
24
|
+
* Smoothing filters (Gaussian, median, weighted median)
|
|
25
|
+
* Sharpening and Laplacian operators
|
|
26
|
+
|
|
27
|
+
### **Edge and Gradient Detection**
|
|
28
|
+
|
|
29
|
+
* Prewitt and Sobel (horizontal & vertical)
|
|
30
|
+
* Second-order derivatives
|
|
31
|
+
* Canny edge detector (planned)
|
|
32
|
+
|
|
33
|
+
### **Segmentation & Classification**
|
|
34
|
+
|
|
35
|
+
* Otsu thresholding
|
|
36
|
+
* K-means clustering for image segmentation
|
|
37
|
+
* Mean-Shift clustering (planned)
|
|
38
|
+
* EM/Bayesian pixel classification (planned)
|
|
39
|
+
|
|
40
|
+
---
|
|
41
|
+
|
|
42
|
+
## Project Roadmap
|
|
43
|
+
|
|
44
|
+
| Stage | Goal |
|
|
45
|
+
| ------ | ------------------------------------------------------- |
|
|
46
|
+
| Part 1 | Basic conversions, contour extraction, histogram tools |
|
|
47
|
+
| Part 2 | Convolution, enhancement techniques, gradient operators |
|
|
48
|
+
| Part 3 | Automatic segmentation & clustering |
|
|
49
|
+
| Part 4 | Advanced edge detection and EM classification |
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Installation (when it’s live)
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install pixrr
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
*(Currently under development. Not on PyPI yet.)*
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## Usage Example
|
|
64
|
+
|
|
65
|
+
```python
|
|
66
|
+
import pixrr as pix
|
|
67
|
+
|
|
68
|
+
# Example
|
|
69
|
+
img = pix.handle_image("peda_img/test_images/test2.png")
|
|
70
|
+
grey = pix.convert_to_gray(img)
|
|
71
|
+
# Applying gaussian
|
|
72
|
+
pix.show_image(pix.gaussian_smoothing(grey, kernel_size=3))
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## Contribution
|
|
78
|
+
|
|
79
|
+
This library is early-stage but open to improvements, bug reports, and algorithm implementations.
|
|
80
|
+
Ideal for students learning image processing or developers wanting simple utilities without heavy dependencies.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[project]
|
|
7
|
+
name = "pixrr"
|
|
8
|
+
version = "0.1.0"
|
|
9
|
+
description = "A lightweight image processing toolkit for Python"
|
|
10
|
+
readme = "README.md"
|
|
11
|
+
license = { file = "LICENSE" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Hrishikesh Tiwari", email = "tiwarihrishikesh686@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
requires-python = ">=3.9"
|
|
17
|
+
|
|
18
|
+
dependencies = [
|
|
19
|
+
"numpy>=2.3.5",
|
|
20
|
+
"scipy>=1.16.3",
|
|
21
|
+
"matplotlib>=3.10.7",
|
|
22
|
+
"pillow>=12.0.0",
|
|
23
|
+
"numba>=0.63.1"
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
keywords = ["image-processing", "computer-vision", "education", "filters", "numpy"]
|
|
27
|
+
|
|
28
|
+
classifiers = [
|
|
29
|
+
"Programming Language :: Python :: 3",
|
|
30
|
+
"License :: OSI Approved :: MIT License",
|
|
31
|
+
"Operating System :: OS Independent",
|
|
32
|
+
"Topic :: Scientific/Engineering :: Image Processing",
|
|
33
|
+
"Intended Audience :: Education",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
[project.urls]
|
|
38
|
+
"Homepage" = "https://github.com/Hrishi11572/pixrr"
|
|
39
|
+
"Documentation" = "https://github.com/Hrishi11572/pixrr/tree/main/docs"
|
|
40
|
+
|
pixrr-0.1.0/setup.cfg
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# pixrr/__init__.py
|
|
2
|
+
|
|
3
|
+
from .edges import gradient_prewitt, gradient_sobel, contour_extractor
|
|
4
|
+
from .enhance import linear_contrast_enhancement, histogram_equalization
|
|
5
|
+
from .filters import padd_image, conv2D, laplacian, sharpen_image, gaussian_filter, gaussian_smoothing
|
|
6
|
+
from .io import handle_image, convert_to_gray, show_image, save_image, plot_img_hist
|
|
7
|
+
from .threshold import threshold_image, otsu_thresholding
|
|
8
|
+
from .segmentation import kmeans_segmentation
|
|
9
|
+
from .utils import crop_image
|
|
10
|
+
|
|
11
|
+
# 2. DEFINING EXPORTS
|
|
12
|
+
# This list controls what happens if someone types "from peda_img import *"
|
|
13
|
+
# It also cleans up the namespace so IDEs know what is public.
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
'gradient_prewitt',
|
|
17
|
+
'gradient_sobel',
|
|
18
|
+
'contour_extractor',
|
|
19
|
+
'linear_contrast_enhancement',
|
|
20
|
+
'histogram_equalization',
|
|
21
|
+
'padd_image',
|
|
22
|
+
'conv2D',
|
|
23
|
+
'laplacian',
|
|
24
|
+
'sharpen_image',
|
|
25
|
+
'gaussian_filter',
|
|
26
|
+
'gaussian_smoothing',
|
|
27
|
+
'handle_image',
|
|
28
|
+
'convert_to_gray',
|
|
29
|
+
'show_image',
|
|
30
|
+
'save_image',
|
|
31
|
+
'plot_img_hist',
|
|
32
|
+
'threshold_image',
|
|
33
|
+
'kmeans_segmentation',
|
|
34
|
+
'otsu_thresholding',
|
|
35
|
+
'crop_image'
|
|
36
|
+
]
|
|
37
|
+
|
|
38
|
+
# Optional: Library Metadata
|
|
39
|
+
__version__ = "0.1.0"
|
|
40
|
+
__author__ = "Hrishikesh Tiwari"
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
from .io import convert_to_gray
|
|
4
|
+
from .filters import conv2D
|
|
5
|
+
import os
|
|
6
|
+
|
|
7
|
+
def gradient_prewitt(img: np.ndarray,
|
|
8
|
+
kernel_size : int = 3,
|
|
9
|
+
direction: str = "both",
|
|
10
|
+
hstep: int = 1,
|
|
11
|
+
vstep :int = 1) -> np.ndarray :
|
|
12
|
+
'''
|
|
13
|
+
Docstring for gradient_prewitt
|
|
14
|
+
|
|
15
|
+
:param img : input the image as a numpy array
|
|
16
|
+
:type img : np.ndarray
|
|
17
|
+
:param kernel_size : the size of the kernel, should be always odd
|
|
18
|
+
:type kernel_size : int (default = 3)
|
|
19
|
+
:param direction : in which direction you want to use the kernel : "both", "h", "v"
|
|
20
|
+
:type direction : str (Default = "both")
|
|
21
|
+
:param hstep : horizontal stride
|
|
22
|
+
:type hstep : int (Default = 1)
|
|
23
|
+
:param vstep : vertical stride
|
|
24
|
+
:type vstep : int (Default = 1)
|
|
25
|
+
:return: the convolved image, as numpy array
|
|
26
|
+
:rtype: ndarray[_AnyShape, dtype[Any]]
|
|
27
|
+
'''
|
|
28
|
+
|
|
29
|
+
if img.ndim == 3:
|
|
30
|
+
img = convert_to_gray(img)
|
|
31
|
+
|
|
32
|
+
if kernel_size % 2 == 0:
|
|
33
|
+
raise ValueError("Mask should be of the following form (odd, odd)")
|
|
34
|
+
|
|
35
|
+
# create the prewitt kernel of the given size
|
|
36
|
+
prewitt_horizontal = np.zeros((kernel_size, kernel_size-2))
|
|
37
|
+
prewitt_horizontal = np.hstack((prewitt_horizontal, np.full((kernel_size,1), -1)))
|
|
38
|
+
prewitt_horizontal = np.hstack((prewitt_horizontal[:,::-1], np.full((kernel_size,1), fill_value=1)))
|
|
39
|
+
|
|
40
|
+
prewitt_vertical = prewitt_horizontal.T[::-1,:]
|
|
41
|
+
|
|
42
|
+
if direction == "h":
|
|
43
|
+
horizontal_gradient = conv2D(img, prewitt_horizontal, hstep , vstep)
|
|
44
|
+
return horizontal_gradient
|
|
45
|
+
|
|
46
|
+
elif direction == "v":
|
|
47
|
+
vertical_gradient = conv2D(img, prewitt_vertical, hstep, vstep)
|
|
48
|
+
return vertical_gradient
|
|
49
|
+
|
|
50
|
+
elif direction == "both":
|
|
51
|
+
horizontal_gradient = conv2D(img, prewitt_horizontal, hstep, vstep)
|
|
52
|
+
vertical_gradient = conv2D(img, prewitt_vertical, hstep, vstep)
|
|
53
|
+
grad= np.sqrt(horizontal_gradient **2 + vertical_gradient **2)
|
|
54
|
+
grad = (grad / grad.max() * 255)
|
|
55
|
+
return grad.astype(np.uint8)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def gradient_sobel(img: np.ndarray,
|
|
60
|
+
kernel_size : int = 3,
|
|
61
|
+
direction: str = "both",
|
|
62
|
+
hstep: int = 1,
|
|
63
|
+
vstep :int = 1) -> np.ndarray :
|
|
64
|
+
'''
|
|
65
|
+
Docstring for gradient_sobel
|
|
66
|
+
|
|
67
|
+
:param img : input the image as a numpy array
|
|
68
|
+
:type img : np.ndarray
|
|
69
|
+
:param kernel_size : the size of the kernel, should be always odd
|
|
70
|
+
:type kernel_size : int (default = 3)
|
|
71
|
+
:param direction : the direction where you want to use the kernel : "both", "h" , "v"
|
|
72
|
+
:type direction : str (Default = "both")
|
|
73
|
+
:param hstep : horizontal stride
|
|
74
|
+
:type hstep : int (Default = 1)
|
|
75
|
+
:param vstep : vertical stride
|
|
76
|
+
:type vstep : int (Default = 1)
|
|
77
|
+
:return: the convolved image, as numpy array
|
|
78
|
+
:rtype: ndarray[_AnyShape, dtype[Any]]
|
|
79
|
+
'''
|
|
80
|
+
if img.ndim == 3:
|
|
81
|
+
img = convert_to_gray(img)
|
|
82
|
+
|
|
83
|
+
if kernel_size % 2 == 0:
|
|
84
|
+
raise ValueError("Mask should be of the following form (odd, odd)")
|
|
85
|
+
|
|
86
|
+
# create the prewitt kernel of the given size
|
|
87
|
+
def get_sobel_kernels(size: int):
|
|
88
|
+
if size % 2 == 0 or size < 3:
|
|
89
|
+
raise ValueError("Size must be odd and at least 3")
|
|
90
|
+
|
|
91
|
+
# 1. Generate Pascal's Triangle row for smoothing
|
|
92
|
+
def get_pascal_row(n):
|
|
93
|
+
row = [1]
|
|
94
|
+
for k in range(n):
|
|
95
|
+
row.append(row[k] * (n - k) // (k + 1))
|
|
96
|
+
return np.array(row, dtype=np.float32)
|
|
97
|
+
|
|
98
|
+
# Smoothing vector (s)
|
|
99
|
+
s = get_pascal_row(size - 1)
|
|
100
|
+
|
|
101
|
+
# Derivative vector (d)
|
|
102
|
+
# Logic: difference of the Pascal row one degree smaller
|
|
103
|
+
d_prev = get_pascal_row(size - 2)
|
|
104
|
+
d = np.zeros(size)
|
|
105
|
+
d[:-1] += d_prev
|
|
106
|
+
d[1:] -= d_prev
|
|
107
|
+
|
|
108
|
+
# 2. Create Kernels using Outer Product
|
|
109
|
+
# Gx (Horizontal) detects vertical edges
|
|
110
|
+
sobel_x = np.outer(s, d)
|
|
111
|
+
|
|
112
|
+
# Gy (Vertical) detects horizontal edges
|
|
113
|
+
sobel_y = np.outer(d, s)
|
|
114
|
+
|
|
115
|
+
return sobel_x, sobel_y
|
|
116
|
+
|
|
117
|
+
sobel_horizontal, sobel_vertical = get_sobel_kernels(kernel_size)
|
|
118
|
+
|
|
119
|
+
if direction == "h":
|
|
120
|
+
horizontal_gradient = conv2D(img, sobel_horizontal, hstep , vstep)
|
|
121
|
+
return horizontal_gradient
|
|
122
|
+
|
|
123
|
+
elif direction == "v":
|
|
124
|
+
vertical_gradient = conv2D(img, sobel_vertical, hstep, vstep)
|
|
125
|
+
return vertical_gradient
|
|
126
|
+
|
|
127
|
+
elif direction == "both":
|
|
128
|
+
horizontal_gradient = conv2D(img, sobel_horizontal, hstep, vstep)
|
|
129
|
+
vertical_gradient = conv2D(img, sobel_vertical, hstep, vstep)
|
|
130
|
+
grad= np.sqrt(horizontal_gradient **2 + vertical_gradient **2)
|
|
131
|
+
grad = (grad / grad.max() * 255)
|
|
132
|
+
return grad.astype(np.uint8)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def contour_extractor(img : np.ndarray = None,
|
|
136
|
+
save : bool = False,
|
|
137
|
+
directory : str = None,
|
|
138
|
+
filename : str | None = "default.png")->np.ndarray:
|
|
139
|
+
'''
|
|
140
|
+
Docstring for contour_extractor :
|
|
141
|
+
|
|
142
|
+
extract the contour of the thresholded image, where interior is white and background is black
|
|
143
|
+
|
|
144
|
+
:param img: image as np.ndarray
|
|
145
|
+
:type img: np.ndarray
|
|
146
|
+
:param save : boolean variable, asking whether you want to save the output image
|
|
147
|
+
:type save : bool
|
|
148
|
+
:param directory : the path where you want to save the image
|
|
149
|
+
:type directory : str (Default = None)
|
|
150
|
+
:return: contour image
|
|
151
|
+
:rtype: ndarray[_AnyShape, dtype[Any]]
|
|
152
|
+
'''
|
|
153
|
+
|
|
154
|
+
if img.ndim == 3:
|
|
155
|
+
raise ValueError("Please enter a binary image only")
|
|
156
|
+
|
|
157
|
+
dx = [-1,-1,0,1,1,1,0,-1]
|
|
158
|
+
dy = [0,-1,-1,-1,0,1,1,1]
|
|
159
|
+
|
|
160
|
+
height, width = img.shape
|
|
161
|
+
padded_arr = np.pad(img, pad_width=1, mode='constant', constant_values=255)
|
|
162
|
+
has_zero_neighbor = np.zeros((height, width), dtype=bool)
|
|
163
|
+
|
|
164
|
+
for k in range(8):
|
|
165
|
+
r_start = 1 + dx[k]
|
|
166
|
+
r_end = 1 + dx[k] + height
|
|
167
|
+
c_start = 1 + dy[k]
|
|
168
|
+
c_end = 1 + dy[k] + width
|
|
169
|
+
|
|
170
|
+
neighbor_view = padded_arr[r_start:r_end, c_start:c_end]
|
|
171
|
+
has_zero_neighbor |= (neighbor_view == 0)
|
|
172
|
+
|
|
173
|
+
contour_mask = (img == 255) & has_zero_neighbor
|
|
174
|
+
contour = np.argwhere(contour_mask)
|
|
175
|
+
contour_list = [tuple(x) for x in contour]
|
|
176
|
+
|
|
177
|
+
'''Display the contour and save it, if asked'''
|
|
178
|
+
|
|
179
|
+
fig, ax = plt.subplots()
|
|
180
|
+
ax.imshow(img, cmap="gray")
|
|
181
|
+
row, col = zip(*contour_list)
|
|
182
|
+
ax.scatter(col, row, s=2, c="red")
|
|
183
|
+
ax.axis("off")
|
|
184
|
+
plt.show()
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
contour_img = np.zeros((img.shape[0], img.shape[1]),dtype=np.uint8);
|
|
188
|
+
for (i, j) in contour_list:
|
|
189
|
+
contour_img[i, j] = 255
|
|
190
|
+
|
|
191
|
+
if save:
|
|
192
|
+
# creating new gray scale figure for saving
|
|
193
|
+
fig2, ax2 = plt.subplots()
|
|
194
|
+
ax2.imshow(contour_img, cmap="gray")
|
|
195
|
+
ax2.axis("off")
|
|
196
|
+
|
|
197
|
+
if directory is None or not os.path.exists(directory) :
|
|
198
|
+
directory = os.getcwd()
|
|
199
|
+
|
|
200
|
+
file_path = os.path.join(directory, filename)
|
|
201
|
+
|
|
202
|
+
fig2.savefig(file_path, dpi=600, bbox_inches="tight", pad_inches=0)
|
|
203
|
+
plt.close(fig2)
|
|
204
|
+
|
|
205
|
+
return contour_img
|
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
import matplotlib.pyplot as plt
|
|
3
|
+
from .io import convert_to_gray
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
def linear_contrast_enhancement(img : np.ndarray,
|
|
7
|
+
low: int = 0,
|
|
8
|
+
high : int = 255 ,
|
|
9
|
+
directory: str | None = None,
|
|
10
|
+
save : bool = False,
|
|
11
|
+
filename : str | None = None)->np.ndarray:
|
|
12
|
+
|
|
13
|
+
'''
|
|
14
|
+
Docstring for linear_contrast_enhancement
|
|
15
|
+
|
|
16
|
+
:param img : the input image as a numpy array
|
|
17
|
+
:type img : np.ndarray
|
|
18
|
+
:param low : the lowest intensity value
|
|
19
|
+
:type low : int
|
|
20
|
+
:param high : the highest intensity value
|
|
21
|
+
:type high : int
|
|
22
|
+
:param save : whether to save the resulting image or not
|
|
23
|
+
:type save : bool
|
|
24
|
+
:param directory : the path where you want to save the image
|
|
25
|
+
:type directory : str | None
|
|
26
|
+
:param filename : the name with which the image would be saved
|
|
27
|
+
:type filename : str
|
|
28
|
+
'''
|
|
29
|
+
|
|
30
|
+
if img.ndim == 3:
|
|
31
|
+
# convert colored to gray scale
|
|
32
|
+
img = convert_to_gray(img)
|
|
33
|
+
|
|
34
|
+
if max(low, high) > 255 or min(low, high) < 0:
|
|
35
|
+
raise ValueError("{low} and {high} should be between 0 and 255")
|
|
36
|
+
|
|
37
|
+
new_img = np.asarray(img)
|
|
38
|
+
|
|
39
|
+
min_intensity = img.min()
|
|
40
|
+
max_intensity = img.max()
|
|
41
|
+
|
|
42
|
+
print(f"Min intensity in original image : {min_intensity}")
|
|
43
|
+
print(f"Max intensity in original image : {max_intensity}")
|
|
44
|
+
|
|
45
|
+
# Edge case (Zero denominator)
|
|
46
|
+
if max_intensity == min_intensity:
|
|
47
|
+
return np.full_like(img, low, dtype=np.uint8)
|
|
48
|
+
|
|
49
|
+
new_img = ((high - low)/(max_intensity - min_intensity)) * (img - min_intensity) + low
|
|
50
|
+
new_img = new_img.astype(np.uint8)
|
|
51
|
+
|
|
52
|
+
fig, ax = plt.subplot_mosaic([
|
|
53
|
+
['original', 'enhanced']
|
|
54
|
+
], figsize=(7, 3.5))
|
|
55
|
+
|
|
56
|
+
ax["original"].imshow(img, cmap="gray")
|
|
57
|
+
ax["original"].axis("off")
|
|
58
|
+
ax["original"].set_title("Original Image")
|
|
59
|
+
|
|
60
|
+
ax["enhanced"].imshow(new_img, cmap="gray")
|
|
61
|
+
ax["enhanced"].axis("off")
|
|
62
|
+
ax["enhanced"].set_title("Enhanced Image")
|
|
63
|
+
plt.show()
|
|
64
|
+
|
|
65
|
+
if save:
|
|
66
|
+
# 1. Create a new figure and axes for saving
|
|
67
|
+
fig2, ax2 = plt.subplots()
|
|
68
|
+
|
|
69
|
+
# 2. Draw the new image onto the *saving* axes
|
|
70
|
+
ax2.imshow(new_img, cmap="gray")
|
|
71
|
+
|
|
72
|
+
# 3. Configure the axes
|
|
73
|
+
ax2.axis("off")
|
|
74
|
+
|
|
75
|
+
HIGH_RES_DPI = 600
|
|
76
|
+
|
|
77
|
+
if directory is None or not os.path.exists(directory) :
|
|
78
|
+
directory = os.getcwd()
|
|
79
|
+
|
|
80
|
+
file_path = os.path.join(directory, filename)
|
|
81
|
+
|
|
82
|
+
# **The key line for high resolution is here:**
|
|
83
|
+
fig2.savefig(
|
|
84
|
+
file_path,
|
|
85
|
+
dpi=HIGH_RES_DPI, # Sets the resolution
|
|
86
|
+
bbox_inches="tight", # Crops unnecessary white space
|
|
87
|
+
pad_inches=0 # Removes padding
|
|
88
|
+
)
|
|
89
|
+
plt.close(fig2)
|
|
90
|
+
|
|
91
|
+
return new_img
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def histogram_equalization(img : np.ndarray,
|
|
96
|
+
low: int = 0,
|
|
97
|
+
high : int = 255,
|
|
98
|
+
save : bool = False,
|
|
99
|
+
directory : str | None = None,
|
|
100
|
+
filename : str | None = None)->np.ndarray:
|
|
101
|
+
|
|
102
|
+
'''
|
|
103
|
+
Docstring for histogram_equalization
|
|
104
|
+
|
|
105
|
+
:param img : the input image as a numpy array
|
|
106
|
+
:type img : np.ndarray
|
|
107
|
+
:param low : the lowest intensity value
|
|
108
|
+
:type low : int
|
|
109
|
+
:param high : the highest intensity value
|
|
110
|
+
:type high : int
|
|
111
|
+
:param save : whether to save the resulting image or not
|
|
112
|
+
:type save : bool
|
|
113
|
+
:param directory : the path where you want to save the image
|
|
114
|
+
:type directory : str | None
|
|
115
|
+
:param filename : the name with which the image would be saved
|
|
116
|
+
:type filename : str
|
|
117
|
+
|
|
118
|
+
'''
|
|
119
|
+
|
|
120
|
+
# convert to gray scale if necessary
|
|
121
|
+
if img.ndim == 3:
|
|
122
|
+
img = convert_to_gray(img)
|
|
123
|
+
|
|
124
|
+
if not (0 <= low < high <= 255):
|
|
125
|
+
raise ValueError(f"{low} and {high} must satisfy 0 <= low < high <= 255")
|
|
126
|
+
|
|
127
|
+
hist , _ = np.histogram(img, bins=256, range=(0,256))
|
|
128
|
+
hist = hist/hist.sum()
|
|
129
|
+
|
|
130
|
+
cdf_original = np.cumsum(hist)
|
|
131
|
+
|
|
132
|
+
new_img = np.round(cdf_original[img] * (high - low) + low).astype(np.uint8)
|
|
133
|
+
|
|
134
|
+
# display the enhanced image
|
|
135
|
+
fig, ax = plt.subplot_mosaic([
|
|
136
|
+
['original', 'enhanced']
|
|
137
|
+
], figsize=(7, 3.5))
|
|
138
|
+
|
|
139
|
+
ax["original"].imshow(img, cmap="gray")
|
|
140
|
+
ax["original"].axis("off")
|
|
141
|
+
ax["original"].set_title("Original Image")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
ax["enhanced"].imshow(new_img, cmap="gray")
|
|
145
|
+
ax["enhanced"].axis("off")
|
|
146
|
+
ax["enhanced"].set_title("Enhanced Image")
|
|
147
|
+
plt.show()
|
|
148
|
+
plt.close(fig)
|
|
149
|
+
|
|
150
|
+
# save the new image if asked to do so
|
|
151
|
+
if save:
|
|
152
|
+
|
|
153
|
+
# 1. Create a new figure and axes for saving
|
|
154
|
+
fig2, ax2 = plt.subplots()
|
|
155
|
+
|
|
156
|
+
# 2. Draw the new image onto the *saving* axes
|
|
157
|
+
ax2.imshow(new_img, cmap="gray")
|
|
158
|
+
|
|
159
|
+
# 3. Configure the axes
|
|
160
|
+
ax2.axis("off")
|
|
161
|
+
|
|
162
|
+
HIGH_RES_DPI = 600
|
|
163
|
+
|
|
164
|
+
if directory is None or not os.path.exists(directory) :
|
|
165
|
+
directory = os.getcwd()
|
|
166
|
+
|
|
167
|
+
file_path = os.path.join(directory, filename)
|
|
168
|
+
|
|
169
|
+
# **The key line for high resolution is here:**
|
|
170
|
+
fig2.savefig(
|
|
171
|
+
file_path,
|
|
172
|
+
dpi=HIGH_RES_DPI, # Sets the resolution
|
|
173
|
+
bbox_inches="tight", # Crops unnecessary white space
|
|
174
|
+
pad_inches=0 # Removes padding
|
|
175
|
+
)
|
|
176
|
+
plt.close(fig2)
|
|
177
|
+
|
|
178
|
+
return new_img
|