{"record":{"id":"a2a775bb8f7472c5","repo":"open-mmlab/mmdetection","slug":"tpfp-fn-has-to-be-a-function-or-none-but-got-tpf","errorCode":null,"errorMessage":"tpfp_fn has to be a function or None, but got {tpfp_fn}","messagePattern":"tpfp_fn has to be a function or None, but got (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"mmdet/evaluation/functional/mean_ap.py","lineNumber":623,"sourceCode":"        nproc = min(nproc, num_imgs)\n        pool = Pool(nproc)\n\n    eval_results = []\n    for i in range(num_classes):\n        # get gt and det bboxes of this class\n        cls_dets, cls_gts, cls_gts_ignore = get_cls_results(\n            det_results, annotations, i)\n        # choose proper function according to datasets to compute tp and fp\n        if tpfp_fn is None:\n            if dataset in ['det', 'vid']:\n                tpfp_fn = tpfp_imagenet\n            elif dataset in ['oid_challenge', 'oid_v6'] \\\n                    or use_group_of is True:\n                tpfp_fn = tpfp_openimages\n            else:\n                tpfp_fn = tpfp_default\n        if not callable(tpfp_fn):\n            raise ValueError(\n                f'tpfp_fn has to be a function or None, but got {tpfp_fn}')\n\n        if num_imgs > 1:\n            # compute tp and fp for each image with multiple processes\n            args = []\n            if use_group_of:\n                # used in Open Images Dataset evaluation\n                gt_group_ofs = get_cls_group_ofs(annotations, i)\n                args.append(gt_group_ofs)\n                args.append([use_group_of for _ in range(num_imgs)])\n            if ioa_thr is not None:\n                args.append([ioa_thr for _ in range(num_imgs)])\n\n            tpfp = pool.starmap(\n                tpfp_fn,\n                zip(cls_dets, cls_gts, cls_gts_ignore,\n                    [iou_thr for _ in range(num_imgs)],\n                    [area_ranges for _ in range(num_imgs)],","sourceCodeStart":605,"sourceCodeEnd":641,"githubUrl":"https://github.com/open-mmlab/mmdetection/blob/cfd5d3a985b0249de009b67d04f37263e11cdf3d/mmdet/evaluation/functional/mean_ap.py#L605-L641","documentation":"eval_map accepts a custom tp/fp scoring function via tpfp_fn; after default selection logic (tpfp_default / tpfp_openimages), the value must be callable, otherwise ValueError.","triggerScenarios":"Passing tpfp_fn as a string ('tpfp_default'), a module, or an uninitialized class instead of a function reference.","commonSituations":"Copying config snippets where tpfp_fn is serialized as a string; wrapping custom NMS scoring incorrectly.","solutions":["Pass the function object itself: tpfp_fn=tpfp_default or tpfp_fn=my_module.my_tpfp","If loading from config, resolve the string to a callable (e.g. mmcv.utils.import_from_string / locate) before calling eval_map","Omit tpfp_fn (pass None) to use the built-in default"],"exampleFix":"# before\nfrom mmdet.evaluation.functional import tpfp_default\neval_map(..., tpfp_fn='tpfp_default')\n# after\neval_map(..., tpfp_fn=tpfp_default)","handlingStrategy":"type-guard","validationCode":"if tpfp_fn is not None:\n    assert callable(tpfp_fn), 'tpfp_fn must be callable'","typeGuard":"def is_tpfp_fn(x) -> bool:\n    return x is None or callable(x)","tryCatchPattern":null,"preventionTips":["Pass function objects, not strings","Resolve config strings via mmengine.locate before eval_map"],"tags":["mean-ap","mmdetection","callable-validation"],"backgroundTag":"invalid-argument-type","analyzedSha":"cfd5d3a985b0249de009b67d04f37263e11cdf3d","analyzedAt":"2026-08-27T20:54:20.183Z","schemaVersion":2},"datasetVersion":"2026-08-28T00:17:15.603Z"}