{ "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:: \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 }