|
a |
|
b/eval/detection/bbox.py |
|
|
1 |
from .utils import * |
|
|
2 |
|
|
|
3 |
|
|
|
4 |
class BoundingBox: |
|
|
5 |
def __init__(self, |
|
|
6 |
imageName, |
|
|
7 |
classId, |
|
|
8 |
x, |
|
|
9 |
y, |
|
|
10 |
w, |
|
|
11 |
h, |
|
|
12 |
typeCoordinates=CoordinatesType.Absolute, |
|
|
13 |
imgSize=None, |
|
|
14 |
bbType=BBType.GroundTruth, |
|
|
15 |
classConfidence=None, |
|
|
16 |
format=BBFormat.XYWH): |
|
|
17 |
"""Constructor. |
|
|
18 |
Args: |
|
|
19 |
imageName: String representing the image name. |
|
|
20 |
classId: String value representing class id. |
|
|
21 |
x: Float value representing the X upper-left coordinate of the bounding box. |
|
|
22 |
y: Float value representing the Y upper-left coordinate of the bounding box. |
|
|
23 |
w: Float value representing the width bounding box. |
|
|
24 |
h: Float value representing the height bounding box. |
|
|
25 |
typeCoordinates: (optional) Enum (Relative or Absolute) represents if the bounding box |
|
|
26 |
coordinates (x,y,w,h) are absolute or relative to size of the image. Default:'Absolute'. |
|
|
27 |
imgSize: (optional) 2D vector (width, height)=>(int, int) represents the size of the |
|
|
28 |
image of the bounding box. If typeCoordinates is 'Relative', imgSize is required. |
|
|
29 |
bbType: (optional) Enum (Groundtruth or Detection) identifies if the bounding box |
|
|
30 |
represents a ground truth or a detection. If it is a detection, the classConfidence has |
|
|
31 |
to be informed. |
|
|
32 |
classConfidence: (optional) Float value representing the confidence of the detected |
|
|
33 |
class. If detectionType is Detection, classConfidence needs to be informed. |
|
|
34 |
format: (optional) Enum (BBFormat.XYWH or BBFormat.XYX2Y2) indicating the format of the |
|
|
35 |
coordinates of the bounding boxes. BBFormat.XYWH: <left> <top> <width> <height> |
|
|
36 |
BBFormat.XYX2Y2: <left> <top> <right> <bottom>. |
|
|
37 |
""" |
|
|
38 |
self._imageName = imageName |
|
|
39 |
self._typeCoordinates = typeCoordinates |
|
|
40 |
if typeCoordinates == CoordinatesType.Relative and imgSize is None: |
|
|
41 |
raise IOError( |
|
|
42 |
'Parameter \'imgSize\' is required. It is necessary to inform the image size.') |
|
|
43 |
if bbType == BBType.Detected and classConfidence is None: |
|
|
44 |
raise IOError( |
|
|
45 |
'For bbType=\'Detection\', it is necessary to inform the classConfidence value.') |
|
|
46 |
# if classConfidence != None and (classConfidence < 0 or classConfidence > 1): |
|
|
47 |
# raise IOError('classConfidence value must be a real value between 0 and 1. Value: %f' % |
|
|
48 |
# classConfidence) |
|
|
49 |
|
|
|
50 |
self._classConfidence = classConfidence |
|
|
51 |
self._bbType = bbType |
|
|
52 |
self._classId = classId |
|
|
53 |
self._format = format |
|
|
54 |
|
|
|
55 |
# If relative coordinates, convert to absolute values |
|
|
56 |
# For relative coords: (x,y,w,h)=(X_center/img_width , |
|
|
57 |
# Y_center/img_height) |
|
|
58 |
if (typeCoordinates == CoordinatesType.Relative): |
|
|
59 |
(self._x, self._y, self._w, self._h) = convertToAbsoluteValues( |
|
|
60 |
imgSize, (x, y, w, h)) |
|
|
61 |
self._width_img = imgSize[0] |
|
|
62 |
self._height_img = imgSize[1] |
|
|
63 |
if format == BBFormat.XYWH: |
|
|
64 |
self._x2 = self._w |
|
|
65 |
self._y2 = self._h |
|
|
66 |
self._w = self._x2 - self._x |
|
|
67 |
self._h = self._y2 - self._y |
|
|
68 |
else: |
|
|
69 |
raise IOError( |
|
|
70 |
'For relative coordinates, the format must be XYWH (x,y,width,height)') |
|
|
71 |
# For absolute coords: (x,y,w,h)=real bb coords |
|
|
72 |
else: |
|
|
73 |
self._x = x |
|
|
74 |
self._y = y |
|
|
75 |
if format == BBFormat.XYWH: |
|
|
76 |
self._w = w |
|
|
77 |
self._h = h |
|
|
78 |
self._x2 = self._x + self._w |
|
|
79 |
self._y2 = self._y + self._h |
|
|
80 |
else: # format == BBFormat.XYX2Y2: <left> <top> <right> <bottom>. |
|
|
81 |
self._x2 = w |
|
|
82 |
self._y2 = h |
|
|
83 |
self._w = self._x2 - self._x |
|
|
84 |
self._h = self._y2 - self._y |
|
|
85 |
if imgSize is None: |
|
|
86 |
self._width_img = None |
|
|
87 |
self._height_img = None |
|
|
88 |
else: |
|
|
89 |
self._width_img = imgSize[0] |
|
|
90 |
self._height_img = imgSize[1] |
|
|
91 |
|
|
|
92 |
def getAbsoluteBoundingBox(self, format=BBFormat.XYWH): |
|
|
93 |
if format == BBFormat.XYWH: |
|
|
94 |
return (self._x, self._y, self._w, self._h) |
|
|
95 |
elif format == BBFormat.XYX2Y2: |
|
|
96 |
return (self._x, self._y, self._x2, self._y2) |
|
|
97 |
|
|
|
98 |
def getRelativeBoundingBox(self, imgSize=None): |
|
|
99 |
if imgSize is None and self._width_img is None and self._height_img is None: |
|
|
100 |
raise IOError( |
|
|
101 |
'Parameter \'imgSize\' is required. It is necessary to inform the image size.') |
|
|
102 |
if imgSize is not None: |
|
|
103 |
return convertToRelativeValues( |
|
|
104 |
(imgSize[0], imgSize[1]), (self._x, self._x2, self._y, self._y2)) |
|
|
105 |
else: |
|
|
106 |
return convertToRelativeValues( |
|
|
107 |
(self._width_img, self._height_img), (self._x, self._x2, self._y, self._y2)) |
|
|
108 |
|
|
|
109 |
def getImageName(self): |
|
|
110 |
return self._imageName |
|
|
111 |
|
|
|
112 |
def getConfidence(self): |
|
|
113 |
return self._classConfidence |
|
|
114 |
|
|
|
115 |
def getFormat(self): |
|
|
116 |
return self._format |
|
|
117 |
|
|
|
118 |
def getClassId(self): |
|
|
119 |
return self._classId |
|
|
120 |
|
|
|
121 |
def getImageSize(self): |
|
|
122 |
return (self._width_img, self._height_img) |
|
|
123 |
|
|
|
124 |
def getCoordinatesType(self): |
|
|
125 |
return self._typeCoordinates |
|
|
126 |
|
|
|
127 |
def getBBType(self): |
|
|
128 |
return self._bbType |
|
|
129 |
|
|
|
130 |
@staticmethod |
|
|
131 |
def compare(det1, det2): |
|
|
132 |
det1BB = det1.getAbsoluteBoundingBox() |
|
|
133 |
det1ImgSize = det1.getImageSize() |
|
|
134 |
det2BB = det2.getAbsoluteBoundingBox() |
|
|
135 |
det2ImgSize = det2.getImageSize() |
|
|
136 |
|
|
|
137 |
if det1.getClassId() == det2.getClassId() and \ |
|
|
138 |
det1.classConfidence == det2.classConfidenc() and \ |
|
|
139 |
det1BB[0] == det2BB[0] and \ |
|
|
140 |
det1BB[1] == det2BB[1] and \ |
|
|
141 |
det1BB[2] == det2BB[2] and \ |
|
|
142 |
det1BB[3] == det2BB[3] and \ |
|
|
143 |
det1ImgSize[0] == det1ImgSize[0] and \ |
|
|
144 |
det2ImgSize[1] == det2ImgSize[1]: |
|
|
145 |
return True |
|
|
146 |
return False |
|
|
147 |
|
|
|
148 |
@staticmethod |
|
|
149 |
def clone(boundingBox): |
|
|
150 |
absBB = boundingBox.getAbsoluteBoundingBox(format=BBFormat.XYWH) |
|
|
151 |
# return (self._x,self._y,self._x2,self._y2) |
|
|
152 |
newBoundingBox = BoundingBox( |
|
|
153 |
boundingBox.getImageName(), |
|
|
154 |
boundingBox.getClassId(), |
|
|
155 |
absBB[0], |
|
|
156 |
absBB[1], |
|
|
157 |
absBB[2], |
|
|
158 |
absBB[3], |
|
|
159 |
typeCoordinates=boundingBox.getCoordinatesType(), |
|
|
160 |
imgSize=boundingBox.getImageSize(), |
|
|
161 |
bbType=boundingBox.getBBType(), |
|
|
162 |
classConfidence=boundingBox.getConfidence(), |
|
|
163 |
format=BBFormat.XYWH) |
|
|
164 |
return newBoundingBox |
|
|
165 |
|
|
|
166 |
|
|
|
167 |
class BoundingBoxes: |
|
|
168 |
def __init__(self): |
|
|
169 |
self._boundingBoxes = [] |
|
|
170 |
|
|
|
171 |
def addBoundingBox(self, bb): |
|
|
172 |
self._boundingBoxes.append(bb) |
|
|
173 |
|
|
|
174 |
def removeBoundingBox(self, _boundingBox): |
|
|
175 |
for d in self._boundingBoxes: |
|
|
176 |
if BoundingBox.compare(d, _boundingBox): |
|
|
177 |
del self._boundingBoxes[d] |
|
|
178 |
return |
|
|
179 |
|
|
|
180 |
def removeAllBoundingBoxes(self): |
|
|
181 |
self._boundingBoxes = [] |
|
|
182 |
|
|
|
183 |
def getBoundingBoxes(self): |
|
|
184 |
return self._boundingBoxes |
|
|
185 |
|
|
|
186 |
def getBoundingBoxByClass(self, classId): |
|
|
187 |
boundingBoxes = [] |
|
|
188 |
for d in self._boundingBoxes: |
|
|
189 |
if d.getClassId() == classId: # get only specified bounding box type |
|
|
190 |
boundingBoxes.append(d) |
|
|
191 |
return boundingBoxes |
|
|
192 |
|
|
|
193 |
def getClasses(self): |
|
|
194 |
classes = [] |
|
|
195 |
for d in self._boundingBoxes: |
|
|
196 |
c = d.getClassId() |
|
|
197 |
if c not in classes: |
|
|
198 |
classes.append(c) |
|
|
199 |
return classes |
|
|
200 |
|
|
|
201 |
def getBoundingBoxesByType(self, bbType): |
|
|
202 |
# get only specified bb type |
|
|
203 |
return [d for d in self._boundingBoxes if d.getBBType() == bbType] |
|
|
204 |
|
|
|
205 |
def getBoundingBoxesByImageName(self, imageName): |
|
|
206 |
# get only specified bb type |
|
|
207 |
return [d for d in self._boundingBoxes if d.getImageName() == imageName] |
|
|
208 |
|
|
|
209 |
def count(self, bbType=None): |
|
|
210 |
if bbType is None: # Return all bounding boxes |
|
|
211 |
return len(self._boundingBoxes) |
|
|
212 |
count = 0 |
|
|
213 |
for d in self._boundingBoxes: |
|
|
214 |
if d.getBBType() == bbType: # get only specified bb type |
|
|
215 |
count += 1 |
|
|
216 |
return count |
|
|
217 |
|
|
|
218 |
def clone(self): |
|
|
219 |
newBoundingBoxes = BoundingBoxes() |
|
|
220 |
for d in self._boundingBoxes: |
|
|
221 |
det = BoundingBox.clone(d) |
|
|
222 |
newBoundingBoxes.addBoundingBox(det) |
|
|
223 |
return newBoundingBoxes |
|
|
224 |
|
|
|
225 |
def drawAllBoundingBoxes(self, image, imageName): |
|
|
226 |
bbxes = self.getBoundingBoxesByImageName(imageName) |
|
|
227 |
for bb in bbxes: |
|
|
228 |
if bb.getBBType() == BBType.GroundTruth: # if ground truth |
|
|
229 |
image = add_bb_into_image(image, bb, color=(0, 255, 0)) # green |
|
|
230 |
else: # if detection |
|
|
231 |
image = add_bb_into_image(image, bb, color=(255, 0, 0)) # red |
|
|
232 |
return image |