1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 __all__ = ['ObjectDetector', 'ObjectDetectionInfo']
26
27
28 from cPickle import load
29 from ctypes import pointer, byref
30 from numpy import array, zeros, concatenate, ones
31 from scipy.weave import inline
32 from time import time
33 from cv import fromIplImage
34 from pycv.cs.ml.cla.boost import DiscreteBoostedClassifier, SimpleCascade, \
35 GeneralizedCascade
36
37 from pycv.interfaces.opencv import cvLoadCast, CvHaarClassifierCascade, \
38 cvCreateMemStorage, cvReleaseMemStorage, cvReleaseHaarClassifierCascade, \
39 cvHaarDetectObjects, CvSize, cvGetSeqElem, CvRect, haar_cascades, \
40 cvReleaseImage, cvCopy, cvResize, CV_INTER_NN, IPL_DEPTH_8U, \
41 IPL_DEPTH_32S, IPL_DEPTH_64F, cvIntegral, cvSet, cvSetZero, CvScalar, \
42 cvCreateImage, cvCvtColor, cvSetImagesForHaarClassifierCascade, \
43 cvRunHaarClassifierCascade, cvSetImageROI, cvResetImageROI, CvPoint, \
44 cvResizeSubRect, cvGetSize, CvRECT, cvRectangle, CV_CVTIMG_FLIP, \
45 CV_RGB2GRAY, CV_BGR2GRAY, cvFlip, CvMat, cvGetMat
46
47 from haar import THaarClassifier
48
49 from integralimage import integral, integral_uint32, integral_int32
50
51 from pycv.ext import detect_detect_block, detect_compute_ivecs2
52
53
55 ivecs = None
56 dvecs = None
57 patch_len = None
58
60 f = open(filepath,'w')
61 f.write('========DATA========\n')
62 M = len(self.ivecs)
63 f.write(str(M)+' '+str(self.patch_len)+'\n')
64 for m in xrange(M):
65 for i in xrange(64):
66 f.write(str(self.ivecs[m,i])+' ')
67 for i in xrange(4):
68 f.write(str(self.dvecs[m,i])+' ')
69 f.write('\n')
70
72 f = open(filepath,'r')
73 s = f.read().split()
74 while s.pop(0) != '========DATA========':
75 pass
76 M = int(s.pop(0))
77 patch_len = int(s.pop(0))
78 ivecs = zeros((M,64),'int')
79 dvecs = zeros((M,4),'double')
80 for m in xrange(M):
81 for i in xrange(64):
82 ivecs[m,i] = int(s.pop(0))
83 for i in xrange(4):
84 dvecs[m,i] = float(s.pop(0))
85
86 self.ivecs = ivecs
87 self.dvecs = dvecs
88 self.patch_len = patch_len
89
93
94 - def copy_from(self, ivecs, dvecs, patch_len):
98
99 - def _convert_Haar(self,lc,patch_len,c,is_improver,to_reset,reset_value=0):
100 """Convert from THaarClassifier to a new classifier with scale s so
101 that I can detect from image with different scales.
102
103 Input:
104 lc: a THaarClassifier
105 patch_len: the length of the patch to be used by lc
106 c: the voting weight (double)
107 is_improver: (boolean) an improver or a filter?
108 to_reset: (boolean) to reset previous sum?
109 reset_value: (double) value to be reset to
110 Output:
111 all the parameters of the haar classifier will be stored in an int
112 array and a double array
113
114 New reference:
115
116 ivec: 64 items, format as follows
117 - ivec[0]: the number of indices
118 - ivec[1]: flag, in which:
119 - bit 0: set if it is an improver, otherwise it is a filter
120 - bit 1: set if the previous sum is to be reset
121 - starting from ivec[4]: tuples of 3 integers:
122 - x position w.r.t. the top-left corner
123 - y position w.r.t. the top-left corner
124 - coefficient/weight
125
126 dvec: 4 items, format as follows
127 - dvec[0]: threshold b
128 - dvec[1]: voting weight c
129 - dvec[2]: reset value -- only meaningful if bit 1 of ivec[1] is set
130
131 Old reference:
132
133 ivec: format as follows
134 - first 32 ints: (vectorized) indices w.r.t. the location
135 of the top-left corner
136 - ivec[32]: the number of indices
137 - ivec[33]: flag, in which:
138 - bit 0: set if it is an improver, otherwise it is a filter
139 - bit 1: set if the previous sum is to be reset
140
141 dvec: format as follows
142 - first 32 doubles: weights
143 - dvec[32]: threshold b
144 - dvec[33]: voting weight c
145 - dvec[34]: reset value -- only meaningful if bit 1 of ivec[33] is set
146 """
147 ivec = zeros(64,'int')
148 dvec = zeros(4,'double')
149
150
151
152
153 N = len(lc.p)
154 ivec[0] = N
155 for n in xrange(N):
156 ivec[n*3+4] = lc.p[n] % patch_len
157 ivec[n*3+5] = lc.p[n] / patch_len
158 ivec[n*3+6] = int(round(lc.w[n]))
159
160 dvec[0] = lc.b
161 dvec[1] = c
162 dvec[2] = reset_value
163
164 if is_improver: ivec[1] |= 1
165 if to_reset: ivec[1] |= 2
166
167 return ivec, dvec
168
170 """Convert from composite to a new classifier with scale s so that I can detect from image with different scales.
171
172 Input:
173 cc: a composite classifier, which can be:
174 - A THaarClassifier
175 - A SimpleCascade of composite classifiers
176 - A GeneralizedCascade of composite classifiers
177 - A DiscreteBoostedClassifier of composite classifiers
178 patch_len: the length of the patch to be used by lc
179 Output:
180 ivecs: a 2D int array representing the 'int' parameters of the composite classifier
181 dvecs: a 2D double array representing the 'double' parameters of the composite classifier
182 """
183 if isinstance(cc,THaarClassifier):
184 ivec, dvec = self._convert_Haar(cc,patch_len,1,False,True)
185 return ivec.reshape(1,64), dvec.reshape(1,4)
186
187 if isinstance(cc,DiscreteBoostedClassifier):
188 for weak in cc.weaks:
189 if not isinstance(weak,THaarClassifier):
190 raise ValueError("cc is a DiscreteBoostedClassifier, but one of its weak classifiers is not THaarClassifier")
191 N = len(cc.weaks)
192 ivecs = zeros((N,64),'int')
193 dvecs = zeros((N,4),'double')
194 for n in xrange(N):
195 ivecs[n], dvecs[n] = self._convert_Haar(cc.weaks[n],patch_len,cc.c[n],n != N-1,n == 0,0)
196 return ivecs, dvecs
197
198 if isinstance(cc,SimpleCascade):
199 for weak in cc.bclassifiers:
200 if not isinstance(weak,THaarClassifier) and \
201 not isinstance(weak,DiscreteBoostedClassifier) and \
202 not isinstance(weak,GeneralizedCascade):
203 raise ValueError("cc is a SimpleCascade, but one of its weak \
204 classifiers is neither a THaarClassifier, nor a \
205 DiscreteBoostedClassifier, nor a GeneralizedCascade")
206
207 z = [self._convert_Haars(x,patch_len) for x in cc.bclassifiers]
208 ivecs = concatenate([x[0] for x in z])
209 dvecs = concatenate([x[1] for x in z])
210 return ivecs, dvecs
211
212 if isinstance(cc,GeneralizedCascade):
213 N = len(cc.weaks)
214 for n in xrange(1,N):
215 if not isinstance(cc.weaks[n],THaarClassifier):
216 raise ValueError("cc is a GeneralizedCascade, but one of its weak classifiers is not a THaarClassifier")
217 ivecs = zeros((N-1,64),'int')
218 dvecs = zeros((N-1,4),'double')
219 ivecs[0], dvecs[0] = self._convert_Haar(cc.weaks[1],patch_len,cc.c[1],cc.z[1] and 1 != N,True,1)
220 for n in xrange(2,N):
221 ivecs[n-1], dvecs[n-1] = self._convert_Haar(cc.weaks[n],patch_len,cc.c[n],cc.z[n] and n != N-1,False,0)
222 return ivecs, dvecs
223
224 raise ValueError("Unknown class type of cc")
225
226
228
229 - def __init__(self, object_type, flag = 0, param1 = None, param2 = None,
230 block_size=256):
231 """Initialize an ObjectDetector.
232
233 Input:
234 object_type: the type of object to detect,
235 in ('frontal_face','profile_face','upper_body','lower_body','full_body')
236 flag: the type of operation of the object detector
237 0 = use opencv's default haar cascade
238 1 = use the cascade stored in a BinaryClassifier, see `param1`, `param2`
239 2 = same as 1 but use opencv's default haar cascade to post-verify
240 3 = use the cascade stored in a text file named by `param1`
241 4 = same as 3 but use opencv's default haar cascade to post-verify
242 param1: [optional] a strong classifier to classify a patch into object/non-object
243 If flag is 1 or 2: This holds a BinaryClassifier
244 If flag is 3 or 4: This holds a path to the text file storing the cascade
245 param2: [optional] If flag is 1 or 2:
246 this holds the length of the image patch (e.g. 20-by-20 or 24-by-24)
247 block_size : int
248 length of a square block. An image is divided into
249 blocks if it is too large. This offers fast and stable
250 object detection.
251
252 """
253 self.has_classifier = flag != 0
254 self.use_opencv = flag in (0,2,4)
255 self.block_size = block_size
256
257 self.N = None
258
259 if flag in (1,2):
260 self.odi = ObjectDetectionInfo()
261 self.odi.load_from_classifier(param1, param2)
262 self.ivecs = self.odi.ivecs
263 self.dvecs = self.odi.dvecs
264 self.patch_len = self.odi.patch_len
265 elif flag in (3,4):
266 self.odi = ObjectDetectionInfo()
267 self.odi.load_from_file(param1)
268 self.ivecs = self.odi.ivecs
269 self.dvecs = self.odi.dvecs
270 self.patch_len = self.odi.patch_len
271
272 if self.has_classifier:
273 size = CvSize(block_size, block_size)
274 self.img = cvCreateImage( size, IPL_DEPTH_8U, 1 )
275 self.pimg = fromIplImage(self.img)
276 self.mask = zeros((block_size,block_size), 'uint8')
277 self.img_cum = zeros((block_size,block_size),'int32')
278 self.org_img = None
279
280
281 self.ivecs2 = zeros(self.ivecs.shape, 'int')
282 img_ws = int(self.img_cum.strides[0] / 4)
283 detect_compute_ivecs2(img_ws, self.ivecs, self.ivecs2)
284
285 self.q = zeros((2,10000),'int')
286 self.out = zeros((2,1000),'int')
287
288
289
290 if self.use_opencv:
291 self.haar_cascade = cvLoadCast(haar_cascades[object_type],CvHaarClassifierCascade)
292 self.storage = cvCreateMemStorage(0)
293
294 if self.has_classifier:
295 size = CvSize(block_size+1, block_size+1)
296 self.img_sum = cvCreateImage( size, IPL_DEPTH_32S, 1 )
297 self.img_sqsum = cvCreateImage( size, IPL_DEPTH_64F, 1 )
298 self.img_tlsum = cvCreateImage( size, IPL_DEPTH_32S, 1 )
299
315
316
318 """Detect objects in the current block.
319
320 :Parameters:
321 roi : CvRECT
322 rectangle of interest of the top-left corner
323
324 :Returns:
325 array of the top-left corner(s) of the detected face(s)
326 """
327
328 mask = self.mask
329 img = self.img_cum
330 rect = array((roi.x,roi.y,roi.x+roi.w,roi.y+roi.h)).astype('int')
331
332 min_neighbors = int(self.min_neighbors)
333 plen = int(self.patch_len)
334
335 ivecs = self.ivecs
336 ivecs2 = self.ivecs2
337 dvecs = self.dvecs
338
339 qx = self.q[0]
340 qy = self.q[1]
341 outx = self.out[0]
342 outy = self.out[1]
343
344
345 w = rect[2]+plen
346 h = rect[3]+plen
347
348
349 integral_int32(self.pimg[:h,:w], self.img_cum[:h,:w])
350
351
352 nout = detect_detect_block(mask,img,ivecs2,dvecs, \
353 min_neighbors,qx,qy,outx,outy,rect)
354
355 z = []
356
357
358 if nout > 0 and self.use_opencv:
359 cvIntegral( self.img, self.img_sum, self.img_sqsum, self.img_tlsum )
360 cvSetImagesForHaarClassifierCascade(self.haar_cascade, \
361 self.img_sum,self.img_sqsum,self.img_tlsum,1.0)
362
363 for i in xrange(nout):
364 x1 = int(outx[i])
365 y1 = int(outy[i])
366
367 if self.use_opencv and \
368 cvRunHaarClassifierCascade(self.haar_cascade, CvPoint(x1,y1)) <= 0:
369 continue
370
371 z.append((x1,y1,plen,plen))
372
373 return z
374
376 """Diplay the current block.
377
378 :Parameters:
379 roi : CvRECT
380 rectangle of interest of the top-left corner
381
382 :Returns:
383 array of the top-left corner(s) of the detected face(s)
384 """
385 cvRectangle(self.img, CvPoint(int(roi.x), int(roi.y)),
386 CvPoint(int(roi.x+roi.w), int(roi.y+roi.h)), CvScalar(255,0,0,0),3)
387 cvShowImage('screen2', self.img)
388 cvWaitKey(0)
389 return []
390
392
393 if self.org_img is None:
394 self.org_img = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1)
395 elif self.org_img.contents.width != img.contents.width or \
396 self.org_img.contents.height != img.contents.height:
397 cvReleaseImage(self.org_img)
398 self.org_img = cvCreateImage(cvGetSize(img), IPL_DEPTH_8U, 1)
399
400 if img[0].origin > 0:
401 mat = byref(CvMat())
402 cvGetMat(self.org_img, mat, None, 0)
403 if img[0].nChannels > 1:
404 cvCvtColor(img, mat, CV_BGR2GRAY)
405 else:
406 cvCopy(img, mat)
407 cvFlip(mat)
408 else:
409 if img[0].nChannels > 1:
410 cvCvtColor(img, self.org_img, CV_BGR2GRAY)
411 else:
412 cvCopy(img, self.org_img)
413
415 """Group overlapping windows
416
417 :Paramters:
418 z : array of (x,y,w,h)
419 array of windows
420
421 :Returns:
422 y : array of (x,y,w,h)
423 array of windows after groupping
424 """
425
426 def overlapping(i,j):
427 x1 = max(z[i,0],z[j,0])
428 y1 = max(z[i,1],z[j,1])
429 x2 = min(z[i,0]+z[i,2],z[j,0]+z[j,2])
430 y2 = min(z[i,1]+z[i,3],z[j,1]+z[j,3])
431 if x1 >= x2 or y1 >= y2:
432 return False
433 a = (x2-x1)*(y2-y1)
434 return a >= 0.5*min(z[i,2]*z[i,3], z[j,2]*z[j,3])
435
436 mask = ones(len(z))
437 z2 = []
438 for i in xrange(len(z)):
439 if mask[i]:
440 x = z[i,0]
441 y = z[i,1]
442 w = z[i,2]
443 h = z[i,3]
444 n = 1
445 mask[i] = 0
446 for j in xrange(i+1,len(z)):
447 if mask[j]==1 and overlapping(i,j):
448 x += z[j,0]
449 y += z[j,1]
450 w += z[j,2]
451 h += z[j,3]
452 n += 1
453 mask[j] = 0
454
455 z2.append(array((x,y,w,h))/n)
456
457 return array(z2)
458
459 - def detect(self, img, scale_factor=1.1, min_neighbors=1,
460 group_overlapping=True):
461 """Detect objects from a grayscale image.
462
463 Input:
464 img: a POINTER(IplImage) of depth IPL_DEPTH_8U and 1 or 3 channels
465 scale_factor: The factor by which the search window is scaled
466 between the subsequent scans, for example, 1.1 means increasing window by 10%.
467 min_neighbors: Minimum number (minus 1) of neighbor rectangles that makes up an object.
468 All the groups of a smaller number of rectangles than min_neighbors-1 are rejected.
469 If min_neighbors is 0, the function does not any grouping at all and returns all
470 the detected candidate rectangles, which may be useful if the user wants to apply
471 a customized grouping procedure.
472 group_overlapping: if True then overlapping locations are grouped
473 Output:
474 z: an 'int' numpy.array of tuples (x,y,w,h) representing the object locations
475 """
476 if self.has_classifier:
477 self.min_neighbors = min_neighbors
478 self._update_org_img(img)
479
480 self.faces = []
481
482
483 src_w = self.org_img.contents.width
484 src_h = self.org_img.contents.height
485 dst_size = self.block_size-self.patch_len
486
487 scale = 1.0
488
489 while True:
490 src_size = dst_size*scale
491 src_outer_size = self.block_size*scale
492
493 for iy in xrange(int(src_h/src_size)+1):
494 src_y = iy*src_size
495 dst_h = min((src_h-src_y)/scale, dst_size)
496 for ix in xrange(int(src_w/src_size)+1):
497 src_x = ix*src_size
498 dst_w = min((src_w-src_x)/scale, dst_size)
499
500 rect = CvRECT(src_x, src_y,
501 src_outer_size, src_outer_size)
502 cvResizeSubRect(self.org_img, self.img, rect)
503
504 roi = CvRECT(0, 0, dst_w, dst_h)
505 z = self._detect_block(roi)
506 if len(z) > 0:
507 z = array(z)*scale
508 z[:,0] += src_x
509 z[:,1] += src_y
510 self.faces.extend(z)
511
512
513
514
515 if min(src_w,src_h) / scale < self.patch_len*2:
516 break
517 scale *= scale_factor
518
519 z = array(self.faces)
520
521 else:
522 faces = cvHaarDetectObjects( img, self.haar_cascade, self.storage, scale_factor,
523 min_neighbors, 0, CvSize(0,0) )
524
525 z = zeros((len(faces),4),'int')
526 for i in xrange(len(faces)):
527 z[i,0] = faces[i].x
528 z[i,1] = faces[i].y
529 z[i,2] = faces[i].width
530 z[i,3] = faces[i].height
531
532 if group_overlapping:
533 z = self._group_overlapping(z)
534
535 return z.astype('int')
536