def evaluateCurrentClassifier(adaBoostClassifierList, newAdaboostClassifier, vaildImageBundle, vaildSizeBundle): currentCascadeClassifier = CascadeClassifier(copy.deepcopy(adaBoostClassifierList)) posSampleList = vaildImageBundle[0] negSampleList = vaildImageBundle[1] numOfPosSample = vaildSizeBundle[0] numOfNegSample = vaildSizeBundle[1] detectionCounter = 0 falsePositiveCounter = 0 if len(adaBoostClassifierList) == 0: for posSample in posSampleList: image = posSample.getImage() integralImage = Util.getIntegralImage(image) if newAdaboostClassifier.isFace(integralImage): detectionCounter += 1 for negSample in negSampleList: image = negSample.getImage() integralImage = Util.getIntegralImage(image) if newAdaboostClassifier.isFace(integralImage): falsePositiveCounter += 1 else: for posSample in posSampleList: image = posSample.getImage() integralImage = Util.getIntegralImage(image) if currentCascadeClassifier.isFace(integralImage): if newAdaboostClassifier.isFace(integralImage): detectionCounter += 1 for negSample in negSampleList: image = negSample.getImage() integralImage = Util.getIntegralImage(image) if currentCascadeClassifier.isFace(integralImage): if newAdaboostClassifier.isFace(integralImage): falsePositiveCounter += 1 return tuple([float(falsePositiveCounter) / numOfNegSample, float(detectionCounter) / numOfPosSample])
def getBestDecisionStump(feature, posSampleList, negSampleList, sumOfPosWeight, sumOfNegWeight): sampleImageList = posSampleList + negSampleList length = len(sampleImageList) sumOfLeftPosWeight = 0.0 # just represent the sum of positive weights on the left of threshold sumOfLeftNegWeight = 0.0 # just represent the sum of negative weights on the left of threshold threshold = (sampleImageList[0].getFeatureValue() + sampleImageList[1].getFeatureValue()) / 2 polarity = POS_POLARITY error = sumOfPosWeight + sumOfNegWeight # calculate feature values for all training samples based on given feature for sampleImage in sampleImageList: image = sampleImage.getImage() integralImage = Util.getIntegralImage(image) featureValue = Util.featureExtracion(integralImage, feature) sampleImage.setFeatureValue(featureValue) # sort feature bundle list (quick sort in util) Util.quickSort(sampleImageList, 0, length - 1) # (for loop) get the threshold and polarity with lowest error of weight sum for i in xrange(length - 1): sampleImage = sampleImageList[i] sampleType = sampleImage.getSampleType() featureValue = sampleImage.getFeatureValue() nextFeatureValue = sampleImageList[i + 1].getFeatureValue() weight = sampleImage.getWeight() newThreshold = (featureValue + nextFeatureValue) / 2.0 newPolarity = POS_POLARITY newError = error if sampleType == Util.FACE: sumOfLeftPosWeight += weight else: sumOfLeftNegWeight += weight error1 = sumOfLeftPosWeight + (sumOfNegWeight - sumOfLeftNegWeight) error2 = (sumOfPosWeight - sumOfLeftPosWeight) + sumOfLeftNegWeight if error1 > error2: newError = error2 else: newPolarity = NEG_POLARITY newError = error1 if newError < error: threshold = newThreshold polarity = newPolarity error = newError return DecisionStump(feature, threshold, polarity, error)
def getCascadeClassifier(maxFPR, minDR, targetFPR, sampleImageBundle, sampleSizeBundle): trainImageBundle = sampleImageBundle[0] vaildImageBundle = sampleImageBundle[1] trainSizeBundle = sampleSizeBundle[0] vaildSizeBundle = sampleSizeBundle[1] posTrainSampleList = trainImageBundle[0] negTrainSampleList = trainImageBundle[1] posTrainSampleSize = trainSizeBundle[0] negTrainSampleSize = trainSizeBundle[1] negTrainSampleSizeO = negTrainSampleSize adaBoostClassifierList = list() # initialise the false positive rate, detection rate and layer index fPR = 1.0 dR = 1.0 layerIndex = 0 numOfFeature = 0 # for test only # generate each layer / stage print "===== start training cascade classifier =====" while (fPR > targetFPR) and (len(negTrainSampleList) > 0): layerIndex += 1 # numOfFeature = 0 # original version newFRP = fPR newDR = dR print "===== ", layerIndex, " stage / adaboost classifier =====" # generate new stage via adaboost newAdaboostClassifier = None while newFRP > (maxFPR * fPR): numOfFeature += 1 featureList = Util.getRandomFeatureSet(numOfFeature) trainImageBundle = tuple([posTrainSampleList, negTrainSampleList]) trainSizeBundle = tuple([posTrainSampleSize, negTrainSampleSize]) newAdaboostClassifier = getAdaboostClassifier(featureList, trainImageBundle, trainSizeBundle) # reduce the stage threshold via minus fix step size # (newFRP, newDR) = evaluateCurrentClassifier(adaBoostClassifierList, # newAdaboostClassifier, # vaildImageBundle, # vaildSizeBundle) # # print '===== newFRP: ', newFRP, ', newDR: ', newDR, ', sT: ', newAdaboostClassifier.getStageThreshold(), ' =====' # # newStageThreshold = newAdaboostClassifier.getStageThreshold() # stepSize = abs(newStageThreshold) * DEFAULT_STEP_SIZE_RATIO # lowerBoundary = newStageThreshold - abs(newStageThreshold) # while (newDR < (minDR * dR)) and (newStageThreshold > lowerBoundary): # # reduce the stage threshold # newStageThreshold -= stepSize # newAdaboostClassifier.setStageThreshold(newStageThreshold) # (newFRP, newDR) = evaluateCurrentClassifier(adaBoostClassifierList, # newAdaboostClassifier, # vaildImageBundle, # vaildSizeBundle) # # print '===== newFRP: ', newFRP, ', newDR: ', newDR, ', sT: ', newAdaboostClassifier.getStageThreshold(), ' =====' # TODO: correct this part to get correct result # reduce the stage threshold via binary search newStageThreshold = newAdaboostClassifier.getStageThreshold() highStageThreshold = newStageThreshold midStageThreshold = newStageThreshold lowStageThreshold = newStageThreshold - abs(newStageThreshold) - DEFAULT_STEP_SIZE_RATIO tolerantError = abs(newStageThreshold) * DEFAULT_TOLERANT_ERROR_RATIO if tolerantError <= 0.0: tolerantError = DEFAULT_EPSILON (newFRP, newDR) = evaluateCurrentClassifier( adaBoostClassifierList, newAdaboostClassifier, vaildImageBundle, vaildSizeBundle ) print "===== newFRP: ", newFRP, ", newDR: ", newDR, ", highST: ", highStageThreshold, " =====" if newDR < (minDR * dR): newAdaboostClassifier.setStageThreshold(lowStageThreshold) (newFRP, newDR) = evaluateCurrentClassifier( adaBoostClassifierList, newAdaboostClassifier, vaildImageBundle, vaildSizeBundle ) print "===== newFRP: ", newFRP, ", newDR: ", newDR, ", lowST: ", lowStageThreshold, " =====" if newDR >= (minDR * dR): while (highStageThreshold - lowStageThreshold) > tolerantError: midStageThreshold = (highStageThreshold + lowStageThreshold) / 2.0 newAdaboostClassifier.setStageThreshold(midStageThreshold) (newFRP, newDR) = evaluateCurrentClassifier( adaBoostClassifierList, newAdaboostClassifier, vaildImageBundle, vaildSizeBundle ) print "===== newFRP: ", newFRP, ", newDR: ", newDR, ", midST: ", midStageThreshold, " =====" if newDR < (minDR * dR): highStageThreshold = midStageThreshold else: lowStageThreshold = midStageThreshold if newDR >= (minDR * dR): newAdaboostClassifier.setStageThreshold(lowStageThreshold) else: newFRP = 1.0 newDR = 1.0 fPR = newFRP dR = newDR adaBoostClassifierList.append(newAdaboostClassifier) print "===== get a new eligible adaboost classifier with fPR ", fPR, " dR ", dR, " =====" # clear negTrainSampleList # if newFRP > targetFPR, add new negative samples into negTrainSampleList, which satisfy # for current cascade classifier, the negative samples will be detected as faces # update negTrainSampleSize newNegTrainSampleList = list() if newFRP > targetFPR: currentCascadeClassifier = CascadeClassifier(copy.deepcopy(adaBoostClassifierList)) for negTrainSample in negTrainSampleList: image = negTrainSample.getImage() integralImage = Util.getIntegralImage(image) if currentCascadeClassifier.isFace(integralImage): newNegTrainSampleList.append(negTrainSample) negTrainSampleList = newNegTrainSampleList negTrainSampleSize = len(negTrainSampleList) return CascadeClassifier(adaBoostClassifierList, maxFPR, minDR, targetFPR, posTrainSampleSize, negTrainSampleSizeO)
def detectFaceFromCamera(cascadeClassifierFile=Util.DEFAULT_JSON_FILE, scaleRatio=Util.DEFAULT_SCALE_RATIO): cascadeClassifier = getCascadeClassifierFromFile(cascadeClassifierFile) cap = cv2.VideoCapture(0) cv2.namedWindow('Face Detection', cv2.WINDOW_NORMAL) while (True): startTime = time.time() ret, inputImage = cap.read() height = len(inputImage) width = len(inputImage[0]) # TODO: change the size of input image if height > 96 or width > 96: heightRatio = height / 96 widthRatio = width / 96 if heightRatio == 0: heightRatio = 1 if widthRatio == 0: widthRatio = 1 resizeRatio = 1.0 / min(heightRatio, widthRatio) inputImage = cv2.resize(inputImage, (0, 0), fx=resizeRatio, fy=resizeRatio) # inputImage = cv2.GaussianBlur(inputImage, (5, 5), 0) grayImage = cv2.cvtColor(inputImage, cv2.COLOR_BGR2GRAY) height = len(grayImage) width = len(grayImage[0]) # retrieve all the scales to generate windows faceBundleList = list() winSize = Util.WIN_SIZE while (winSize <= height) and (winSize <= width): winSize *= scaleRatio trueWinSize = int(winSize) trueStepSize = trueWinSize / 5 # slide window on each scale to detect face via cascade classifier for x in xrange(0, height - trueWinSize + 1, trueStepSize): for y in xrange(0, width - trueWinSize + 1, trueStepSize): newWindow = grayImage[x:x + trueWinSize, y:y + trueWinSize] scaledNewWindow = cv2.resize(newWindow, (Util.WIN_SIZE, Util.WIN_SIZE)) scaledIntegralImage = Util.getIntegralImage(scaledNewWindow) # collect face area (rectangle), and rescale them back to original size if cascadeClassifier.isFace(scaledIntegralImage): rectangle = Util.Rectangle(x, y, trueWinSize, trueWinSize) rectangleSize = rectangle.getSize() faceBundleList.append(tuple([rectangle, rectangleSize])) # # TODO: sort the face in face bundle list (desc order) # redundancyIndexList = set() # length = len(faceBundleList) # Util.rectangleQuickSort(faceBundleList, 0, length - 1) # # TODO: check if larger one contains smaller one # for i in xrange(length - 1): # for ii in xrange(i + 1, length): # if faceBundleList[i][0].isContain(faceBundleList[ii][0]): # redundancyIndexList.add(ii) # # redundancyIndexList = list(redundancyIndexList) # redundancyIndexList = sorted(redundancyIndexList, reverse=True) # # TODO: delete redundant rectangle # for index in redundancyIndexList: # del faceBundleList[index] # TODO: draw rectangles # TODO: (optional) just draw large ones length = len(faceBundleList) # for faceBundle in faceBundleList: print 'face #: ', length startInex = 0 endIndex = length # if length < 5: # endIndex = length / 2 + 1 # elif length < 10: # endIndex = length / 5 + 1 # else: # endIndex = length / 10 + 1 for faceBundle in faceBundleList[startInex:endIndex]: x = faceBundle[0].getX() y = faceBundle[0].getY() h = faceBundle[0].getH() w = faceBundle[0].getW() cv2.rectangle(inputImage, (y, x), (y + h, x + w), (255, 0, 0), 1) endTime = time.time() print 'FPS: ', 60.0 / (endTime - startTime) cv2.imshow('Face Detection', inputImage) if cv2.waitKey(40) & 0xFF == ord('q'): break return None