chokkhu 0.2.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.
- chokkhu-0.2.0/LICENSE +21 -0
- chokkhu-0.2.0/PKG-INFO +175 -0
- chokkhu-0.2.0/README.md +124 -0
- chokkhu-0.2.0/pyproject.toml +28 -0
- chokkhu-0.2.0/setup.cfg +65 -0
- chokkhu-0.2.0/setup.py +27 -0
- chokkhu-0.2.0/src/chokkhu/__init__.py +22 -0
- chokkhu-0.2.0/src/chokkhu/eda/__init__.py +4 -0
- chokkhu-0.2.0/src/chokkhu/eda/image.py +213 -0
- chokkhu-0.2.0/src/chokkhu/eda/tabular.py +114 -0
- chokkhu-0.2.0/src/chokkhu/eda/visualizer.py +27 -0
- chokkhu-0.2.0/src/chokkhu/models/__init__.py +1 -0
- chokkhu-0.2.0/src/chokkhu/preprocessing/__init__.py +3 -0
- chokkhu-0.2.0/src/chokkhu/preprocessing/image.py +397 -0
- chokkhu-0.2.0/src/chokkhu/utils/__init__.py +4 -0
- chokkhu-0.2.0/src/chokkhu/utils/custom_exception.py +6 -0
- chokkhu-0.2.0/src/chokkhu/utils/logger.py +10 -0
- chokkhu-0.2.0/src/chokkhu.egg-info/PKG-INFO +175 -0
- chokkhu-0.2.0/src/chokkhu.egg-info/SOURCES.txt +21 -0
- chokkhu-0.2.0/src/chokkhu.egg-info/dependency_links.txt +1 -0
- chokkhu-0.2.0/src/chokkhu.egg-info/requires.txt +21 -0
- chokkhu-0.2.0/src/chokkhu.egg-info/top_level.txt +1 -0
chokkhu-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Tamim Hossain
|
|
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.
|
chokkhu-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,175 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: chokkhu
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: A small python image classification package
|
|
5
|
+
Home-page: https://github.com/tamimystic/Chokkhu-PyPi-Package
|
|
6
|
+
Author: tamimystic
|
|
7
|
+
Author-email: hossainsmtamim@gamil.com
|
|
8
|
+
License: MIT
|
|
9
|
+
Project-URL: Bug Tracker, https://github.com/tamimystic/Chokkhu-PyPi-Package/issues
|
|
10
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.8
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Operating System :: OS Independent
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: tensorflow>=2.12
|
|
25
|
+
Requires-Dist: numpy>=1.23
|
|
26
|
+
Requires-Dist: Pillow>=9.0
|
|
27
|
+
Requires-Dist: matplotlib>=3.7
|
|
28
|
+
Requires-Dist: seaborn>=0.12
|
|
29
|
+
Requires-Dist: scikit-learn>=1.2
|
|
30
|
+
Requires-Dist: pandas>=2.0.0
|
|
31
|
+
Requires-Dist: opencv-python>=4.8.0
|
|
32
|
+
Requires-Dist: tqdm>=4.65.0
|
|
33
|
+
Provides-Extra: testing
|
|
34
|
+
Requires-Dist: pytest<9.0,>=7.2; extra == "testing"
|
|
35
|
+
Requires-Dist: tox<5.0,>=3.28; extra == "testing"
|
|
36
|
+
Requires-Dist: flake8<8.0,>=6.1; extra == "testing"
|
|
37
|
+
Requires-Dist: mypy<2.0,>=1.5; extra == "testing"
|
|
38
|
+
Provides-Extra: dev
|
|
39
|
+
Requires-Dist: black<25.0,>=23.3; extra == "dev"
|
|
40
|
+
Requires-Dist: isort<7.0,>=5.12; extra == "dev"
|
|
41
|
+
Requires-Dist: build<2.0,>=1.0; extra == "dev"
|
|
42
|
+
Requires-Dist: twine<6.0,>=4.0; extra == "dev"
|
|
43
|
+
Dynamic: author
|
|
44
|
+
Dynamic: author-email
|
|
45
|
+
Dynamic: description
|
|
46
|
+
Dynamic: description-content-type
|
|
47
|
+
Dynamic: home-page
|
|
48
|
+
Dynamic: license-file
|
|
49
|
+
Dynamic: project-url
|
|
50
|
+
Dynamic: summary
|
|
51
|
+
|
|
52
|
+
# Chokkhu
|
|
53
|
+
|
|
54
|
+
Chokkhu is a deep learning image dataset EDA and preprocessing toolkit designed to
|
|
55
|
+
prepare train-ready image data for TensorFlow / Keras–based image classification.
|
|
56
|
+
It helps users analyze datasets, preprocess images, handle class imbalance, and
|
|
57
|
+
train deep learning models using a clean and reproducible pipeline. The package
|
|
58
|
+
follows industry-grade Python packaging standards, supports CI/CD pipelines, and
|
|
59
|
+
works seamlessly in Google Colab and Jupyter Notebook environments.
|
|
60
|
+
|
|
61
|
+
INSTALLATION (IMPORTANT – FIRST STEP)
|
|
62
|
+
|
|
63
|
+
>>> pip install Chokkhu <<<
|
|
64
|
+
|
|
65
|
+
TensorFlow is installed automatically as a runtime dependency.
|
|
66
|
+
|
|
67
|
+
Chokkhu’s main responsibility is data preparation. It performs image exploratory
|
|
68
|
+
data analysis (EDA), class-wise distribution visualization, image size, aspect
|
|
69
|
+
ratio, RGB intensity and blur analysis, standard preprocessing (resize to 224×224
|
|
70
|
+
and normalization), stratified train/validation/test splitting, and automatic
|
|
71
|
+
class balancing using data augmentation. After this step, the dataset is fully
|
|
72
|
+
ready to be used for training any deep learning model.
|
|
73
|
+
|
|
74
|
+
Complete usage example showing the full workflow in one place:
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
|
|
78
|
+
from Chokkhu import ImageEDA, ImagePreProcessor
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
# Dataset EDA
|
|
82
|
+
eda = ImageEDA(dataset_path="your_dataset_path")
|
|
83
|
+
|
|
84
|
+
# Dataset preprocessing
|
|
85
|
+
processor = ImagePreProcessor(datapath="your_dataset_path")
|
|
86
|
+
(train_X, train_y), (val_X, val_y), (test_X, test_y) = processor.get_data()
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
After excecuting this, You can train your model like this.
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
import tensorflow as tf
|
|
100
|
+
# Example 1: Custom CNN (from scratch)
|
|
101
|
+
custom_model = tf.keras.Sequential([
|
|
102
|
+
tf.keras.layers.Conv2D(32, 3, activation="relu", input_shape=(224,224,3)),
|
|
103
|
+
tf.keras.layers.MaxPooling2D(),
|
|
104
|
+
tf.keras.layers.Conv2D(64, 3, activation="relu"),
|
|
105
|
+
tf.keras.layers.MaxPooling2D(),
|
|
106
|
+
tf.keras.layers.Flatten(),
|
|
107
|
+
tf.keras.layers.Dense(128, activation="relu"),
|
|
108
|
+
tf.keras.layers.Dense(num_classes, activation="softmax")
|
|
109
|
+
])
|
|
110
|
+
|
|
111
|
+
custom_model.compile(
|
|
112
|
+
optimizer="adam",
|
|
113
|
+
loss="sparse_categorical_crossentropy",
|
|
114
|
+
metrics=["accuracy"]
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
custom_model.fit(
|
|
118
|
+
train_X,
|
|
119
|
+
train_y,
|
|
120
|
+
validation_data=(val_X, val_y),
|
|
121
|
+
epochs=10
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# Example 2: Transfer Learning with ConvNeXt-Tiny (frozen backbone)
|
|
128
|
+
import tensorflow as tf
|
|
129
|
+
base_model = tf.keras.applications.ConvNeXtTiny(
|
|
130
|
+
weights="imagenet",
|
|
131
|
+
include_top=False,
|
|
132
|
+
input_shape=(224,224,3)
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
base_model.trainable = False
|
|
136
|
+
|
|
137
|
+
transfer_model = tf.keras.Sequential([
|
|
138
|
+
base_model,
|
|
139
|
+
tf.keras.layers.GlobalAveragePooling2D(),
|
|
140
|
+
tf.keras.layers.Dense(256, activation="relu"),
|
|
141
|
+
tf.keras.layers.Dense(num_classes, activation="softmax")
|
|
142
|
+
])
|
|
143
|
+
|
|
144
|
+
transfer_model.compile(
|
|
145
|
+
optimizer="adam",
|
|
146
|
+
loss="sparse_categorical_crossentropy",
|
|
147
|
+
metrics=["accuracy"]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
transfer_model.fit(
|
|
151
|
+
train_X,
|
|
152
|
+
train_y,
|
|
153
|
+
validation_data=(val_X, val_y),
|
|
154
|
+
epochs=5
|
|
155
|
+
)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
# Example 3: Fine-tuning ConvNeXt-Tiny (unfrozen backbone)
|
|
161
|
+
import tensorflow as tf
|
|
162
|
+
base_model.trainable = True
|
|
163
|
+
|
|
164
|
+
transfer_model.compile(
|
|
165
|
+
optimizer=tf.keras.optimizers.Adam(1e-5),
|
|
166
|
+
loss="sparse_categorical_crossentropy",
|
|
167
|
+
metrics=["accuracy"]
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
transfer_model.fit(
|
|
171
|
+
train_X,
|
|
172
|
+
train_y,
|
|
173
|
+
validation_data=(val_X, val_y),
|
|
174
|
+
epochs=5
|
|
175
|
+
)
|
chokkhu-0.2.0/README.md
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
# Chokkhu
|
|
2
|
+
|
|
3
|
+
Chokkhu is a deep learning image dataset EDA and preprocessing toolkit designed to
|
|
4
|
+
prepare train-ready image data for TensorFlow / Keras–based image classification.
|
|
5
|
+
It helps users analyze datasets, preprocess images, handle class imbalance, and
|
|
6
|
+
train deep learning models using a clean and reproducible pipeline. The package
|
|
7
|
+
follows industry-grade Python packaging standards, supports CI/CD pipelines, and
|
|
8
|
+
works seamlessly in Google Colab and Jupyter Notebook environments.
|
|
9
|
+
|
|
10
|
+
INSTALLATION (IMPORTANT – FIRST STEP)
|
|
11
|
+
|
|
12
|
+
>>> pip install Chokkhu <<<
|
|
13
|
+
|
|
14
|
+
TensorFlow is installed automatically as a runtime dependency.
|
|
15
|
+
|
|
16
|
+
Chokkhu’s main responsibility is data preparation. It performs image exploratory
|
|
17
|
+
data analysis (EDA), class-wise distribution visualization, image size, aspect
|
|
18
|
+
ratio, RGB intensity and blur analysis, standard preprocessing (resize to 224×224
|
|
19
|
+
and normalization), stratified train/validation/test splitting, and automatic
|
|
20
|
+
class balancing using data augmentation. After this step, the dataset is fully
|
|
21
|
+
ready to be used for training any deep learning model.
|
|
22
|
+
|
|
23
|
+
Complete usage example showing the full workflow in one place:
|
|
24
|
+
|
|
25
|
+
```python
|
|
26
|
+
|
|
27
|
+
from Chokkhu import ImageEDA, ImagePreProcessor
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
# Dataset EDA
|
|
31
|
+
eda = ImageEDA(dataset_path="your_dataset_path")
|
|
32
|
+
|
|
33
|
+
# Dataset preprocessing
|
|
34
|
+
processor = ImagePreProcessor(datapath="your_dataset_path")
|
|
35
|
+
(train_X, train_y), (val_X, val_y), (test_X, test_y) = processor.get_data()
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
After excecuting this, You can train your model like this.
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
import tensorflow as tf
|
|
49
|
+
# Example 1: Custom CNN (from scratch)
|
|
50
|
+
custom_model = tf.keras.Sequential([
|
|
51
|
+
tf.keras.layers.Conv2D(32, 3, activation="relu", input_shape=(224,224,3)),
|
|
52
|
+
tf.keras.layers.MaxPooling2D(),
|
|
53
|
+
tf.keras.layers.Conv2D(64, 3, activation="relu"),
|
|
54
|
+
tf.keras.layers.MaxPooling2D(),
|
|
55
|
+
tf.keras.layers.Flatten(),
|
|
56
|
+
tf.keras.layers.Dense(128, activation="relu"),
|
|
57
|
+
tf.keras.layers.Dense(num_classes, activation="softmax")
|
|
58
|
+
])
|
|
59
|
+
|
|
60
|
+
custom_model.compile(
|
|
61
|
+
optimizer="adam",
|
|
62
|
+
loss="sparse_categorical_crossentropy",
|
|
63
|
+
metrics=["accuracy"]
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
custom_model.fit(
|
|
67
|
+
train_X,
|
|
68
|
+
train_y,
|
|
69
|
+
validation_data=(val_X, val_y),
|
|
70
|
+
epochs=10
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
# Example 2: Transfer Learning with ConvNeXt-Tiny (frozen backbone)
|
|
77
|
+
import tensorflow as tf
|
|
78
|
+
base_model = tf.keras.applications.ConvNeXtTiny(
|
|
79
|
+
weights="imagenet",
|
|
80
|
+
include_top=False,
|
|
81
|
+
input_shape=(224,224,3)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
base_model.trainable = False
|
|
85
|
+
|
|
86
|
+
transfer_model = tf.keras.Sequential([
|
|
87
|
+
base_model,
|
|
88
|
+
tf.keras.layers.GlobalAveragePooling2D(),
|
|
89
|
+
tf.keras.layers.Dense(256, activation="relu"),
|
|
90
|
+
tf.keras.layers.Dense(num_classes, activation="softmax")
|
|
91
|
+
])
|
|
92
|
+
|
|
93
|
+
transfer_model.compile(
|
|
94
|
+
optimizer="adam",
|
|
95
|
+
loss="sparse_categorical_crossentropy",
|
|
96
|
+
metrics=["accuracy"]
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
transfer_model.fit(
|
|
100
|
+
train_X,
|
|
101
|
+
train_y,
|
|
102
|
+
validation_data=(val_X, val_y),
|
|
103
|
+
epochs=5
|
|
104
|
+
)
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
# Example 3: Fine-tuning ConvNeXt-Tiny (unfrozen backbone)
|
|
110
|
+
import tensorflow as tf
|
|
111
|
+
base_model.trainable = True
|
|
112
|
+
|
|
113
|
+
transfer_model.compile(
|
|
114
|
+
optimizer=tf.keras.optimizers.Adam(1e-5),
|
|
115
|
+
loss="sparse_categorical_crossentropy",
|
|
116
|
+
metrics=["accuracy"]
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
transfer_model.fit(
|
|
120
|
+
train_X,
|
|
121
|
+
train_y,
|
|
122
|
+
validation_data=(val_X, val_y),
|
|
123
|
+
epochs=5
|
|
124
|
+
)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=42.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
[tool.pytest.ini_options]
|
|
7
|
+
testpaths = ["tests"]
|
|
8
|
+
addopts = "-ra -q"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
[tool.mypy]
|
|
12
|
+
mypy_path = "src"
|
|
13
|
+
ignore_missing_imports = true
|
|
14
|
+
strict_optional = false
|
|
15
|
+
disallow_untyped_defs = false
|
|
16
|
+
check_untyped_defs = true
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
[tool.isort]
|
|
20
|
+
profile = "black"
|
|
21
|
+
line_length = 88
|
|
22
|
+
known_first_party = ["Chokkhu"]
|
|
23
|
+
src_paths = ["src"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
[tool.black]
|
|
27
|
+
line-length = 88
|
|
28
|
+
target-version = ["py38", "py39", "py310", "py311"]
|
chokkhu-0.2.0/setup.cfg
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
[metadata]
|
|
2
|
+
license = MIT
|
|
3
|
+
license_file = LICENSE
|
|
4
|
+
url = https://github.com/tamimystic/Chokkhu-PyPi-Package
|
|
5
|
+
project_urls =
|
|
6
|
+
Bug Tracker = https://github.com/tamimystic/Chokkhu-PyPi-Package/issues
|
|
7
|
+
Source Code = https://github.com/tamimystic/Chokkhu-PyPi-Package
|
|
8
|
+
classifiers =
|
|
9
|
+
Development Status :: 2 - Pre-Alpha
|
|
10
|
+
Intended Audience :: Developers
|
|
11
|
+
Intended Audience :: Science/Research
|
|
12
|
+
License :: OSI Approved :: MIT License
|
|
13
|
+
Programming Language :: Python :: 3
|
|
14
|
+
Programming Language :: Python :: 3.8
|
|
15
|
+
Programming Language :: Python :: 3.9
|
|
16
|
+
Programming Language :: Python :: 3.10
|
|
17
|
+
Programming Language :: Python :: 3.11
|
|
18
|
+
Operating System :: OS Independent
|
|
19
|
+
Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
|
|
21
|
+
[options]
|
|
22
|
+
package_dir =
|
|
23
|
+
= src
|
|
24
|
+
packages = find:
|
|
25
|
+
python_requires = >=3.8
|
|
26
|
+
install_requires =
|
|
27
|
+
tensorflow>=2.12
|
|
28
|
+
numpy>=1.23
|
|
29
|
+
Pillow>=9.0
|
|
30
|
+
matplotlib>=3.7
|
|
31
|
+
seaborn>=0.12
|
|
32
|
+
scikit-learn>=1.2
|
|
33
|
+
pandas>=2.0.0
|
|
34
|
+
opencv-python>=4.8.0
|
|
35
|
+
tqdm>=4.65.0
|
|
36
|
+
|
|
37
|
+
[options.packages.find]
|
|
38
|
+
where = src
|
|
39
|
+
|
|
40
|
+
[options.extras_require]
|
|
41
|
+
testing =
|
|
42
|
+
pytest>=7.2,<9.0
|
|
43
|
+
tox>=3.28,<5.0
|
|
44
|
+
flake8>=6.1,<8.0
|
|
45
|
+
mypy>=1.5,<2.0
|
|
46
|
+
dev =
|
|
47
|
+
black>=23.3,<25.0
|
|
48
|
+
isort>=5.12,<7.0
|
|
49
|
+
build>=1.0,<2.0
|
|
50
|
+
twine>=4.0,<6.0
|
|
51
|
+
|
|
52
|
+
[options.package_data]
|
|
53
|
+
chokkhu = py.typed
|
|
54
|
+
|
|
55
|
+
[flake8]
|
|
56
|
+
max-line-length = 160
|
|
57
|
+
exclude =
|
|
58
|
+
__init__.py
|
|
59
|
+
build
|
|
60
|
+
dist
|
|
61
|
+
|
|
62
|
+
[egg_info]
|
|
63
|
+
tag_build =
|
|
64
|
+
tag_date = 0
|
|
65
|
+
|
chokkhu-0.2.0/setup.py
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import setuptools
|
|
2
|
+
|
|
3
|
+
with open("README.md", "r", encoding="utf-8") as f:
|
|
4
|
+
long_description=f.read()
|
|
5
|
+
|
|
6
|
+
__version__="0.2.0"
|
|
7
|
+
|
|
8
|
+
REPO_NAME="Chokkhu-PyPi-Package"
|
|
9
|
+
AUTHOR_USER_NAME="tamimystic"
|
|
10
|
+
AUTHOR_EMAIL="hossainsmtamim@gamil.com"
|
|
11
|
+
SRC_REPO="chokkhu"
|
|
12
|
+
|
|
13
|
+
setuptools.setup(
|
|
14
|
+
name=SRC_REPO,
|
|
15
|
+
version=__version__,
|
|
16
|
+
author=AUTHOR_USER_NAME,
|
|
17
|
+
author_email=AUTHOR_EMAIL,
|
|
18
|
+
description="A small python image classification package",
|
|
19
|
+
long_description=long_description,
|
|
20
|
+
long_description_content_type="text/markdown",
|
|
21
|
+
url=f"https://github.com/{AUTHOR_USER_NAME}/{REPO_NAME}",
|
|
22
|
+
project_urls={
|
|
23
|
+
"Bug Tracker": f"https://github.com/{AUTHOR_USER_NAME}/{REPO_NAME}/issues",
|
|
24
|
+
},
|
|
25
|
+
package_dir={"": "src"},
|
|
26
|
+
packages=setuptools.find_packages(where="src")
|
|
27
|
+
)
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from .eda.image import ImageEDA
|
|
2
|
+
from .eda.tabular import TabularEDA
|
|
3
|
+
from .preprocessing.image import ImagePreProcessor
|
|
4
|
+
|
|
5
|
+
class EDAWrapper:
|
|
6
|
+
@staticmethod
|
|
7
|
+
def image(dataset_path: str, save_reports: bool = True, save_dir: str = "chokkhu_outputs/EDA_Reports") -> ImageEDA:
|
|
8
|
+
"""
|
|
9
|
+
Runs the full Exploratory Data Analysis on the image dataset.
|
|
10
|
+
"""
|
|
11
|
+
return ImageEDA(dataset_path=dataset_path, save_reports=save_reports, save_dir=save_dir)
|
|
12
|
+
|
|
13
|
+
@staticmethod
|
|
14
|
+
def tabular(dataset_path: str, save_reports: bool = True, save_dir: str = "chokkhu_outputs/EDA_Reports") -> TabularEDA:
|
|
15
|
+
"""
|
|
16
|
+
Runs the full Exploratory Data Analysis on the tabular dataset.
|
|
17
|
+
"""
|
|
18
|
+
return TabularEDA(dataset_path=dataset_path, save_reports=save_reports, save_dir=save_dir)
|
|
19
|
+
|
|
20
|
+
eda = EDAWrapper()
|
|
21
|
+
|
|
22
|
+
__all__ = ["ImageEDA", "TabularEDA", "ImagePreProcessor", "eda"]
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import warnings
|
|
3
|
+
from typing import Any, Dict, List
|
|
4
|
+
|
|
5
|
+
import cv2
|
|
6
|
+
import matplotlib.pyplot as plt
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import seaborn as sns
|
|
10
|
+
from PIL import Image
|
|
11
|
+
from tqdm import tqdm
|
|
12
|
+
from sklearn.decomposition import PCA
|
|
13
|
+
|
|
14
|
+
from .visualizer import PlotVisualizer
|
|
15
|
+
|
|
16
|
+
warnings.filterwarnings("ignore", category=FutureWarning)
|
|
17
|
+
|
|
18
|
+
class ImageEDA:
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
dataset_path: str,
|
|
22
|
+
save_reports: bool = True,
|
|
23
|
+
save_dir: str = "chokkhu_outputs/EDA_Reports",
|
|
24
|
+
):
|
|
25
|
+
"""
|
|
26
|
+
Initializes the Ultra Pro Max ImageEDA class and triggers the analysis pipeline.
|
|
27
|
+
"""
|
|
28
|
+
self.dataset_path: str = dataset_path
|
|
29
|
+
self.save_reports: bool = save_reports
|
|
30
|
+
self.save_dir: str = save_dir
|
|
31
|
+
self.results: Dict[str, Any] = {}
|
|
32
|
+
self.class_paths: List[str] = []
|
|
33
|
+
|
|
34
|
+
if self.save_reports:
|
|
35
|
+
os.makedirs(self.save_dir, exist_ok=True)
|
|
36
|
+
|
|
37
|
+
PlotVisualizer.setup_theme()
|
|
38
|
+
self._perform_eda()
|
|
39
|
+
|
|
40
|
+
def _perform_eda(self) -> None:
|
|
41
|
+
print(f"--- Executing Ultra Pro Max EDA for: {self.dataset_path} ---")
|
|
42
|
+
self._collect_paths()
|
|
43
|
+
if not self.class_paths:
|
|
44
|
+
print("Error: No valid images found in the specified path.")
|
|
45
|
+
return
|
|
46
|
+
self.results = self._analyze_data()
|
|
47
|
+
self._visual_reports()
|
|
48
|
+
|
|
49
|
+
def _collect_paths(self) -> None:
|
|
50
|
+
for root, _, files in os.walk(self.dataset_path):
|
|
51
|
+
if any(f.lower().endswith((".png", ".jpg", ".jpeg")) for f in files):
|
|
52
|
+
self.class_paths.append(root)
|
|
53
|
+
|
|
54
|
+
def _analyze_data(self) -> Dict[str, Any]:
|
|
55
|
+
exts = (".png", ".jpg", ".jpeg")
|
|
56
|
+
metrics_list = []
|
|
57
|
+
total_rgb_hist = np.zeros((256, 3))
|
|
58
|
+
processed_count = 0
|
|
59
|
+
pca_samples = []
|
|
60
|
+
pca_labels = []
|
|
61
|
+
|
|
62
|
+
# Max samples for PCA per class to avoid memory overload
|
|
63
|
+
MAX_PCA_SAMPLES = 200
|
|
64
|
+
|
|
65
|
+
for path in self.class_paths:
|
|
66
|
+
class_name = os.path.basename(path)
|
|
67
|
+
files = [f for f in os.listdir(path) if f.lower().endswith(exts)]
|
|
68
|
+
|
|
69
|
+
pca_sampled = 0
|
|
70
|
+
for img_name in tqdm(files, desc=f"Processing {class_name}"):
|
|
71
|
+
img_path = os.path.join(path, img_name)
|
|
72
|
+
img_bgr = cv2.imread(img_path)
|
|
73
|
+
if img_bgr is None:
|
|
74
|
+
continue
|
|
75
|
+
|
|
76
|
+
img_rgb = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2RGB)
|
|
77
|
+
gray = cv2.cvtColor(img_bgr, cv2.COLOR_BGR2GRAY)
|
|
78
|
+
|
|
79
|
+
h, w, _ = img_rgb.shape
|
|
80
|
+
|
|
81
|
+
# Metrics Extraction
|
|
82
|
+
brightness = gray.mean()
|
|
83
|
+
contrast = gray.std()
|
|
84
|
+
blur = cv2.Laplacian(gray, cv2.CV_64F).var()
|
|
85
|
+
edges = cv2.Canny(gray, 100, 200).mean()
|
|
86
|
+
|
|
87
|
+
metrics_list.append({
|
|
88
|
+
"Class": class_name,
|
|
89
|
+
"Image": img_name,
|
|
90
|
+
"Width": w,
|
|
91
|
+
"Height": h,
|
|
92
|
+
"Aspect_Ratio": w / h if h > 0 else 0,
|
|
93
|
+
"Brightness": brightness,
|
|
94
|
+
"Contrast": contrast,
|
|
95
|
+
"Blur_Score": blur,
|
|
96
|
+
"Edge_Intensity": edges
|
|
97
|
+
})
|
|
98
|
+
|
|
99
|
+
# RGB Distribution
|
|
100
|
+
for i in range(3):
|
|
101
|
+
hist = cv2.calcHist([img_rgb], [i], None, [256], [0, 256])
|
|
102
|
+
total_rgb_hist[:, i] += hist.flatten() # type: ignore
|
|
103
|
+
|
|
104
|
+
# Sampling for PCA (Downscale to 32x32 for memory efficiency)
|
|
105
|
+
if pca_sampled < MAX_PCA_SAMPLES:
|
|
106
|
+
resized_for_pca = cv2.resize(gray, (32, 32)).flatten()
|
|
107
|
+
pca_samples.append(resized_for_pca)
|
|
108
|
+
pca_labels.append(class_name)
|
|
109
|
+
pca_sampled += 1
|
|
110
|
+
|
|
111
|
+
processed_count += 1
|
|
112
|
+
|
|
113
|
+
df_metrics = pd.DataFrame(metrics_list)
|
|
114
|
+
avg_hist = total_rgb_hist / processed_count if processed_count > 0 else total_rgb_hist
|
|
115
|
+
|
|
116
|
+
# PCA Computation
|
|
117
|
+
df_pca = None
|
|
118
|
+
if pca_samples:
|
|
119
|
+
pca = PCA(n_components=2)
|
|
120
|
+
pca_result = pca.fit_transform(np.array(pca_samples))
|
|
121
|
+
df_pca = pd.DataFrame({
|
|
122
|
+
"PCA1": pca_result[:, 0],
|
|
123
|
+
"PCA2": pca_result[:, 1],
|
|
124
|
+
"Class": pca_labels
|
|
125
|
+
})
|
|
126
|
+
|
|
127
|
+
return {
|
|
128
|
+
"df_metrics": df_metrics,
|
|
129
|
+
"avg_rgb_hist": avg_hist,
|
|
130
|
+
"df_pca": df_pca,
|
|
131
|
+
"total_classes": len(self.class_paths),
|
|
132
|
+
"total_images": processed_count,
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
def _visual_reports(self) -> None:
|
|
136
|
+
df = self.results["df_metrics"]
|
|
137
|
+
|
|
138
|
+
# 1. Class Distribution
|
|
139
|
+
fig, ax = plt.subplots(figsize=(10, 5))
|
|
140
|
+
class_counts = df["Class"].value_counts().reset_index()
|
|
141
|
+
class_counts.columns = ["Class", "Count"]
|
|
142
|
+
sns.barplot(data=class_counts, x="Class", y="Count", palette="viridis", ax=ax)
|
|
143
|
+
for p in ax.patches:
|
|
144
|
+
ax.annotate(f"{int(p.get_height())}", (p.get_x() + p.get_width() / 2., p.get_height()), ha='center', va='center', xytext=(0, 8), textcoords='offset points')
|
|
145
|
+
ax.set_title("Class-wise Image Distribution")
|
|
146
|
+
ax.tick_params(axis='x', rotation=45)
|
|
147
|
+
PlotVisualizer.save_and_show(fig, "1_class_distribution.png", self.save_dir, self.save_reports)
|
|
148
|
+
|
|
149
|
+
# 2. Dimensions & Aspect Ratio
|
|
150
|
+
fig, axes = plt.subplots(1, 3, figsize=(18, 5))
|
|
151
|
+
sns.histplot(data=df, x="Width", hue="Class", kde=False, element="step", ax=axes[0], palette="Set2")
|
|
152
|
+
axes[0].set_title("Width Distribution by Class")
|
|
153
|
+
sns.histplot(data=df, x="Height", hue="Class", kde=False, element="step", ax=axes[1], palette="Set2")
|
|
154
|
+
axes[1].set_title("Height Distribution by Class")
|
|
155
|
+
sns.boxplot(data=df, x="Class", y="Aspect_Ratio", ax=axes[2], palette="Set2")
|
|
156
|
+
axes[2].set_title("Aspect Ratio by Class")
|
|
157
|
+
axes[2].tick_params(axis='x', rotation=45)
|
|
158
|
+
PlotVisualizer.save_and_show(fig, "2_dimension_analysis.png", self.save_dir, self.save_reports)
|
|
159
|
+
|
|
160
|
+
# 3. Quality Metrics (Brightness, Contrast, Blur, Edges)
|
|
161
|
+
fig, axes = plt.subplots(2, 2, figsize=(16, 12))
|
|
162
|
+
sns.violinplot(data=df, x="Class", y="Brightness", palette="coolwarm", ax=axes[0, 0])
|
|
163
|
+
axes[0, 0].set_title("Brightness Distribution")
|
|
164
|
+
axes[0, 0].tick_params(axis='x', rotation=45)
|
|
165
|
+
|
|
166
|
+
sns.violinplot(data=df, x="Class", y="Contrast", palette="coolwarm", ax=axes[0, 1])
|
|
167
|
+
axes[0, 1].set_title("Contrast Distribution")
|
|
168
|
+
axes[0, 1].tick_params(axis='x', rotation=45)
|
|
169
|
+
|
|
170
|
+
sns.boxplot(data=df, x="Class", y="Blur_Score", palette="crest", ax=axes[1, 0])
|
|
171
|
+
axes[1, 0].set_title("Blur Score (Laplacian Variance)")
|
|
172
|
+
axes[1, 0].set_yscale('log')
|
|
173
|
+
axes[1, 0].tick_params(axis='x', rotation=45)
|
|
174
|
+
|
|
175
|
+
sns.boxplot(data=df, x="Class", y="Edge_Intensity", palette="crest", ax=axes[1, 1])
|
|
176
|
+
axes[1, 1].set_title("Edge Intensity (Canny)")
|
|
177
|
+
axes[1, 1].tick_params(axis='x', rotation=45)
|
|
178
|
+
PlotVisualizer.save_and_show(fig, "3_quality_metrics.png", self.save_dir, self.save_reports)
|
|
179
|
+
|
|
180
|
+
# 4. RGB Intensity Distribution
|
|
181
|
+
fig, ax = plt.subplots(figsize=(10, 6))
|
|
182
|
+
for i, col in enumerate(["red", "green", "blue"]):
|
|
183
|
+
ax.plot(self.results["avg_rgb_hist"][:, i], color=col, label=f"{col.upper()} Channel", linewidth=2)
|
|
184
|
+
ax.fill_between(range(256), self.results["avg_rgb_hist"][:, i], color=col, alpha=0.15)
|
|
185
|
+
ax.set_title("Global Average RGB Intensity Distribution")
|
|
186
|
+
ax.legend()
|
|
187
|
+
PlotVisualizer.save_and_show(fig, "4_rgb_intensity.png", self.save_dir, self.save_reports)
|
|
188
|
+
|
|
189
|
+
# 5. PCA Feature Space
|
|
190
|
+
df_pca = self.results.get("df_pca")
|
|
191
|
+
if df_pca is not None:
|
|
192
|
+
fig, ax = plt.subplots(figsize=(10, 8))
|
|
193
|
+
sns.scatterplot(data=df_pca, x="PCA1", y="PCA2", hue="Class", palette="tab10", alpha=0.7, ax=ax)
|
|
194
|
+
ax.set_title("PCA Feature Space (2D) - Are classes separable?")
|
|
195
|
+
PlotVisualizer.save_and_show(fig, "5_pca_feature_space.png", self.save_dir, self.save_reports)
|
|
196
|
+
|
|
197
|
+
# 6. Sample Grid
|
|
198
|
+
fig = plt.figure(figsize=(15, 10))
|
|
199
|
+
for i, path in enumerate(self.class_paths[:9]):
|
|
200
|
+
files = [f for f in os.listdir(path) if f.lower().endswith((".png", ".jpg", ".jpeg"))]
|
|
201
|
+
if files:
|
|
202
|
+
img = Image.open(os.path.join(path, files[0]))
|
|
203
|
+
ax = fig.add_subplot(3, 3, i + 1)
|
|
204
|
+
ax.imshow(img)
|
|
205
|
+
ax.set_title(os.path.basename(path))
|
|
206
|
+
ax.axis("off")
|
|
207
|
+
fig.suptitle("Sample Images per Class", fontsize=20)
|
|
208
|
+
PlotVisualizer.save_and_show(fig, "6_sample_grid.png", self.save_dir, self.save_reports)
|
|
209
|
+
|
|
210
|
+
# Save CSV Metrics
|
|
211
|
+
if self.save_reports:
|
|
212
|
+
df.to_csv(os.path.join(self.save_dir, "detailed_image_metrics.csv"), index=False)
|
|
213
|
+
print(f"\n[INFO] All reports and visualizations have been saved in: {self.save_dir}")
|