{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":129750,"databundleVersionId":15563344,"sourceType":"competition"},{"sourceId":14844923,"sourceType":"datasetVersion","datasetId":9494612}],"dockerImageVersionId":31259,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip -q install ensemble-boxes pandas numpy\n\nimport pandas as pd\nimport numpy as np\nfrom pathlib import Path\nfrom ensemble_boxes import weighted_boxes_fusion\n\n# 1) Put your submission files here\nsub_paths = [\n    \"/kaggle/input/datasets/asalhi/dal-shemagh-challenge-subs/submission740.csv\",\n    \"/kaggle/input/datasets/asalhi/dal-shemagh-challenge-subs/submission753.csv\",\n] #can add more\n\n# 2) Optional model weights (same length as sub_paths)\nweights = [0.3,0.7] # can add more , change weights \n\n# 3) WBF params\niou_thr = 0.7\nskip_box_thr = 0.0001\n\ndef parse_prediction_string(ps):\n    if pd.isna(ps):\n        return [], [], []\n    ps = str(ps).strip()\n    if ps in (\"\", \"-\"):\n        return [], [], []\n\n    toks = ps.split()\n    if len(toks) % 6 != 0:\n        raise ValueError(f\"Invalid prediction_string: {ps}\")\n\n    boxes, scores, labels = [], [], []\n    for i in range(0, len(toks), 6):\n        cls = int(float(toks[i]))\n        score = float(toks[i + 1])\n        xc = float(toks[i + 2])\n        yc = float(toks[i + 3])\n        w = float(toks[i + 4])\n        h = float(toks[i + 5])\n\n        x1 = max(0.0, xc - w / 2.0)\n        y1 = max(0.0, yc - h / 2.0)\n        x2 = min(1.0, xc + w / 2.0)\n        y2 = min(1.0, yc + h / 2.0)\n\n        if x2 <= x1 or y2 <= y1:\n            continue\n\n        boxes.append([x1, y1, x2, y2])\n        scores.append(score)\n        labels.append(cls)\n\n    return boxes, scores, labels\n\ndef format_prediction_string(boxes, scores, labels, nd=6):\n    if len(boxes) == 0:\n        return \"-\"\n    items = sorted(zip(boxes, scores, labels), key=lambda x: x[1], reverse=True)\n    out = []\n    for b, s, l in items:\n        x1, y1, x2, y2 = b\n        xc = (x1 + x2) / 2.0\n        yc = (y1 + y2) / 2.0\n        w = max(0.0, x2 - x1)\n        h = max(0.0, y2 - y1)\n        out += [str(int(l)), f\"{float(s):.{nd}f}\", f\"{xc:.{nd}f}\", f\"{yc:.{nd}f}\", f\"{w:.{nd}f}\", f\"{h:.{nd}f}\"]\n    return \" \".join(out)\n\n# Load and validate\ndfs = [pd.read_csv(p) for p in sub_paths]\nrequired_cols = {\"filename\", \"right_place\", \"prediction_string\"}\nfor p, df in zip(sub_paths, dfs):\n    miss = required_cols - set(df.columns)\n    if miss:\n        raise ValueError(f\"{p} missing columns: {miss}\")\n\n# Ensure same filename set\nbase_files = set(dfs[0][\"filename\"].tolist())\nfor i in range(1, len(dfs)):\n    if set(dfs[i][\"filename\"].tolist()) != base_files:\n        raise ValueError(f\"Filename mismatch between {sub_paths[0]} and {sub_paths[i]}\")\n\n# Index for fast lookup\nidx_dfs = [df.set_index(\"filename\")[[\"right_place\", \"prediction_string\"]] for df in dfs]\n\ndef fname_sort_key(x):\n    stem = Path(x).stem\n    return int(stem) if stem.isdigit() else stem\n\nrows = []\nfor fname in sorted(base_files, key=fname_sort_key):\n    all_boxes, all_scores, all_labels = [], [], []\n    rp_vals = []\n\n    for df in idx_dfs:\n        rp_vals.append(int(df.at[fname, \"right_place\"]))\n        b, s, l = parse_prediction_string(df.at[fname, \"prediction_string\"])\n        all_boxes.append(b)\n        all_scores.append(s)\n        all_labels.append(l)\n\n    # Detection ensemble\n    boxes, scores, labels = weighted_boxes_fusion(\n        all_boxes, all_scores, all_labels,\n        weights=weights,\n        iou_thr=iou_thr,\n        skip_box_thr=skip_box_thr\n    )\n\n    # right_place ensemble: weighted vote\n    rp = int(np.average(rp_vals, weights=weights) >= 0.5)\n\n    pred_str = format_prediction_string(boxes, scores, labels)\n    rows.append((fname, rp, pred_str))\n\nensemble_df = pd.DataFrame(rows, columns=[\"filename\", \"right_place\", \"prediction_string\"])\nout_path = \"submission.csv\"\nensemble_df.to_csv(out_path, index=False)\n\nprint(\"Saved:\", out_path, \"shape:\", ensemble_df.shape)\ndisplay(ensemble_df.head())\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}