{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.11","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":35150,"databundleVersionId":4957097,"sourceType":"competition"},{"sourceId":11975532,"sourceType":"datasetVersion","datasetId":7467640}],"dockerImageVersionId":31041,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"import matplotlib.pyplot as plt\nimport torch.nn.functional as F\nimport numpy as np\nimport torchvision\nimport itertools\nimport random\nimport psutil\nimport torch\n# import faiss # !pip install faiss-cpu faiss-gpu-cu12\nimport numba\nimport copy\nimport json\nimport time\nimport tqdm\nimport sys\nimport os","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T20:12:57.927619Z","iopub.execute_input":"2025-05-27T20:12:57.928377Z","iopub.status.idle":"2025-05-27T20:12:57.932590Z","shell.execute_reply.started":"2025-05-27T20:12:57.928350Z","shell.execute_reply":"2025-05-27T20:12:57.931834Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# SUPCON_RUNID, SUPCON_EPOCH_CNT = 1747037150, 300\n# SUPCON_RUNID, SUPCON_EPOCH_CNT = 1747991087, 50\n# SUPCON_RUNID, SUPCON_EPOCH_CNT = 1747991087, 150\n# SUPCON_RUNID, SUPCON_EPOCH_CNT = 1748116222, 150\n# SUPCON_RUNID, SUPCON_EPOCH_CNT = 1748172677, 150\nSUPCON_RUNID, SUPCON_EPOCH_CNT = 1748346687, 50\n\nBASE_SERVER_FOLDER = \"/kaggle/input\"\n\nDEVICE = torch.device(\"cuda:0\") if torch.cuda.is_available() else torch.device(\"cpu\")\n\nCOCO_CLASS_NAMES = [\"other\", \"chair\", \"couch\", \"bed\", \"dining table\", \"toilet\", \"tv\"]\nDSET_TYPES = [\"train\", \"val\"]\n\nUNET_IM_LEN = 256\nUNET_RESHAPER = torchvision.transforms.Resize(UNET_IM_LEN, antialias = None)\nUNET_NUM_CROP_TRIES = 3\n\nSUPCON_EMBEDDING_SIZE = 512\n\nINFERENCE_BATCH_SIZE = 128\nINFERENCE_MEAN_OVER_TOP = 4 # 10 # pentru un hotel_id, fac media peste cele mai bune ?? citiri.\nINFERENCE_NEED_TOP = 5 # cate hoteluri tin minte pentru un test image.\n\ndef print_used_memory():\n    free, total = torch.cuda.mem_get_info(DEVICE)\n    print(f\"Host memory used: {round(psutil.Process().memory_info().rss / (2 ** 30), 3)} GB.\\nDevice memory used: {round((total - free) / (2 ** 30), 3)} GB.\", flush = True)\n\ndef get_px(fpath: str, device = DEVICE):\n    im = torchvision.io.decode_image(fpath) / 255\n    im = UNET_RESHAPER(im)\n    if im.shape[0] == 1: # posibil ca imaginea primita sa fie uni-canal.\n        im = im.broadcast_to([3, *im.shape[1:]])\n    return im.to(device)\n\n# rdup_arr([0, 1, 3, 2, 5], 3) = [0, 0, 0, 1, 1, 1, 3, 3, 3, 2, 2, 2, 5, 5, 5]\ndef rdup_arr(arr: list, k: int):\n    return [y for x in arr for y in [x] * k]\n\ndef get_hotel_image_crop_offset(hotel_im: torch.tensor, crop_ind: int):\n    h, w = hotel_im.shape[1:]\n    diff = int(np.linspace(0, max(h - UNET_IM_LEN, w - UNET_IM_LEN), UNET_NUM_CROP_TRIES)[crop_ind])\n    dy, dx = (0, diff) if h == UNET_IM_LEN else (diff, 0)\n    return dy, dx\n\n@numba.jit(nopython=True)\ndef load_mmi_neighs(h: np.int32, w: np.int32, i: np.int32, j: np.int32, type_neighs: np.int32):\n    # return [(i+di, j+dj) for di, dj in dys_xs if 0 <= i + di < h and 0 <= j + dj < w]\n    sol = []\n    for di in range(-1, 2):\n        for dj in range(-1, 2):\n            if (type_neighs == 0 and abs(di) != abs(dj)) or (type_neighs == -1 and di != 0 and dj == 0) or (type_neighs == 1 and di == 0 and dj != 0):\n                if 0 <= i + di < h and 0 <= j + dj < w:\n                    sol.append((i+di, j+dj))\n    return sol\n\n@numba.jit(nopython=True)\ndef load_mmi_bfs(h: np.int32, w: np.int32, inf: np.int32, im: np.ndarray, mask: np.ndarray, base: np.ndarray, qus):\n    for type_neighs in [-1, 1]:\n        qus[0].clear()\n        qus[1].clear()\n\n        for i in range(h):\n            for j in range(w):\n                neighs = load_mmi_neighs(h, w, i, j, 0)\n                if len(neighs) > 0:\n                    has_mask_neighbor = False\n                    for y, x in neighs:\n                        if mask[y, x]:\n                            has_mask_neighbor = True\n                            break\n                    if has_mask_neighbor:\n                        qus[0].append((np.int32(i), np.int32(j)))\n\n        pin = 0\n        d = inf * mask\n\n        while len(qus[pin]):\n            for i, j in qus[pin]:\n                neighbors = load_mmi_neighs(h, w, i, j, type_neighs)\n                for y, x in neighbors:\n                    op_y, op_x = base[i, j, 0] - (y - base[i, j, 0]), base[i, j, 1] - (x - base[i, j, 1])\n                    if 0 <= op_y < h and 0 <= op_x < w and d[y, x] > 1 + d[i, j]:\n                        d[y, x] = 1 + d[i, j]\n                        im[:, y, x] += 0.5 * im[:, op_y, op_x]\n                        base[y, x] = base[i, j]\n                        qus[pin^1].append((np.int32(y), np.int32(x)))\n            qus[pin].clear()\n            pin ^= 1\n\ndef load_mirror_masked_image(fpath: str, device = DEVICE):\n    t_start = time.time()\n\n    im = get_px(fpath, device = torch.device(\"cpu\"))\n\n    mask = torch.logical_and(\n        torch.all(im.permute(1, 2, 0) >= torch.tensor([0.95, 0, 0]), dim = 2),\n        torch.all(im.permute(1, 2, 0) <= torch.tensor([1, 0.1, 0.1]), dim = 2),\n    )\n\n    # dilatam imaginea. e posibil sa ramana dungi rosii pe margine daca aplicam doar masca normala.\n    mask = F.conv2d(\n        mask.unsqueeze(dim = 0).unsqueeze(dim = 0).float(),\n        torch.ones(1, 1, 9, 9),\n        padding = \"same\"\n    )[0, 0] > 0\n    \n    # im.shape = [3, ?, ??], cu min(?, ??) = 256.\n    # mask.shape = [?, ??]. mask[i, j] = True <=> trebuie inlocuit pixelul respectiv.\n    h, w = mask.shape\n    \n    base = np.array([[(i, j) for j in range(w)] for i in range(h)], dtype = np.int32)\n    inf = np.int32(mask.shape[0] * mask.shape[1] + 1)\n    im, mask = im.numpy(), mask.numpy()\n    im[:, mask] = 0\n\n    # print(f\"starting bfs related after {time.time() - t_start} s.\", flush = True)\n\n    list_type = numba.types.UniTuple(numba.types.int32, 2) # numba.types.Tuple((numba.types.int32, numba.types.int32))\n    qus = [numba.typed.List.empty_list(list_type), numba.typed.List.empty_list(list_type)]\n    \n    load_mmi_bfs(h, w, inf, im, mask, base, qus)\n    im = np.minimum(np.ones_like(im), im)\n\n    # print(f\"finished bfs after {time.time() - t_start} s.\", flush = True)\n    \n    return torch.tensor(im, device = DEVICE)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T20:12:59.341754Z","iopub.execute_input":"2025-05-27T20:12:59.342071Z","iopub.status.idle":"2025-05-27T20:12:59.366928Z","shell.execute_reply.started":"2025-05-27T20:12:59.342050Z","shell.execute_reply":"2025-05-27T20:12:59.366173Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# !ls /kaggle/input/hotel-id-supcon-embeddings/supcon_embeddings | wc -l\n# !ls /kaggle/input/hotel-id-to-combat-human-trafficking-2022-fgvc9/test_images\n# !ls -l /kaggle/input/hotel-id-to-combat-human-trafficking-2022-fgvc9/train_masks | wc -l","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-21T08:50:00.050997Z","iopub.execute_input":"2025-05-21T08:50:00.051777Z","iopub.status.idle":"2025-05-21T08:50:00.063090Z","shell.execute_reply.started":"2025-05-21T08:50:00.051756Z","shell.execute_reply":"2025-05-21T08:50:00.062548Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# load supcon resnet.\nt_start = time.time()\n\nnet = torchvision.models.resnet18() # weights = \"DEFAULT\"\n\n# reteaua are un singur strat FC, il inlocuiesc.\nnet.fc = torch.nn.Linear(net.fc.in_features, SUPCON_EMBEDDING_SIZE)\n\nnet = net.to(DEVICE)\n\nnet.load_state_dict(torch.load(\n    f\"{BASE_SERVER_FOLDER}/hotel-id-supcon-embeddings/net_{SUPCON_RUNID}_{SUPCON_EPOCH_CNT}.pt\",\n    weights_only = True, map_location = DEVICE)\n)\nnet.eval()\n\nprint(f\"Loaded pretrained supcon net {SUPCON_RUNID = }, {SUPCON_EPOCH_CNT = }, {round(time.time() - t_start, 3)} s passed.\", flush = True)\nprint_used_memory()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T20:13:03.878372Z","iopub.execute_input":"2025-05-27T20:13:03.878651Z","iopub.status.idle":"2025-05-27T20:13:04.126566Z","shell.execute_reply.started":"2025-05-27T20:13:03.878630Z","shell.execute_reply":"2025-05-27T20:13:04.125844Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# load supcon embeddings.\nembeddings_all = []\nemb_hotel_ids = []\n\n# f\"{BASE_SERVER_FOLDER}/hotel-id-supcon-embeddings/supcon_embeddings_{SUPCON_RUNID}/\"\n\nfor root, dirs, files in os.walk(f\"{BASE_SERVER_FOLDER}/hotel-id-supcon-embeddings/supcon_embeddings_{SUPCON_RUNID}_{SUPCON_EPOCH_CNT}/\"):\n    for fname in tqdm.tqdm(files):\n        hotel_id = int(fname.split('_')[-1].split('.')[0])\n\n        try:\n            embeddings = torch.load(os.path.join(root, fname), weights_only = True, map_location = DEVICE)\n            embeddings = embeddings / torch.norm(embeddings, dim = 1).unsqueeze(dim = 1)        \n\n            embeddings_all.append(embeddings)\n            emb_hotel_ids.extend([hotel_id] * len(embeddings))\n        except:\n            print(f\"Failed to load {hotel_id = }\")\n\nembeddings_all = torch.cat(embeddings_all)\n\nprint(f\"{embeddings_all.shape = }\")\nprint_used_memory()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T20:13:05.718503Z","iopub.execute_input":"2025-05-27T20:13:05.719004Z","iopub.status.idle":"2025-05-27T20:13:25.856053Z","shell.execute_reply.started":"2025-05-27T20:13:05.718979Z","shell.execute_reply":"2025-05-27T20:13:25.855421Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"mirrored_test_images = []\ntest_ims_embeddings = []\ntest_fnames = []\n\nwith torch.no_grad():\n    for root, dirs, files in os.walk(f\"{BASE_SERVER_FOLDER}/hotel-id-to-combat-human-trafficking-2022-fgvc9/test_images\"):\n        for fname in tqdm.tqdm(files):\n            test_fnames.append(fname)\n            mirrored_im = load_mirror_masked_image(os.path.join(root, fname))\n    \n            for crop_ind in range(UNET_NUM_CROP_TRIES):\n                dy, dx = get_hotel_image_crop_offset(mirrored_im, crop_ind)        \n                mirrored_test_images.append(mirrored_im[:, dy: dy + UNET_IM_LEN, dx: dx + UNET_IM_LEN])\n\n            if len(mirrored_test_images) >= INFERENCE_BATCH_SIZE:\n                test_ims_embeddings.append(net(torch.stack(mirrored_test_images)))\n                del mirrored_test_images\n                mirrored_test_images = []\n\n    if len(mirrored_test_images) > 0:\n        test_ims_embeddings.append(net(torch.stack(mirrored_test_images)))\n        del mirrored_test_images\n    \n    test_ims_embeddings = torch.cat(test_ims_embeddings)\n    test_ims_embeddings = test_ims_embeddings / torch.norm(test_ims_embeddings, dim = 1).unsqueeze(dim = 1)\n    \n    print(f\"{test_ims_embeddings.shape = }\")\n    print_used_memory()","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T20:13:28.227414Z","iopub.execute_input":"2025-05-27T20:13:28.227808Z","iopub.status.idle":"2025-05-27T20:13:29.715586Z","shell.execute_reply.started":"2025-05-27T20:13:28.227778Z","shell.execute_reply":"2025-05-27T20:13:29.714958Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"test_sims = test_ims_embeddings @ embeddings_all.T\n\n# pun toate sim scores de la cropurile din aceeasi imagine pe acelasi rand.\ntest_sims = test_sims.view(-1, test_sims.shape[1] * UNET_NUM_CROP_TRIES).cpu().numpy()\n\nbest_hotel_id_matches = []\nfor i in tqdm.tqdm(range(len(test_sims))):\n    mean_hotel_id_scores = {}\n    for hotel_id, j in zip(emb_hotel_ids, itertools.count()):\n        if hotel_id not in mean_hotel_id_scores:\n            mean_hotel_id_scores[hotel_id] = []\n\n        mean_hotel_id_scores[hotel_id].append(test_sims[i, j])\n\n    best_scores_hotel_ids = sorted(\n        [(np.mean(sorted(mean_hotel_id_scores[hotel_id], reverse = True)[:INFERENCE_MEAN_OVER_TOP]), hotel_id) for hotel_id in mean_hotel_id_scores],\n        reverse = True\n    )[:INFERENCE_NEED_TOP]\n    \n    best_hotel_id_matches.append([hotel_id for mean, hotel_id in best_scores_hotel_ids])        ","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T20:13:32.400371Z","iopub.execute_input":"2025-05-27T20:13:32.401094Z","iopub.status.idle":"2025-05-27T20:13:32.521741Z","shell.execute_reply.started":"2025-05-27T20:13:32.401062Z","shell.execute_reply":"2025-05-27T20:13:32.521003Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"with open(\"submission.csv\", 'w') as fout:\n    fout.write(\"image_id,hotel_id\\n\")\n    for fname, hotel_matches in zip(test_fnames, best_hotel_id_matches):\n        fout.write(f\"{fname},{' '.join([str(hotel_id) for hotel_id in hotel_matches])}\\n\")","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T20:13:35.002401Z","iopub.execute_input":"2025-05-27T20:13:35.002645Z","iopub.status.idle":"2025-05-27T20:13:35.007343Z","shell.execute_reply.started":"2025-05-27T20:13:35.002628Z","shell.execute_reply":"2025-05-27T20:13:35.006676Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# !cat submission.csv","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-05-27T20:13:36.115635Z","iopub.execute_input":"2025-05-27T20:13:36.116492Z","iopub.status.idle":"2025-05-27T20:13:36.257695Z","shell.execute_reply.started":"2025-05-27T20:13:36.116460Z","shell.execute_reply":"2025-05-27T20:13:36.256770Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}