first refactored version
This commit is contained in:
parent
6cb2e768e0
commit
391620fd5c
@ -6,7 +6,7 @@ from urllib.parse import urlparse
|
|||||||
from tqdm.auto import tqdm
|
from tqdm.auto import tqdm
|
||||||
|
|
||||||
|
|
||||||
# --- KONFIGURATION ---
|
# s3 bucket configuration
|
||||||
MINIO_CONFIG = {
|
MINIO_CONFIG = {
|
||||||
'endpoint_url': 'https://minio.hgk.ch',
|
'endpoint_url': 'https://minio.hgk.ch',
|
||||||
'access_key': 'meinAccessKey',
|
'access_key': 'meinAccessKey',
|
||||||
@ -14,28 +14,32 @@ MINIO_CONFIG = {
|
|||||||
'bucket': 'skiai'
|
'bucket': 'skiai'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# input specs, annotations
|
||||||
JSON_PATH = 'datasets/skier_pose/labelstudio_export.json'
|
JSON_PATH = 'datasets/skier_pose/labelstudio_export.json'
|
||||||
OUTPUT_DIR = 'datasets/skier_pose'
|
# input specs, keypoint orde must stay consistent
|
||||||
TRAIN_RATIO = 0.8
|
|
||||||
|
|
||||||
# Die Reihenfolge MUSS konsistent bleiben
|
|
||||||
KP_ORDER = [
|
KP_ORDER = [
|
||||||
"leftski_tip", "leftski_tail", "rightski_tip", "rightski_tail",
|
"leftski_tip", "leftski_tail", "rightski_tip", "rightski_tail",
|
||||||
"leftpole_top", "leftpole_bottom", "rightpole_top", "rightpole_bottom"
|
"leftpole_top", "leftpole_bottom", "rightpole_top", "rightpole_bottom"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
# output specs
|
||||||
|
OUTPUT_DIR = 'datasets/skier_pose'
|
||||||
|
TRAIN_RATIO = 0.8
|
||||||
|
|
||||||
|
|
||||||
|
# create folder structure
|
||||||
|
def __setup_directories():
|
||||||
|
|
||||||
def setup_directories():
|
|
||||||
"""Erstellt die Struktur: train/images, train/labels, val/images, val/labels"""
|
|
||||||
for split in ['train', 'val']:
|
for split in ['train', 'val']:
|
||||||
os.makedirs(os.path.join(OUTPUT_DIR, split, 'images'), exist_ok=True)
|
os.makedirs(os.path.join(OUTPUT_DIR, split, 'images'), exist_ok=True)
|
||||||
os.makedirs(os.path.join(OUTPUT_DIR, split, 'labels'), exist_ok=True)
|
os.makedirs(os.path.join(OUTPUT_DIR, split, 'labels'), exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
def download_from_minio(s3_path, local_path):
|
# download image from s3
|
||||||
|
def __download_from_minio(s3_path, local_path):
|
||||||
parsed = urlparse(s3_path)
|
parsed = urlparse(s3_path)
|
||||||
bucket = MINIO_CONFIG['bucket']
|
bucket = MINIO_CONFIG['bucket']
|
||||||
# Entfernt 's3://bucketname/' falls vorhanden, sonst nur den slash
|
# removes 's3://bucketname/' if existing, otherwise slash
|
||||||
key = parsed.path.lstrip('/')
|
key = parsed.path.lstrip('/')
|
||||||
|
|
||||||
s3 = boto3.client('s3',
|
s3 = boto3.client('s3',
|
||||||
@ -45,8 +49,11 @@ def download_from_minio(s3_path, local_path):
|
|||||||
s3.download_file(bucket, key, local_path)
|
s3.download_file(bucket, key, local_path)
|
||||||
|
|
||||||
|
|
||||||
def convert_to_yolo():
|
# create YOLO dataset
|
||||||
setup_directories()
|
def createYOLOdataset():
|
||||||
|
__setup_directories()
|
||||||
|
|
||||||
|
# read annotations
|
||||||
with open(JSON_PATH, 'r', encoding='utf-8') as f:
|
with open(JSON_PATH, 'r', encoding='utf-8') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
|
|
||||||
@ -54,10 +61,11 @@ def convert_to_yolo():
|
|||||||
random.shuffle(data)
|
random.shuffle(data)
|
||||||
split_idx = int(len(data) * TRAIN_RATIO)
|
split_idx = int(len(data) * TRAIN_RATIO)
|
||||||
|
|
||||||
|
# loop over all images
|
||||||
for i, entry in enumerate(tqdm(data, desc="Importing Images", unit="img")):
|
for i, entry in enumerate(tqdm(data, desc="Importing Images", unit="img")):
|
||||||
split = 'train' if i < split_idx else 'val'
|
split = 'train' if i < split_idx else 'val'
|
||||||
|
|
||||||
# Dateinamen aus dem 'data'-Feld holen
|
# get image name
|
||||||
image_s3_path = entry['data']['image']
|
image_s3_path = entry['data']['image']
|
||||||
filename = os.path.basename(image_s3_path)
|
filename = os.path.basename(image_s3_path)
|
||||||
base_name = os.path.splitext(filename)[0]
|
base_name = os.path.splitext(filename)[0]
|
||||||
@ -66,20 +74,20 @@ def convert_to_yolo():
|
|||||||
label_local_path = os.path.join(OUTPUT_DIR, split, 'labels', f"{base_name}.txt")
|
label_local_path = os.path.join(OUTPUT_DIR, split, 'labels', f"{base_name}.txt")
|
||||||
|
|
||||||
try:
|
try:
|
||||||
download_from_minio(image_s3_path, img_local_path)
|
__download_from_minio(image_s3_path, img_local_path)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
tqdm.write(f"Error treating {filename}: {e}")
|
tqdm.write(f"Error treating {filename}: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
yolo_lines = []
|
yolo_lines = []
|
||||||
|
|
||||||
# Sicherstellen, dass Annotationen vorhanden sind
|
# check if annotations, otherwise skip
|
||||||
if not entry.get('annotations'):
|
if not entry.get('annotations'):
|
||||||
continue
|
continue
|
||||||
|
|
||||||
results = entry['annotations'][0].get('result', [])
|
results = entry['annotations'][0].get('result', [])
|
||||||
|
|
||||||
# Hilfsmaps für das Matching über IDs
|
# dummy vars
|
||||||
kp_map = {} # ID -> {label, x, y}
|
kp_map = {} # ID -> {label, x, y}
|
||||||
visibility_map = {} # ID -> v_status (1 oder 2)
|
visibility_map = {} # ID -> v_status (1 oder 2)
|
||||||
bboxes = [] # Liste aller gefundenen BBoxes
|
bboxes = [] # Liste aller gefundenen BBoxes
|
||||||
@ -97,8 +105,6 @@ def convert_to_yolo():
|
|||||||
}
|
}
|
||||||
|
|
||||||
elif res_type == 'choices':
|
elif res_type == 'choices':
|
||||||
# Matching: Label Studio nutzt die gleiche ID für Keypoint und Choice
|
|
||||||
# Wir prüfen, ob die Checkbox "1" (dein Alias für verdeckt) gewählt wurde
|
|
||||||
if "1" in val.get('choices', []):
|
if "1" in val.get('choices', []):
|
||||||
visibility_map[res_id] = 1
|
visibility_map[res_id] = 1
|
||||||
|
|
||||||
@ -110,18 +116,16 @@ def convert_to_yolo():
|
|||||||
by = (val['y'] / 100.0) + (bh / 2.0)
|
by = (val['y'] / 100.0) + (bh / 2.0)
|
||||||
bboxes.append(f"{bx:.6f} {by:.6f} {bw:.6f} {bh:.6f}")
|
bboxes.append(f"{bx:.6f} {by:.6f} {bw:.6f} {bh:.6f}")
|
||||||
|
|
||||||
# Für jede gefundene BBox eine YOLO Zeile generieren
|
# create yolo data
|
||||||
# (Hinweis: Aktuell werden alle Keypoints an jede BBox gehängt)
|
|
||||||
for bbox_coords in bboxes:
|
for bbox_coords in bboxes:
|
||||||
line = f"0 {bbox_coords}"
|
line = f"0 {bbox_coords}"
|
||||||
|
|
||||||
for kp_name in KP_ORDER:
|
for kp_name in KP_ORDER:
|
||||||
# Finde die ID des Keypoints mit diesem Namen
|
|
||||||
target_id = next((id for id, d in kp_map.items() if d['label'] == kp_name), None)
|
target_id = next((id for id, d in kp_map.items() if d['label'] == kp_name), None)
|
||||||
|
|
||||||
if target_id:
|
if target_id:
|
||||||
coords = kp_map[target_id]
|
coords = kp_map[target_id]
|
||||||
# Sichtbarkeit: 1 (verdeckt) wenn in visibility_map, sonst 2 (sichtbar)
|
# visibility, 0 missing, 1 invisible, 2 visible
|
||||||
v = visibility_map.get(target_id, 2)
|
v = visibility_map.get(target_id, 2)
|
||||||
line += f" {coords['x']:.6f} {coords['y']:.6f} {v}"
|
line += f" {coords['x']:.6f} {coords['y']:.6f} {v}"
|
||||||
else:
|
else:
|
||||||
@ -132,7 +136,7 @@ def convert_to_yolo():
|
|||||||
with open(label_local_path, 'w', encoding='utf-8') as f:
|
with open(label_local_path, 'w', encoding='utf-8') as f:
|
||||||
f.write('\n'.join(yolo_lines))
|
f.write('\n'.join(yolo_lines))
|
||||||
|
|
||||||
print(f"Fertig! Daten liegen in: {os.path.abspath(OUTPUT_DIR)}")
|
print(f"Finished! Dataset saved to: {os.path.abspath(OUTPUT_DIR)}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
convert_to_yolo()
|
createYOLOdataset()
|
||||||
8
main.py
8
main.py
@ -1,16 +1,18 @@
|
|||||||
|
|
||||||
|
|
||||||
from ultralytics import YOLO
|
from ultralytics import YOLO
|
||||||
|
|
||||||
# Create a new YOLO model from scratch
|
# Create a new YOLO model from scratch
|
||||||
#model = YOLO("yolo11n.yaml")
|
#model = YOLO("yolo11n.yaml")
|
||||||
|
|
||||||
# Load a pretrained YOLO model (recommended for training)
|
# Load a pretrained YOLO model (recommended for training)
|
||||||
model = YOLO("yolo11n-pose.pt")
|
#model = YOLO("yolo11n-pose.pt")
|
||||||
|
|
||||||
# Train the model using the 'coco8.yaml' dataset for 3 epochs
|
# Train the model using the 'coco8.yaml' dataset for 3 epochs
|
||||||
results = model.train(data="datasets/skier_pose/skier_pose.yaml", epochs=200, imgsz=640, device='mps')
|
#results = model.train(data="datasets/skier_pose/skier_pose.yaml", epochs=200, imgsz=640, device='mps')
|
||||||
|
|
||||||
# Evaluate the model's performance on the validation set
|
# Evaluate the model's performance on the validation set
|
||||||
results = model.val()
|
#results = model.val()
|
||||||
|
|
||||||
# Perform object detection on an image using the model
|
# Perform object detection on an image using the model
|
||||||
#results = model("bus.jpg", device='cpu')
|
#results = model("bus.jpg", device='cpu')
|
||||||
|
|||||||
2703
specs/labelstudio_export.json
Normal file
2703
specs/labelstudio_export.json
Normal file
File diff suppressed because it is too large
Load Diff
24
specs/skier_pose.yaml
Normal file
24
specs/skier_pose.yaml
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
path: datasets/skier_pose # relativer Pfad zum Dataset-Ordner
|
||||||
|
train: train
|
||||||
|
val: val
|
||||||
|
|
||||||
|
# Keypoints Konfiguration
|
||||||
|
kpt_shape: [8, 3] # 8 Keypoints, jeweils (x, y, visibility)
|
||||||
|
|
||||||
|
# Flip-Indizes für Augmentation (Spiegelung)
|
||||||
|
# Wenn das Bild gespiegelt wird:
|
||||||
|
# 0 (left tip) wird zu 2 (right tip) und umgekehrt
|
||||||
|
# 1 (left tail) wird zu 3 (right tail) und umgekehrt
|
||||||
|
# 4 (left pole top) wird zu 6 (right pole top) etc.
|
||||||
|
flip_idx: [2, 3, 0, 1, 6, 7, 4, 5]
|
||||||
|
|
||||||
|
# Skeleton: Verbindungen für die Visualisierung und Strukturhilfe
|
||||||
|
# Definiert Paare von Keypoint-Indizes (0-basiert)
|
||||||
|
skeleton:
|
||||||
|
- [0, 1] # Linker Ski (Tip zu Tail)
|
||||||
|
- [2, 3] # Rechter Ski (Tip zu Tail)
|
||||||
|
- [4, 5] # Linker Stock (Top zu Bottom)
|
||||||
|
- [6, 7] # Rechter Stock (Top zu Bottom)
|
||||||
|
|
||||||
|
names:
|
||||||
|
0: skier
|
||||||
Loading…
Reference in New Issue
Block a user