| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257 |
- #!/usr/bin/env python
- # -*- coding: utf-8 -*-
- from collections import namedtuple
- import numpy as np
- from shapely.geometry import Polygon
- """
- reference from :
- https://github.com/MhLiao/DB/blob/3c32b808d4412680310d3d28eeb6a2d5bf1566c5/concern/icdar2015_eval/detection/iou.py#L8
- """
- class DetectionIoUEvaluator(object):
- def __init__(self, iou_constraint=0.5, area_precision_constraint=0.5):
- self.iou_constraint = iou_constraint
- self.area_precision_constraint = area_precision_constraint
- def evaluate_image(self, gt, pred):
- def get_union(pD, pG):
- return Polygon(pD).union(Polygon(pG)).area
- def get_intersection_over_union(pD, pG):
- return get_intersection(pD, pG) / get_union(pD, pG)
- def get_intersection(pD, pG):
- return Polygon(pD).intersection(Polygon(pG)).area
- def compute_ap(confList, matchList, numGtCare):
- correct = 0
- AP = 0
- if len(confList) > 0:
- confList = np.array(confList)
- matchList = np.array(matchList)
- sorted_ind = np.argsort(-confList)
- confList = confList[sorted_ind]
- matchList = matchList[sorted_ind]
- for n in range(len(confList)):
- match = matchList[n]
- if match:
- correct += 1
- AP += float(correct) / (n + 1)
- if numGtCare > 0:
- AP /= numGtCare
- return AP
- perSampleMetrics = {}
- matchedSum = 0
- Rectangle = namedtuple("Rectangle", "xmin ymin xmax ymax")
- numGlobalCareGt = 0
- numGlobalCareDet = 0
- arrGlobalConfidences = []
- arrGlobalMatches = []
- recall = 0
- precision = 0
- hmean = 0
- detMatched = 0
- iouMat = np.empty([1, 1])
- gtPols = []
- detPols = []
- gtPolPoints = []
- detPolPoints = []
- # Array of Ground Truth Polygons' keys marked as don't Care
- gtDontCarePolsNum = []
- # Array of Detected Polygons' matched with a don't Care GT
- detDontCarePolsNum = []
- pairs = []
- detMatchedNums = []
- arrSampleConfidences = []
- arrSampleMatch = []
- evaluationLog = ""
- for n in range(len(gt)):
- points = gt[n]["points"]
- dontCare = gt[n]["ignore"]
- if not Polygon(points).is_valid:
- continue
- gtPol = points
- gtPols.append(gtPol)
- gtPolPoints.append(points)
- if dontCare:
- gtDontCarePolsNum.append(len(gtPols) - 1)
- evaluationLog += (
- "GT polygons: "
- + str(len(gtPols))
- + (
- " (" + str(len(gtDontCarePolsNum)) + " don't care)\n"
- if len(gtDontCarePolsNum) > 0
- else "\n"
- )
- )
- for n in range(len(pred)):
- points = pred[n]["points"]
- if not Polygon(points).is_valid:
- continue
- detPol = points
- detPols.append(detPol)
- detPolPoints.append(points)
- if len(gtDontCarePolsNum) > 0:
- for dontCarePol in gtDontCarePolsNum:
- dontCarePol = gtPols[dontCarePol]
- intersected_area = get_intersection(dontCarePol, detPol)
- pdDimensions = Polygon(detPol).area
- precision = (
- 0 if pdDimensions == 0 else intersected_area / pdDimensions
- )
- if precision > self.area_precision_constraint:
- detDontCarePolsNum.append(len(detPols) - 1)
- break
- evaluationLog += (
- "DET polygons: "
- + str(len(detPols))
- + (
- " (" + str(len(detDontCarePolsNum)) + " don't care)\n"
- if len(detDontCarePolsNum) > 0
- else "\n"
- )
- )
- if len(gtPols) > 0 and len(detPols) > 0:
- # Calculate IoU and precision matrixs
- outputShape = [len(gtPols), len(detPols)]
- iouMat = np.empty(outputShape)
- gtRectMat = np.zeros(len(gtPols), np.int8)
- detRectMat = np.zeros(len(detPols), np.int8)
- for gtNum in range(len(gtPols)):
- for detNum in range(len(detPols)):
- pG = gtPols[gtNum]
- pD = detPols[detNum]
- iouMat[gtNum, detNum] = get_intersection_over_union(pD, pG)
- for gtNum in range(len(gtPols)):
- for detNum in range(len(detPols)):
- if (
- gtRectMat[gtNum] == 0
- and detRectMat[detNum] == 0
- and gtNum not in gtDontCarePolsNum
- and detNum not in detDontCarePolsNum
- ):
- if iouMat[gtNum, detNum] > self.iou_constraint:
- gtRectMat[gtNum] = 1
- detRectMat[detNum] = 1
- detMatched += 1
- pairs.append({"gt": gtNum, "det": detNum})
- detMatchedNums.append(detNum)
- evaluationLog += (
- "Match GT #"
- + str(gtNum)
- + " with Det #"
- + str(detNum)
- + "\n"
- )
- numGtCare = len(gtPols) - len(gtDontCarePolsNum)
- numDetCare = len(detPols) - len(detDontCarePolsNum)
- if numGtCare == 0:
- recall = float(1)
- precision = float(0) if numDetCare > 0 else float(1)
- else:
- recall = float(detMatched) / numGtCare
- precision = 0 if numDetCare == 0 else float(detMatched) / numDetCare
- hmean = (
- 0
- if (precision + recall) == 0
- else 2.0 * precision * recall / (precision + recall)
- )
- matchedSum += detMatched
- numGlobalCareGt += numGtCare
- numGlobalCareDet += numDetCare
- perSampleMetrics = {
- "gtCare": numGtCare,
- "detCare": numDetCare,
- "detMatched": detMatched,
- }
- return perSampleMetrics
- def combine_results(self, results):
- numGlobalCareGt = 0
- numGlobalCareDet = 0
- matchedSum = 0
- for result in results:
- numGlobalCareGt += result["gtCare"]
- numGlobalCareDet += result["detCare"]
- matchedSum += result["detMatched"]
- methodRecall = (
- 0 if numGlobalCareGt == 0 else float(matchedSum) / numGlobalCareGt
- )
- methodPrecision = (
- 0 if numGlobalCareDet == 0 else float(matchedSum) / numGlobalCareDet
- )
- methodHmean = (
- 0
- if methodRecall + methodPrecision == 0
- else 2 * methodRecall * methodPrecision / (methodRecall + methodPrecision)
- )
- methodMetrics = {
- "precision": methodPrecision,
- "recall": methodRecall,
- "hmean": methodHmean,
- }
- return methodMetrics
- if __name__ == "__main__":
- evaluator = DetectionIoUEvaluator()
- gts = [
- [
- {
- "points": [(0, 0), (1, 0), (1, 1), (0, 1)],
- "text": 1234,
- "ignore": False,
- },
- {
- "points": [(2, 2), (3, 2), (3, 3), (2, 3)],
- "text": 5678,
- "ignore": False,
- },
- ]
- ]
- preds = [
- [
- {
- "points": [(0.1, 0.1), (1, 0), (1, 1), (0, 1)],
- "text": 123,
- "ignore": False,
- }
- ]
- ]
- results = []
- for gt, pred in zip(gts, preds):
- results.append(evaluator.evaluate_image(gt, pred))
- metrics = evaluator.combine_results(results)
- print(metrics)
|