{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceType":"competition","sourceId":4117,"databundleVersionId":46665},{"sourceType":"datasetVersion","sourceId":3161014,"datasetId":1917019,"databundleVersionId":3210346}],"dockerImageVersionId":31328,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import os\nimport glob\nimport numpy as np\nfrom PIL import Image\nimport zipfile\nimport shutil\nfrom concurrent.futures import ProcessPoolExecutor\nfrom tqdm import tqdm\nimport warnings\n\n# Import thư viện xử lý ảnh cho phương pháp RGB đa kênh\nfrom skimage.filters.rank import entropy\nfrom skimage.feature import local_binary_pattern\nfrom skimage.morphology import disk\n\nwarnings.filterwarnings(\"ignore\")\n\n# ==========================================\n# CẤU HÌNH ĐƯỜNG DẪN & THÔNG SỐ\n# ==========================================\nINPUT_DIR = \"/kaggle/input/datasets/songwonmin/malware-only-byte/only_byte\"  \nOUTPUT_DIR = \"/kaggle/working/train_images_gray\" # Đổi tên cho tổng quát\nTEMP_DIR = \"/kaggle/working/temp_images\"\n\nBATCH_SIZE = 1000\nNUM_WORKERS = 4\n\n# TÙY CHỌN CHẾ ĐỘ XUẤT ẢNH: Chọn 'RGB' hoặc 'GRAY'\nIMAGE_MODE = 'GRAY' \n\nos.makedirs(OUTPUT_DIR, exist_ok=True)\n\n# ==========================================\n# HÀM BỔ TRỢ: TÍNH CHIỀU RỘNG ẢNH ĐỘNG (Theo Nataraj)\n# ==========================================\ndef get_dynamic_width(file_size_bytes):\n    # Đổi dung lượng từ byte sang Kilobyte (kB)\n    kb = file_size_bytes / 1024.0\n    \n    if kb < 10:\n        return 32\n    elif kb < 30:\n        return 64\n    elif kb < 60:\n        return 128\n    elif kb < 100:\n        return 256\n    elif kb < 200:\n        return 384\n    elif kb < 500:\n        return 512\n    elif kb < 1000:\n        return 768\n    else: # >1000 kB\n        return 1024\n\n# ==========================================\n# HÀM CHUYỂN ĐỔI GRAYSCALE SANG RGB ĐA KÊNH\n# ==========================================\ndef to_rgb_features(gray_img):\n    # Kênh R: Ảnh gốc (Pixel-Proxy)\n    R = gray_img.copy()\n    \n    # Kênh G: Shannon Entropy (cửa sổ 16px)\n    G = entropy(gray_img, disk(8))\n    g_max = G.max()\n    if g_max > 0:\n        G = (G / g_max * 255).astype(np.uint8)\n    else:\n        G = G.astype(np.uint8)\n        \n    # Kênh B: Local Binary Pattern (Texture)\n    B = local_binary_pattern(gray_img, P=8, R=1, method='uniform')\n    b_max = B.max()\n    if b_max > 0:\n        B = (B / b_max * 255).astype(np.uint8)\n    else:\n        B = B.astype(np.uint8)\n        \n    return np.stack((R, G, B), axis=-1)\n\n# ==========================================\n# HÀM XỬ LÝ 1 FILE .BYTES\n# ==========================================\ndef process_single_byte_file(args):\n    filepath, temp_out_dir, mode = args\n    \n    try:\n        file_size = os.path.getsize(filepath)\n        if file_size == 0: return False\n        \n        byte_list = []\n        with open(filepath, 'r', encoding='utf-8', errors='ignore') as f:\n            for line in f:\n                tokens = line.strip().split()\n                for t in tokens:\n                    if len(t) == 2:\n                        if t == '??':\n                            byte_list.append(0)\n                        else:\n                            try:\n                                byte_list.append(int(t, 16))\n                            except ValueError:\n                                pass\n                                \n        if len(byte_list) < 256: \n            return False\n            \n        byte_arr = np.array(byte_list, dtype=np.uint8)\n        actual_size = len(byte_list)\n        width = get_dynamic_width(actual_size)\n        height = len(byte_arr) // width\n        \n        if height == 0: return False \n        \n        byte_arr = byte_arr[:height * width]\n        gray_matrix = np.reshape(byte_arr, (height, width))\n        \n        # --- THAY ĐỔI Ở ĐÂY: Xử lý theo Mode và KHÔNG Resize ---\n        if mode == 'RGB':\n            rgb_matrix = to_rgb_features(gray_matrix)\n            img = Image.fromarray(rgb_matrix, 'RGB')\n        elif mode == 'GRAY':\n            img = Image.fromarray(gray_matrix, 'L')\n        else:\n            raise ValueError(\"IMAGE_MODE chỉ hỗ trợ 'RGB' hoặc 'GRAY'\")\n        # ---------------------------------------------------------\n        \n        filename = os.path.basename(filepath).replace('.bytes', '.png')\n        out_path = os.path.join(temp_out_dir, filename)\n        img.save(out_path)\n        \n        return True\n        \n    except Exception as e:\n        print(f\"\\n[LỖI - {os.path.basename(filepath)}]: {str(e)}\")\n        return False\n\n# ==========================================\n# HÀM CHÍNH: QUẢN LÝ BATCH, ZIP & RESUME\n# ==========================================\ndef main():\n    all_files = glob.glob(os.path.join(INPUT_DIR, \"**/*.bytes\"), recursive=True)\n    all_files.sort() \n    \n    total_files = len(all_files)\n    print(f\"Tổng số file cần xử lý: {total_files}\")\n    print(f\"Chế độ xuất ảnh: {IMAGE_MODE}\")\n    \n    for i in range(0, total_files, BATCH_SIZE):\n        batch_files = all_files[i : i + BATCH_SIZE]\n        batch_id = i // BATCH_SIZE\n        \n        # Đổi tên file zip theo mode để không bị nhầm lẫn\n        zip_filename = os.path.join(OUTPUT_DIR, f\"dataset_{IMAGE_MODE.lower()}_batch_{batch_id:03d}.zip\")\n        \n        if os.path.exists(zip_filename):\n            print(f\"⏭️ Bỏ qua Batch {batch_id:03d} (Đã nén thành công trước đó)\")\n            continue\n            \n        print(f\"\\n⏳ Đang xử lý Batch {batch_id:03d} ({len(batch_files)} files)...\")\n        os.makedirs(TEMP_DIR, exist_ok=True)\n        \n        # Truyền biến IMAGE_MODE vào thay vì FIXED_IMG_SIZE\n        worker_args = [(f, TEMP_DIR, IMAGE_MODE) for f in batch_files]\n        \n        success_count = 0\n        with ProcessPoolExecutor(max_workers=NUM_WORKERS) as executor:\n            results = list(tqdm(executor.map(process_single_byte_file, worker_args), total=len(worker_args)))\n            success_count = sum(1 for r in results if r)\n            \n        print(f\"✅ Xử lý xong {success_count}/{len(batch_files)} files. Đang nén (Zipping)...\")\n        \n        with zipfile.ZipFile(zip_filename, 'w', zipfile.ZIP_DEFLATED) as zipf:\n            for root, _, files in os.walk(TEMP_DIR):\n                for file in files:\n                    file_path = os.path.join(root, file)\n                    zipf.write(file_path, arcname=file)\n                    \n        print(f\"📦 Đã lưu: {zip_filename}\")\n        shutil.rmtree(TEMP_DIR)\n\nif __name__ == \"__main__\":\n    main()","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-04-06T19:55:43.252397Z","iopub.execute_input":"2026-04-06T19:55:43.252770Z","iopub.status.idle":"2026-04-06T20:19:51.102761Z","shell.execute_reply.started":"2026-04-06T19:55:43.252736Z","shell.execute_reply":"2026-04-06T20:19:51.101505Z"}},"outputs":[],"execution_count":null}]}