{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.10.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":71885,"databundleVersionId":8143495,"sourceType":"competition"},{"sourceId":8373646,"sourceType":"datasetVersion","datasetId":4978446},{"sourceId":8376262,"sourceType":"datasetVersion","datasetId":4980503},{"sourceId":8390185,"sourceType":"datasetVersion","datasetId":4990539}],"dockerImageVersionId":30664,"isInternetEnabled":false,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"'''Image Matching Challenge 2024 - Hexathlon | Metric Example Notebook'''\n\nimport time\nimport math\nimport numpy as np\nimport pandas as pd\nimport pandas.api.types\n\n_EPS = np.finfo(float).eps * 4.0\n\n# mAA evaluation thresholds per scene, different accoring to the scene\ntranslation_thresholds_meters_dict = {\n 'multi-temporal-temple-baalshamin':  np.array([0.025,  0.05,  0.1,  0.2,  0.5,  1.0]),\n 'pond':                              np.array([0.025,  0.05,  0.1,  0.2,  0.5,  1.0]),\n 'transp_obj_glass_cylinder':         np.array([0.0025, 0.005, 0.01, 0.02, 0.05, 0.1]),\n 'transp_obj_glass_cup':              np.array([0.0025, 0.005, 0.01, 0.02, 0.05, 0.1]),\n 'church':                            np.array([0.025,  0.05,  0.1,  0.2,  0.5,  1.0]),\n 'lizard':                            np.array([0.025,  0.05,  0.1,  0.2,  0.5,  1.0]),\n 'dioscuri':                          np.array([0.025,  0.05,  0.1,  0.2,  0.5,  1.0]), \n}\n\n\ndef vector_norm(data, axis=None, out=None):\n    '''Return length, i.e. Euclidean norm, of ndarray along axis.'''\n    data = np.array(data, dtype=np.float64, copy=True)\n    if out is None:\n        if data.ndim == 1:\n            return math.sqrt(np.dot(data, data))\n        data *= data\n        out = np.atleast_1d(np.sum(data, axis=axis))\n        np.sqrt(out, out)\n        return out\n    data *= data\n    np.sum(data, axis=axis, out=out)\n    np.sqrt(out, out)\n    return None\n\n\ndef quaternion_matrix(quaternion):\n    '''Return homogeneous rotation matrix from quaternion.'''\n    q = np.array(quaternion, dtype=np.float64, copy=True)\n    n = np.dot(q, q)\n    if n < _EPS:\n        # print(\"special case\")\n        return np.identity(4)\n    q *= math.sqrt(2.0 / n)\n    q = np.outer(q, q)\n    return np.array(\n        [\n            [\n                1.0 - q[2, 2] - q[3, 3],\n                q[1, 2] - q[3, 0],\n                q[1, 3] + q[2, 0],\n                0.0,\n            ],\n            [\n                q[1, 2] + q[3, 0],\n                1.0 - q[1, 1] - q[3, 3],\n                q[2, 3] - q[1, 0],\n                0.0,\n            ],\n            [\n                q[1, 3] - q[2, 0],\n                q[2, 3] + q[1, 0],\n                1.0 - q[1, 1] - q[2, 2],\n                0.0,\n            ],\n            [0.0, 0.0, 0.0, 1.0],\n        ]\n    )\n\n\n# based on the 3D registration from https://github.com/cgohlke/transformations\ndef affine_matrix_from_points(v0, v1, shear=False, scale=True, usesvd=True):\n    '''Return affine transform matrix to register two point sets.\n    v0 and v1 are shape (ndims, -1) arrays of at least ndims non-homogeneous\n    coordinates, where ndims is the dimensionality of the coordinate space.\n    If shear is False, a similarity transformation matrix is returned.\n    If also scale is False, a rigid/Euclidean traffansformation matrix\n    is returned.\n    By default the algorithm by Hartley and Zissermann [15] is used.\n    If usesvd is True, similarity and Euclidean transformation matrices\n    are calculated by minimizing the weighted sum of squared deviations\n    (RMSD) according to the algorithm by Kabsch [8].\n    Otherwise, and if ndims is 3, the quaternion based algorithm by Horn [9]\n    is used, which is slower when using this Python implementation.\n    The returned matrix performs rotation, translation and uniform scaling\n    (if specified).'''\n    \n    v0 = np.array(v0, dtype=np.float64, copy=True)\n    v1 = np.array(v1, dtype=np.float64, copy=True)\n\n    ndims = v0.shape[0]\n    if ndims < 2 or v0.shape[1] < ndims or v0.shape != v1.shape:\n        raise ValueError(\"input arrays are of wrong shape or type\")\n\n    # move centroids to origin\n    t0 = -np.mean(v0, axis=1)\n    M0 = np.identity(ndims + 1)\n    M0[:ndims, ndims] = t0\n    v0 += t0.reshape(ndims, 1)\n    t1 = -np.mean(v1, axis=1)\n    M1 = np.identity(ndims + 1)\n    M1[:ndims, ndims] = t1\n    v1 += t1.reshape(ndims, 1)\n\n    if shear:\n        # Affine transformation\n        A = np.concatenate((v0, v1), axis=0)\n        u, s, vh = np.linalg.svd(A.T)\n        vh = vh[:ndims].T\n        B = vh[:ndims]\n        C = vh[ndims: 2 * ndims]\n        t = np.dot(C, np.linalg.pinv(B))\n        t = np.concatenate((t, np.zeros((ndims, 1))), axis=1)\n        M = np.vstack((t, ((0.0,) * ndims) + (1.0,)))\n    elif usesvd or ndims != 3:\n        # Rigid transformation via SVD of covariance matrix\n        u, s, vh = np.linalg.svd(np.dot(v1, v0.T))\n        # rotation matrix from SVD orthonormal bases\n        R = np.dot(u, vh)\n        if np.linalg.det(R) < 0.0:\n            # R does not constitute right handed system\n            R -= np.outer(u[:, ndims - 1], vh[ndims - 1, :] * 2.0)\n            s[-1] *= -1.0\n        # homogeneous transformation matrix\n        M = np.identity(ndims + 1)\n        M[:ndims, :ndims] = R\n    else:\n        # Rigid transformation matrix via quaternion\n        # compute symmetric matrix N\n        xx, yy, zz = np.sum(v0 * v1, axis=1)\n        xy, yz, zx = np.sum(v0 * np.roll(v1, -1, axis=0), axis=1)\n        xz, yx, zy = np.sum(v0 * np.roll(v1, -2, axis=0), axis=1)\n        N = [\n            [xx + yy + zz, 0.0, 0.0, 0.0],\n            [yz - zy, xx - yy - zz, 0.0, 0.0],\n            [zx - xz, xy + yx, yy - xx - zz, 0.0],\n            [xy - yx, zx + xz, yz + zy, zz - xx - yy],\n        ]\n        # quaternion: eigenvector corresponding to most positive eigenvalue\n        w, V = np.linalg.eigh(N)\n        q = V[:, np.argmax(w)]\n        # print (vector_norm(q), np.linalg.norm(q))\n        q /= vector_norm(q)  # unit quaternion\n        # homogeneous transformation matrix\n        M = quaternion_matrix(q)\n\n    if scale and not shear:\n        # Affine transformation; scale is ratio of RMS deviations from centroid\n        v0 *= v0\n        v1 *= v1\n        M[:ndims, :ndims] *= math.sqrt(np.sum(v1) / np.sum(v0))\n\n    # move centroids back\n    M = np.dot(np.linalg.inv(M1), np.dot(M, M0))\n    M /= M[ndims, ndims]\n\n    # print(\"transformation matrix Python Script: \", M)\n\n    return M\n\n\n# This is the IMC 3D error metric code\ndef register_by_Horn(ev_coord, gt_coord, ransac_threshold, inl_cf, strict_cf):\n    '''Return the best similarity transforms T that registers 3D points pt_ev in <ev_coord> to\n    the corresponding ones pt_gt in <gt_coord> according to a RANSAC-like approach for each\n    threshold value th in <ransac_threshold>.\n    \n    Given th, each triplet of 3D correspondences is examined if not already present as strict inlier,\n    a correspondence is a strict inlier if <strict_cf> * err_best < th, where err_best is the registration\n    error for the best model so far.\n    The minimal model given by the triplet is then refined using also its inliers if their total is greater\n    than <inl_cf> * ninl_best, where ninl_best is th number of inliers for the best model so far. Inliers\n    are 3D correspondences (pt_ev, pt_gt) for which the Euclidean distance |pt_gt-T*pt_ev| is less than th.'''\n    \n    # remove invalid cameras, the index is returned\n    idx_cams = np.all(np.isfinite(ev_coord), axis=0)\n    ev_coord = ev_coord[:, idx_cams]\n    gt_coord = gt_coord[:, idx_cams]\n\n    # initialization\n    n = ev_coord.shape[1]\n    print(f'my n={n}')\n    r = ransac_threshold.shape[0]\n    ransac_threshold = np.expand_dims(ransac_threshold, axis=0)\n    ransac_threshold2 = ransac_threshold**2\n    ev_coord_1 = np.vstack((ev_coord, np.ones(n)))\n\n    max_no_inl = np.zeros((1, r))\n    best_inl_err = np.full(r, np.Inf)\n    best_transf_matrix = np.zeros((r, 4, 4))\n    best_err = np.full((n, r), np.Inf)\n    strict_inl = np.full((n, r), False)\n    triplets_used = np.zeros((3, r))\n\n    # run on camera triplets\n    for ii in range(n-2):\n        for jj in range(ii+1, n-1):\n            for kk in range(jj+1, n):\n                i = [ii, jj, kk]\n                triplets_used_now = np.full((n), False)\n                triplets_used_now[i] = True\n                # if both ii, jj, kk are strict inliers for the best current model just skip\n                if np.all(strict_inl[i]):\n                    continue\n                # get transformation T by Horn on the triplet camera center correspondences\n                transf_matrix = affine_matrix_from_points(ev_coord[:, i], gt_coord[:, i], usesvd=False)\n                # apply transformation T to test camera centres\n                rotranslated = np.matmul(transf_matrix[:3], ev_coord_1)\n                # compute error and inliers\n                err = np.sum((rotranslated - gt_coord)**2, axis=0)\n                inl = np.expand_dims(err, axis=1) < ransac_threshold2\n                no_inl = np.sum(inl, axis=0)\n                # if the number of inliers is close to that of the best model so far, go for refinement\n                to_ref = np.squeeze(((no_inl > 2) & (no_inl > max_no_inl * inl_cf)), axis=0)\n                for q in np.argwhere(to_ref):                        \n                    qq = q[0]\n                    if np.any(np.all((np.expand_dims(inl[:, qq], axis=1) == inl[:, :qq]), axis=0)):\n                        # already done for this set of inliers\n                        continue\n                    # get transformation T by Horn on the inlier camera center correspondences\n                    transf_matrix = affine_matrix_from_points(ev_coord[:, inl[:, qq]], gt_coord[:, inl[:, qq]])\n                    # apply transformation T to test camera centres\n                    rotranslated = np.matmul(transf_matrix[:3], ev_coord_1)\n                    # compute error and inliers\n                    err_ref = np.sum((rotranslated - gt_coord)**2, axis=0)\n                    err_ref_sum = np.sum(err_ref, axis=0)\n                    err_ref = np.expand_dims(err_ref, axis=1)\n                    inl_ref = err_ref < ransac_threshold2\n                    no_inl_ref = np.sum(inl_ref, axis=0)\n                    # update the model if better for each threshold\n                    to_update = np.squeeze((no_inl_ref > max_no_inl) | ((no_inl_ref == max_no_inl) & (err_ref_sum < best_inl_err)), axis=0)\n                    if np.any(to_update):\n                        triplets_used[0, to_update] = ii\n                        triplets_used[1, to_update] = jj\n                        triplets_used[2, to_update] = kk\n                        max_no_inl[:, to_update] = no_inl_ref[to_update]\n                        best_err[:, to_update] = np.sqrt(err_ref)\n                        best_inl_err[to_update] = err_ref_sum\n                        strict_inl[:, to_update] = (best_err[:, to_update] < strict_cf * ransac_threshold[:, to_update])\n                        best_transf_matrix[to_update] = transf_matrix\n\n    for i in range(r):\n       print(f'Registered cameras {int(max_no_inl[0, i])}/{n} for threshold {ransac_threshold[0, i]}')\n\n    best_model = {\n        \"valid_cams\": idx_cams,        \n        \"no_inl\": max_no_inl,\n        \"err\": best_err,\n        \"triplets_used\": triplets_used,\n        \"transf_matrix\": best_transf_matrix}\n    return best_model\n\n\n# mAA computation\ndef mAA_on_cameras(err, thresholds, n, skip_top_thresholds, to_dec=3):\n    '''mAA is the mean of mAA_i, where for each threshold th_i in <thresholds>, excluding the first <skip_top_thresholds values>,\n    mAA_i = max(0, sum(err_i < th_i) - <to_dec>) / (n - <to_dec>)\n    where <n> is the number of ground-truth cameras and err_i is the camera registration error for the best \n    registration corresponding to threshold th_i'''\n    \n    aux = err[:, skip_top_thresholds:] < np.expand_dims(np.asarray(thresholds[skip_top_thresholds:]), axis=0)\n    return np.sum(np.maximum(np.sum(aux, axis=0) - to_dec, 0)) / (len(thresholds[skip_top_thresholds:]) * (n - to_dec))\n\n\n# import data - no error handling in case float(x) fails\ndef get_camera_centers_from_df(df):\n    out = {}\n    for row in df.iterrows():\n        row = row[1]\n        fname = row['image_path']\n        R = np.array([float(x) for x in (row['rotation_matrix'].split(';'))]).reshape(3,3)\n        t = np.array([float(x) for x in (row['translation_vector'].split(';'))]).reshape(3)\n        center = -R.T @ t\n        out[fname] = center\n    return out\n\n\ndef evaluate_rec(gt_df, user_df, inl_cf = 0.8, strict_cf=0.5, skip_top_thresholds=2, to_dec=3,\n                 thresholds=[0.005, 0.01, 0.02, 0.03, 0.04, 0.05, 0.1, 0.15, 0.2]):\n    ''' Register the <user_df> camera centers to the ground-truth <gt_df> camera centers and\n    return the corresponding mAA as the average percentage of registered camera threshold.\n    \n    For each threshold value in <thresholds>, the best similarity transformation found which\n    maximizes the number of registered cameras is employed. A camera is marked as registered\n    if after the transformation its Euclidean distance to the corresponding ground-truth camera\n    center is less than the mentioned threshold. Current measurements are in meter.\n    \n    Registration parameters:\n    <inl_cf> coefficient to activate registration refinement, set to 1 to refine a new model\n    only when it gives more inliers, to 0 to refine a new model always; high values increase\n    speed but decrease precision.\n    <strict_cf> threshold coefficient to define strict inliers for the best registration so far,\n    new minimal models made up of strict inliers are skipped. It can vary from 0 (slower) to\n    1 (faster); set to -1 to check exhaustively all the minimal model triplets.\n\n    mAA parameters:\n    <skip_top_thresholds> excluded lower thresholds in the mAA computation; in case of using\n    heuristics for the registration, i.e. inl_cf!=0 and strict_cf!=-1, best model for lower\n    threshold can be not the optimal, so skip them in the mAA computation.\n    <to_dec> excludes the minimal model cameras from the computation of the mAA. Given the\n    minimal model, i.e. three pairs of 3D correspondences, there is a high chance to register by\n    a similarity transformation at any threshold, so do not account for mAA'''\n    \n    # get camera centers\n    ucameras = get_camera_centers_from_df(user_df)\n    gcameras = get_camera_centers_from_df(gt_df)    \n\n    # the denominator for mAA ratio\n    m = gt_df.shape[0]\n    \n    # get the image list to use\n    good_cams = []\n    for image_path in gcameras.keys():\n        if image_path in ucameras.keys():\n            good_cams.append(image_path)\n        \n    # put corresponding camera centers into matrices\n    n = len(good_cams)\n    u_cameras = np.zeros((3, n))\n    g_cameras = np.zeros((3, n))\n    \n    ii = 0\n    for i in good_cams:\n        u_cameras[:, ii] = ucameras[i]\n        g_cameras[:, ii] = gcameras[i]\n        ii += 1\n        \n    # Horn camera centers registration, a different best model for each camera threshold\n    model = register_by_Horn(u_cameras, g_cameras, np.asarray(thresholds), inl_cf, strict_cf)\n    \n    # transformation matrix\n    print(\"\\nTransformation matrix for maximum threshold\")\n    T = np.squeeze(model['transf_matrix'][-1])\n    print(T)\n    \n    # mAA\n    mAA = mAA_on_cameras(model[\"err\"], thresholds, m, skip_top_thresholds, to_dec)\n    # print(f'mAA = {mAA * 100 : .2f}% considering {m} input cameras - {to_dec}')\n    return mAA\n\n\ndef score(solution: pd.DataFrame, submission: pd.DataFrame) -> float:\n    '''The metric is an mean average accuracy between solution and submission camera centers.\n    Prior to calculate the metric, a function performs exhaustive registration (like RANSAC, but\n    not random, considering all possible configurations) to align the user camera system to the GT'''\n    \n    scenes = list(set(solution['dataset'].tolist()))\n    results_per_dataset = []\n    for dataset in scenes:\n        print(f\"\\n*** {dataset} ***\")\n        start = time.time()\n        gt_ds = solution[solution['dataset'] == dataset]\n        user_ds = submission[submission['dataset'] == dataset]\n        gt_ds = gt_ds.sort_values(by=['image_path'], ascending = True)\n        user_ds = user_ds.sort_values(by=['image_path'], ascending = True)\n        print(f'gt_ds size:{gt_ds.shape}\\nuser_ds size:{user_ds.shape}')\n        result = evaluate_rec(gt_ds, user_ds, inl_cf=0, strict_cf=-1, skip_top_thresholds=0, to_dec=3,\n                 thresholds=translation_thresholds_meters_dict[dataset])\n        end = time.time()\n        print(f\"\\nmAA: {result*100}%\")\n        print(\"Running time: %s\" % (end - start))        \n        results_per_dataset.append(result)\n    return float(np.array(results_per_dataset).mean())","metadata":{"_uuid":"2a1239f3-55fc-4dbb-9d3a-e90bfffa038c","_cell_guid":"4cf02a6f-b7e9-4360-892d-b1a50793eb12","collapsed":false,"jupyter":{"outputs_hidden":false},"execution":{"iopub.status.busy":"2024-05-12T09:29:15.752761Z","iopub.execute_input":"2024-05-12T09:29:15.753188Z","iopub.status.idle":"2024-05-12T09:29:16.253601Z","shell.execute_reply.started":"2024-05-12T09:29:15.753153Z","shell.execute_reply":"2024-05-12T09:29:16.251465Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"import random\n\n# load gt and renaming header image_name --> image_path' according to the submission format\ngt_csv = '/kaggle/input/image-matching-challenge-2024/train/train_labels.csv'\ngt_df = pd.read_csv(gt_csv)\ngt_df = gt_df.rename(columns={'image_name': 'image_path'}) \n\n# ****************************************************\n# randomly reduce size just to speed-up the examples\n# ****************************************************\n\nn = 55 # expected number of cameras per scenes\n\nscenes = list(set(gt_df['dataset'].tolist()))\n\nscene_count = {}\nfor dataset in scenes:\n    scene_count[dataset] = len(gt_df[gt_df['dataset'] == dataset])    \nprint('*** initial scene count ***')\nprint(scene_count)    \nprint('\\n')    \n    \nfor idx, row in gt_df.iterrows():\n    if random.random() > n / scene_count[row['dataset']]:\n        gt_df.drop(idx, inplace=True)\n\nscene_count = {}\nfor dataset in scenes:\n    scene_count[dataset] = len(gt_df[gt_df['dataset'] == dataset])    \nprint('*** final scene count ***')\nprint(scene_count)    \n          \n# save as a further csv\nout_csv = '/kaggle/working/example_gt.csv'\ngt_df.to_csv(out_csv, index=False, index_label=False)","metadata":{"execution":{"iopub.status.busy":"2024-05-10T08:54:55.119343Z","iopub.execute_input":"2024-05-10T08:54:55.119921Z","iopub.status.idle":"2024-05-10T08:54:56.204686Z","shell.execute_reply.started":"2024-05-10T08:54:55.119884Z","shell.execute_reply":"2024-05-10T08:54:56.203391Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# perfect solution test\nprint('--- Perfect solution test ---')\n\ngt_csv = '/kaggle/working/example_gt.csv'\nuser_csv = '/kaggle/working/example_gt.csv'\n\ngt_df = pd.read_csv(gt_csv)\nuser_df = pd.read_csv(user_csv)\n\nstart = time.time()\nres = score(gt_df, user_df)\nend = time.time()\n\nprint(f\"\\nGlobal mAA: {res*100}%\")\nprint(\"Total running time: %s\" % (end - start))","metadata":{"execution":{"iopub.status.busy":"2024-05-10T08:54:56.205839Z","iopub.execute_input":"2024-05-10T08:54:56.206163Z","iopub.status.idle":"2024-05-10T09:00:05.997121Z","shell.execute_reply.started":"2024-05-10T08:54:56.206134Z","shell.execute_reply":"2024-05-10T09:00:05.995438Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# invalid cameras test\nprint('--- Invalid cameras test ---')\n\ngt_csv = '/kaggle/working/example_gt.csv'\nuser_csv = '/kaggle/working/example_gt.csv'\n\ngt_df = pd.read_csv(gt_csv)\nuser_df = pd.read_csv(user_csv)\n\n# setting randomly about 25% of cameras as invalid using NaN\nimport random\nfor idx, row in user_df.iterrows():\n        if random.random() < 0.25:\n            user_df.loc[idx, 'translation_vector'] = 'nan;0;0' \n   \nstart = time.time()\nres = score(gt_df, user_df)\nend = time.time()\n\nprint(f\"\\nGlobal mAA: {res*100}%\")\nprint(\"Total running time: %s\" % (end - start))","metadata":{"execution":{"iopub.status.busy":"2024-05-10T09:00:05.999845Z","iopub.execute_input":"2024-05-10T09:00:06.000753Z","iopub.status.idle":"2024-05-10T09:02:11.976147Z","shell.execute_reply.started":"2024-05-10T09:00:06.000689Z","shell.execute_reply":"2024-05-10T09:02:11.974884Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# noisy camera test\nprint('--- Noisy solution test ---')\n\n# adding noise to pose estimation simple function\ndef generate_noisy_data_from_gt(in_csv, out_csv, sigma_t=0.05, sigma_r=0.01):\n    gt_df = pd.read_csv(in_csv)\n\n    n_sample = gt_df.shape[0]\n    shuffle_idx = np.random.permutation(gt_df.shape[0])[:n_sample]\n    user_df = pd.DataFrame(columns = gt_df.columns)\n    for i in shuffle_idx:\n        q = gt_df.iloc[i]\n        user_df.loc[len(user_df)] = q\n\n        qn = np.asarray([float(x) for x in q['translation_vector'].split(';')]) + np.random.normal(0, sigma_t, size=(3,))\n        qs = ''.join([\"%f;\" % number for number in qn])[:-1]        \n        user_df.loc[len(user_df)-1].at['translation_vector'] = qs\n\n        qn = np.asarray([float(x) for x in q['rotation_matrix'].split(';')]) + np.random.normal(0, sigma_r, size=(9,))\n        qs = ''.join([\"%f;\" % number for number in qn])[:-1]\n        user_df.loc[len(user_df)-1].at['rotation_matrix'] = qs\n\n    user_df.to_csv(out_csv, index=False, index_label=False)\n\ngt_csv = '/kaggle/working/example_gt.csv'\nuser_csv = '/kaggle/working/example_noisy_gt.csv'\n\n# noisy user_csv\ngenerate_noisy_data_from_gt(gt_csv, user_csv)\n\ngt_df = pd.read_csv(gt_csv)\nuser_df = pd.read_csv(user_csv)\n   \nstart = time.time()\nres = score(gt_df, user_df)\nend = time.time()\n\nprint(f\"\\nGlobal mAA: {res*100}%\")\nprint(\"Total running time: %s\" % (end - start))\n","metadata":{"execution":{"iopub.status.busy":"2024-05-10T09:02:11.979576Z","iopub.execute_input":"2024-05-10T09:02:11.981043Z","iopub.status.idle":"2024-05-10T09:08:03.898626Z","shell.execute_reply.started":"2024-05-10T09:02:11.980957Z","shell.execute_reply":"2024-05-10T09:08:03.896674Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# all the same test\nprint('--- All the same test ---')\n\ngt_csv = '/kaggle/working/example_gt.csv'\nuser_csv = '/kaggle/working/example_gt.csv'\n\ngt_df = pd.read_csv(gt_csv)\nuser_df = pd.read_csv(user_csv)\n\n# all camera positions are the same (degenerate case)\n# will raise at the begin a divide by zero warning, just ignore it\nfor idx, row in user_df.iterrows():\n    user_df.loc[idx, 'translation_vector'] = '1;0;0' \n    user_df.loc[idx, 'rotation_matrix'] = '1;0;0;0;1;0;0;0;1' \n               \nstart = time.time()\nres = score(gt_df, user_df)\nend = time.time()\n\nprint(f\"\\nGlobal mAA: {res*100}%\")\nprint(\"Total running time: %s\" % (end - start))","metadata":{"execution":{"iopub.status.busy":"2024-05-10T09:08:03.899992Z","iopub.status.idle":"2024-05-10T09:08:03.900526Z","shell.execute_reply.started":"2024-05-10T09:08:03.900260Z","shell.execute_reply":"2024-05-10T09:08:03.900281Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# that's all folks!","metadata":{"execution":{"iopub.status.busy":"2024-05-10T09:08:03.902666Z","iopub.status.idle":"2024-05-10T09:08:03.903130Z","shell.execute_reply.started":"2024-05-10T09:08:03.902915Z","shell.execute_reply":"2024-05-10T09:08:03.902935Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Personal Test with Baseline Data","metadata":{}},{"cell_type":"code","source":"#copied from above\nimport random\n\n# load gt and renaming header image_name --> image_path' according to the submission format\ngt_csv = '/kaggle/input/image-matching-challenge-2024/sample_submission.csv'\ngt_df = pd.read_csv(gt_csv)\ngt_df = gt_df.rename(columns={'image_name': 'image_path'}) \n\n# ****************************************************\n# randomly reduce size just to speed-up the examples\n# ****************************************************\n\n# n = 55 # expected number of cameras per scenes\n\n# scenes = list(set(gt_df['dataset'].tolist()))\n\n# scene_count = {}\n# for dataset in scenes:\n#     scene_count[dataset] = len(gt_df[gt_df['dataset'] == dataset])    \n# print('*** initial scene count ***')\n# print(scene_count)    \n# print('\\n')    \n    \n# for idx, row in gt_df.iterrows():\n#     if random.random() > n / scene_count[row['dataset']]:\n#         gt_df.drop(idx, inplace=True)\n\n# scene_count = {}\n# for dataset in scenes:\n#     scene_count[dataset] = len(gt_df[gt_df['dataset'] == dataset])    \n# print('*** final scene count ***')\n# print(scene_count)    \n          \n# save as a further csv\ngt_out_csv = '/kaggle/working/mytest_gt.csv'\ngt_df.to_csv(gt_out_csv, index=False, index_label=False)","metadata":{"execution":{"iopub.status.busy":"2024-05-10T11:12:18.918949Z","iopub.execute_input":"2024-05-10T11:12:18.919558Z","iopub.status.idle":"2024-05-10T11:12:18.935978Z","shell.execute_reply.started":"2024-05-10T11:12:18.919517Z","shell.execute_reply":"2024-05-10T11:12:18.934812Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# load baseline submission data\nbaseline_csv = '/kaggle/input/baseline-data/baseline_submission.csv'\nbaseline_df = pd.read_csv(baseline_csv)\n\n# ****************************************************\n# randomly reduce size just to speed-up the examples\n# ****************************************************\n\n# n = 55 # expected number of cameras per scenes\n\n# scenes = list(set(gt_df['dataset'].tolist()))\n\n# scene_count = {}\n# for dataset in scenes:\n#     scene_count[dataset] = len(gt_df[gt_df['dataset'] == dataset])    \n# print('*** initial scene count ***')\n# print(scene_count)    \n# print('\\n')    \n    \n# for idx, row in gt_df.iterrows():\n#     if random.random() > n / scene_count[row['dataset']]:\n#         gt_df.drop(idx, inplace=True)\n\n# scene_count = {}\n# for dataset in scenes:\n#     scene_count[dataset] = len(gt_df[gt_df['dataset'] == dataset])    \n# print('*** final scene count ***')\n# print(scene_count)    \n          \n# save as a further csv\nbaseline_out_csv = '/kaggle/working/mytest_baseline.csv'\nbaseline_df.to_csv(baseline_out_csv, index=False, index_label=False)","metadata":{"execution":{"iopub.status.busy":"2024-05-10T11:12:26.274699Z","iopub.execute_input":"2024-05-10T11:12:26.275122Z","iopub.status.idle":"2024-05-10T11:12:26.288064Z","shell.execute_reply.started":"2024-05-10T11:12:26.275086Z","shell.execute_reply":"2024-05-10T11:12:26.286874Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"code","source":"# baseline solution test\nprint('--- baseline solution test ---')\n\ngt_csv = '/kaggle/working/mytest_gt.csv'\nuser_csv = '/kaggle/working/mytest_baseline.csv'\n\ngt_df = pd.read_csv(gt_csv)\nuser_df = pd.read_csv(user_csv)\n\nprint(f'gt_df size:{gt_df.shape}')\nprint(f'user_df size:{user_df.shape}')\n\nstart = time.time()\nres = score(gt_df, user_df)\nend = time.time()\n\nprint(f\"\\nGlobal mAA: {res*100}%\")\nprint(\"Total running time: %s\" % (end - start))","metadata":{"execution":{"iopub.status.busy":"2024-05-10T11:12:29.273479Z","iopub.execute_input":"2024-05-10T11:12:29.274300Z","iopub.status.idle":"2024-05-10T11:12:56.049916Z","shell.execute_reply.started":"2024-05-10T11:12:29.274265Z","shell.execute_reply":"2024-05-10T11:12:56.048376Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"# Your Test","metadata":{}},{"cell_type":"markdown","source":"**Ground Truth**","metadata":{}},{"cell_type":"code","source":"import random\n\n# load gt and renaming header image_name --> image_path' according to the submission format\nsample_sub_csv = '/kaggle/input/image-matching-challenge-2024/sample_submission.csv'\nsample_sub_df = pd.read_csv(sample_sub_csv)\n\ngt_csv = '/kaggle/input/image-matching-challenge-2024/train/train_labels.csv'\ngt_df = pd.read_csv(gt_csv)\ngt_df = gt_df[gt_df['dataset'] == 'church']\ngt_df = gt_df.rename(columns={'image_name': 'image_path'})\n   \ngt_df['image_path'] = gt_df['image_path'].apply(lambda x: \"test/church/images/\" + x)\ngt_df = gt_df[gt_df['image_path'].isin(sample_sub_df['image_path'])]","metadata":{"execution":{"iopub.status.busy":"2024-05-12T09:29:32.640371Z","iopub.execute_input":"2024-05-12T09:29:32.640812Z","iopub.status.idle":"2024-05-12T09:29:32.699683Z","shell.execute_reply.started":"2024-05-12T09:29:32.640786Z","shell.execute_reply":"2024-05-12T09:29:32.697880Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Test Input**","metadata":{}},{"cell_type":"code","source":"#change this directory to change input\nsub_base_csv = '/kaggle/input/jeans-rotate/submission-KDMbaseline.csv'\nsub_base_df = pd.read_csv(sub_base_csv)","metadata":{"execution":{"iopub.status.busy":"2024-05-12T09:32:30.741946Z","iopub.execute_input":"2024-05-12T09:32:30.742267Z","iopub.status.idle":"2024-05-12T09:32:30.751121Z","shell.execute_reply.started":"2024-05-12T09:32:30.742240Z","shell.execute_reply":"2024-05-12T09:32:30.748779Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Run Test**","metadata":{}},{"cell_type":"code","source":"print('--- KDMbaseline solution test ---')\n\nprint(f'ground truth size:{gt_df.shape}')\nprint(f'submission_baseline size:{sub_base_df.shape}')\n\nstart = time.time()\nres = score(gt_df, sub_base_df)\nend = time.time()\n\nprint(f\"\\nGlobal mAA: {res*100}%\")\nprint(\"Total running time: %s\" % (end - start))","metadata":{"execution":{"iopub.status.busy":"2024-05-12T09:32:35.450611Z","iopub.execute_input":"2024-05-12T09:32:35.451024Z","iopub.status.idle":"2024-05-12T09:32:40.023309Z","shell.execute_reply.started":"2024-05-12T09:32:35.450994Z","shell.execute_reply":"2024-05-12T09:32:40.022474Z"},"trusted":true},"execution_count":null,"outputs":[]},{"cell_type":"markdown","source":"**Additional Test**","metadata":{}},{"cell_type":"code","source":"# rotate_before_csv = '/kaggle/input/jeans-data/submission_RotationBeforeEmbbeding.csv'\n# rotate_before_df = pd.read_csv(rotate_before_csv)\n\n# print('--- rotation before embedding solution test ---')\n\n# print(f'ground truth size:{gt_df.shape}')\n# print(f'RotationBeforeEmbbeding size:{rotate_before_df.shape}')\n\n# start = time.time()\n# res = score(gt_df, rotate_before_df)\n# end = time.time()\n\n# print(f\"\\nGlobal mAA: {res*100}%\")\n# print(\"Total running time: %s\" % (end - start))","metadata":{"execution":{"iopub.status.busy":"2024-05-10T14:27:09.845815Z","iopub.execute_input":"2024-05-10T14:27:09.846412Z","iopub.status.idle":"2024-05-10T14:27:21.502339Z","shell.execute_reply.started":"2024-05-10T14:27:09.846373Z","shell.execute_reply":"2024-05-10T14:27:21.498436Z"},"trusted":true},"execution_count":null,"outputs":[]}]}