CV-Facelandmark训练营

affine.py

import numpy as np

class Affine(object):
    @classmethod
    def transPntForward(cls, pt, T):
        newPt = np.zeros(2, dtype=pt.dtype)
        newPt[0] = T[0, 0] * pt[0] + T[1, 0] * pt[1] + T[2, 0]
        newPt[1] = T[0, 1] * pt[0] + T[1, 1] * pt[1] + T[2, 1]
        return newPt

    @classmethod
    def transPntsForwardWithSameT(cls, pts, T):
        if pts.ndim != 2:
            raise Exception("Must 2-D array")
        newPts = np.zeros(pts.shape)
        newPts[:, 0] = T[0, 0] * pts[:, 0] + T[1, 0] * pts[:, 1] + T[2, 0]
        newPts[:, 1] = T[0, 1] * pts[:, 0] + T[1, 1] * pts[:, 1] + T[2, 1]
        return newPts

    @classmethod
    def transPntsForwardWithDiffT(cls, pts, Ts):
        if pts.ndim != 2:
            raise Exception("Must 2-D array")
        nPts = np.zeros(pts.shape)
        pntNum = pts.shape[0]

        for i in range(pntNum):
            T = Ts[i]
            nPts[i, 0] = T[0, 0] * pts[i, 0] + T[1, 0] * pts[i, 1] + T[2, 0]
            nPts[i, 1] = T[0, 1] * pts[i, 0] + T[1, 1] * pts[i, 1] + T[2, 1]
        return nPts

    @classmethod
    def fitGeoTrans(cls, src, dst,
                    mode="NonreflectiveSimilarity"):
        """
        This function is the same as matlab fitgeotrans
        """

        ### Subtract the mean
        src0 = np.subtract(src, np.mean(src, axis=0))
        dst0 = np.subtract(dst, np.mean(dst, axis=0))

        if "NonreflectiveSimilarity" == mode:
            return cls.findNonreflectiveSimilarity(src0, dst0)
        else:
            raise Exception("Unsupported transformation")

    @classmethod
    def findNonreflectiveSimilarity(cls, uv, xy):
        uv, normMatSrc = cls.normalizeControlPoints(uv)
        xy, normMatDst = cls.normalizeControlPoints(xy)
        ptNum = uv.shape[0]
        minNonCollinearPairs = 2

        x = xy[:, 0].reshape(ptNum, 1)
        y = xy[:, 1].reshape(ptNum, 1)
        X = np.concatenate((
            np.concatenate((x, y,
                            np.ones((ptNum, 1)),
                            np.zeros((ptNum, 1))), axis=1),
            np.concatenate((y, -x,
                            np.zeros((ptNum, 1)),
                            np.ones((ptNum, 1))), axis=1)
        ))

        u = uv[:, 0].reshape(ptNum, 1)
        v = uv[:, 1].reshape(ptNum, 1)
        U = np.concatenate((u, v), axis=0,)

        ### X*r = U, Solve the r by least squared error
        if np.linalg.matrix_rank(X) >= 2 * minNonCollinearPairs:
            U = np.array(U, dtype='float')
            r = np.linalg.lstsq(X, U, rcond=None)[0]
        else:
            raise Exception("At least 2 noncollinear Pts")

        sc, ss, tx, ty = r
        Tinv = np.array(((sc, -ss, 0.),
                         (ss, sc, 0.),
                         (tx, ty, 1.)))
        Tinv = np.array(Tinv, dtype='float')
        Tinv = np.linalg.lstsq(normMatDst,
                               np.dot(Tinv, normMatSrc), rcond=None)[0]

        T = np.linalg.inv(Tinv)
        T[:, 2] = [0, 0, 1]
        return T

    @classmethod
    def normalizeControlPoints(cls, pts):
        ptNum = pts.shape[0]
        cent = np.mean(pts, axis=0)
        ptsNorm = np.subtract(pts, cent)
        distSum = np.sum(np.power(ptsNorm, 2))
        if distSum > 0:
            scaleFactor = np.sqrt(2 * ptNum) / np.sqrt(distSum)
        else:
            scaleFactor = 1

        ptsNorm = scaleFactor * ptsNorm
        normMatInv = np.array(((1 / scaleFactor, 0, 0),
                               (0, 1 / scaleFactor, 0),
                               (cent[0], cent[1], 1)))
        return ptsNorm, normMatInv

cascade.py

import sys
import os
import numpy as np
import pickle
from data_load import DataWrapper
from affine import Affine
from regressorWrapper import RegressorWrapper
from shape import Shape


class Cascador(object):
    """
    Cascade regression for landmark
    """

    def __init__(self):
        self.name = None
        # self.version = None
        self.stageNum = None

        self.dataWrapper = None
        self.regWrapper = None
        self.regressors = []
        self.meanShape = None

    def printParas(self):
        print('------------------------------------------')
        print('----------   Configuration    ------------')
        print('Name           = %s' % (self.name))
        # print('Version        = %s' % (self.version))
        print('Stage Num      = %s' % (self.stageNum))
        print('\n-- Data Config --')
        self.dataWrapper.printParas()
        print('\n-- Regressor Config --')
        self.regWrapper.printParas()
        print('---------   End of Configuration   -------')
        print('------------------------------------------\n')

    def config(self, paras):
        self.name = paras['name']
        # self.version = paras['version']
        self.stageNum = paras['stageNum']

        # Construct the regressor wrapper
        regPara = paras['regressorPara']
        self.regWrapper = RegressorWrapper(regPara)

        # Construct the data wrapper
        dataPara = paras['dataPara']
        if 'dataset' in paras:
            dataPara['dataset'] = paras['dataset']
        self.dataWrapper = DataWrapper(dataPara)

    def train(self, save_path):
        # mkdir model folder for train model
        if not os.path.exists('%s/model' % (save_path)):
            os.mkdir('%s/model' % (save_path))

        # read data first

        trainSet = self.dataWrapper.read()
        dataNum = trainSet.initShapes.shape[0]
        self.meanShape = trainSet.meanShape

        print("\tData Number : %d" % (dataNum))
        trainSet.calResiduals()
        sumR = np.mean(np.abs(trainSet.residuals))
        print("\tManhattan Distance in MeanShape : %f\n" % sumR)

        for idx in range(self.stageNum):
            print("\t%drd stage begin ..." % idx)
            # train one stage
            reg = self.regWrapper.getClassInstance(idx)
            reg.train(trainSet)
            self.regressors.append(reg)

            # calculate the residuals
            trainSet.calResiduals()
            sumR = np.mean(np.abs(trainSet.residuals))
            print("\tManhattan Distance in MeanShape : %f" % sumR)

        self.saveModel(save_path)

    def detect(self, img, bndbox, initShape):
        mShape = Shape.shapeNorm2Real(self.meanShape,
                                      bndbox)
        for reg in self.regressors:
            affineT = Affine.fitGeoTrans(mShape,
                                         initShape)
            reg.detect(img, bndbox, initShape, affineT)

    def loadModel(self, model):
        path_obj = open(model, 'r').readline().strip()
        folder = os.path.split(model)[0]
        objFile = open("%s/%s" % (folder, path_obj), 'rb')
        self = pickle.load(objFile)
        objFile.close()
        return self

    def saveModel(self, save_path):
        name = self.name.lower()
        model_path = "%s/model/train.model" % (save_path)
        model = open(model_path, 'w')
        model.write("%s.pyobj" % (name))
        obj_path = "%s/model/%s.pyobj" % (save_path, name)

        objFile = open(obj_path, 'wb')
        pickle.dump(self, objFile)
        objFile.close()
        model.close()


if __name__ == '__main__':
    config = {
        'name': "face",

        # Different dataset using different reading method
        'dataset': "I",
        'version': "1.0",
        'stageNum': 4,

        'regressorPara':
            {
                'name': 'lbf_reg',
                'para':
                    {
                        'maxTreeNums': [100],
                        'treeDepths': [4],
                        'feaNums': [1000, 750, 500, 375, 250],
                        'radiuses': [0.4, 0.3, 0.2, 0.15, 0.12],
                        # Following para is used to quantize the feature
                        'binNums': [511],
                        'feaRanges': [[-255, 255]],
                    }
            },

        'dataPara':
            {
                'path': "./data/I/",

                # augNum < 1 means don't do augmenting
                'augNum': 0
            }
    }
    cascade = Cascador()
    cascade.config(config)
    save_path = './'
    cascade.train(save_path)


data_load.py

from PIL import Image
import numpy as np
import re
import math
import copy
from shape import Shape
from affine import Affine
import cv2

class TrainSet(object):
    def __init__(self):
        self.imgDatas = []
        self.gtShapes = []
        self.bndBoxs  = []
        self.initShapes = []
        self.ms2reals  = []
        self.real2mss  = []
        self.meanShape = None
        self.augNum = 1

    def getBBoxByPts(self, pts):
        maxV = np.max(pts, axis=0)
        # print('maxV: {}'.format(maxV))
        minV = np.min(pts, axis=0)
        # print('minV: {}'.format(minV))
        return (minV[0], minV[1],
                maxV[0] - minV[0] + 1,
                maxV[1] - minV[1] + 1)

    def read(self, line, folder):
        gtShape = []
        # Load the ground truth of shape
        line = re.split(r' ', line)
        #rect = line[1:5]
        x = line[5::2]
        y = line[6::2]
        for i in range(len(x)):
            gtShape.append((x[i], y[i]))
        gtShape = np.asarray(gtShape, dtype=np.float32)
        # gtShape = gtShape[0, :, :]
        #print(gtShape.shape)  # (1, 21, 2) --> (21, 2)

        # Load the image data
        img_name = line[0]
        img_path = folder + img_name
        img = Image.open(img_path)
        # gray image
        img = img.convert('L')
        img = np.asarray(img, dtype=np.uint8)
        # print(img)

        # Crop the image
        bndBox = self.getBBoxByPts(gtShape)
        #print('bndBox: {}'.format(bndBox))
        return img, gtShape, bndBox

    def cropRegion(self, bbox, scale, img):
        height, width = img.shape
        w = math.floor(scale * bbox[2])
        h = math.floor(scale * bbox[3])
        x = max(0, math.floor(bbox[0] - (w - bbox[2]) / 2))
        y = max(0, math.floor(bbox[1] - (h - bbox[3]) / 2))
        w = min(width - x, w)
        h = min(height - y, h)

        ### If not use deepcopy, the subImg will hold the whole img's memory
        subImg = copy.deepcopy(img[y:y + h, x:x + w])
        return (x, y, w, h), subImg

    def add(self, img, gtShape, bndBox):
        self.imgDatas.append(img)
        self.gtShapes.append(gtShape)
        self.bndBoxs.append(bndBox)

    def calMeanShape(self):
        meanShape = np.zeros(self.gtShapes[0].shape)
        for i, s in enumerate(self.gtShapes):
            normS = Shape.shapeReal2Norm(s, self.bndBoxs[i])
            meanShape = np.add(meanShape, normS)

        self.meanShape = meanShape / len(self.gtShapes)

    def genTrainData(self, augNum):
        # Step1 : Compute the mean shape
        self.calMeanShape()

        # Set meanshape as the initshape
        for bb in self.bndBoxs:
            initShape = Shape.shapeNorm2Real(self.meanShape,
                                             bb)
            self.initShapes.append(initShape)

        # Translate list into numpy's array
        self.initShapes = np.asarray(self.initShapes,
                                     dtype=np.float32)
        self.gtShapes = np.asarray(self.gtShapes,
                                   dtype=np.float32)
        self.bndBoxs = np.asarray(self.bndBoxs,
                                  dtype=np.float32)

        # Shape augment
        if augNum > 1:
            self.augNum = augNum
            self.initShapes = np.repeat(self.initShapes,
                                        augNum,
                                        axis=0)
            self.gtShapes = np.repeat(self.gtShapes,
                                      augNum,
                                      axis=0)
            self.bndBoxs = np.repeat(self.bndBoxs,
                                     augNum,
                                     axis=0)
            # Permutate the augmented shape
            sampleNum = self.initShapes.shape[0]
            for i in range(sampleNum):
                if 0 == i % sampleNum:
                    continue
                shape = self.initShapes[i]
                self.initShapes[i] = Shape.augment(shape)
        return

    def getAffineT(self):
        num = self.gtShapes.shape[0]
        self.ms2real  = []
        self.real2ms  = []

        for i in range(num):
            ### Project to meanshape coordinary
            bndBox = self.bndBoxs[i]
            initShape = self.initShapes[i]
            mShape = Shape.shapeNorm2Real(self.meanShape,
                                         bndBox)
            T = Affine.fitGeoTrans(initShape, mShape)
            self.real2mss.append(T)
            T = Affine.fitGeoTrans(mShape, initShape)
            self.ms2reals.append(T)

    def calResiduals(self):
        ### Compute the affine matrix
        self.getAffineT()

        self.residuals = np.zeros(self.gtShapes.shape)
        num = self.gtShapes.shape[0]
        for i in range(num):
            # Project to meanshape coordinary
            T = self.real2mss[i]
            bndBox = self.bndBoxs[i]
            err = self.gtShapes[i] - self.initShapes[i]
            err = np.divide(err, (bndBox[2], bndBox[3]))
            err = Affine.transPntsForwardWithSameT(err, T)
            self.residuals[i, :] = err

class DataWrapper(object):
    def __init__(self, para):
        self.path = para['path']  # './data/I/'
        self.augNum = para['augNum']  # 1

    def read(self):
        trainSet = TrainSet()
        # folders = ['data/I/', 'data/II/']
        folder = self.path
        ann_path = folder + 'label.txt'
        lines = open(ann_path, 'r').readlines()
        # print((lines[0].strip()))
        for line in lines:
            img, gtShape, bndBox = trainSet.read(line, folder)
            scale = 2
            cropB, img = trainSet.cropRegion(bndBox, scale, img)
            gtShape = np.subtract(gtShape,
                                  (cropB[0], cropB[1]))
            # Get the bndBox.
            bndBox = trainSet.getBBoxByPts(gtShape)

            trainSet.add(img, gtShape, bndBox)


        # Generate the meanShape
        trainSet.genTrainData(self.augNum)
        return trainSet

    def printParas(self):
        print('\tDataset     = %s' % (self.path))
        print('\tAugment Num = %d' % (self.augNum))

if __name__ == '__main__':

    config = {
        'name': "face",

        # Different dataset using different reading method
        'dataset': "I",
        'version': "1.0",
        'stageNum': 4,

        'regressorPara':
            {
                'name': 'lbf_reg',
                'para':
                    {
                        'maxTreeNums': [100],
                        'treeDepths': [4],
                        'feaNums': [1000, 750, 500, 375, 250],
                        'radiuses': [0.4, 0.3, 0.2, 0.15, 0.12],
                        # Following para is used to quantize the feature
                        'binNums': [511],
                        'feaRanges': [[-255, 255]],
                    }
            },

        'dataPara':
            {
                'path': "./data/I/",

                # augNum < 1 means don't do augmenting
                'augNum': 1
            }
    }

    dataloder = DataWrapper(config['dataPara'])
    trainSet = dataloder.read()
    cv2.imshow('img',trainSet.imgDatas[100])
    key = cv2.waitKey()
    print(len(trainSet.imgDatas))

/data/2020-CV-FaceLandmark-训练营-datasets # 数据集路径
demo_train.py

from cascade import Cascador


config = {
    'name': "face",

    # Different dataset using different reading method
    'dataset': "I",
    'version': "1.0",
    'stageNum': 4,

    'regressorPara':
        {
            'name': 'lbf_reg',
            'para':
                {
                    'maxTreeNums': [100],
                    'treeDepths': [4],
                    'feaNums': [1000, 750, 500, 375, 250],
                    'radiuses': [0.4, 0.3, 0.2, 0.15, 0.12],
                    # Following para is used to quantize the feature
                    'binNums': [511],
                    'feaRanges': [[-255, 255]],
                }
        },

    'dataPara':
        {
            'path': "./data/I/",

            # augNum < 1 means don't do augmenting
            'augNum': 0
        }
}
cascade = Cascador()
cascade.config(config)
save_path = './'
cascade.train(save_path)

lbfRegressor.py

import numpy as np
from scipy.sparse import lil_matrix
from sklearn.svm import LinearSVR
from randForest import RandForest
from affine import Affine


class LBFRegressor(object):
    """
    Face Alignment at 3000 FPS via Regressing LBF
    """

    def __init__(self, paras):
        self.maxTreeNum = paras["maxTreeNum"]
        self.treeDepth = paras["treeDepth"]
        self.feaNum = paras["feaNum"]
        self.radius = paras["radius"]
        self.binNum = paras["binNum"]
        self.feaRange = paras["feaRange"]
        self.rfs = []
        self.regs = []

    def train(self, trainSet):
        pntNum = trainSet.meanShape.shape[0]
        treeNum = int(self.maxTreeNum / pntNum)

        # Train the random forests
        for i in range(pntNum):
            rf = RandForest(treeDepth=self.treeDepth,
                            treeNum=treeNum,
                            feaNum=self.feaNum,
                            radius=self.radius,
                            binNum=self.binNum,
                            feaRange=self.feaRange)
            rf.train(trainSet, i)
            self.rfs.append(rf)

        # Extract the local binary features
        feas = self.genFeaOnTrainset(trainSet)

        # Global regression
        y = trainSet.residuals
        y = y.reshape(y.shape[0], y.shape[1] * y.shape[2])
        for i in range(pntNum * 2):
            # TODO Show the training result
            reg = LinearSVR(epsilon=0.0,
                            C=1.0 / feas.shape[0],
                            loss='squared_epsilon_insensitive',
                            fit_intercept=True)
            reg.fit(feas, y[:, i])
            self.regs.append(reg)

        # Update the initshapes
        for i in range(pntNum):
            regX = self.regs[2 * i]
            regY = self.regs[2 * i + 1]

            x = regX.predict(feas)
            y = regY.predict(feas)
            delta = np.squeeze(np.dstack((x, y)))
            delta = Affine.transPntsForwardWithDiffT(delta,
                                                     trainSet.ms2reals)
            delta = np.multiply(delta,
                                trainSet.bndBoxs[:, [2, 3]])
            trainSet.initShapes[:, i, :] = trainSet.initShapes[:, i, :] + delta

    def detect(self, img, bndbox, initShape, affineT):
        # Extract features
        fea = self.extractFea(img, bndbox,
                              initShape, affineT)
        pntNum = initShape.shape[0]
        # Get the residules
        for i in range(pntNum):
            regX = self.regs[2 * i]
            regY = self.regs[2 * i + 1]

            x = regX.predict(fea)
            y = regY.predict(fea)
            delta = np.squeeze(np.dstack((x, y)))
            delta = Affine.transPntForward(delta, affineT)
            delta = np.multiply(delta, (bndbox[2], bndbox[3]))
            initShape[i, :] = initShape[i, :] + delta

    def getFeaDim(self):
        feaDim = 0
        for rf in self.rfs:
            for tree in rf.trees:
                feaDim = feaDim + tree.leafNum
        return feaDim

    def extractFea(self, img, bndbox, initShape, affineT):
        feaDim = self.getFeaDim()
        fea = lil_matrix((1, feaDim),
                         dtype=np.int8)

        offset = 0
        for j, rf in enumerate(self.rfs):
            point = initShape[j]
            for t in rf.trees:
                # TODO judge the empty tree
                leafIdx, dim = t.genBinaryFea(img,
                                              bndbox,
                                              affineT,
                                              point)
                fea[0, offset + leafIdx] = 1
                offset = offset + dim
        return fea

    def genFeaOnTrainset(self, trainSet):
        feaDim = self.getFeaDim()
        sampleNum = trainSet.initShapes.shape[0]
        feas = lil_matrix((sampleNum, feaDim),
                          dtype=np.int8)

        augNum = trainSet.augNum
        for i in range(sampleNum):
            imgData = trainSet.imgDatas[int(i / augNum)]
            bndBox = trainSet.bndBoxs[i]
            affineT = trainSet.ms2reals[i]
            shape = trainSet.initShapes[i]

            offset = 0
            for j, rf in enumerate(self.rfs):
                point = shape[j]
                for t in rf.trees:
                    # TODO judge the empty tree
                    leafIdx, dim = t.genBinaryFea(imgData,
                                                  bndBox,
                                                  affineT,
                                                  point)
                    feas[i, offset + leafIdx] = 1
                    offset = offset + dim
        return feas

p1.py

Jupyter Notebook
p1.py
2020728日
Python
File
Edit
View
Language
1
#cfrom my_cascade import Cascador
2
from cascade import Cascador
3
from data_load import TrainSet
4
from shape import Shape
5
import numpy as np
6
import cv2
789
def display(img, gtPnts, resPnts):
10
    gtPnts = np.round(gtPnts).astype(np.int32)
11
    resPnts = np.round(resPnts).astype(np.int32)
1213
    showImg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
14
    for i in range(gtPnts.shape[0]):
15
        cv2.circle(showImg, (gtPnts[i, 0], gtPnts[i, 1]),
16
                   3, (0, 0, 255), -1)
17
        cv2.circle(showImg, (resPnts[i, 0], resPnts[i, 1]),
18
                   3, (255, 0, 0), -1)
19
    return showImg
2021
model = './model/train.model'
22
cas = Cascador()
23
cas = cas.loadModel(model)
2425
folders = ['data/I/', 'data/II/']
26
imgListPath = folders[1] + 'label.txt'
27
pathList = open(imgListPath, 'r').readlines()
28
reader = TrainSet()
2930
for imgPath in pathList:

predict.py

#from my_cascade import Cascador
from cascade import Cascador
from data_load import TrainSet
from shape import Shape
import numpy as np
import cv2


def display(img, gtPnts, resPnts):
    gtPnts = np.round(gtPnts).astype(np.int32)
    resPnts = np.round(resPnts).astype(np.int32)

    showImg = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)
    for i in range(gtPnts.shape[0]):
        cv2.circle(showImg, (gtPnts[i, 0], gtPnts[i, 1]),
                   3, (0, 0, 255), -1)
        cv2.circle(showImg, (resPnts[i, 0], resPnts[i, 1]),
                   3, (255, 0, 0), -1)
    return showImg

model = './model/train.model'
cas = Cascador()
cas = cas.loadModel(model)

folders = ['data/I/', 'data/II/']
imgListPath = folders[1] + 'label.txt'
pathList = open(imgListPath, 'r').readlines()
reader = TrainSet()

for imgPath in pathList:
    img, gtShape, bndBox = reader.read(imgPath, folders[1])
    scale = 3
    cropB, img = reader.cropRegion(bndBox, scale, img)
    gtShape = np.subtract(gtShape, (cropB[0], cropB[1]))
    # TODO try face detector.
    bndbox = Shape.getBBoxByPts(gtShape)
    # Set the initial shape
    # print('bndbox: {}'.format(bndbox))
    # print('type bndbox: {}'.format(type(bndbox)))
    initShape = Shape.shapeNorm2Real(cas.meanShape, bndbox)
    # Detect the landmark
    cas.detect(img, bndbox, initShape)
    showImg = display(img, gtShape, initShape)
    cv2.imshow("Landmark", showImg)
    key = cv2.waitKey(1000)
    if key in [ord("q"), 27]:
        break

cv2.destroyAllWindows()

randForest.py

from lbfRegressor import *


class RegressorWrapper(object):
    def __init__(self, paras):        
        self.name = paras['name'].upper()
        self.para = paras['para']

    def printParas(self):        
        print('\t%-20s= %s'%('name', self.name))
        for key in self.para:
            print('\t%-20s= %s'%(key, str(self.para[key])))
                  
    def getParaLBF(self, idx):
        regPara = dict()        

        length = len(self.para['maxTreeNums'])
        _idx = min(idx, length-1)
        regPara['maxTreeNum'] = self.para['maxTreeNums'][_idx]
                     
        length = len(self.para['treeDepths'])
        _idx = min(idx, length-1)
        regPara['treeDepth'] = self.para['treeDepths'][_idx] 

        length = len(self.para['feaNums'])
        _idx = min(idx, length-1)
        regPara['feaNum'] = self.para['feaNums'][_idx] 

        length = len(self.para['radiuses'])
        _idx = min(idx, length-1)
        regPara['radius'] = self.para['radiuses'][_idx]

        length = len(self.para['binNums'])
        _idx = min(idx, length-1)
        regPara['binNum'] = self.para['binNums'][_idx]

        length = len(self.para['feaRanges'])
        _idx = min(idx, length-1)
        regPara['feaRange'] = self.para['feaRanges'][_idx]

        return regPara

    def getClassInstance(self, idx):                   
        if "LBF_REG" == self.name:
            regPara = self.getParaLBF(idx)
            regClass = LBFRegressor
        else:
            raise Exception("Unsupport: %s " %(self.name))
        
        return regClass(regPara)

regressorWrapper.py

from lbfRegressor import *


class RegressorWrapper(object):
    def __init__(self, paras):        
        self.name = paras['name'].upper()
        self.para = paras['para']

    def printParas(self):        
        print('\t%-20s= %s'%('name', self.name))
        for key in self.para:
            print('\t%-20s= %s'%(key, str(self.para[key])))
                  
    def getParaLBF(self, idx):
        regPara = dict()        

        length = len(self.para['maxTreeNums'])
        _idx = min(idx, length-1)
        regPara['maxTreeNum'] = self.para['maxTreeNums'][_idx]
                     
        length = len(self.para['treeDepths'])
        _idx = min(idx, length-1)
        regPara['treeDepth'] = self.para['treeDepths'][_idx] 

        length = len(self.para['feaNums'])
        _idx = min(idx, length-1)
        regPara['feaNum'] = self.para['feaNums'][_idx] 

        length = len(self.para['radiuses'])
        _idx = min(idx, length-1)
        regPara['radius'] = self.para['radiuses'][_idx]

        length = len(self.para['binNums'])
        _idx = min(idx, length-1)
        regPara['binNum'] = self.para['binNums'][_idx]

        length = len(self.para['feaRanges'])
        _idx = min(idx, length-1)
        regPara['feaRange'] = self.para['feaRanges'][_idx]

        return regPara

    def getClassInstance(self, idx):                   
        if "LBF_REG" == self.name:
            regPara = self.getParaLBF(idx)
            regClass = LBFRegressor
        else:
            raise Exception("Unsupport: %s " %(self.name))
        
        return regClass(regPara)
            

                     


shape.py

import numpy as np
import math
import random


# API for shape operation
class Shape(object):
    @classmethod
    def getBBoxByPts(cls, pts):
        maxV = np.max(pts, axis=0)
        minV = np.min(pts, axis=0)
        return (minV[0], minV[1],
                maxV[0] - minV[0] + 1,
                maxV[1] - minV[1] + 1)

    @classmethod
    def shapeReal2Norm(cls, realShape, bndBox):
        normShape = np.subtract(realShape,
                                (bndBox[0], bndBox[1]))
        normShape = np.divide(normShape,
                              (bndBox[2] - 1, bndBox[3] - 1))
        return normShape

    @classmethod
    def shapeNorm2Real(cls, normShape, bndBox):
        realShape = np.multiply(normShape,
                                (bndBox[2] - 1, bndBox[3] - 1))
        realShape = np.add(realShape,
                           (bndBox[0], bndBox[1]))
        return realShape

    @classmethod
    def augment(cls, shape):
        shape = cls.scale(shape)
        shape = cls.rotate(shape)
        shape = cls.shift(shape)
        return shape

    @classmethod
    def scale(cls, shape):
        ### scale in [0.9, 1.1]
        scale = 1 + 0.2 * (random() - 0.5)
        cent = np.mean(shape, axis=0)
        newShape = scale * (shape - cent) + cent
        return newShape

    @classmethod
    def rotate(cls, shape):
        ### Rotate in [-30,30]
        angle = (random() - 0.5) * math.pi / 3

        ### TODO try the middle point instead of mean point
        # cent  = (np.max(shape, axis=0)-
        #          np.min(shape, axis=0))/2.0
        cent = np.mean(shape, axis=0)
        rotateT = np.array(((math.cos(angle),
                             -math.sin(angle)),
                            (math.sin(angle),
                             math.cos(angle))),
                           dtype=np.float32)
        newShape = np.dot(shape - cent, rotateT) + cent
        return newShape

    @classmethod
    def shift(cls, shape):
        ### shift in [-0.1, 0.1]
        shiftX = 0.2 * (random() - 0.5)
        shiftY = 0.2 * (random() - 0.5)
        shift = np.max(shape, axis=0) - np.min(shape, axis=0)
        shift = shift * (shiftX, shiftY)
        newShape = shape + shift
        return newShape










  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值