ImagePRO-Python 0.5.1__tar.gz → 1.0.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.
- imagepro_python-1.0.0/PKG-INFO +30 -0
- {imagepro_python-0.5.1 → imagepro_python-1.0.0}/pyproject.toml +14 -3
- imagepro_python-1.0.0/src/ImagePRO/__init__.py +33 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/__init__.py +7 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/body_analysis/__init__.py +7 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/body_analysis/body_pose_estimation.py +170 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/body_analysis/hand_tracking.py +202 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/face_analysis/__init__.py +16 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/face_analysis/eye_status_analysis.py +226 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/face_analysis/face_comparison.py +129 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/face_analysis/face_detection.py +121 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/face_analysis/face_mesh_analysis.py +237 -0
- imagepro_python-1.0.0/src/ImagePRO/human_analysis/face_analysis/head_pose_estimation.py +221 -0
- imagepro_python-1.0.0/src/ImagePRO/object_analysis/__init__.py +6 -0
- imagepro_python-1.0.0/src/ImagePRO/object_analysis/object_detection.py +95 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/__init__.py +22 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/blur.py +230 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/contrast.py +161 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/crop.py +72 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/dataset_generator.py +198 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/grayscale.py +50 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/resize.py +54 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/rotate.py +148 -0
- imagepro_python-1.0.0/src/ImagePRO/pre_processing/sharpen.py +115 -0
- imagepro_python-1.0.0/src/ImagePRO/utils/__init__.py +6 -0
- imagepro_python-1.0.0/src/ImagePRO/utils/image.py +118 -0
- imagepro_python-1.0.0/src/ImagePRO/utils/result.py +146 -0
- imagepro_python-1.0.0/src/ImagePRO_Python.egg-info/PKG-INFO +30 -0
- {imagepro_python-0.5.1 → imagepro_python-1.0.0}/src/ImagePRO_Python.egg-info/SOURCES.txt +3 -1
- imagepro_python-0.5.1/PKG-INFO +0 -20
- imagepro_python-0.5.1/src/ImagePRO/__init__.py +0 -0
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/__init__.py +0 -0
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/body_analysis/__init__.py +0 -0
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/body_analysis/body_pose_estimation.py +0 -133
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/body_analysis/hand_tracking.py +0 -152
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/face_analysis/__init__.py +0 -0
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/face_analysis/eye_status_analysis.py +0 -182
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/face_analysis/face_comparison.py +0 -60
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/face_analysis/face_detection.py +0 -109
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/face_analysis/face_mesh_analysis.py +0 -225
- imagepro_python-0.5.1/src/ImagePRO/human_analysis/face_analysis/head_pose_estimation.py +0 -177
- imagepro_python-0.5.1/src/ImagePRO/object_analysis/object_detection.py +0 -98
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/__init__.py +0 -0
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/blur.py +0 -198
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/contrast.py +0 -153
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/crop.py +0 -76
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/dataset_generator.py +0 -178
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/grayscale.py +0 -49
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/resize.py +0 -64
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/rotate.py +0 -115
- imagepro_python-0.5.1/src/ImagePRO/pre_processing/sharpen.py +0 -90
- imagepro_python-0.5.1/src/ImagePRO/utils/__init__.py +0 -0
- imagepro_python-0.5.1/src/ImagePRO/utils/io_handler.py +0 -180
- imagepro_python-0.5.1/src/ImagePRO_Python.egg-info/PKG-INFO +0 -20
- {imagepro_python-0.5.1 → imagepro_python-1.0.0}/LICENSE +0 -0
- {imagepro_python-0.5.1 → imagepro_python-1.0.0}/setup.cfg +0 -0
- {imagepro_python-0.5.1 → imagepro_python-1.0.0}/src/ImagePRO_Python.egg-info/dependency_links.txt +0 -0
- {imagepro_python-0.5.1 → imagepro_python-1.0.0}/src/ImagePRO_Python.egg-info/requires.txt +0 -0
- {imagepro_python-0.5.1 → imagepro_python-1.0.0}/src/ImagePRO_Python.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: ImagePRO-Python
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Professional & modular image processing library with OpenCV, MediaPipe, and YOLO integration.
|
|
5
|
+
Author-email: Parsa Safaie <parsasafaie.2568@proton.me>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/parsasafaie/ImagePRO
|
|
8
|
+
Project-URL: Repository, https://github.com/parsasafaie/ImagePRO.git
|
|
9
|
+
Project-URL: Documentation, https://github.com/parsasafaie/ImagePRO#readme
|
|
10
|
+
Project-URL: Issues, https://github.com/parsasafaie/ImagePRO/issues
|
|
11
|
+
Keywords: opencv,mediapipe,computer-vision,face,pose,yolo,image-processing
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
18
|
+
Classifier: Operating System :: OS Independent
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Image Processing
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: ==3.10.*
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: numpy<3.0,>=1.24
|
|
26
|
+
Requires-Dist: opencv-python<5.0,>=4.8
|
|
27
|
+
Requires-Dist: ultralytics<9.0,>=8.2.0
|
|
28
|
+
Requires-Dist: mediapipe<1.0,>=0.10.0
|
|
29
|
+
Requires-Dist: insightface<1.0,>=0.7
|
|
30
|
+
Dynamic: license-file
|
|
@@ -7,18 +7,24 @@ where = ["src"]
|
|
|
7
7
|
|
|
8
8
|
[project]
|
|
9
9
|
name = "ImagePRO-Python"
|
|
10
|
-
version = "0.
|
|
11
|
-
description = "
|
|
10
|
+
version = "1.0.0"
|
|
11
|
+
description = "Professional & modular image processing library with OpenCV, MediaPipe, and YOLO integration."
|
|
12
12
|
readme = "README.md"
|
|
13
13
|
requires-python = "==3.10.*"
|
|
14
14
|
license = {text = "MIT"}
|
|
15
15
|
authors = [{name = "Parsa Safaie", email = "parsasafaie.2568@proton.me"}]
|
|
16
|
-
keywords = ["opencv", "mediapipe", "computer-vision", "face", "pose"]
|
|
16
|
+
keywords = ["opencv", "mediapipe", "computer-vision", "face", "pose", "yolo", "image-processing"]
|
|
17
17
|
classifiers = [
|
|
18
|
+
"Development Status :: 4 - Beta",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"Intended Audience :: Science/Research",
|
|
18
21
|
"Programming Language :: Python :: 3",
|
|
22
|
+
"Programming Language :: Python :: 3.10",
|
|
19
23
|
"License :: OSI Approved :: MIT License",
|
|
20
24
|
"Operating System :: OS Independent",
|
|
21
25
|
"Topic :: Scientific/Engineering :: Image Processing",
|
|
26
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence",
|
|
27
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
22
28
|
]
|
|
23
29
|
|
|
24
30
|
dependencies = [
|
|
@@ -29,6 +35,11 @@ dependencies = [
|
|
|
29
35
|
"insightface>=0.7,<1.0",
|
|
30
36
|
]
|
|
31
37
|
|
|
38
|
+
[project.urls]
|
|
39
|
+
Homepage = "https://github.com/parsasafaie/ImagePRO"
|
|
40
|
+
Repository = "https://github.com/parsasafaie/ImagePRO.git"
|
|
41
|
+
Documentation = "https://github.com/parsasafaie/ImagePRO#readme"
|
|
42
|
+
Issues = "https://github.com/parsasafaie/ImagePRO/issues"
|
|
32
43
|
|
|
33
44
|
[tool.hatch.build.targets.wheel]
|
|
34
45
|
packages = ["src/ImagePRO"]
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ImagePRO - Professional & Modular Image Processing Library in Python
|
|
3
|
+
|
|
4
|
+
A clean, modular, and easy-to-use Python library for image processing tasks,
|
|
5
|
+
built with OpenCV, MediaPipe, YOLO and designed to be extensible for developers.
|
|
6
|
+
|
|
7
|
+
Whether you're working on computer vision pipelines, preprocessing images for AI models,
|
|
8
|
+
or simply automating batch image edits — ImagePRO gives you powerful tools with minimal effort.
|
|
9
|
+
|
|
10
|
+
Features:
|
|
11
|
+
- Image I/O management and batch processing
|
|
12
|
+
- Pre-processing: resize, crop, rotate, blur, sharpen, contrast enhancement
|
|
13
|
+
- Human analysis: face mesh, pose estimation, hand tracking
|
|
14
|
+
- Object detection with YOLO models
|
|
15
|
+
- Real-time processing capabilities
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
__version__ = "1.0.0"
|
|
19
|
+
__author__ = "Parsa Safaie"
|
|
20
|
+
__email__ = "parsasafaie.2568@proton.me"
|
|
21
|
+
|
|
22
|
+
# Core modules
|
|
23
|
+
from . import utils
|
|
24
|
+
from . import pre_processing
|
|
25
|
+
from . import human_analysis
|
|
26
|
+
from . import object_analysis
|
|
27
|
+
|
|
28
|
+
__all__ = [
|
|
29
|
+
"utils",
|
|
30
|
+
"pre_processing",
|
|
31
|
+
"human_analysis",
|
|
32
|
+
"object_analysis"
|
|
33
|
+
]
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import cv2
|
|
4
|
+
import mediapipe as mp
|
|
5
|
+
|
|
6
|
+
from ImagePRO.utils.image import Image
|
|
7
|
+
from ImagePRO.utils.result import Result
|
|
8
|
+
|
|
9
|
+
# Constants
|
|
10
|
+
mp_pose = mp.solutions.pose
|
|
11
|
+
TOTAL_LANDMARKS = 33
|
|
12
|
+
DEFAULT_CONFIDENCE = 0.7
|
|
13
|
+
LANDMARK_RADIUS = 3
|
|
14
|
+
LANDMARK_COLOR = (0, 0, 255) # Red color for landmarks
|
|
15
|
+
|
|
16
|
+
def detect_body_pose(
|
|
17
|
+
image: Image,
|
|
18
|
+
*,
|
|
19
|
+
min_confidence: float = DEFAULT_CONFIDENCE,
|
|
20
|
+
landmarks_idx: list[int] | None = None,
|
|
21
|
+
pose_obj: mp.solutions.pose.Pose | None = None
|
|
22
|
+
) -> Result:
|
|
23
|
+
"""Detect body landmarks in an image using MediaPipe Pose.
|
|
24
|
+
|
|
25
|
+
Extracts 3D body landmark coordinates and optionally draws them.
|
|
26
|
+
Can focus on specific landmarks or detect all 33 points.
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
image: Input image to process.
|
|
30
|
+
min_confidence: Detection confidence threshold.
|
|
31
|
+
Must be between 0 and 1.
|
|
32
|
+
Default: 0.7
|
|
33
|
+
landmarks_idx: Specific landmark indices to detect.
|
|
34
|
+
If None, detects all 33 landmarks.
|
|
35
|
+
Default: None
|
|
36
|
+
pose_obj: Pre-initialized pose detector.
|
|
37
|
+
If None, creates new instance.
|
|
38
|
+
Default: None
|
|
39
|
+
|
|
40
|
+
Returns:
|
|
41
|
+
Result object with detections and visualization:
|
|
42
|
+
- image: Input image with landmarks drawn
|
|
43
|
+
- data: List of [idx, x, y, z] coordinates
|
|
44
|
+
- meta: Operation info and parameters
|
|
45
|
+
|
|
46
|
+
Raises:
|
|
47
|
+
TypeError: If image is not an Image instance
|
|
48
|
+
ValueError: If min_confidence not in [0,1]
|
|
49
|
+
TypeError: If landmarks_idx is not list[int]
|
|
50
|
+
"""
|
|
51
|
+
if not isinstance(image, Image):
|
|
52
|
+
raise TypeError("'image' must be an Image instance")
|
|
53
|
+
|
|
54
|
+
if not isinstance(min_confidence, (int, float)) or not (0.0 <= min_confidence <= 1.0):
|
|
55
|
+
raise ValueError("'min_confidence' must be between 0.0 and 1.0")
|
|
56
|
+
|
|
57
|
+
if landmarks_idx is not None and (
|
|
58
|
+
not isinstance(landmarks_idx, list)
|
|
59
|
+
or not all(isinstance(i, int) for i in landmarks_idx)
|
|
60
|
+
):
|
|
61
|
+
raise TypeError("'landmarks_idx' must be a list of integers")
|
|
62
|
+
|
|
63
|
+
# Initialize detector if needed
|
|
64
|
+
if pose_obj is None:
|
|
65
|
+
pose_obj = mp_pose.Pose(
|
|
66
|
+
min_detection_confidence=min_confidence,
|
|
67
|
+
static_image_mode=True
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Use all landmarks if none specified
|
|
71
|
+
if landmarks_idx is None:
|
|
72
|
+
landmarks_idx = list(range(TOTAL_LANDMARKS))
|
|
73
|
+
|
|
74
|
+
# Prepare image
|
|
75
|
+
h, w = image.shape[:2]
|
|
76
|
+
img_copy = image._data.copy()
|
|
77
|
+
img_rgb = cv2.cvtColor(img_copy, cv2.COLOR_BGR2RGB)
|
|
78
|
+
|
|
79
|
+
# Detect pose
|
|
80
|
+
result = pose_obj.process(img_rgb)
|
|
81
|
+
landmarks = []
|
|
82
|
+
|
|
83
|
+
if result.pose_landmarks:
|
|
84
|
+
# Visualize landmarks
|
|
85
|
+
if len(landmarks_idx) == TOTAL_LANDMARKS:
|
|
86
|
+
# Draw full skeleton
|
|
87
|
+
mp.solutions.drawing_utils.draw_landmarks(
|
|
88
|
+
img_copy,
|
|
89
|
+
result.pose_landmarks,
|
|
90
|
+
mp_pose.POSE_CONNECTIONS,
|
|
91
|
+
landmark_drawing_spec=mp.solutions.drawing_styles.get_default_pose_landmarks_style()
|
|
92
|
+
)
|
|
93
|
+
else:
|
|
94
|
+
# Draw only selected points
|
|
95
|
+
for idx in landmarks_idx:
|
|
96
|
+
lm = result.pose_landmarks.landmark[idx]
|
|
97
|
+
x, y = int(w * lm.x), int(h * lm.y)
|
|
98
|
+
cv2.circle(img_copy, (x, y), LANDMARK_RADIUS, LANDMARK_COLOR, -1)
|
|
99
|
+
|
|
100
|
+
# Extract coordinates
|
|
101
|
+
for idx in landmarks_idx:
|
|
102
|
+
lm = result.pose_landmarks.landmark[idx]
|
|
103
|
+
landmarks.append([idx, lm.x, lm.y, lm.z])
|
|
104
|
+
|
|
105
|
+
return Result(
|
|
106
|
+
image=img_copy,
|
|
107
|
+
data=landmarks,
|
|
108
|
+
meta={
|
|
109
|
+
"source": image,
|
|
110
|
+
"operation": "detect_body_pose",
|
|
111
|
+
"min_confidence": min_confidence,
|
|
112
|
+
"landmarks_idx": landmarks_idx
|
|
113
|
+
}
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def detect_body_pose_live() -> None:
|
|
118
|
+
"""Start live webcam feed with real-time body pose detection.
|
|
119
|
+
|
|
120
|
+
Opens the default camera and shows pose landmarks in real-time.
|
|
121
|
+
Press ESC to exit the application.
|
|
122
|
+
|
|
123
|
+
Raises:
|
|
124
|
+
RuntimeError: If camera cannot be accessed
|
|
125
|
+
"""
|
|
126
|
+
# Initialize camera
|
|
127
|
+
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
|
|
128
|
+
if not cap.isOpened():
|
|
129
|
+
raise RuntimeError("Cannot access webcam")
|
|
130
|
+
|
|
131
|
+
# Initialize pose detector in tracking mode
|
|
132
|
+
pose_obj = mp_pose.Pose(
|
|
133
|
+
min_detection_confidence=DEFAULT_CONFIDENCE,
|
|
134
|
+
static_image_mode=False
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
try:
|
|
138
|
+
while True:
|
|
139
|
+
# Get frame
|
|
140
|
+
success, frame = cap.read()
|
|
141
|
+
if not success:
|
|
142
|
+
print("Failed to capture frame, skipping...")
|
|
143
|
+
continue
|
|
144
|
+
|
|
145
|
+
# Process frame
|
|
146
|
+
try:
|
|
147
|
+
result = detect_body_pose(
|
|
148
|
+
image=Image.from_array(frame),
|
|
149
|
+
min_confidence=DEFAULT_CONFIDENCE,
|
|
150
|
+
pose_obj=pose_obj
|
|
151
|
+
)
|
|
152
|
+
display_frame = result.image
|
|
153
|
+
except (TypeError, ValueError):
|
|
154
|
+
# Fall back to raw frame if detection fails
|
|
155
|
+
display_frame = frame
|
|
156
|
+
|
|
157
|
+
# Show result
|
|
158
|
+
cv2.imshow('ImagePRO - Live Body Pose Detection', display_frame)
|
|
159
|
+
|
|
160
|
+
# Check for ESC key
|
|
161
|
+
if cv2.waitKey(5) & 0xFF == 27:
|
|
162
|
+
break
|
|
163
|
+
finally:
|
|
164
|
+
# Clean up resources
|
|
165
|
+
cap.release()
|
|
166
|
+
cv2.destroyAllWindows()
|
|
167
|
+
|
|
168
|
+
|
|
169
|
+
if __name__ == "__main__":
|
|
170
|
+
detect_body_pose_live()
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import cv2
|
|
4
|
+
import mediapipe as mp
|
|
5
|
+
|
|
6
|
+
from ImagePRO.utils.image import Image
|
|
7
|
+
from ImagePRO.utils.result import Result
|
|
8
|
+
|
|
9
|
+
# Constants
|
|
10
|
+
mp_hands = mp.solutions.hands
|
|
11
|
+
DEFAULT_MAX_HANDS = 2
|
|
12
|
+
DEFAULT_MIN_CONFIDENCE = 0.7
|
|
13
|
+
TOTAL_HAND_LANDMARKS = 21
|
|
14
|
+
LANDMARK_RADIUS = 3
|
|
15
|
+
LANDMARK_COLOR = (0, 0, 255) # Red color for landmarks
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def detect_hands(
|
|
19
|
+
image: Image,
|
|
20
|
+
*,
|
|
21
|
+
max_hands: int = DEFAULT_MAX_HANDS,
|
|
22
|
+
min_confidence: float = DEFAULT_MIN_CONFIDENCE,
|
|
23
|
+
landmarks_idx: list[int] | None = None,
|
|
24
|
+
hands_obj: mp.solutions.hands.Hands | None = None
|
|
25
|
+
) -> Result:
|
|
26
|
+
"""Detect hand landmarks in an image using MediaPipe Hands.
|
|
27
|
+
|
|
28
|
+
Identifies hand positions and extracts 3D coordinates of
|
|
29
|
+
key points (joints, fingertips, etc.).
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
image: Input image to process.
|
|
33
|
+
max_hands: Maximum number of hands to detect.
|
|
34
|
+
Must be positive.
|
|
35
|
+
Default: 2
|
|
36
|
+
min_confidence: Detection confidence threshold.
|
|
37
|
+
Must be between 0 and 1.
|
|
38
|
+
Default: 0.7
|
|
39
|
+
landmarks_idx: Specific landmark indices to detect.
|
|
40
|
+
If None, detects all 21 points per hand.
|
|
41
|
+
Default: None
|
|
42
|
+
hands_obj: Pre-initialized hand detector.
|
|
43
|
+
If None, creates new instance.
|
|
44
|
+
Default: None
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
Result object with detections and visualization:
|
|
48
|
+
- image: Input image with landmarks drawn
|
|
49
|
+
- data: List of [hand_id, point_id, x, y, z]
|
|
50
|
+
- meta: Operation info and parameters
|
|
51
|
+
|
|
52
|
+
Raises:
|
|
53
|
+
TypeError: If image is not an Image instance
|
|
54
|
+
ValueError: If max_hands is not positive
|
|
55
|
+
ValueError: If min_confidence not in [0,1]
|
|
56
|
+
TypeError: If landmarks_idx is not list[int]
|
|
57
|
+
"""
|
|
58
|
+
if not isinstance(image, Image):
|
|
59
|
+
raise TypeError("'image' must be an Image instance")
|
|
60
|
+
|
|
61
|
+
if not isinstance(max_hands, int) or max_hands <= 0:
|
|
62
|
+
raise ValueError("'max_hands' must be positive")
|
|
63
|
+
|
|
64
|
+
if not isinstance(min_confidence, (int, float)) or not (0.0 <= min_confidence <= 1.0):
|
|
65
|
+
raise ValueError("'min_confidence' must be between 0.0 and 1.0")
|
|
66
|
+
|
|
67
|
+
if landmarks_idx is not None and (
|
|
68
|
+
not isinstance(landmarks_idx, list)
|
|
69
|
+
or not all(isinstance(i, int) for i in landmarks_idx)
|
|
70
|
+
):
|
|
71
|
+
raise TypeError("'landmarks_idx' must be a list of integers")
|
|
72
|
+
|
|
73
|
+
# Initialize detector if needed
|
|
74
|
+
if hands_obj is None:
|
|
75
|
+
hands_obj = mp_hands.Hands(
|
|
76
|
+
min_detection_confidence=min_confidence,
|
|
77
|
+
max_num_hands=max_hands,
|
|
78
|
+
static_image_mode=True
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
# Use all landmarks if none specified
|
|
82
|
+
if landmarks_idx is None:
|
|
83
|
+
landmarks_idx = list(range(TOTAL_HAND_LANDMARKS))
|
|
84
|
+
|
|
85
|
+
# Prepare image
|
|
86
|
+
img_copy = image._data.copy()
|
|
87
|
+
img_rgb = cv2.cvtColor(img_copy, cv2.COLOR_BGR2RGB)
|
|
88
|
+
|
|
89
|
+
# Detect hands
|
|
90
|
+
results = hands_obj.process(img_rgb)
|
|
91
|
+
landmarks = []
|
|
92
|
+
|
|
93
|
+
# Process detections
|
|
94
|
+
if results.multi_hand_landmarks:
|
|
95
|
+
for hand_id, hand_landmarks in enumerate(results.multi_hand_landmarks):
|
|
96
|
+
# Visualize landmarks
|
|
97
|
+
if len(landmarks_idx) == TOTAL_HAND_LANDMARKS:
|
|
98
|
+
# Draw full hand skeleton
|
|
99
|
+
mp.solutions.drawing_utils.draw_landmarks(
|
|
100
|
+
image=img_copy,
|
|
101
|
+
landmark_list=hand_landmarks,
|
|
102
|
+
connections=mp_hands.HAND_CONNECTIONS,
|
|
103
|
+
landmark_drawing_spec=mp.solutions.drawing_styles.get_default_hand_landmarks_style(),
|
|
104
|
+
connection_drawing_spec=mp.solutions.drawing_styles.get_default_hand_connections_style()
|
|
105
|
+
)
|
|
106
|
+
else:
|
|
107
|
+
# Draw only selected points
|
|
108
|
+
h, w, _ = img_copy.shape
|
|
109
|
+
for idx in landmarks_idx:
|
|
110
|
+
lm = hand_landmarks.landmark[idx]
|
|
111
|
+
x, y = int(w * lm.x), int(h * lm.y)
|
|
112
|
+
cv2.circle(img_copy, (x, y), LANDMARK_RADIUS, LANDMARK_COLOR, -1)
|
|
113
|
+
|
|
114
|
+
# Extract coordinates
|
|
115
|
+
for idx in landmarks_idx:
|
|
116
|
+
lm = hand_landmarks.landmark[idx]
|
|
117
|
+
landmarks.append([hand_id, idx, lm.x, lm.y, lm.z])
|
|
118
|
+
|
|
119
|
+
return Result(
|
|
120
|
+
image=img_copy,
|
|
121
|
+
data=landmarks,
|
|
122
|
+
meta={
|
|
123
|
+
"source": image,
|
|
124
|
+
"operation": "detect_hands",
|
|
125
|
+
"max_hands": max_hands,
|
|
126
|
+
"min_confidence": min_confidence,
|
|
127
|
+
"landmarks_idx": landmarks_idx
|
|
128
|
+
}
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def detect_hands_live(
|
|
133
|
+
max_hands: int = DEFAULT_MAX_HANDS,
|
|
134
|
+
min_confidence: float = DEFAULT_MIN_CONFIDENCE
|
|
135
|
+
) -> None:
|
|
136
|
+
"""Start live webcam feed with real-time hand detection.
|
|
137
|
+
|
|
138
|
+
Opens the default camera and shows hand landmarks in real-time.
|
|
139
|
+
Press ESC to exit.
|
|
140
|
+
|
|
141
|
+
Args:
|
|
142
|
+
max_hands: Maximum number of hands to track.
|
|
143
|
+
Must be positive.
|
|
144
|
+
Default: 2
|
|
145
|
+
min_confidence: Detection confidence threshold.
|
|
146
|
+
Must be between 0 and 1.
|
|
147
|
+
Default: 0.7
|
|
148
|
+
|
|
149
|
+
Raises:
|
|
150
|
+
ValueError: If parameters are invalid
|
|
151
|
+
RuntimeError: If camera cannot be accessed
|
|
152
|
+
"""
|
|
153
|
+
# Validate parameters
|
|
154
|
+
if not isinstance(max_hands, int) or max_hands <= 0:
|
|
155
|
+
raise ValueError("'max_hands' must be positive")
|
|
156
|
+
|
|
157
|
+
if not isinstance(min_confidence, (int, float)) or not (0.0 <= min_confidence <= 1.0):
|
|
158
|
+
raise ValueError("'min_confidence' must be between 0.0 and 1.0")
|
|
159
|
+
|
|
160
|
+
# Initialize camera
|
|
161
|
+
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
|
|
162
|
+
if not cap.isOpened():
|
|
163
|
+
raise RuntimeError("Cannot access webcam")
|
|
164
|
+
|
|
165
|
+
# Initialize hand detector in tracking mode
|
|
166
|
+
hands_obj = mp_hands.Hands(
|
|
167
|
+
min_detection_confidence=min_confidence,
|
|
168
|
+
max_num_hands=max_hands,
|
|
169
|
+
static_image_mode=False
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
try:
|
|
173
|
+
while True:
|
|
174
|
+
# Get frame
|
|
175
|
+
success, frame = cap.read()
|
|
176
|
+
if not success:
|
|
177
|
+
print("Failed to capture frame, skipping...")
|
|
178
|
+
continue
|
|
179
|
+
|
|
180
|
+
# Process frame
|
|
181
|
+
result = detect_hands(
|
|
182
|
+
image=Image.from_array(frame),
|
|
183
|
+
max_hands=max_hands,
|
|
184
|
+
min_confidence=min_confidence,
|
|
185
|
+
hands_obj=hands_obj
|
|
186
|
+
)
|
|
187
|
+
display_frame = result.image if result.image is not None else frame
|
|
188
|
+
|
|
189
|
+
# Show result
|
|
190
|
+
cv2.imshow('ImagePRO - Live Hand Detection', display_frame)
|
|
191
|
+
|
|
192
|
+
# Check for ESC key
|
|
193
|
+
if cv2.waitKey(5) & 0xFF == 27:
|
|
194
|
+
break
|
|
195
|
+
finally:
|
|
196
|
+
# Clean up resources
|
|
197
|
+
cap.release()
|
|
198
|
+
cv2.destroyAllWindows()
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
if __name__ == "__main__":
|
|
202
|
+
detect_hands_live()
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Face Analysis Module
|
|
2
|
+
# Provides facial landmark detection, pose estimation, and eye status analysis
|
|
3
|
+
|
|
4
|
+
from . import eye_status_analysis
|
|
5
|
+
from . import face_comparison
|
|
6
|
+
from . import face_detection
|
|
7
|
+
from . import face_mesh_analysis
|
|
8
|
+
from . import head_pose_estimation
|
|
9
|
+
|
|
10
|
+
__all__ = [
|
|
11
|
+
"eye_status_analysis",
|
|
12
|
+
"face_comparison",
|
|
13
|
+
"face_detection",
|
|
14
|
+
"face_mesh_analysis",
|
|
15
|
+
"head_pose_estimation"
|
|
16
|
+
]
|