You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1799 lines
63 KiB

{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "5a7f7e67",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1. 데이터 스캔 및 가로형(Landscape) 이미지 필터링 중...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 45623/45623 [00:00<00:00, 51406.66it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[스캔 결과]\n",
"- 세로형 제외: 36482장\n",
"- 타겟 클래스 없음 제외: 2271장\n",
"- 사용 가능한 총 이미지: 6870장\n",
" > ID 0 (shirt): 5634장 후보\n",
" > ID 1 (jacket): 1750장 후보\n",
" > ID 2 (pants): 2105장 후보\n",
" > ID 3 (glasses): 1308장 후보\n",
"\n",
"🚀 [1_Uniform] 데이터셋 생성 시작...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Copying Original Images: 100%|██████████| 300/300 [00:00<00:00, 20958.60it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📊 [1_Uniform] 최종 구성 (이미지 300장)\n",
" 포함된 객체 수: {'shirt': 243, 'glasses': 123, 'jacket': 128, 'pants': 143}\n",
"\n",
"🚀 [2_Shirt_Low] 데이터셋 생성 시작...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Copying Original Images: 100%|██████████| 300/300 [00:00<00:00, 22314.88it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📊 [2_Shirt_Low] 최종 구성 (이미지 300장)\n",
" 포함된 객체 수: {'shirt': 239, 'pants': 164, 'jacket': 143, 'glasses': 134}\n",
"\n",
"🚀 [3_Pants_Low] 데이터셋 생성 시작...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Copying Original Images: 100%|██████████| 300/300 [00:00<00:00, 24058.19it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📊 [3_Pants_Low] 최종 구성 (이미지 300장)\n",
" 포함된 객체 수: {'shirt': 251, 'glasses': 127, 'jacket': 138, 'pants': 120}\n",
"\n",
"🚀 [4_Jacket_Low] 데이터셋 생성 시작...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Copying Original Images: 100%|██████████| 300/300 [00:00<00:00, 24327.97it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📊 [4_Jacket_Low] 최종 구성 (이미지 300장)\n",
" 포함된 객체 수: {'shirt': 250, 'glasses': 136, 'jacket': 105, 'pants': 163}\n",
"\n",
"🚀 [5_Glasses_Low] 데이터셋 생성 시작...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Copying Original Images: 100%|██████████| 300/300 [00:00<00:00, 24113.05it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📊 [5_Glasses_Low] 최종 구성 (이미지 300장)\n",
" 포함된 객체 수: {'glasses': 74, 'pants': 154, 'shirt': 251, 'jacket': 149}\n",
"\n",
"🚀 [6_Glasses_High] 데이터셋 생성 시작...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Copying Original Images: 100%|██████████| 300/300 [00:00<00:00, 21095.99it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"📊 [6_Glasses_High] 최종 구성 (이미지 300장)\n",
" 포함된 객체 수: {'glasses': 227, 'pants': 151, 'shirt': 245, 'jacket': 87}\n",
"\n",
"✨ 모든 작업 완료!\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"import shutil\n",
"import random\n",
"from collections import Counter, defaultdict\n",
"from glob import glob\n",
"from tqdm import tqdm\n",
"from PIL import Image\n",
"\n",
"# ================= [설정값] =================\n",
"# 경로 설정\n",
"BASE_ROOT = \"/home/cuuva/git/Detection_Experiment/datasets/fashionpedia_yolo\"\n",
"LABEL_DIR = os.path.join(BASE_ROOT, \"labels/train\")\n",
"IMAGE_DIR = os.path.join(BASE_ROOT, \"images/train\")\n",
"OUTPUT_ROOT = \"/home/cuuva/다운로드/quantization_experiments_4class_original\"\n",
"\n",
"# YAML 파일 기준 클래스 매핑 (0: shirt, 1: jacket, 2: pants, 3: glasses)\n",
"TARGET_MAP = {\n",
" 0: 'shirt',\n",
" 1: 'jacket',\n",
" 2: 'pants',\n",
" 3: 'glasses'\n",
"}\n",
"TARGET_IDS = list(TARGET_MAP.keys())\n",
"\n",
"# 총 추출 개수\n",
"TOTAL_COUNT = 300\n",
"\n",
"# ================= 1. 데이터 인덱싱 및 필터링 =================\n",
"print(\"1. 데이터 스캔 및 가로형(Landscape) 이미지 필터링 중...\")\n",
"\n",
"class_buckets = defaultdict(list)\n",
"all_valid_files = set()\n",
"\n",
"txt_files = glob(os.path.join(LABEL_DIR, \"*.txt\"))\n",
"skipped_vertical = 0\n",
"skipped_no_target = 0\n",
"\n",
"for txt_path in tqdm(txt_files):\n",
" basename = os.path.splitext(os.path.basename(txt_path))[0]\n",
" \n",
" # [이미지 찾기]\n",
" img_path = None\n",
" img_ext = None\n",
" for ext in ['.jpg', '.jpeg', '.png']:\n",
" temp = os.path.join(IMAGE_DIR, basename + ext)\n",
" if os.path.exists(temp):\n",
" img_path = temp\n",
" img_ext = ext\n",
" break\n",
" \n",
" if not img_path: continue\n",
" \n",
" # [가로형 체크] (리사이즈는 안 하지만, 조건은 유지)\n",
" try:\n",
" with Image.open(img_path) as img:\n",
" w, h = img.size\n",
" if h > w: # 세로가 더 길면 탈락\n",
" skipped_vertical += 1\n",
" continue\n",
" except:\n",
" continue\n",
"\n",
" # [라벨 체크] (타겟 클래스 포함 여부)\n",
" found_targets = set()\n",
" with open(txt_path, 'r') as f:\n",
" for line in f:\n",
" parts = line.strip().split()\n",
" if parts:\n",
" cls_id = int(parts[0])\n",
" if cls_id in TARGET_IDS:\n",
" found_targets.add(cls_id)\n",
" \n",
" if not found_targets:\n",
" skipped_no_target += 1\n",
" continue\n",
" \n",
" # 유효 데이터 등록\n",
" all_valid_files.add(basename)\n",
" for tid in found_targets:\n",
" class_buckets[tid].append(basename)\n",
"\n",
"print(f\"\\n[스캔 결과]\")\n",
"print(f\"- 세로형 제외: {skipped_vertical}장\")\n",
"print(f\"- 타겟 클래스 없음 제외: {skipped_no_target}장\")\n",
"print(f\"- 사용 가능한 총 이미지: {len(all_valid_files)}장\")\n",
"for tid, name in TARGET_MAP.items():\n",
" print(f\" > ID {tid} ({name}): {len(class_buckets[tid])}장 후보\")\n",
"\n",
"\n",
"# ================= 2. 샘플링 및 생성 함수 (원본 복사) =================\n",
"\n",
"def create_dataset(case_name, ratio_dict):\n",
" \"\"\"\n",
" ratio_dict: {class_id: count} 형태\n",
" \"\"\"\n",
" print(f\"\\n🚀 [{case_name}] 데이터셋 생성 시작...\")\n",
" \n",
" selected_files = set()\n",
" \n",
" # [Step 1] 할당량만큼 추출\n",
" for tid, target_count in ratio_dict.items():\n",
" candidates = class_buckets[tid][:]\n",
" random.shuffle(candidates)\n",
" \n",
" count = 0\n",
" for fname in candidates:\n",
" if count >= target_count:\n",
" break\n",
" if fname not in selected_files:\n",
" selected_files.add(fname)\n",
" count += 1\n",
" \n",
" # [Step 2] 개수 맞추기 (300장)\n",
" final_list = list(selected_files)\n",
" \n",
" if len(final_list) < TOTAL_COUNT:\n",
" remaining_pool = list(all_valid_files - selected_files)\n",
" needed = TOTAL_COUNT - len(final_list)\n",
" if len(remaining_pool) >= needed:\n",
" final_list.extend(random.sample(remaining_pool, needed))\n",
" else:\n",
" final_list.extend(remaining_pool)\n",
" \n",
" elif len(final_list) > TOTAL_COUNT:\n",
" final_list = random.sample(final_list, TOTAL_COUNT)\n",
" \n",
" # [Step 3] 파일 복사 (Resize 없이 원본 복사)\n",
" save_dir = os.path.join(OUTPUT_ROOT, case_name)\n",
" if os.path.exists(save_dir): shutil.rmtree(save_dir)\n",
" os.makedirs(save_dir, exist_ok=True)\n",
" \n",
" for basename in tqdm(final_list, desc=\"Copying Original Images\"):\n",
" src_img = None\n",
" for ext in ['.jpg', '.jpeg', '.png']:\n",
" temp = os.path.join(IMAGE_DIR, basename + ext)\n",
" if os.path.exists(temp):\n",
" src_img = temp\n",
" break\n",
" \n",
" if src_img:\n",
" # shutil.copy를 사용하여 원본 그대로 복사\n",
" # 파일명 + 원본 확장자 유지\n",
" dst_path = os.path.join(save_dir, os.path.basename(src_img))\n",
" shutil.copy(src_img, dst_path)\n",
"\n",
" # [Step 4] 통계 출력\n",
" actual_counts = Counter()\n",
" for basename in final_list:\n",
" with open(os.path.join(LABEL_DIR, basename + \".txt\"), 'r') as f:\n",
" for line in f:\n",
" parts = line.strip().split()\n",
" if parts:\n",
" tid = int(parts[0])\n",
" if tid in TARGET_IDS:\n",
" actual_counts[TARGET_MAP[tid]] += 1\n",
" \n",
" print(f\"📊 [{case_name}] 최종 구성 (이미지 {len(final_list)}장)\")\n",
" print(f\" 포함된 객체 수: {dict(actual_counts)}\")\n",
"\n",
"\n",
"# ================= 3. 6가지 케이스 실행 =================\n",
"# ID: 0(shirt), 1(jacket), 2(pants), 3(glasses)\n",
"\n",
"# 1. 균일 (Uniform)\n",
"create_dataset(\"1_Uniform\", {0:75, 1:75, 2:75, 3:75})\n",
"\n",
"# 2. Shirts(0) 적게 (Shirt 30, 나머지 90)\n",
"create_dataset(\"2_Shirt_Low\", {0:30, 1:90, 2:90, 3:90})\n",
"\n",
"# 3. Pants(2) 적게 (Pants 30, 나머지 90)\n",
"create_dataset(\"3_Pants_Low\", {0:90, 1:90, 2:30, 3:90})\n",
"\n",
"# 4. Jacket(1) 적게 (Jacket 30, 나머지 90)\n",
"create_dataset(\"4_Jacket_Low\", {0:90, 1:30, 2:90, 3:90})\n",
"\n",
"# 5. Glasses(3) 적게 (Glasses 30, 나머지 90)\n",
"create_dataset(\"5_Glasses_Low\", {0:90, 1:90, 2:90, 3:30})\n",
"\n",
"# 6. Glasses(3) 많게 (Glasses 210, 나머지 30)\n",
"create_dataset(\"6_Glasses_High\", {0:30, 1:30, 2:30, 3:210})\n",
"\n",
"print(\"\\n✨ 모든 작업 완료!\")"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "ac687f57",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1. 전체 데이터 스캔 및 4가지 그룹으로 분류 중...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
" 0%| | 0/45623 [00:00<?, ?it/s]"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 45623/45623 [00:00<00:00, 53732.51it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[스캔 완료]\n",
"- 세로형 제외됨: 36482장\n",
"- 후보군 'shirt_with_glasses': 977장\n",
"- 후보군 'shirt_no_glasses': 4657장\n",
"- 후보군 'jacket_with_glasses': 301장\n",
"- 후보군 'jacket_no_glasses': 1449장\n",
"\n",
"2. 조건에 맞춰 300장 추출 및 저장 시작...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Extracting shirt_with_glasses: 100%|██████████| 100/100 [00:00<00:00, 25043.61it/s]\n",
"Extracting shirt_no_glasses: 100%|██████████| 100/100 [00:00<00:00, 26404.18it/s]\n",
"Extracting jacket_with_glasses: 100%|██████████| 50/50 [00:00<00:00, 22839.82it/s]\n",
"Extracting jacket_no_glasses: 100%|██████████| 50/50 [00:00<00:00, 25785.71it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"✨ 모든 작업 완료!\n",
"📂 저장 경로: /home/cuuva/다운로드/quantization_experiments_4class_original/7_custom\n",
"📊 총 저장된 이미지 수: 300장 / 목표 300장\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"import shutil\n",
"import random\n",
"from glob import glob\n",
"from tqdm import tqdm\n",
"from PIL import Image\n",
"\n",
"# ================= [설정값] =================\n",
"BASE_ROOT = \"/home/cuuva/git/Detection_Experiment/datasets/fashionpedia_yolo\"\n",
"LABEL_DIR = os.path.join(BASE_ROOT, \"labels/train\")\n",
"IMAGE_DIR = os.path.join(BASE_ROOT, \"images/train\")\n",
"OUTPUT_DIR = \"/home/cuuva/다운로드/quantization_experiments_4class_original/7_custom\"\n",
"\n",
"# 클래스 ID (YAML 기준)\n",
"ID_SHIRT = 0\n",
"ID_JACKET = 1\n",
"ID_GLASSES = 3\n",
"\n",
"# 목표 수량 설정\n",
"TARGET_COUNTS = {\n",
" 'shirt_with_glasses': 100,\n",
" 'shirt_no_glasses': 100,\n",
" 'jacket_with_glasses': 50,\n",
" 'jacket_no_glasses': 50\n",
"}\n",
"\n",
"# ================= 1. 데이터 스캔 및 분류 =================\n",
"print(\"1. 전체 데이터 스캔 및 4가지 그룹으로 분류 중...\")\n",
"\n",
"# 4개의 후보 리스트 (Bucket) 생성\n",
"buckets = {\n",
" 'shirt_with_glasses': [],\n",
" 'shirt_no_glasses': [],\n",
" 'jacket_with_glasses': [],\n",
" 'jacket_no_glasses': []\n",
"}\n",
"\n",
"txt_files = glob(os.path.join(LABEL_DIR, \"*.txt\"))\n",
"skipped_vertical = 0\n",
"\n",
"for txt_path in tqdm(txt_files):\n",
" basename = os.path.splitext(os.path.basename(txt_path))[0]\n",
" \n",
" # [1] 이미지 찾기\n",
" img_path = None\n",
" for ext in ['.jpg', '.jpeg', '.png']:\n",
" temp = os.path.join(IMAGE_DIR, basename + ext)\n",
" if os.path.exists(temp):\n",
" img_path = temp\n",
" break\n",
" \n",
" if not img_path: continue\n",
" \n",
" # [2] 가로형(Landscape) 체크 (필수 조건)\n",
" try:\n",
" with Image.open(img_path) as img:\n",
" w, h = img.size\n",
" if h > w: # 세로가 더 길면 탈락\n",
" skipped_vertical += 1\n",
" continue\n",
" except:\n",
" continue\n",
"\n",
" # [3] 클래스 분석\n",
" classes = set()\n",
" with open(txt_path, 'r') as f:\n",
" for line in f:\n",
" parts = line.strip().split()\n",
" if parts:\n",
" classes.add(int(parts[0]))\n",
" \n",
" has_shirt = ID_SHIRT in classes\n",
" has_jacket = ID_JACKET in classes\n",
" has_glasses = ID_GLASSES in classes\n",
" \n",
" # [4] 버킷에 담기 (중복 허용 - 나중에 선택 시 중복 방지)\n",
" # 셔츠 관련\n",
" if has_shirt and has_glasses:\n",
" buckets['shirt_with_glasses'].append(img_path)\n",
" elif has_shirt and not has_glasses:\n",
" buckets['shirt_no_glasses'].append(img_path)\n",
" \n",
" # 재킷 관련\n",
" if has_jacket and has_glasses:\n",
" buckets['jacket_with_glasses'].append(img_path)\n",
" elif has_jacket and not has_glasses:\n",
" buckets['jacket_no_glasses'].append(img_path)\n",
"\n",
"print(f\"\\n[스캔 완료]\")\n",
"print(f\"- 세로형 제외됨: {skipped_vertical}장\")\n",
"for key, lst in buckets.items():\n",
" print(f\"- 후보군 '{key}': {len(lst)}장\")\n",
"\n",
"# ================= 2. 샘플링 및 저장 =================\n",
"print(\"\\n2. 조건에 맞춰 300장 추출 및 저장 시작...\")\n",
"\n",
"if os.path.exists(OUTPUT_DIR):\n",
" shutil.rmtree(OUTPUT_DIR)\n",
"os.makedirs(OUTPUT_DIR, exist_ok=True)\n",
"\n",
"final_selected_files = set() # 중복 방지용 (이미 뽑힌 파일 경로 저장)\n",
"saved_count = 0\n",
"\n",
"# 순서대로 추출 함수\n",
"def extract_and_save(bucket_key, target_count):\n",
" global saved_count\n",
" candidates = buckets[bucket_key]\n",
" random.shuffle(candidates)\n",
" \n",
" current_selected = 0\n",
" \n",
" pbar = tqdm(total=target_count, desc=f\"Extracting {bucket_key}\")\n",
" \n",
" for img_path in candidates:\n",
" if current_selected >= target_count:\n",
" break\n",
" \n",
" # 이미 다른 그룹에서 뽑힌 파일이면 건너뜀 (중복 방지)\n",
" if img_path in final_selected_files:\n",
" continue\n",
" \n",
" # 선택 및 복사\n",
" dst_path = os.path.join(OUTPUT_DIR, os.path.basename(img_path))\n",
" shutil.copy(img_path, dst_path)\n",
" \n",
" final_selected_files.add(img_path)\n",
" current_selected += 1\n",
" saved_count += 1\n",
" pbar.update(1)\n",
" \n",
" pbar.close()\n",
" \n",
" if current_selected < target_count:\n",
" print(f\"⚠️ 경고: '{bucket_key}' 그룹에서 {target_count}장을 원했으나 {current_selected}장만 추출 가능했습니다.\")\n",
"\n",
"# 요청하신 순서대로 실행\n",
"# 1. 셔츠 + 안경 (100장)\n",
"extract_and_save('shirt_with_glasses', 100)\n",
"\n",
"# 2. 셔츠 Only (100장)\n",
"extract_and_save('shirt_no_glasses', 100)\n",
"\n",
"# 3. 재킷 + 안경 (50장)\n",
"extract_and_save('jacket_with_glasses', 50)\n",
"\n",
"# 4. 재킷 Only (50장)\n",
"extract_and_save('jacket_no_glasses', 50)\n",
"\n",
"print(f\"\\n✨ 모든 작업 완료!\")\n",
"print(f\"📂 저장 경로: {OUTPUT_DIR}\")\n",
"print(f\"📊 총 저장된 이미지 수: {saved_count}장 / 목표 300장\")"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "e86629eb",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1. 전체 데이터 스캔 및 4가지 복합 그룹 분류 중...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 45623/45623 [00:00<00:00, 53369.91it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[스캔 완료]\n",
"- 세로형 제외됨: 36482장\n",
"- 후보군 'shirt_pants_glass': 456장\n",
"- 후보군 'shirt_pants_no_glass': 1276장\n",
"- 후보군 'jacket_pants_glass': 158장\n",
"- 후보군 'jacket_pants_no_glass': 488장\n",
"\n",
"2. 조건에 맞춰 300장 추출 및 저장 시작...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Extracting shirt_pants_glass: 100%|██████████| 100/100 [00:00<00:00, 23083.68it/s]\n",
"Extracting shirt_pants_no_glass: 100%|██████████| 100/100 [00:00<00:00, 25809.51it/s]\n",
"Extracting jacket_pants_glass: 100%|██████████| 50/50 [00:00<00:00, 18032.26it/s]\n",
"Extracting jacket_pants_no_glass: 100%|██████████| 50/50 [00:00<00:00, 22369.62it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"✨ 모든 작업 완료!\n",
"📂 저장 경로: /home/cuuva/다운로드/quantization_experiments_4class_original/8_custom2\n",
"📊 총 저장된 이미지 수: 300장 / 목표 300장\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"import shutil\n",
"import random\n",
"from glob import glob\n",
"from tqdm import tqdm\n",
"from PIL import Image\n",
"\n",
"# ================= [설정값] =================\n",
"BASE_ROOT = \"/home/cuuva/git/Detection_Experiment/datasets/fashionpedia_yolo\"\n",
"LABEL_DIR = os.path.join(BASE_ROOT, \"labels/train\")\n",
"IMAGE_DIR = os.path.join(BASE_ROOT, \"images/train\")\n",
"OUTPUT_DIR = \"/home/cuuva/다운로드/quantization_experiments_4class_original/8_custom2\"\n",
"\n",
"# 클래스 ID (YAML 기준)\n",
"ID_SHIRT = 0\n",
"ID_JACKET = 1\n",
"ID_PANTS = 2\n",
"ID_GLASSES = 3\n",
"\n",
"# ================= 1. 데이터 스캔 및 분류 =================\n",
"print(\"1. 전체 데이터 스캔 및 4가지 복합 그룹 분류 중...\")\n",
"\n",
"# 4개의 후보 리스트 (Bucket) 생성\n",
"buckets = {\n",
" 'shirt_pants_glass': [], # 셔츠+팬츠+안경\n",
" 'shirt_pants_no_glass': [], # 셔츠+팬츠 (안경X)\n",
" 'jacket_pants_glass': [], # 재킷+팬츠+안경\n",
" 'jacket_pants_no_glass': [] # 재킷+팬츠 (안경X)\n",
"}\n",
"\n",
"txt_files = glob(os.path.join(LABEL_DIR, \"*.txt\"))\n",
"skipped_vertical = 0\n",
"\n",
"for txt_path in tqdm(txt_files):\n",
" basename = os.path.splitext(os.path.basename(txt_path))[0]\n",
" \n",
" # [1] 이미지 찾기\n",
" img_path = None\n",
" for ext in ['.jpg', '.jpeg', '.png']:\n",
" temp = os.path.join(IMAGE_DIR, basename + ext)\n",
" if os.path.exists(temp):\n",
" img_path = temp\n",
" break\n",
" \n",
" if not img_path: continue\n",
" \n",
" # [2] 가로형(Landscape) 체크\n",
" try:\n",
" with Image.open(img_path) as img:\n",
" w, h = img.size\n",
" if h > w: # 세로가 더 길면 탈락\n",
" skipped_vertical += 1\n",
" continue\n",
" except:\n",
" continue\n",
"\n",
" # [3] 클래스 분석\n",
" classes = set()\n",
" with open(txt_path, 'r') as f:\n",
" for line in f:\n",
" parts = line.strip().split()\n",
" if parts:\n",
" classes.add(int(parts[0]))\n",
" \n",
" has_shirt = ID_SHIRT in classes\n",
" has_jacket = ID_JACKET in classes\n",
" has_pants = ID_PANTS in classes\n",
" has_glasses = ID_GLASSES in classes\n",
" \n",
" # [4] 버킷 담기 logic (중복 허용)\n",
" \n",
" # Group 1: Shirt + Pants\n",
" if has_shirt and has_pants:\n",
" if has_glasses:\n",
" buckets['shirt_pants_glass'].append(img_path)\n",
" else:\n",
" buckets['shirt_pants_no_glass'].append(img_path)\n",
" \n",
" # Group 2: Jacket + Pants\n",
" if has_jacket and has_pants:\n",
" if has_glasses:\n",
" buckets['jacket_pants_glass'].append(img_path)\n",
" else:\n",
" buckets['jacket_pants_no_glass'].append(img_path)\n",
"\n",
"print(f\"\\n[스캔 완료]\")\n",
"print(f\"- 세로형 제외됨: {skipped_vertical}장\")\n",
"for key, lst in buckets.items():\n",
" print(f\"- 후보군 '{key}': {len(lst)}장\")\n",
"\n",
"# ================= 2. 샘플링 및 저장 =================\n",
"print(\"\\n2. 조건에 맞춰 300장 추출 및 저장 시작...\")\n",
"\n",
"if os.path.exists(OUTPUT_DIR):\n",
" shutil.rmtree(OUTPUT_DIR)\n",
"os.makedirs(OUTPUT_DIR, exist_ok=True)\n",
"\n",
"final_selected_files = set() # 중복 방지용\n",
"saved_count = 0\n",
"\n",
"def extract_and_save(bucket_key, target_count):\n",
" global saved_count\n",
" candidates = buckets[bucket_key]\n",
" random.shuffle(candidates)\n",
" \n",
" current_selected = 0\n",
" pbar = tqdm(total=target_count, desc=f\"Extracting {bucket_key}\")\n",
" \n",
" for img_path in candidates:\n",
" if current_selected >= target_count:\n",
" break\n",
" \n",
" # 중복 방지 (이미 다른 그룹에서 뽑힌 파일 제외)\n",
" if img_path in final_selected_files:\n",
" continue\n",
" \n",
" # 복사 (원본 유지)\n",
" dst_path = os.path.join(OUTPUT_DIR, os.path.basename(img_path))\n",
" shutil.copy(img_path, dst_path)\n",
" \n",
" final_selected_files.add(img_path)\n",
" current_selected += 1\n",
" saved_count += 1\n",
" pbar.update(1)\n",
" \n",
" pbar.close()\n",
" \n",
" if current_selected < target_count:\n",
" print(f\"⚠️ 경고: '{bucket_key}' 조건 만족 이미지가 부족합니다. ({current_selected}/{target_count})\")\n",
"\n",
"# 요청 순서대로 실행\n",
"\n",
"# 1. 셔츠+팬츠+안경 (100장)\n",
"extract_and_save('shirt_pants_glass', 100)\n",
"\n",
"# 2. 셔츠+팬츠 (안경X) (100장)\n",
"extract_and_save('shirt_pants_no_glass', 100)\n",
"\n",
"# 3. 재킷+팬츠+안경 (50장)\n",
"extract_and_save('jacket_pants_glass', 50)\n",
"\n",
"# 4. 재킷+팬츠 (안경X) (50장)\n",
"extract_and_save('jacket_pants_no_glass', 50)\n",
"\n",
"print(f\"\\n✨ 모든 작업 완료!\")\n",
"print(f\"📂 저장 경로: {OUTPUT_DIR}\")\n",
"print(f\"📊 총 저장된 이미지 수: {saved_count}장 / 목표 300장\")"
]
},
{
"cell_type": "code",
"execution_count": 16,
"id": "44b7fb14",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"이미지 리사이즈 시작: 640x384 (WxH)\n",
"작업 경로: /home/cuuva/다운로드/quantization_experiments_4class_original/gogo\n",
"\n",
"📂 Processing [custom1/external1] - 300 images\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 300/300 [00:00<00:00, 53821.43it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" -> 완료: 300/300장 변환됨\n",
"\n",
"📂 Processing [custom2/external1] - 300 images\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 300/300 [00:00<00:00, 50625.27it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" -> 완료: 300/300장 변환됨\n",
"\n",
"📂 Processing [custom3/external1] - 300 images\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 300/300 [00:00<00:00, 55219.70it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" -> 완료: 300/300장 변환됨\n",
"\n",
"📂 Processing [custom4/external1] - 300 images\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 300/300 [00:00<00:00, 55428.89it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" -> 완료: 300/300장 변환됨\n",
"\n",
"📂 Processing [custom5/external1] - 300 images\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 300/300 [00:00<00:00, 55372.79it/s]\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" -> 완료: 300/300장 변환됨\n",
"\n",
"📂 Processing [custom6/external1] - 300 images\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 300/300 [00:00<00:00, 1788.60it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
" -> 완료: 300/300장 변환됨\n",
"\n",
"✨ 모든 작업 완료!\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"from glob import glob\n",
"from tqdm import tqdm\n",
"from PIL import Image\n",
"\n",
"# ================= 설정값 =================\n",
"# 작업할 베이스 경로\n",
"BASE_DIR = \"/home/cuuva/다운로드/quantization_experiments_4class_original/gogo\"\n",
"\n",
"# 대상 폴더 리스트\n",
"TARGET_FOLDERS = [\n",
" \"custom1\",\n",
" \"custom2\",\n",
" \"custom3\",\n",
" \"custom4\",\n",
" \"custom5\",\n",
" \"custom6\"\n",
"]\n",
"\n",
"# 서브 폴더 이름\n",
"SUB_DIR = \"external1\"\n",
"\n",
"# 목표 해상도 (PIL은 Width, Height 순서임)\n",
"# User request: Height 384, Width 640\n",
"TARGET_WIDTH = 640\n",
"TARGET_HEIGHT = 384\n",
"\n",
"# ================= 리사이즈 실행 =================\n",
"\n",
"print(f\"이미지 리사이즈 시작: {TARGET_WIDTH}x{TARGET_HEIGHT} (WxH)\")\n",
"print(f\"작업 경로: {BASE_DIR}\\n\")\n",
"\n",
"for folder in TARGET_FOLDERS:\n",
" # 실제 이미지 경로: .../gogo/{folder}/external1\n",
" work_path = os.path.join(BASE_DIR, folder, SUB_DIR)\n",
" \n",
" # 폴더 존재 여부 확인\n",
" if not os.path.exists(work_path):\n",
" print(f\"⚠️ Warning: 경로를 찾을 수 없습니다 -> {work_path}\")\n",
" continue\n",
" \n",
" # 이미지 파일 리스트업\n",
" image_files = []\n",
" for ext in ['*.jpg', '*.jpeg', '*.png', '*.bmp']:\n",
" image_files.extend(glob(os.path.join(work_path, ext)))\n",
" \n",
" print(f\"📂 Processing [{folder}/{SUB_DIR}] - {len(image_files)} images\")\n",
" \n",
" # 리사이즈 진행 (덮어쓰기)\n",
" success_count = 0\n",
" for img_path in tqdm(image_files):\n",
" try:\n",
" with Image.open(img_path) as img:\n",
" # 이미 목표 사이즈라면 건너뜀 (최적화)\n",
" if img.size == (TARGET_WIDTH, TARGET_HEIGHT):\n",
" success_count += 1\n",
" continue\n",
" \n",
" # 리사이즈 (LANCZOS: 고품질 리샘플링)\n",
" # PIL resize는 (width, height) 순서\n",
" resized_img = img.resize((TARGET_WIDTH, TARGET_HEIGHT), Image.Resampling.LANCZOS)\n",
" \n",
" # 덮어쓰기 저장 (원본 파일명 유지)\n",
" # 만약 포맷 문제(png->jpg 등)가 있다면 convert('RGB') 필요할 수 있음\n",
" resized_img.save(img_path)\n",
" success_count += 1\n",
" \n",
" except Exception as e:\n",
" print(f\" ❌ Error processing {os.path.basename(img_path)}: {e}\")\n",
"\n",
" print(f\" -> 완료: {success_count}/{len(image_files)}장 변환됨\\n\")\n",
"\n",
"print(\"✨ 모든 작업 완료!\")"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "cc508d55",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"1. 데이터 스캔 (조건: 가로/세로 비율 1.3 이상)...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 45623/45623 [00:00<00:00, 55749.77it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"[스캔 완료]\n",
"- 세로형/정사각형 제외: 40360장\n",
"- 애매한 가로형 제외 (비율 < 1.3): 405장\n",
"- 후보군 'shirt_pants_glass': 214장\n",
"- 후보군 'shirt_pants_no_glass': 758장\n",
"- 후보군 'jacket_pants_glass': 87장\n",
"- 후보군 'jacket_pants_no_glass': 327장\n",
"\n",
"2. 데이터셋 4개 생성 시작...\n",
"\n",
"🚀 Creating [dataset_1]...\n",
" -> 완료: 300장\n",
"\n",
"🚀 Creating [dataset_2]...\n",
" -> 완료: 300장\n",
"\n",
"🚀 Creating [dataset_3]...\n",
" -> 완료: 300장\n",
"\n",
"🚀 Creating [dataset_4]...\n",
" -> 완료: 300장\n",
"\n",
"✨ 모든 작업 완료!\n",
"📂 저장 위치: /home/cuuva/다운로드/quantization_experiments_4class_original/8_custom2_wide_sets\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"import shutil\n",
"import random\n",
"from glob import glob\n",
"from tqdm import tqdm\n",
"from PIL import Image\n",
"\n",
"# ================= [설정값] =================\n",
"BASE_ROOT = \"/home/cuuva/git/Detection_Experiment/datasets/fashionpedia_yolo\"\n",
"LABEL_DIR = os.path.join(BASE_ROOT, \"labels/train\")\n",
"IMAGE_DIR = os.path.join(BASE_ROOT, \"images/train\")\n",
"OUTPUT_ROOT = \"/home/cuuva/다운로드/quantization_experiments_4class_original/8_custom2_wide_sets\"\n",
"\n",
"# 클래스 ID\n",
"ID_SHIRT = 0\n",
"ID_JACKET = 1\n",
"ID_PANTS = 2\n",
"ID_GLASSES = 3\n",
"\n",
"# [핵심] 가로/세로 비율 임계값\n",
"# 1.3으로 설정하면, 가로가 세로보다 30% 이상 더 길어야 함 (확실한 가로형)\n",
"# 예: 1000x800 -> 1.25 (탈락), 1000x700 -> 1.42 (통과)\n",
"ASPECT_RATIO_THRESHOLD = 1.3\n",
"\n",
"# 생성할 데이터셋 개수\n",
"NUM_DATASETS = 4\n",
"\n",
"# ================= 1. 데이터 스캔 및 분류 =================\n",
"print(f\"1. 데이터 스캔 (조건: 가로/세로 비율 {ASPECT_RATIO_THRESHOLD} 이상)...\")\n",
"\n",
"buckets = {\n",
" 'shirt_pants_glass': [],\n",
" 'shirt_pants_no_glass': [],\n",
" 'jacket_pants_glass': [],\n",
" 'jacket_pants_no_glass': []\n",
"}\n",
"\n",
"txt_files = glob(os.path.join(LABEL_DIR, \"*.txt\"))\n",
"skipped_ratio = 0\n",
"skipped_vertical = 0\n",
"\n",
"for txt_path in tqdm(txt_files):\n",
" basename = os.path.splitext(os.path.basename(txt_path))[0]\n",
" \n",
" # [1] 이미지 찾기\n",
" img_path = None\n",
" for ext in ['.jpg', '.jpeg', '.png']:\n",
" temp = os.path.join(IMAGE_DIR, basename + ext)\n",
" if os.path.exists(temp):\n",
" img_path = temp\n",
" break\n",
" \n",
" if not img_path: continue\n",
" \n",
" # [2] 비율 체크 (Strict Aspect Ratio)\n",
" try:\n",
" with Image.open(img_path) as img:\n",
" w, h = img.size\n",
" \n",
" # 세로형이거나 정사각형이면 일단 제외\n",
" if h >= w:\n",
" skipped_vertical += 1\n",
" continue\n",
" \n",
" # [수정된 부분] 가로/세로 비율 계산\n",
" ratio = w / h\n",
" if ratio < ASPECT_RATIO_THRESHOLD:\n",
" skipped_ratio += 1 # 가로가 길긴 한데, 애매해서 탈락\n",
" continue\n",
" \n",
" except:\n",
" continue\n",
"\n",
" # [3] 클래스 분석 (동일 로직)\n",
" classes = set()\n",
" with open(txt_path, 'r') as f:\n",
" for line in f:\n",
" parts = line.strip().split()\n",
" if parts:\n",
" classes.add(int(parts[0]))\n",
" \n",
" has_shirt = ID_SHIRT in classes\n",
" has_jacket = ID_JACKET in classes\n",
" has_pants = ID_PANTS in classes\n",
" has_glasses = ID_GLASSES in classes\n",
" \n",
" if has_shirt and has_pants:\n",
" if has_glasses:\n",
" buckets['shirt_pants_glass'].append(img_path)\n",
" else:\n",
" buckets['shirt_pants_no_glass'].append(img_path)\n",
" \n",
" if has_jacket and has_pants:\n",
" if has_glasses:\n",
" buckets['jacket_pants_glass'].append(img_path)\n",
" else:\n",
" buckets['jacket_pants_no_glass'].append(img_path)\n",
"\n",
"print(f\"\\n[스캔 완료]\")\n",
"print(f\"- 세로형/정사각형 제외: {skipped_vertical}장\")\n",
"print(f\"- 애매한 가로형 제외 (비율 < {ASPECT_RATIO_THRESHOLD}): {skipped_ratio}장\")\n",
"\n",
"for key, lst in buckets.items():\n",
" print(f\"- 후보군 '{key}': {len(lst)}장\")\n",
"\n",
"\n",
"# ================= 2. 4개 데이터셋 생성 =================\n",
"print(f\"\\n2. 데이터셋 {NUM_DATASETS}개 생성 시작...\")\n",
"\n",
"if os.path.exists(OUTPUT_ROOT):\n",
" shutil.rmtree(OUTPUT_ROOT)\n",
"os.makedirs(OUTPUT_ROOT, exist_ok=True)\n",
"\n",
"TARGET_QUOTAS = {\n",
" 'shirt_pants_glass': 100,\n",
" 'shirt_pants_no_glass': 100,\n",
" 'jacket_pants_glass': 50,\n",
" 'jacket_pants_no_glass': 50\n",
"}\n",
"\n",
"for i in range(1, NUM_DATASETS + 1):\n",
" set_name = f\"dataset_{i}\"\n",
" set_dir = os.path.join(OUTPUT_ROOT, set_name)\n",
" os.makedirs(set_dir, exist_ok=True)\n",
" \n",
" print(f\"\\n🚀 Creating [{set_name}]...\")\n",
" \n",
" current_set_count = 0\n",
" current_set_files = set() \n",
" \n",
" for bucket_key, target_count in TARGET_QUOTAS.items():\n",
" candidates = buckets[bucket_key][:]\n",
" random.shuffle(candidates)\n",
" \n",
" extracted = 0\n",
" for img_path in candidates:\n",
" if extracted >= target_count:\n",
" break\n",
" \n",
" basename = os.path.basename(img_path)\n",
" if basename in current_set_files:\n",
" continue\n",
" \n",
" dst_path = os.path.join(set_dir, basename)\n",
" shutil.copy(img_path, dst_path)\n",
" \n",
" current_set_files.add(basename)\n",
" extracted += 1\n",
" current_set_count += 1\n",
" \n",
" if extracted < target_count:\n",
" print(f\" ⚠️ Warning: [{bucket_key}] 후보 부족 ({extracted}/{target_count})\")\n",
" \n",
" print(f\" -> 완료: {current_set_count}장\")\n",
"\n",
"print(f\"\\n✨ 모든 작업 완료!\")\n",
"print(f\"📂 저장 위치: {OUTPUT_ROOT}\")"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "bce0eb29",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"📂 결과 저장 경로 생성: /home/cuuva/다운로드/face_recog/images/labels_16\n",
"🤖 모델 로딩 중: /home/cuuva/git/Detection_Experiment/fashion_yolo/fashionpedia_exp/yolov8m_fashion_final/weights/best_fashion_16class.pt\n",
"📷 총 385장의 이미지에 대해 추론을 시작합니다...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 385/385 [00:03<00:00, 113.43it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"감지된 객체가 없으면 빈 파일 생성 (YOLO 학습 시 빈 파일도 필요할 수 있음)\n",
"감지된 객체가 없으면 빈 파일 생성 (YOLO 학습 시 빈 파일도 필요할 수 있음)\n",
"\n",
"✨ 모든 작업이 완료되었습니다!\n",
"확인 명령어: ls /home/cuuva/다운로드/face_recog/images/labels_16\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"from glob import glob\n",
"from ultralytics import YOLO\n",
"from tqdm import tqdm\n",
"\n",
"# ================= 설정값 =================\n",
"# 1. 학습된 모델 경로\n",
"model_path = \"/home/cuuva/git/Detection_Experiment/fashion_yolo/fashionpedia_exp/yolov8m_fashion_final/weights/best_fashion_16class.pt\"\n",
"\n",
"# 2. 이미지가 있는 폴더 (Inference 대상)\n",
"source_dir = \"/home/cuuva/다운로드/face_recog/images\"\n",
"\n",
"# 3. 라벨을 저장할 폴더 (자동 생성됨)\n",
"labels_dir = os.path.join(source_dir, \"labels_16\")\n",
"\n",
"# ================= 실행 로직 =================\n",
"\n",
"def run_inference_and_save_labels():\n",
" # 저장할 폴더 생성\n",
" os.makedirs(labels_dir, exist_ok=True)\n",
" print(f\"📂 결과 저장 경로 생성: {labels_dir}\")\n",
"\n",
" # 모델 로드\n",
" print(f\"🤖 모델 로딩 중: {model_path}\")\n",
" model = YOLO(model_path)\n",
"\n",
" # 이미지 파일 리스트 가져오기 (jpg, png 등)\n",
" image_extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp']\n",
" img_files = []\n",
" for ext in image_extensions:\n",
" img_files.extend(glob(os.path.join(source_dir, ext)))\n",
" \n",
" img_files.sort()\n",
" print(f\"📷 총 {len(img_files)}장의 이미지에 대해 추론을 시작합니다...\")\n",
"\n",
" # 추론 실행 (stream=True로 메모리 효율성 확보)\n",
" # save=False: 이미지를 저장하지 않음 (라벨만 필요하므로)\n",
" results = model(img_files, stream=True, verbose=False)\n",
"\n",
" for result in tqdm(results, total=len(img_files)):\n",
" # 원본 파일명 추출 (경로 제외, 확장자 제외)\n",
" basename = os.path.basename(result.path)\n",
" filename_no_ext = os.path.splitext(basename)[0]\n",
" \n",
" # 저장할 txt 파일 경로\n",
" txt_path = os.path.join(labels_dir, f\"{filename_no_ext}.txt\")\n",
"\n",
" # 결과 작성\n",
" with open(txt_path, 'w') as f:\n",
" # 감지된 객체가 있는 경우\n",
" if result.boxes:\n",
" # boxes.cls: 클래스 ID\n",
" # boxes.xywhn: Normalized Center_X, Center_Y, Width, Height (0~1 사이 값)\n",
" for cls, box in zip(result.boxes.cls, result.boxes.xywhn):\n",
" class_id = int(cls)\n",
" x_center, y_center, width, height = box.tolist()\n",
" \n",
" # YOLO 포맷: class_id x_center y_center width height\n",
" # 소수점 6자리까지 기록\n",
" line = f\"{class_id} {x_center:.6f} {y_center:.6f} {width:.6f} {height:.6f}\\n\"\n",
" f.write(line)\n",
" else:\n",
" # 감지된 객체가 없으면 빈 파일 생성 (YOLO 학습 시 빈 파일도 필요할 수 있음)\n",
" print(\"감지된 객체가 없으면 빈 파일 생성 (YOLO 학습 시 빈 파일도 필요할 수 있음)\")\n",
" pass \n",
"\n",
" print(\"\\n✨ 모든 작업이 완료되었습니다!\")\n",
" print(f\"확인 명령어: ls {labels_dir}\")\n",
"\n",
"run_inference_and_save_labels()\n"
]
},
{
"cell_type": "markdown",
"id": "bdec23a6",
"metadata": {},
"source": [
"## 클래스 4짜리 infer"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ec743743",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"📂 저장 폴더 생성: /home/cuuva/다운로드/face_recog/images/infer\n",
"🎨 총 385장의 이미지에 시각화를 진행합니다...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 385/385 [00:01<00:00, 259.49it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"✨ 시각화 완료!\n",
"확인 경로: /home/cuuva/다운로드/face_recog/images/infer\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"import cv2\n",
"from glob import glob\n",
"from tqdm import tqdm\n",
"\n",
"# ================= 설정값 =================\n",
"# 경로 설정\n",
"BASE_DIR = \"/home/cuuva/다운로드/face_recog/images\"\n",
"LABEL_DIR = os.path.join(BASE_DIR, \"labels2\")\n",
"OUTPUT_DIR = os.path.join(BASE_DIR, \"infer\")\n",
"\n",
"# 클래스 정의 (0, 1, 2, 3 순서대로)\n",
"CLASS_NAMES = ['shirt', 'jacket', 'pants', 'glasses']\n",
"\n",
"# 색상 정의 (B, G, R 순서) - 클래스별로 다르게 표시\n",
"COLORS = [\n",
" (255, 0, 0), # 파랑 (Shirt)\n",
" (0, 255, 0), # 초록 (Jacket)\n",
" (0, 0, 255), # 빨강 (Pants)\n",
" (255, 255, 0) # 하늘색 (Glasses)\n",
"]\n",
"\n",
"# ================= 실행 로직 =================\n",
"\n",
"def visualize_inference():\n",
" # 1. infer 폴더 생성\n",
" if not os.path.exists(OUTPUT_DIR):\n",
" os.makedirs(OUTPUT_DIR)\n",
" print(f\"📂 저장 폴더 생성: {OUTPUT_DIR}\")\n",
"\n",
" # 2. 이미지 파일 리스트업\n",
" img_files = glob(os.path.join(BASE_DIR, \"*.jpg\")) + glob(os.path.join(BASE_DIR, \"*.png\"))\n",
" img_files.sort()\n",
"\n",
" print(f\"🎨 총 {len(img_files)}장의 이미지에 시각화를 진행합니다...\")\n",
"\n",
" for img_path in tqdm(img_files):\n",
" # 이미지 읽기\n",
" img = cv2.imread(img_path)\n",
" if img is None:\n",
" continue\n",
" \n",
" h_img, w_img, _ = img.shape\n",
" \n",
" # 대응하는 라벨 파일 찾기\n",
" basename = os.path.basename(img_path)\n",
" txt_name = os.path.splitext(basename)[0] + \".txt\"\n",
" txt_path = os.path.join(LABEL_DIR, txt_name)\n",
"\n",
" # 라벨 파일이 존재하면 읽어서 그리기\n",
" if os.path.exists(txt_path):\n",
" with open(txt_path, 'r') as f:\n",
" lines = f.readlines()\n",
" \n",
" for line in lines:\n",
" parts = line.strip().split()\n",
" if len(parts) < 5: continue\n",
"\n",
" # YOLO Format: class x_center y_center width height (Normalized 0~1)\n",
" cls_id = int(parts[0])\n",
" x_n, y_n, w_n, h_n = map(float, parts[1:])\n",
"\n",
" # 좌표 변환 (Normalized -> Pixel)\n",
" x_center = x_n * w_img\n",
" y_center = y_n * h_img\n",
" box_w = w_n * w_img\n",
" box_h = h_n * h_img\n",
"\n",
" # 좌상단(x1, y1), 우하단(x2, y2) 좌표 계산\n",
" x1 = int(x_center - box_w / 2)\n",
" y1 = int(y_center - box_h / 2)\n",
" x2 = int(x_center + box_w / 2)\n",
" y2 = int(y_center + box_h / 2)\n",
"\n",
" # 그리기 설정\n",
" color = COLORS[cls_id % len(COLORS)]\n",
" label_text = CLASS_NAMES[cls_id] if cls_id < len(CLASS_NAMES) else str(cls_id)\n",
"\n",
" # 1. 사각형 그리기\n",
" cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)\n",
"\n",
" # 2. 텍스트 배경 및 텍스트 쓰기 (가독성을 위해)\n",
" (text_w, text_h), _ = cv2.getTextSize(label_text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)\n",
" cv2.rectangle(img, (x1, y1 - 20), (x1 + text_w, y1), color, -1) # 텍스트 배경 채우기\n",
" cv2.putText(img, label_text, (x1, y1 - 5), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)\n",
"\n",
" # 결과 이미지 저장\n",
" save_path = os.path.join(OUTPUT_DIR, basename)\n",
" cv2.imwrite(save_path, img)\n",
"\n",
" print(\"\\n✨ 시각화 완료!\")\n",
" print(f\"확인 경로: {OUTPUT_DIR}\")\n",
"\n",
"visualize_inference()"
]
},
{
"cell_type": "markdown",
"id": "0c406a5e",
"metadata": {},
"source": [
"## 클래스 16짜리 infer"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "f846220b",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"📂 저장 폴더 생성: /home/cuuva/다운로드/face_recog/images/infer\n",
"🎨 총 385장의 이미지에 시각화를 진행합니다...\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"100%|██████████| 385/385 [00:01<00:00, 257.94it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"✨ 시각화 완료!\n",
"확인 경로: /home/cuuva/다운로드/face_recog/images/infer\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"import cv2\n",
"from glob import glob\n",
"from tqdm import tqdm\n",
"\n",
"# ================= 설정값 =================\n",
"# 경로 설정\n",
"BASE_DIR = \"/home/cuuva/다운로드/face_recog/images\"\n",
"LABEL_DIR = os.path.join(BASE_DIR, \"labels_16\")\n",
"OUTPUT_DIR = os.path.join(BASE_DIR, \"infer\")\n",
"\n",
"# 1. 클래스 정의 (16개, 0~15 순서대로)\n",
"CLASS_NAMES = [\n",
" 'shirt(blouse)', # 0\n",
" 't-shirt(sweatshirt)', # 1\n",
" 'sweater', # 2\n",
" 'cardigan', # 3\n",
" 'jacket', # 4\n",
" 'vest', # 5\n",
" 'pants', # 6\n",
" 'shorts', # 7\n",
" 'skirt', # 8\n",
" 'coat', # 9\n",
" 'dress', # 10\n",
" 'glasses', # 11\n",
" 'hat', # 12\n",
" 'shoe', # 13\n",
" 'bag, wallet', # 14\n",
" 'scarf' # 15\n",
"]\n",
"\n",
"# 2. 색상 정의 (B, G, R 순서) - 16개 클래스 구분을 위한 팔레트\n",
"COLORS = [\n",
" (255, 0, 0), # 0: Blue\n",
" (0, 255, 0), # 1: Green\n",
" (0, 0, 255), # 2: Red\n",
" (255, 255, 0), # 3: Cyan\n",
" (255, 0, 255), # 4: Magenta\n",
" (0, 255, 255), # 5: Yellow\n",
" (0, 140, 255), # 6: Orange\n",
" (128, 0, 128), # 7: Purple\n",
" (147, 20, 255), # 8: Deep Pink\n",
" (50, 205, 50), # 9: Lime Green\n",
" (255, 191, 0), # 10: Deep Sky Blue\n",
" (128, 128, 0), # 11: Teal\n",
" (0, 0, 128), # 12: Maroon\n",
" (0, 128, 128), # 13: Olive\n",
" (19, 69, 139), # 14: Saddle Brown\n",
" (180, 105, 255) # 15: Hot Pink\n",
"]\n",
"\n",
"# ================= 실행 로직 (기존과 동일) =================\n",
"\n",
"def visualize_inference():\n",
" # 1. infer 폴더 생성\n",
" if not os.path.exists(OUTPUT_DIR):\n",
" os.makedirs(OUTPUT_DIR)\n",
" print(f\"📂 저장 폴더 생성: {OUTPUT_DIR}\")\n",
"\n",
" # 2. 이미지 파일 리스트업\n",
" img_files = glob(os.path.join(BASE_DIR, \"*.jpg\")) + glob(os.path.join(BASE_DIR, \"*.png\"))\n",
" img_files.sort()\n",
"\n",
" print(f\"🎨 총 {len(img_files)}장의 이미지에 시각화를 진행합니다...\")\n",
"\n",
" for img_path in tqdm(img_files):\n",
" # 이미지 읽기\n",
" img = cv2.imread(img_path)\n",
" if img is None:\n",
" continue\n",
" \n",
" h_img, w_img, _ = img.shape\n",
" \n",
" # 대응하는 라벨 파일 찾기\n",
" basename = os.path.basename(img_path)\n",
" txt_name = os.path.splitext(basename)[0] + \".txt\"\n",
" txt_path = os.path.join(LABEL_DIR, txt_name)\n",
"\n",
" # 라벨 파일이 존재하면 읽어서 그리기\n",
" if os.path.exists(txt_path):\n",
" with open(txt_path, 'r') as f:\n",
" lines = f.readlines()\n",
" \n",
" for line in lines:\n",
" parts = line.strip().split()\n",
" if len(parts) < 5: continue\n",
"\n",
" # YOLO Format: class x_center y_center width height (Normalized 0~1)\n",
" cls_id = int(parts[0])\n",
" x_n, y_n, w_n, h_n = map(float, parts[1:])\n",
"\n",
" # 좌표 변환 (Normalized -> Pixel)\n",
" x_center = x_n * w_img\n",
" y_center = y_n * h_img\n",
" box_w = w_n * w_img\n",
" box_h = h_n * h_img\n",
"\n",
" # 좌상단(x1, y1), 우하단(x2, y2) 좌표 계산\n",
" x1 = int(x_center - box_w / 2)\n",
" y1 = int(y_center - box_h / 2)\n",
" x2 = int(x_center + box_w / 2)\n",
" y2 = int(y_center + box_h / 2)\n",
"\n",
" # 그리기 설정 (클래스 ID가 범위 밖일 경우 안전장치 포함)\n",
" color = COLORS[cls_id % len(COLORS)]\n",
" label_text = CLASS_NAMES[cls_id] if cls_id < len(CLASS_NAMES) else str(cls_id)\n",
"\n",
" # 1. 사각형 그리기\n",
" cv2.rectangle(img, (x1, y1), (x2, y2), color, 2)\n",
"\n",
" # 2. 텍스트 배경 및 텍스트 쓰기 (가독성을 위해)\n",
" (text_w, text_h), _ = cv2.getTextSize(label_text, cv2.FONT_HERSHEY_SIMPLEX, 0.5, 1)\n",
" \n",
" # 텍스트가 이미지 위로 넘어가지 않도록 좌표 조정\n",
" text_y = y1 - 5\n",
" if text_y < 20: \n",
" text_y = y1 + 20\n",
"\n",
" cv2.rectangle(img, (x1, text_y - 15), (x1 + text_w, text_y + 5), color, -1) # 텍스트 배경 채우기\n",
" cv2.putText(img, label_text, (x1, text_y), cv2.FONT_HERSHEY_SIMPLEX, 0.5, (255, 255, 255), 1)\n",
"\n",
" # 결과 이미지 저장\n",
" save_path = os.path.join(OUTPUT_DIR, basename)\n",
" cv2.imwrite(save_path, img)\n",
"\n",
" print(\"\\n✨ 시각화 완료!\")\n",
" print(f\"확인 경로: {OUTPUT_DIR}\")\n",
"\n",
"visualize_inference()"
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "d437bb10",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"📂 작업 경로: /home/cuuva/다운로드/finetuned/external1/\n",
"🎯 목표 해상도: 640 x 384 (Width x Height)\n",
"📷 발견된 이미지: 300장\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"Resizing: 100%|██████████| 300/300 [00:02<00:00, 114.43it/s]"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"✨ 작업 완료! 총 300장의 이미지가 변환되었습니다.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"\n"
]
}
],
"source": [
"import os\n",
"from glob import glob\n",
"from tqdm import tqdm\n",
"from PIL import Image\n",
"\n",
"# ================= 설정값 =================\n",
"# 작업할 경로\n",
"WORK_DIR = \"/home/cuuva/다운로드/finetuned/external1/\"\n",
"\n",
"# 목표 해상도 설정\n",
"# User Request: Height 384, Width 640\n",
"# PIL Library uses (Width, Height) order\n",
"TARGET_WIDTH = 640\n",
"TARGET_HEIGHT = 384\n",
"\n",
"# ================= 실행 로직 =================\n",
"\n",
"def resize_images_in_folder():\n",
" # 1. 경로 확인\n",
" if not os.path.exists(WORK_DIR):\n",
" print(f\"❌ 오류: 경로가 존재하지 않습니다 -> {WORK_DIR}\")\n",
" return\n",
"\n",
" # 2. 이미지 파일 리스트업\n",
" extensions = ['*.jpg', '*.jpeg', '*.png', '*.bmp', '*.JPG', '*.PNG']\n",
" img_files = []\n",
" for ext in extensions:\n",
" img_files.extend(glob(os.path.join(WORK_DIR, ext)))\n",
" \n",
" print(f\"📂 작업 경로: {WORK_DIR}\")\n",
" print(f\"🎯 목표 해상도: {TARGET_WIDTH} x {TARGET_HEIGHT} (Width x Height)\")\n",
" print(f\"📷 발견된 이미지: {len(img_files)}장\")\n",
"\n",
" success_count = 0\n",
" \n",
" # 3. 리사이즈 및 덮어쓰기 진행\n",
" for img_path in tqdm(img_files, desc=\"Resizing\"):\n",
" try:\n",
" with Image.open(img_path) as img:\n",
" # 이미 목표 사이즈라면 스킵 (최적화)\n",
" if img.size == (TARGET_WIDTH, TARGET_HEIGHT):\n",
" success_count += 1\n",
" continue\n",
" \n",
" # 리사이즈 수행 (LANCZOS 필터 사용 - 고화질)\n",
" resized_img = img.resize((TARGET_WIDTH, TARGET_HEIGHT), Image.Resampling.LANCZOS)\n",
" \n",
" # 덮어쓰기 저장\n",
" # 원본 포맷 유지를 위해 convert('RGB')는 jpg 저장 시 필요할 수 있음\n",
" resized_img.convert('RGB').save(img_path)\n",
" \n",
" success_count += 1\n",
" \n",
" except Exception as e:\n",
" print(f\"⚠️ 에러 발생 ({os.path.basename(img_path)}): {e}\")\n",
"\n",
" print(f\"\\n✨ 작업 완료! 총 {success_count}장의 이미지가 변환되었습니다.\")\n",
"\n",
"\n",
"resize_images_in_folder()"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "cdb4d04e",
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Ultralytics 8.3.225 🚀 Python-3.10.18 torch-2.9.1+cu128 CUDA:0 (NVIDIA GeForce RTX 5090, 32109MiB)\n",
"Model summary (fused): 92 layers, 25,849,024 parameters, 0 gradients, 78.7 GFLOPs\n",
"\n",
"\u001b[34m\u001b[1mPyTorch:\u001b[0m starting from '/home/cuuva/git/Detection_Experiment/fashion_yolo/finetuning_exp/finetuned_exp/yolov8m_finetuned_16class2/weights/best_16_finetuned_clothes_2.pt' with input shape (1, 3, 640, 640) BCHW and output shape(s) (1, 20, 8400) (49.6 MB)\n",
"\n",
"\u001b[34m\u001b[1mONNX:\u001b[0m starting export with onnx 1.19.1 opset 20...\n",
"\u001b[34m\u001b[1mONNX:\u001b[0m slimming with onnxslim 0.1.71...\n",
"\u001b[34m\u001b[1mONNX:\u001b[0m export success ✅ 0.7s, saved as '/home/cuuva/git/Detection_Experiment/fashion_yolo/finetuning_exp/finetuned_exp/yolov8m_finetuned_16class2/weights/best_16_finetuned_clothes_2.onnx' (98.9 MB)\n",
"\n",
"Export complete (0.8s)\n",
"Results saved to \u001b[1m/home/cuuva/git/Detection_Experiment/fashion_yolo/finetuning_exp/finetuned_exp/yolov8m_finetuned_16class2/weights\u001b[0m\n",
"Predict: yolo predict task=detect model=/home/cuuva/git/Detection_Experiment/fashion_yolo/finetuning_exp/finetuned_exp/yolov8m_finetuned_16class2/weights/best_16_finetuned_clothes_2.onnx imgsz=640 \n",
"Validate: yolo val task=detect model=/home/cuuva/git/Detection_Experiment/fashion_yolo/finetuning_exp/finetuned_exp/yolov8m_finetuned_16class2/weights/best_16_finetuned_clothes_2.onnx imgsz=640 data=/home/cuuva/git/Detection_Experiment/fashion_yolo/finetuning_exp/finetuning_dataset.yaml \n",
"Visualize: https://netron.app\n"
]
},
{
"data": {
"text/plain": [
"'/home/cuuva/git/Detection_Experiment/fashion_yolo/finetuning_exp/finetuned_exp/yolov8m_finetuned_16class2/weights/best_16_finetuned_clothes_2.onnx'"
]
},
"execution_count": 3,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from ultralytics import YOLO\n",
"model = YOLO(\"/home/cuuva/git/Detection_Experiment/fashion_yolo/finetuning_exp/finetuned_exp/yolov8m_finetuned_16class2/weights/best_16_finetuned_clothes_2.pt\")\n",
"model.export(format=\"onnx\", imgsz=640, device=0)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "5bc62906",
"metadata": {},
"outputs": [],
"source": [
" "
]
}
],
"metadata": {
"kernelspec": {
"display_name": "1stagedetect",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.18"
}
},
"nbformat": 4,
"nbformat_minor": 5
}