-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcapture_arm_only.py
More file actions
220 lines (181 loc) Β· 8.16 KB
/
capture_arm_only.py
File metadata and controls
220 lines (181 loc) Β· 8.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
#!/usr/bin/env python3
"""
Capture ONLY arm images to add to existing hand_cls dataset
Uses clean frame saving like ml-visions (no overlays in saved images)
20 seconds at 10 fps = 200 frames
"""
import cv2
import os
import time
from pathlib import Path
import shutil
from datetime import datetime
class ArmCapture:
def __init__(self):
self.temp_dir = Path("temp_arm_captures")
self.data_dir = Path("data/hand_cls") # Use existing hand_cls structure
self.fps = 10 # 10 frames per second
self.duration = 20 # 20 seconds
self.total_frames = self.fps * self.duration # 200 frames
def capture_arms(self):
"""Capture arm images with clean frames (no overlays)"""
print("\nπΈ ARM Capture Mode")
print("=" * 50)
print("πͺ Instructions: Show your ARM/FOREARM (not focused on hand)")
print(" - Show forearm, elbow area")
print(" - Keep hand out of focus or partially visible")
print(" - Move arm naturally")
print(f"\nCapturing {self.total_frames} frames ({self.duration} seconds at {self.fps} fps)")
cap = cv2.VideoCapture(0)
if not cap.isOpened():
print("β Could not open camera")
return None
# Create temp directory
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
temp_session = self.temp_dir / timestamp
temp_session.mkdir(parents=True, exist_ok=True)
# 3-second countdown
for i in range(3, 0, -1):
ret, frame = cap.read()
if ret:
display_frame = cv2.flip(frame, 1) # Mirror for display
cv2.putText(display_frame, f"GET READY: ARM CAPTURE", (50, 100),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 165, 255), 2)
cv2.putText(display_frame, str(i), (320, 240),
cv2.FONT_HERSHEY_SIMPLEX, 3.0, (0, 165, 255), 3)
cv2.putText(display_frame, "Show ARM/FOREARM (not hand)", (50, 400),
cv2.FONT_HERSHEY_SIMPLEX, 0.6, (200, 200, 200), 1)
cv2.imshow('Arm Capture', display_frame)
cv2.waitKey(1000)
# Capture frames
frames_captured = 0
start_time = time.time()
frame_interval = 1.0 / self.fps
last_capture_time = 0
print("π΄ RECORDING...")
while frames_captured < self.total_frames:
ret, frame = cap.read()
if not ret:
break
# Calculate timing
current_time = time.time() - start_time
remaining = self.duration - (time.time() - start_time)
# Save CLEAN frame at specified FPS (no overlays)
if current_time - last_capture_time >= frame_interval:
# Save the ORIGINAL frame without any overlays
clean_frame = cv2.flip(frame, 1) # Just mirror, no text
filename = temp_session / f"arm_{frames_captured:04d}.jpg"
cv2.imwrite(str(filename), clean_frame)
frames_captured += 1
last_capture_time = current_time
# Create display frame with overlays (for user feedback only)
display_frame = cv2.flip(frame, 1)
# Progress bar
progress = frames_captured / self.total_frames
bar_width = int(progress * 400)
cv2.rectangle(display_frame, (50, 30), (450, 60), (200, 200, 200), 2)
cv2.rectangle(display_frame, (50, 30), (50 + bar_width, 60), (0, 165, 255), -1)
# Text overlays (only on display, not saved)
cv2.putText(display_frame, "ARM CAPTURE", (50, 100),
cv2.FONT_HERSHEY_SIMPLEX, 1.0, (0, 165, 255), 2)
cv2.putText(display_frame, f"Frames: {frames_captured}/{self.total_frames}", (50, 140),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.putText(display_frame, f"Time: {remaining:.1f}s", (50, 180),
cv2.FONT_HERSHEY_SIMPLEX, 0.7, (255, 255, 255), 2)
cv2.imshow('Arm Capture', display_frame)
if cv2.waitKey(1) & 0xFF == ord('q'):
print("\nβ οΈ Capture interrupted")
break
cap.release()
cv2.destroyAllWindows()
print(f"\nβ
Captured {frames_captured} clean frames (no overlays)")
return temp_session, frames_captured
def review_and_add(self, temp_session, count):
"""Review captured images and add to dataset"""
print(f"\nπ Review: {count} arm images captured")
print("\nWhat would you like to do?")
print(" 1. β
ADD to training dataset")
print(" 2. ποΈ DELETE them (discard)")
print(" 3. π VIEW them first")
while True:
choice = input("\nEnter choice (1/2/3): ").strip()
if choice == '1':
# Add to unified dataset
self.add_to_unified(temp_session)
print(f"β
Added {count} arm images to unified dataset")
shutil.rmtree(temp_session)
return True
elif choice == '2':
shutil.rmtree(temp_session)
print(f"ποΈ Deleted {count} temporary images")
return False
elif choice == '3':
import subprocess
import platform
if platform.system() == 'Darwin':
subprocess.run(['open', str(temp_session)])
print(f"\nπ Opened {temp_session}")
print("Review the images, then choose 1 or 2")
def add_to_unified(self, temp_session):
"""Add arm images to unified dataset"""
images = sorted(list(temp_session.glob("*.jpg")))
# 80/20 split
train_count = int(len(images) * 0.8)
train_images = images[:train_count]
val_images = images[train_count:]
# Get next indices
train_dir = self.data_dir / 'train' / 'arm'
val_dir = self.data_dir / 'val' / 'arm'
train_dir.mkdir(parents=True, exist_ok=True)
val_dir.mkdir(parents=True, exist_ok=True)
train_existing = len(list(train_dir.glob("*.jpg")))
val_existing = len(list(val_dir.glob("*.jpg")))
# Copy to train
for i, img in enumerate(train_images):
dest = train_dir / f"arm_{train_existing + i:04d}.jpg"
shutil.copy2(img, dest)
# Copy to val
for i, img in enumerate(val_images):
dest = val_dir / f"arm_{val_existing + i:04d}.jpg"
shutil.copy2(img, dest)
print(f" Added {len(train_images)} to training")
print(f" Added {len(val_images)} to validation")
def show_stats(self):
"""Show current hand_cls dataset statistics"""
print("\nπ Dataset Statistics (hand_cls):")
print("=" * 50)
total = 0
for split in ['train', 'val']:
print(f"\n{split.upper()}:")
for category in ['hand', 'arm', 'not_hand']:
path = self.data_dir / split / category
if path.exists():
count = len(list(path.glob("*.jpg")))
total += count
print(f" {category:10s}: {count:4d} images")
print(f"\nTOTAL: {total} images")
return total
def run(self):
"""Main capture workflow"""
print("\nπ― Arm Capture Tool")
print("=" * 50)
print("This adds ARM images to your existing hand/not_hand dataset")
# Show current stats
self.show_stats()
print("\n" + "=" * 50)
input("Press ENTER when ready to capture ARM images...")
# Capture arms
result = self.capture_arms()
if result:
temp_session, count = result
if self.review_and_add(temp_session, count):
# Show final stats
total = self.show_stats()
print("\nπ Ready to train unified model!")
print("Next: python3 train_unified.py")
# Clean up
if self.temp_dir.exists() and not any(self.temp_dir.iterdir()):
self.temp_dir.rmdir()
if __name__ == "__main__":
capture = ArmCapture()
capture.run()