588 lines
21 KiB
Plaintext
588 lines
21 KiB
Plaintext
{
|
|
"cells": [
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 6,
|
|
"id": "ddc93ea2",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Classes: ['Kaleng', 'Residu', 'botol_plastik_PET_bening2', 'botol_plastik_PET_bewarna']\n",
|
|
"✅ Model loaded\n",
|
|
"\n",
|
|
"🧠 Prediction: botol_plastik_PET_bening2\n",
|
|
"📊 Confidence: 83.07%\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import torch\n",
|
|
"import torch.nn as nn\n",
|
|
"from torchvision import models, transforms\n",
|
|
"from PIL import Image\n",
|
|
"\n",
|
|
"# ==========================================\n",
|
|
"# CONFIG\n",
|
|
"# ==========================================\n",
|
|
"MODEL_PATH = 'model_sampah_final_3.pth'\n",
|
|
"LABEL_PATH = 'labels.txt'\n",
|
|
"IMG_PATH = '20260204_145002.jpg'\n",
|
|
"\n",
|
|
"DEVICE = torch.device(\"cuda\" if torch.cuda.is_available() else \"cpu\")\n",
|
|
"\n",
|
|
"# ==========================================\n",
|
|
"# LOAD LABELS\n",
|
|
"# ==========================================\n",
|
|
"with open(LABEL_PATH, 'r') as f:\n",
|
|
" class_names = [line.strip() for line in f.readlines()]\n",
|
|
"\n",
|
|
"print(\"Classes:\", class_names)\n",
|
|
"\n",
|
|
"# ==========================================\n",
|
|
"# LOAD MODEL\n",
|
|
"# ==========================================\n",
|
|
"model = models.mobilenet_v2(weights=None)\n",
|
|
"num_ftrs = model.classifier[1].in_features\n",
|
|
"model.classifier[1] = nn.Linear(num_ftrs, len(class_names))\n",
|
|
"\n",
|
|
"model.load_state_dict(torch.load(MODEL_PATH, map_location=DEVICE))\n",
|
|
"model.to(DEVICE)\n",
|
|
"model.eval()\n",
|
|
"\n",
|
|
"print(\"✅ Model loaded\")\n",
|
|
"\n",
|
|
"# ==========================================\n",
|
|
"# PREPROCESS (SAME AS TRAINING)\n",
|
|
"# ==========================================\n",
|
|
"transform = transforms.Compose([\n",
|
|
" transforms.Resize((224, 224)),\n",
|
|
" transforms.ToTensor(),\n",
|
|
" transforms.Normalize(\n",
|
|
" mean=[0.485, 0.456, 0.406],\n",
|
|
" std=[0.229, 0.224, 0.225]\n",
|
|
" )\n",
|
|
"])\n",
|
|
"\n",
|
|
"# ==========================================\n",
|
|
"# LOAD IMAGE\n",
|
|
"# ==========================================\n",
|
|
"img = Image.open(IMG_PATH).convert(\"RGB\")\n",
|
|
"input_tensor = transform(img).unsqueeze(0).to(DEVICE)\n",
|
|
"\n",
|
|
"# ==========================================\n",
|
|
"# INFERENCE\n",
|
|
"# ==========================================\n",
|
|
"with torch.no_grad():\n",
|
|
" outputs = model(input_tensor)\n",
|
|
" probs = torch.softmax(outputs, dim=1)\n",
|
|
" confidence, pred = torch.max(probs, 1)\n",
|
|
"\n",
|
|
"# ==========================================\n",
|
|
"# RESULT\n",
|
|
"# ==========================================\n",
|
|
"label = class_names[pred.item()]\n",
|
|
"score = confidence.item()\n",
|
|
"\n",
|
|
"print(f\"\\n🧠 Prediction: {label}\")\n",
|
|
"print(f\"📊 Confidence: {score*100:.2f}%\")\n"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 1,
|
|
"id": "17588856",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Folder 'data_gambar' berhasil dibuat!\n",
|
|
"\n",
|
|
"--- INSTRUKSI ---\n",
|
|
"Tekan 'S' untuk menyimpan gambar (Crop)\n",
|
|
"Tekan 'Q' untuk keluar\n",
|
|
"Berhasil disimpan: data_gambar/gambar_20260204_142759.jpg\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import cv2\n",
|
|
"import os\n",
|
|
"import time\n",
|
|
"\n",
|
|
"# --- KONFIGURASI ---\n",
|
|
"NAMA_FOLDER = \"data_gambar\" # Nama folder penyimpanan\n",
|
|
"UKURAN_BOX = 300 # Ukuran kotak ROI (piksel)\n",
|
|
"\n",
|
|
"# 1. CEK & BUAT FOLDER JIKA BELUM ADA\n",
|
|
"if not os.path.exists(NAMA_FOLDER):\n",
|
|
" os.makedirs(NAMA_FOLDER)\n",
|
|
" print(f\"Folder '{NAMA_FOLDER}' berhasil dibuat!\")\n",
|
|
"else:\n",
|
|
" print(f\"Folder '{NAMA_FOLDER}' sudah ada. Gambar akan disimpan di sana.\")\n",
|
|
"\n",
|
|
"# 2. SETUP KAMERA\n",
|
|
"cap = cv2.VideoCapture(0)\n",
|
|
"\n",
|
|
"print(\"\\n--- INSTRUKSI ---\")\n",
|
|
"print(\"Tekan 'S' untuk menyimpan gambar (Crop)\")\n",
|
|
"print(\"Tekan 'Q' untuk keluar\")\n",
|
|
"\n",
|
|
"while True:\n",
|
|
" ret, frame = cap.read()\n",
|
|
" if not ret:\n",
|
|
" print(\"Gagal membaca kamera\")\n",
|
|
" break\n",
|
|
"\n",
|
|
" # Flip frame (efek cermin)\n",
|
|
" frame = cv2.flip(frame, 1)\n",
|
|
"\n",
|
|
" # 3. HITUNG KOORDINAT ROI (TENGAH)\n",
|
|
" height, width, _ = frame.shape\n",
|
|
" x1 = (width - UKURAN_BOX) // 2\n",
|
|
" y1 = (height - UKURAN_BOX) // 2\n",
|
|
" x2 = x1 + UKURAN_BOX\n",
|
|
" y2 = y1 + UKURAN_BOX\n",
|
|
"\n",
|
|
" # 4. GAMBAR KOTAK PANDUAN (HIJAU)\n",
|
|
" # Ini hanya visualisasi, tidak ikut tersimpan saat di-crop nanti\n",
|
|
" cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)\n",
|
|
" \n",
|
|
" # Tambahkan teks instruksi di layar\n",
|
|
" cv2.putText(frame, \"Tekan 'S' untuk Simpan\", (x1, y2 + 25), \n",
|
|
" cv2.FONT_HERSHEY_SIMPLEX, 0.6, (0, 255, 0), 1)\n",
|
|
"\n",
|
|
" # Tampilkan layar\n",
|
|
" cv2.imshow('Kamera Pengumpul Data', frame)\n",
|
|
"\n",
|
|
" # 5. BACA TOMBOL KEYBOARD\n",
|
|
" key = cv2.waitKey(1) & 0xFF\n",
|
|
"\n",
|
|
" # --- LOGIKA SIMPAN (CROP) ---\n",
|
|
" if key == ord('s') or key == ord('S'):\n",
|
|
" # Ambil potongan gambar asli (bersih tanpa garis kotak hijau)\n",
|
|
" # Kita ambil dari koordinat yang sudah dihitung di atas\n",
|
|
" roi_crop = frame[y1:y2, x1:x2]\n",
|
|
"\n",
|
|
" # Buat nama file unik (berdasarkan waktu detik ini)\n",
|
|
" timestamp = time.strftime(\"%Y%m%d_%H%M%S\")\n",
|
|
" filename = f\"{NAMA_FOLDER}/gambar_{timestamp}.jpg\"\n",
|
|
"\n",
|
|
" # Simpan ke folder\n",
|
|
" cv2.imwrite(filename, roi_crop)\n",
|
|
" print(f\"Berhasil disimpan: {filename}\")\n",
|
|
" \n",
|
|
" # Efek visual sesaat (layar berkedip putih sedikit tanda tersimpan) - Opsional\n",
|
|
" cv2.putText(frame, \"TERSIMPAN!\", (x1, y1-10), \n",
|
|
" cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 255), 2)\n",
|
|
" cv2.imshow('Kamera Pengumpul Data', frame)\n",
|
|
" cv2.waitKey(200) # Jeda sebentar biar tulisan terbaca\n",
|
|
"\n",
|
|
" # Keluar program\n",
|
|
" elif key == ord('q'):\n",
|
|
" break\n",
|
|
"\n",
|
|
"cap.release()\n",
|
|
"cv2.destroyAllWindows()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 12,
|
|
"id": "724ac841",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Sedang memuat model... (tunggu sebentar)\n",
|
|
"Model siap!\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import cv2\n",
|
|
"import numpy as np\n",
|
|
"import tensorflow as tf\n",
|
|
"from tensorflow.keras.applications.mobilenet_v2 import MobileNetV2, preprocess_input, decode_predictions\n",
|
|
"from tensorflow.keras.preprocessing import image\n",
|
|
"\n",
|
|
"# 1. INISIALISASI MODEL\n",
|
|
"print(\"Sedang memuat model... (tunggu sebentar)\")\n",
|
|
"model = MobileNetV2(weights='imagenet', include_top=True)\n",
|
|
"print(\"Model siap!\")\n",
|
|
"\n",
|
|
"# 2. SETUP KAMERA\n",
|
|
"cap = cv2.VideoCapture(0) \n",
|
|
"\n",
|
|
"# Ukuran kotak ROI\n",
|
|
"BOX_SIZE = 300 \n",
|
|
"\n",
|
|
"while True:\n",
|
|
" ret, frame = cap.read()\n",
|
|
" if not ret:\n",
|
|
" break\n",
|
|
"\n",
|
|
" # Flip frame (efek cermin)\n",
|
|
" frame = cv2.flip(frame, 1)\n",
|
|
"\n",
|
|
" # Ambil ukuran frame\n",
|
|
" height, width, _ = frame.shape\n",
|
|
" \n",
|
|
" # Hitung koordinat tengah untuk kotak hijau\n",
|
|
" x1 = (width - BOX_SIZE) // 2\n",
|
|
" y1 = (height - BOX_SIZE) // 2\n",
|
|
" x2 = x1 + BOX_SIZE\n",
|
|
" y2 = y1 + BOX_SIZE\n",
|
|
"\n",
|
|
" # --- PROSES AI (Latar Belakang) ---\n",
|
|
" # Kita tetap perlu crop gambar untuk diserahkan ke AI,\n",
|
|
" # tapi tidak kita tempel/tampilkan ke layar utama.\n",
|
|
" roi_frame = frame[y1:y2, x1:x2]\n",
|
|
"\n",
|
|
" if roi_frame.size != 0:\n",
|
|
" # Resize ke 224x224 (Wajib untuk MobileNet)\n",
|
|
" img_input = cv2.resize(roi_frame, (224, 224))\n",
|
|
" \n",
|
|
" # Konversi ke array & preprocess\n",
|
|
" img_array = image.img_to_array(img_input)\n",
|
|
" img_array = np.expand_dims(img_array, axis=0)\n",
|
|
" img_array = preprocess_input(img_array)\n",
|
|
"\n",
|
|
" # Prediksi\n",
|
|
" preds = model.predict(img_array, verbose=0)\n",
|
|
" results = decode_predictions(preds, top=1)[0]\n",
|
|
" \n",
|
|
" # Ambil Label & Akurasi\n",
|
|
" label = results[0][1]\n",
|
|
" confidence = results[0][2] * 100\n",
|
|
"\n",
|
|
" # Tampilkan Teks Hasil Prediksi di atas kotak\n",
|
|
" text_display = f\"{label}: {confidence:.1f}%\"\n",
|
|
" cv2.putText(frame, text_display, (x1, y1 - 10), \n",
|
|
" cv2.FONT_HERSHEY_SIMPLEX, 0.7, (0, 255, 0), 2)\n",
|
|
"\n",
|
|
" # --- VISUALISASI UTAMA ---\n",
|
|
" # Hanya gambar kotak hijau panduan\n",
|
|
" cv2.rectangle(frame, (x1, y1), (x2, y2), (0, 255, 0), 2)\n",
|
|
" \n",
|
|
" # Tampilkan layar utama saja\n",
|
|
" cv2.imshow('MobileNetV2 Clean View', frame)\n",
|
|
"\n",
|
|
" if cv2.waitKey(1) & 0xFF == ord('q'):\n",
|
|
" break\n",
|
|
"\n",
|
|
"cap.release()\n",
|
|
"cv2.destroyAllWindows()"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 16,
|
|
"id": "95017f76",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Memulai proses download ke folder 'dataset_botol_bewarna_bing'...\n",
|
|
"\n",
|
|
"--> Sedang mencari gambar: Super O2\n",
|
|
"[%] Downloading Images to g:\\My Drive\\Smart-Bin\\dataset\\dataset_botol_bewarna_bing\\Super O2\n",
|
|
"[!] Issue getting: https://img.21food.com/img/product/2012/10/25/indogalung-01510470.jpg\n",
|
|
"[!] Error:: Remote end closed connection without response\n",
|
|
"[Error]Invalid image, not saving https://lookaside.fbsbx.com/lookaside/crawler/media/?media_id=4116299515060023\n",
|
|
"\n",
|
|
"[!] Issue getting: https://lookaside.fbsbx.com/lookaside/crawler/media/?media_id=4116299515060023\n",
|
|
"[!] Error:: Invalid image, not saving https://lookaside.fbsbx.com/lookaside/crawler/media/?media_id=4116299515060023\n",
|
|
"\n",
|
|
"[Error]Invalid image, not saving https://down-id.img.susercontent.com/file/039e5682e363425a1b87b8a725a4253e\n",
|
|
"\n",
|
|
"[!] Issue getting: https://down-id.img.susercontent.com/file/039e5682e363425a1b87b8a725a4253e\n",
|
|
"[!] Error:: Invalid image, not saving https://down-id.img.susercontent.com/file/039e5682e363425a1b87b8a725a4253e\n",
|
|
"\n",
|
|
"[Error]Invalid image, not saving https://cf.shopee.co.id/file/734aeebfee8922e2c5ec88523cb5e6e5_tn\n",
|
|
"\n",
|
|
"[!] Issue getting: https://cf.shopee.co.id/file/734aeebfee8922e2c5ec88523cb5e6e5_tn\n",
|
|
"[!] Error:: Invalid image, not saving https://cf.shopee.co.id/file/734aeebfee8922e2c5ec88523cb5e6e5_tn\n",
|
|
"\n",
|
|
"[Error]Invalid image, not saving https://down-id.img.susercontent.com/file/9bd8bd58dc85b83e4944981ce2f97e73\n",
|
|
"\n",
|
|
"[!] Issue getting: https://down-id.img.susercontent.com/file/9bd8bd58dc85b83e4944981ce2f97e73\n",
|
|
"[!] Error:: Invalid image, not saving https://down-id.img.susercontent.com/file/9bd8bd58dc85b83e4944981ce2f97e73\n",
|
|
"\n",
|
|
"[!] Issue getting: https://ceklist.id/wp-content/uploads/2023/12/2858-super-o2-air-mineral-sportivo-600ml-1-370x370.jpg\n",
|
|
"[!] Error:: <urlopen error [WinError 10060] A connection attempt failed because the connected party did not properly respond after a period of time, or established connection failed because connected host has failed to respond>\n",
|
|
"[Error]Invalid image, not saving https://down-id.img.susercontent.com/file/sg-11134201-23010-r89m6d1to1lv22\n",
|
|
"\n",
|
|
"[!] Issue getting: https://down-id.img.susercontent.com/file/sg-11134201-23010-r89m6d1to1lv22\n",
|
|
"[!] Error:: Invalid image, not saving https://down-id.img.susercontent.com/file/sg-11134201-23010-r89m6d1to1lv22\n",
|
|
"\n",
|
|
"[Error]Invalid image, not saving https://down-id.img.susercontent.com/file/734aeebfee8922e2c5ec88523cb5e6e5\n",
|
|
"\n",
|
|
"[!] Issue getting: https://down-id.img.susercontent.com/file/734aeebfee8922e2c5ec88523cb5e6e5\n",
|
|
"[!] Error:: Invalid image, not saving https://down-id.img.susercontent.com/file/734aeebfee8922e2c5ec88523cb5e6e5\n",
|
|
"\n",
|
|
"[Error]Invalid image, not saving https://down-id.img.susercontent.com/file/0859ac0c9a420144d06c20bd2a16d154\n",
|
|
"\n",
|
|
"[!] Issue getting: https://down-id.img.susercontent.com/file/0859ac0c9a420144d06c20bd2a16d154\n",
|
|
"[!] Error:: Invalid image, not saving https://down-id.img.susercontent.com/file/0859ac0c9a420144d06c20bd2a16d154\n",
|
|
"\n",
|
|
"\n",
|
|
"\n",
|
|
"[%] Done. Downloaded 50 images.\n",
|
|
" Selesai download Super O2!\n",
|
|
"\n",
|
|
"========================================\n",
|
|
"SEMUA SELESAI!\n",
|
|
"Cek folder 'dataset_botol_bewarna_bing' di komputer Anda.\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"from bing_image_downloader import downloader\n",
|
|
"import os\n",
|
|
"\n",
|
|
"# --- KONFIGURASI ---\n",
|
|
"# 1. Tentukan folder penyimpanan utama\n",
|
|
"NAMA_FOLDER_UTAMA = \"dataset_botol_bewarna_bing\"\n",
|
|
"\n",
|
|
"# 2. Tentukan kata kunci benda yang mau dicari\n",
|
|
"# Format: \"Kata Kunci\"\n",
|
|
"daftar_pencarian = [\n",
|
|
" \"Super O2\"\n",
|
|
"]\n",
|
|
"\n",
|
|
"# 3. Berapa banyak gambar per benda?\n",
|
|
"JUMLAH_GAMBAR = 50 # Jangan terlalu banyak dulu untuk tes (max 100-200 aman)\n",
|
|
"\n",
|
|
"# --- MULAI DOWNLOAD ---\n",
|
|
"print(f\"Memulai proses download ke folder '{NAMA_FOLDER_UTAMA}'...\\n\")\n",
|
|
"\n",
|
|
"for kata_kunci in daftar_pencarian:\n",
|
|
" print(f\"--> Sedang mencari gambar: {kata_kunci}\")\n",
|
|
" \n",
|
|
" downloader.download(\n",
|
|
" kata_kunci, \n",
|
|
" limit=JUMLAH_GAMBAR, \n",
|
|
" output_dir=NAMA_FOLDER_UTAMA, \n",
|
|
" adult_filter_off=True, # Matikan filter konten dewasa (aman untuk objek umum)\n",
|
|
" force_replace=False, \n",
|
|
" timeout=60, \n",
|
|
" verbose=False\n",
|
|
" )\n",
|
|
" print(f\" Selesai download {kata_kunci}!\\n\")\n",
|
|
"\n",
|
|
"print(\"=\"*40)\n",
|
|
"print(\"SEMUA SELESAI!\")\n",
|
|
"print(f\"Cek folder '{NAMA_FOLDER_UTAMA}' di komputer Anda.\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 23,
|
|
"id": "643a9a12",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Ditemukan 339 gambar asli.\n",
|
|
"Memproses dengan Threading...\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stderr",
|
|
"output_type": "stream",
|
|
"text": [
|
|
" 27%|██▋ | 90/339 [04:21<04:26, 1.07s/it] c:\\Users\\MSI-PC\\miniconda3\\envs\\skripsi\\lib\\site-packages\\PIL\\Image.py:1034: UserWarning: Palette images with Transparency expressed in bytes should be converted to RGBA images\n",
|
|
" warnings.warn(\n",
|
|
"100%|██████████| 339/339 [06:42<00:00, 1.19s/it]"
|
|
]
|
|
},
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"\n",
|
|
"SUKSES!\n",
|
|
"Cek hasil di folder: dataset_augmented/botol_plastik_PET_bewarna\n"
|
|
]
|
|
},
|
|
{
|
|
"name": "stderr",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import os\n",
|
|
"import cv2\n",
|
|
"import numpy as np\n",
|
|
"from tensorflow.keras.preprocessing.image import ImageDataGenerator, img_to_array, load_img\n",
|
|
"# GANTI DARI ProcessPoolExecutor KE ThreadPoolExecutor\n",
|
|
"from concurrent.futures import ThreadPoolExecutor \n",
|
|
"from tqdm import tqdm \n",
|
|
"\n",
|
|
"# --- KONFIGURASI ---\n",
|
|
"FOLDER_SUMBER = \"botol_plastik_PET_bewarna2\"\n",
|
|
"FOLDER_HASIL = \"dataset_augmented/botol_plastik_PET_bewarna\"\n",
|
|
"JUMLAH_COPY = 5\n",
|
|
"NAMA_BASE = \"botol_pet\"\n",
|
|
"# Threading lebih ringan, kita bisa set angka aman misal 4 atau 8 worker\n",
|
|
"MAX_WORKERS = 4 \n",
|
|
"\n",
|
|
"if not os.path.exists(FOLDER_HASIL):\n",
|
|
" os.makedirs(FOLDER_HASIL)\n",
|
|
"\n",
|
|
"# Fungsi Worker \n",
|
|
"def proses_gambar_spesifik(data):\n",
|
|
" filepath, index_awal = data\n",
|
|
" \n",
|
|
" try:\n",
|
|
" # Load gambar\n",
|
|
" img = load_img(filepath)\n",
|
|
" x = img_to_array(img)\n",
|
|
" x = x.reshape((1,) + x.shape)\n",
|
|
"\n",
|
|
" # Definisikan ImageDataGenerator DI DALAM fungsi agar thread-safe\n",
|
|
" datagen = ImageDataGenerator(\n",
|
|
" rotation_range=30,\n",
|
|
" width_shift_range=0.2,\n",
|
|
" height_shift_range=0.2,\n",
|
|
" shear_range=0.2,\n",
|
|
" zoom_range=0.2,\n",
|
|
" horizontal_flip=True,\n",
|
|
" fill_mode='nearest'\n",
|
|
" )\n",
|
|
" \n",
|
|
" # Loop generate gambar\n",
|
|
" i = 0\n",
|
|
" for batch in datagen.flow(x, batch_size=1):\n",
|
|
" gambar_aug = batch[0].astype('uint8') \n",
|
|
" gambar_aug = cv2.cvtColor(gambar_aug, cv2.COLOR_RGB2BGR)\n",
|
|
" \n",
|
|
" nomor_urut = index_awal + i\n",
|
|
" nama_file_baru = f\"{NAMA_BASE}_{nomor_urut:05d}.jpg\"\n",
|
|
" path_simpan = os.path.join(FOLDER_HASIL, nama_file_baru)\n",
|
|
" \n",
|
|
" cv2.imwrite(path_simpan, gambar_aug)\n",
|
|
" \n",
|
|
" i += 1\n",
|
|
" if i >= JUMLAH_COPY:\n",
|
|
" break\n",
|
|
" \n",
|
|
" except Exception as e:\n",
|
|
" print(f\"Error pada {filepath}: {e}\")\n",
|
|
"\n",
|
|
"# --- MAIN PROGRAM ---\n",
|
|
"if __name__ == '__main__':\n",
|
|
" semua_file = [f for f in os.listdir(FOLDER_SUMBER) \n",
|
|
" if f.lower().endswith(('.png', '.jpg', '.jpeg'))]\n",
|
|
" semua_file.sort()\n",
|
|
" \n",
|
|
" print(f\"Ditemukan {len(semua_file)} gambar asli.\")\n",
|
|
" print(f\"Memproses dengan Threading...\")\n",
|
|
"\n",
|
|
" tasks = []\n",
|
|
" counter = 1\n",
|
|
" \n",
|
|
" for nama_file in semua_file:\n",
|
|
" full_path = os.path.join(FOLDER_SUMBER, nama_file)\n",
|
|
" tasks.append((full_path, counter))\n",
|
|
" counter += JUMLAH_COPY\n",
|
|
"\n",
|
|
" # GUNAKAN ThreadPoolExecutor\n",
|
|
" with ThreadPoolExecutor(max_workers=MAX_WORKERS) as executor:\n",
|
|
" list(tqdm(executor.map(proses_gambar_spesifik, tasks), total=len(tasks)))\n",
|
|
"\n",
|
|
" print(\"\\nSUKSES!\")\n",
|
|
" print(f\"Cek hasil di folder: {FOLDER_HASIL}\")"
|
|
]
|
|
},
|
|
{
|
|
"cell_type": "code",
|
|
"execution_count": 24,
|
|
"id": "f865eb1c",
|
|
"metadata": {},
|
|
"outputs": [
|
|
{
|
|
"name": "stdout",
|
|
"output_type": "stream",
|
|
"text": [
|
|
"Ditemukan 1695 file. Memulai rename...\n",
|
|
"Selesai! Semua file sudah direname.\n"
|
|
]
|
|
}
|
|
],
|
|
"source": [
|
|
"import os\n",
|
|
"\n",
|
|
"# --- KONFIGURASI ---\n",
|
|
"FOLDER_TARGET = \"dataset_augmented/botol_plastik_PET_bewarna\" # Ganti dengan nama foldermu\n",
|
|
"NAMA_BARU = \"botol_pet_bewarna\" # Awalan nama file baru\n",
|
|
"\n",
|
|
"# 1. Ambil semua file gambar\n",
|
|
"files = [f for f in os.listdir(FOLDER_TARGET) \n",
|
|
" if f.lower().endswith(('.png', '.jpg', '.jpeg'))]\n",
|
|
"\n",
|
|
"# 2. Urutkan dulu (biar tidak acak)\n",
|
|
"files.sort()\n",
|
|
"\n",
|
|
"print(f\"Ditemukan {len(files)} file. Memulai rename...\")\n",
|
|
"\n",
|
|
"# 3. Proses Rename\n",
|
|
"for index, nama_file_lama in enumerate(files):\n",
|
|
" # Ambil ekstensi file asli (.jpg atau .png)\n",
|
|
" ekstensi = os.path.splitext(nama_file_lama)[1]\n",
|
|
" \n",
|
|
" # Buat nama baru dengan format 5 digit angka (00001)\n",
|
|
" # index + 1 agar mulai dari 1, bukan 0\n",
|
|
" nama_file_baru = f\"{NAMA_BARU}_{index + 1:05d}{ekstensi}\"\n",
|
|
" \n",
|
|
" # Gabungkan dengan path folder\n",
|
|
" path_lama = os.path.join(FOLDER_TARGET, nama_file_lama)\n",
|
|
" path_baru = os.path.join(FOLDER_TARGET, nama_file_baru)\n",
|
|
" \n",
|
|
" # Lakukan rename\n",
|
|
" try:\n",
|
|
" os.rename(path_lama, path_baru)\n",
|
|
" except OSError as e:\n",
|
|
" print(f\"Error pada {nama_file_lama}: {e}\")\n",
|
|
"\n",
|
|
"print(\"Selesai! Semua file sudah direname.\")"
|
|
]
|
|
}
|
|
],
|
|
"metadata": {
|
|
"kernelspec": {
|
|
"display_name": "skripsi",
|
|
"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.19"
|
|
}
|
|
},
|
|
"nbformat": 4,
|
|
"nbformat_minor": 5
|
|
}
|