ImagePRO-Python 0.5.1__py3-none-any.whl → 1.0.0__py3-none-any.whl
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/__init__.py +33 -0
- ImagePRO/human_analysis/__init__.py +7 -0
- ImagePRO/human_analysis/body_analysis/__init__.py +7 -0
- ImagePRO/human_analysis/body_analysis/body_pose_estimation.py +111 -74
- ImagePRO/human_analysis/body_analysis/hand_tracking.py +126 -76
- ImagePRO/human_analysis/face_analysis/__init__.py +16 -0
- ImagePRO/human_analysis/face_analysis/eye_status_analysis.py +154 -110
- ImagePRO/human_analysis/face_analysis/face_comparison.py +107 -38
- ImagePRO/human_analysis/face_analysis/face_detection.py +94 -82
- ImagePRO/human_analysis/face_analysis/face_mesh_analysis.py +160 -148
- ImagePRO/human_analysis/face_analysis/head_pose_estimation.py +159 -115
- ImagePRO/object_analysis/__init__.py +6 -0
- ImagePRO/object_analysis/object_detection.py +74 -77
- ImagePRO/pre_processing/__init__.py +22 -0
- ImagePRO/pre_processing/blur.py +180 -148
- ImagePRO/pre_processing/contrast.py +131 -123
- ImagePRO/pre_processing/crop.py +49 -53
- ImagePRO/pre_processing/dataset_generator.py +109 -89
- ImagePRO/pre_processing/grayscale.py +44 -43
- ImagePRO/pre_processing/resize.py +43 -53
- ImagePRO/pre_processing/rotate.py +134 -101
- ImagePRO/pre_processing/sharpen.py +97 -72
- ImagePRO/utils/__init__.py +6 -0
- ImagePRO/utils/image.py +118 -0
- ImagePRO/utils/result.py +146 -0
- imagepro_python-1.0.0.dist-info/METADATA +30 -0
- imagepro_python-1.0.0.dist-info/RECORD +30 -0
- ImagePRO/utils/io_handler.py +0 -180
- imagepro_python-0.5.1.dist-info/METADATA +0 -20
- imagepro_python-0.5.1.dist-info/RECORD +0 -28
- {imagepro_python-0.5.1.dist-info → imagepro_python-1.0.0.dist-info}/WHEEL +0 -0
- {imagepro_python-0.5.1.dist-info → imagepro_python-1.0.0.dist-info}/licenses/LICENSE +0 -0
- {imagepro_python-0.5.1.dist-info → imagepro_python-1.0.0.dist-info}/top_level.txt +0 -0
ImagePRO/__init__.py
CHANGED
|
@@ -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
|
+
]
|
|
@@ -1,130 +1,167 @@
|
|
|
1
|
-
import
|
|
2
|
-
from pathlib import Path
|
|
1
|
+
from __future__ import annotations
|
|
3
2
|
|
|
4
3
|
import cv2
|
|
5
4
|
import mediapipe as mp
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
sys.path.append(str(parent_dir))
|
|
10
|
-
|
|
11
|
-
from utils.io_handler import IOHandler
|
|
6
|
+
from ImagePRO.utils.image import Image
|
|
7
|
+
from ImagePRO.utils.result import Result
|
|
12
8
|
|
|
9
|
+
# Constants
|
|
13
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
|
|
14
15
|
|
|
15
16
|
def detect_body_pose(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
Raises
|
|
50
|
-
------
|
|
51
|
-
ValueError, TypeError, FileNotFoundError, IOError
|
|
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]
|
|
52
50
|
"""
|
|
53
|
-
if
|
|
54
|
-
raise TypeError("'
|
|
51
|
+
if not isinstance(image, Image):
|
|
52
|
+
raise TypeError("'image' must be an Image instance")
|
|
55
53
|
|
|
56
|
-
|
|
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")
|
|
57
56
|
|
|
58
|
-
if landmarks_idx is None
|
|
59
|
-
landmarks_idx
|
|
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")
|
|
60
62
|
|
|
63
|
+
# Initialize detector if needed
|
|
61
64
|
if pose_obj is None:
|
|
62
65
|
pose_obj = mp_pose.Pose(
|
|
63
|
-
min_detection_confidence=
|
|
66
|
+
min_detection_confidence=min_confidence,
|
|
64
67
|
static_image_mode=True
|
|
65
68
|
)
|
|
66
69
|
|
|
67
|
-
|
|
68
|
-
|
|
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)
|
|
69
78
|
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
# Detect pose
|
|
80
|
+
result = pose_obj.process(img_rgb)
|
|
81
|
+
landmarks = []
|
|
72
82
|
|
|
73
83
|
if result.pose_landmarks:
|
|
74
|
-
|
|
84
|
+
# Visualize landmarks
|
|
85
|
+
if len(landmarks_idx) == TOTAL_LANDMARKS:
|
|
86
|
+
# Draw full skeleton
|
|
75
87
|
mp.solutions.drawing_utils.draw_landmarks(
|
|
76
|
-
|
|
88
|
+
img_copy,
|
|
77
89
|
result.pose_landmarks,
|
|
78
90
|
mp_pose.POSE_CONNECTIONS,
|
|
79
91
|
landmark_drawing_spec=mp.solutions.drawing_styles.get_default_pose_landmarks_style()
|
|
80
92
|
)
|
|
81
93
|
else:
|
|
82
|
-
|
|
94
|
+
# Draw only selected points
|
|
83
95
|
for idx in landmarks_idx:
|
|
84
96
|
lm = result.pose_landmarks.landmark[idx]
|
|
85
97
|
x, y = int(w * lm.x), int(h * lm.y)
|
|
86
|
-
cv2.circle(
|
|
98
|
+
cv2.circle(img_copy, (x, y), LANDMARK_RADIUS, LANDMARK_COLOR, -1)
|
|
87
99
|
|
|
100
|
+
# Extract coordinates
|
|
88
101
|
for idx in landmarks_idx:
|
|
89
102
|
lm = result.pose_landmarks.landmark[idx]
|
|
90
|
-
|
|
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
|
+
)
|
|
91
115
|
|
|
92
|
-
if output_image_path:
|
|
93
|
-
print(IOHandler.save_image(annotated_image, output_image_path))
|
|
94
|
-
if output_csv_path:
|
|
95
|
-
print(IOHandler.save_csv(all_landmarks, output_csv_path))
|
|
96
116
|
|
|
97
|
-
|
|
117
|
+
def detect_body_pose_live() -> None:
|
|
118
|
+
"""Start live webcam feed with real-time body pose detection.
|
|
98
119
|
|
|
120
|
+
Opens the default camera and shows pose landmarks in real-time.
|
|
121
|
+
Press ESC to exit the application.
|
|
99
122
|
|
|
100
|
-
|
|
101
|
-
|
|
123
|
+
Raises:
|
|
124
|
+
RuntimeError: If camera cannot be accessed
|
|
125
|
+
"""
|
|
126
|
+
# Initialize camera
|
|
102
127
|
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
|
|
103
128
|
if not cap.isOpened():
|
|
104
|
-
raise RuntimeError("
|
|
129
|
+
raise RuntimeError("Cannot access webcam")
|
|
105
130
|
|
|
131
|
+
# Initialize pose detector in tracking mode
|
|
106
132
|
pose_obj = mp_pose.Pose(
|
|
107
|
-
min_detection_confidence=
|
|
133
|
+
min_detection_confidence=DEFAULT_CONFIDENCE,
|
|
108
134
|
static_image_mode=False
|
|
109
135
|
)
|
|
110
136
|
|
|
111
137
|
try:
|
|
112
138
|
while True:
|
|
139
|
+
# Get frame
|
|
113
140
|
success, frame = cap.read()
|
|
114
141
|
if not success:
|
|
115
|
-
print("
|
|
142
|
+
print("Failed to capture frame, skipping...")
|
|
116
143
|
continue
|
|
117
144
|
|
|
145
|
+
# Process frame
|
|
118
146
|
try:
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
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
|
|
125
161
|
if cv2.waitKey(5) & 0xFF == 27:
|
|
126
162
|
break
|
|
127
163
|
finally:
|
|
164
|
+
# Clean up resources
|
|
128
165
|
cap.release()
|
|
129
166
|
cv2.destroyAllWindows()
|
|
130
167
|
|
|
@@ -1,68 +1,76 @@
|
|
|
1
|
-
import
|
|
2
|
-
from pathlib import Path
|
|
1
|
+
from __future__ import annotations
|
|
3
2
|
|
|
4
3
|
import cv2
|
|
5
4
|
import mediapipe as mp
|
|
6
5
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
sys.path.append(str(parent_dir))
|
|
10
|
-
|
|
11
|
-
from utils.io_handler import IOHandler
|
|
6
|
+
from ImagePRO.utils.image import Image
|
|
7
|
+
from ImagePRO.utils.result import Result
|
|
12
8
|
|
|
9
|
+
# Constants
|
|
13
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
|
|
14
16
|
|
|
15
17
|
|
|
16
18
|
def detect_hands(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
ValueError, TypeError, FileNotFoundError, IOError
|
|
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]
|
|
56
57
|
"""
|
|
58
|
+
if not isinstance(image, Image):
|
|
59
|
+
raise TypeError("'image' must be an Image instance")
|
|
60
|
+
|
|
57
61
|
if not isinstance(max_hands, int) or max_hands <= 0:
|
|
58
|
-
raise ValueError("'max_hands' must be
|
|
62
|
+
raise ValueError("'max_hands' must be positive")
|
|
63
|
+
|
|
59
64
|
if not isinstance(min_confidence, (int, float)) or not (0.0 <= min_confidence <= 1.0):
|
|
60
|
-
raise ValueError("'min_confidence' must be
|
|
61
|
-
if landmarks_idx is not None and (not isinstance(landmarks_idx, list) or not all(isinstance(i, int) for i in landmarks_idx)):
|
|
62
|
-
raise TypeError("'landmarks_idx' must be a list of ints or None.")
|
|
65
|
+
raise ValueError("'min_confidence' must be between 0.0 and 1.0")
|
|
63
66
|
|
|
64
|
-
|
|
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")
|
|
65
72
|
|
|
73
|
+
# Initialize detector if needed
|
|
66
74
|
if hands_obj is None:
|
|
67
75
|
hands_obj = mp_hands.Hands(
|
|
68
76
|
min_detection_confidence=min_confidence,
|
|
@@ -70,55 +78,91 @@ def detect_hands(
|
|
|
70
78
|
static_image_mode=True
|
|
71
79
|
)
|
|
72
80
|
|
|
81
|
+
# Use all landmarks if none specified
|
|
73
82
|
if landmarks_idx is None:
|
|
74
|
-
landmarks_idx = list(range(
|
|
83
|
+
landmarks_idx = list(range(TOTAL_HAND_LANDMARKS))
|
|
75
84
|
|
|
76
|
-
|
|
77
|
-
|
|
85
|
+
# Prepare image
|
|
86
|
+
img_copy = image._data.copy()
|
|
87
|
+
img_rgb = cv2.cvtColor(img_copy, cv2.COLOR_BGR2RGB)
|
|
78
88
|
|
|
79
|
-
|
|
80
|
-
|
|
89
|
+
# Detect hands
|
|
90
|
+
results = hands_obj.process(img_rgb)
|
|
91
|
+
landmarks = []
|
|
81
92
|
|
|
93
|
+
# Process detections
|
|
82
94
|
if results.multi_hand_landmarks:
|
|
83
95
|
for hand_id, hand_landmarks in enumerate(results.multi_hand_landmarks):
|
|
84
|
-
|
|
96
|
+
# Visualize landmarks
|
|
97
|
+
if len(landmarks_idx) == TOTAL_HAND_LANDMARKS:
|
|
98
|
+
# Draw full hand skeleton
|
|
85
99
|
mp.solutions.drawing_utils.draw_landmarks(
|
|
86
|
-
image=
|
|
100
|
+
image=img_copy,
|
|
87
101
|
landmark_list=hand_landmarks,
|
|
88
102
|
connections=mp_hands.HAND_CONNECTIONS,
|
|
89
103
|
landmark_drawing_spec=mp.solutions.drawing_styles.get_default_hand_landmarks_style(),
|
|
90
104
|
connection_drawing_spec=mp.solutions.drawing_styles.get_default_hand_connections_style()
|
|
91
105
|
)
|
|
92
106
|
else:
|
|
93
|
-
|
|
107
|
+
# Draw only selected points
|
|
108
|
+
h, w, _ = img_copy.shape
|
|
94
109
|
for idx in landmarks_idx:
|
|
95
110
|
lm = hand_landmarks.landmark[idx]
|
|
96
111
|
x, y = int(w * lm.x), int(h * lm.y)
|
|
97
|
-
cv2.circle(
|
|
112
|
+
cv2.circle(img_copy, (x, y), LANDMARK_RADIUS, LANDMARK_COLOR, -1)
|
|
98
113
|
|
|
114
|
+
# Extract coordinates
|
|
99
115
|
for idx in landmarks_idx:
|
|
100
116
|
lm = hand_landmarks.landmark[idx]
|
|
101
|
-
|
|
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
|
+
|
|
102
131
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
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.
|
|
107
137
|
|
|
108
|
-
|
|
138
|
+
Opens the default camera and shows hand landmarks in real-time.
|
|
139
|
+
Press ESC to exit.
|
|
109
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
|
|
110
148
|
|
|
111
|
-
|
|
112
|
-
|
|
149
|
+
Raises:
|
|
150
|
+
ValueError: If parameters are invalid
|
|
151
|
+
RuntimeError: If camera cannot be accessed
|
|
152
|
+
"""
|
|
153
|
+
# Validate parameters
|
|
113
154
|
if not isinstance(max_hands, int) or max_hands <= 0:
|
|
114
|
-
raise ValueError("'max_hands' must be
|
|
155
|
+
raise ValueError("'max_hands' must be positive")
|
|
156
|
+
|
|
115
157
|
if not isinstance(min_confidence, (int, float)) or not (0.0 <= min_confidence <= 1.0):
|
|
116
|
-
raise ValueError("'min_confidence' must be
|
|
158
|
+
raise ValueError("'min_confidence' must be between 0.0 and 1.0")
|
|
117
159
|
|
|
160
|
+
# Initialize camera
|
|
118
161
|
cap = cv2.VideoCapture(0, cv2.CAP_DSHOW)
|
|
119
162
|
if not cap.isOpened():
|
|
120
|
-
raise RuntimeError("
|
|
163
|
+
raise RuntimeError("Cannot access webcam")
|
|
121
164
|
|
|
165
|
+
# Initialize hand detector in tracking mode
|
|
122
166
|
hands_obj = mp_hands.Hands(
|
|
123
167
|
min_detection_confidence=min_confidence,
|
|
124
168
|
max_num_hands=max_hands,
|
|
@@ -127,26 +171,32 @@ def detect_hands_live(max_hands: int = 2, min_confidence: float = 0.7):
|
|
|
127
171
|
|
|
128
172
|
try:
|
|
129
173
|
while True:
|
|
174
|
+
# Get frame
|
|
130
175
|
success, frame = cap.read()
|
|
131
176
|
if not success:
|
|
132
|
-
print("
|
|
177
|
+
print("Failed to capture frame, skipping...")
|
|
133
178
|
continue
|
|
134
179
|
|
|
135
|
-
|
|
180
|
+
# Process frame
|
|
181
|
+
result = detect_hands(
|
|
182
|
+
image=Image.from_array(frame),
|
|
136
183
|
max_hands=max_hands,
|
|
137
184
|
min_confidence=min_confidence,
|
|
138
|
-
src_np_image=frame,
|
|
139
185
|
hands_obj=hands_obj
|
|
140
186
|
)
|
|
187
|
+
display_frame = result.image if result.image is not None else frame
|
|
141
188
|
|
|
142
|
-
|
|
189
|
+
# Show result
|
|
190
|
+
cv2.imshow('ImagePRO - Live Hand Detection', display_frame)
|
|
143
191
|
|
|
192
|
+
# Check for ESC key
|
|
144
193
|
if cv2.waitKey(5) & 0xFF == 27:
|
|
145
194
|
break
|
|
146
195
|
finally:
|
|
196
|
+
# Clean up resources
|
|
147
197
|
cap.release()
|
|
148
198
|
cv2.destroyAllWindows()
|
|
149
199
|
|
|
150
200
|
|
|
151
201
|
if __name__ == "__main__":
|
|
152
|
-
detect_hands_live(
|
|
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
|
+
]
|