master
/ train / darknet / data-ready.py

data-ready.py @098cb66 raw · history · blame

##  数据来源于 icdr2019 数据下载地址 http://rrc.cvc.uab.es/?ch=14
import numpy as np
import cv2
def polylines(img,points):
    im = np.zeros(img.shape[:2], dtype = "uint8")
    for point in points:
        b = np.array([point],dtype = np.int32)
        cv2.fillPoly(im, b, 255)
    return im

def check_points(points,w,h):
    ##检测标注是否正确
    check=False
    for point in points:
        for x,y in point:
            if x>w or y>h:
                check=True
                break
        if check:
            break
    return check

def get_points(res):
    points =[]
    for line in res:
        points.append(line['points'])
    return points


def resize_im(img, scale=416, max_scale=608):
    h,w = img.shape[:2]
    f=float(scale)/min(h, w)
    if max_scale is not None:
        if  f*max(h, w)>max_scale:
            f=float(max_scale)/max(h, w)
    newW,newH = int(w*f),int(h*f)
    newW,newH = newW-(newW%32),newH-(newH%32)
    fw = w/newW
    fh = h/newH
    tmpImg = cv2.resize(img, None, None, fx=1/fw, fy=1/fh, interpolation=cv2.INTER_LINEAR)
    return tmpImg,fw,fh

def cleam_im(im):
        avg = 127
        im[im>avg]=255
        im[im<=avg]=0
        y,x = np.where(im==255)
        xmin,ymin,xmax,ymax = (min(x),min(y),max(x),max(y))
        return xmin,ymin,xmax,ymax
    
def adjust_height(h):
    """
    调整box高
    """
    heights = [11, 16, 23, 33, 48, 68, 97, 139, 198, 283]
    N = len(heights)
    for i in range(N-1):
        if h<=heights[i]+heights[i]*0.44/2:
            return heights[i]
    return h

def img_split_to_box(im,splitW = 15,adjust=True):
    """
    均等分割box
    """
    tmpIm = im==255
    h,w = tmpIm.shape[:2]
    num = w//splitW+1
    
    box = []
    for i in range(num-1):
        xmin,ymin,xmax,ymax = splitW*i,0,splitW*(i+1),h
        ##迭代寻找最优ymin,ymax
        childIm = tmpIm[ymin:ymax,xmin:xmax]
        checkYmin = False
        checkYmax = False
        for j in range(ymax):
            if not checkYmin:
                if childIm[j].max():
                    ymin = j
                    checkYmin = True
            if not checkYmax:
                if childIm[ymax-j-1].max():
                    ymax = ymax-j
                    checkYmax = True
               
        if adjust:
            childH = ymax-ymin+1
            cy = (ymax+ymin)/2
            childH = adjust_height(childH)
            ymin = cy-childH/2
            ymax = cy+childH/2     
        box.append([xmin,ymin,xmax,ymax])
    
    return box

def resize_img_box(p,scale=416, max_scale=608,splitW=15,adjust=True):
    path = root.format(p)
    img = cv2.imread(path)
    if img is None:
        return None,[]
    points = get_points(train_labels[f'{p}'])
    h,w = img.shape[:2]
    check = check_points(points,w,h)
    if  check:
        return None,[]
    img,fw,fh = resize_im(img, scale=scale, max_scale=max_scale)
    boxes = []
    for point in points:
        point = [[bx[0]/fw,bx[1]/fh] for bx in point]
        im = polylines(img,[point])
        if im.max()==0:
            continue
            
        xmin,ymin,xmax,ymax = cleam_im(im)
        tmp = im[ymin:ymax,xmin:xmax]
        box = img_split_to_box(tmp,splitW = splitW,adjust=adjust)
        childBoxes = []
        for bx in box:
            xmin_,ymin_,xmax_,ymax_ = bx

            xmin_,ymin_,xmax_,ymax_ = xmin+xmin_,ymin_+ymin,xmax_+xmin,ymax_+ymin
            boxes.append([xmin_,ymin_,xmax_,ymax_])

    return img,boxes
    
def convert(size, box):
    """
    box = xmin,ymin,xmax,ymax
    转化为darknet训练所需格式 cx,cy,w,h
    """
    dw = 1./(size[0])
    dh = 1./(size[1])
    x = (box[0] + box[2])/2.0 - 1
    y = (box[1] + box[3])/2.0 - 1
    w = box[2] - box[0]
    h = box[3] - box[1]
    x = x*dw
    w = w*dw
    y = y*dh
    h = h*dh
    return [str(x),str(y),str(w),str(h)]


def convert_annotation(p,scale=608, max_scale=1024,splitW=8,adjust=False):
    img,boxes = resize_img_box(p,scale=scale, max_scale=scale,splitW=splitW,adjust=adjust)
    if img is None or len(boxes)==0:
        return None,''
    h,w = img.shape[:2]
    newBoxes = []
    for bx in boxes:
        cls_id = 1
        bb = convert((w,h), bx)
        newBoxes.append(' '.join([str(cls_id)]+bb))
    return img,'\n'.join(newBoxes)

def write_for_darknet(img,newBoxes,filename):
    imgP = os.path.join(JPEGP,filename+'.jpg')
    txtP = os.path.join(labelP,filename+'.txt')
    cv2.imwrite(imgP,img)
    with open(txtP,'w') as f:
        f.write(newBoxes)
        
if __name__=='__main__':
    import os
    import json
    dataRoot    = '/tmp/ICDR2019/'##icdr 数据所在目录
    darknetRoot = '/tmp/darknet'##darknet目录
    wP          = '/tmp/darknet53.conv.74'##darknet 预训练模型权重

    root = dataRoot+'train_images/{}.jpg'
    with open(dataRoot+'train_labels.json') as f:
        train_labels = json.loads(f.read())


    ##创建voc目录    
    labelP = os.path.join(darknetRoot,'VOCdevkit','VOC2007','labels')
    JPEGP = os.path.join(darknetRoot,'VOCdevkit','VOC2007','JPEGImages')
    if not os.path.exists(labelP):
        os.makedirs(labelP)
    if not os.path.exists(JPEGP):
        os.makedirs(JPEGP)

    
    for p in train_labels.keys():
        img,newBoxes = convert_annotation(p,scale=608, max_scale=1024,splitW=8,adjust=True)
        if img is None or len(newBoxes)==0:
            continue
        write_for_darknet(img,newBoxes,p)
       
    ##训练集,测试集划分
    from sklearn.model_selection import train_test_split
    from glob import glob
    jpgPaths = glob(os.path.join(JPEGP,'*.jpg'))
    train,test = train_test_split(jpgPaths,test_size=0.1)
    trainP     = os.path.join(darknetRoot,'VOCdevkit','VOC2007','train.txt') 
    testP      = os.path.join(darknetRoot,'VOCdevkit','VOC2007','test.txt') 
    with open(trainP,'w') as f:
        f.write('\n'.join(train))
    with open(testP,'w') as f:
        f.write('\n'.join(test))
        
        
    ## 生成 voc.name voc.data
    vocDatap      = os.path.join(darknetRoot,'VOCdevkit','VOC2007','voc.data') 
    vocNameP      = os.path.join(darknetRoot,'VOCdevkit','VOC2007','voc.name')
    with open(vocDatap,'w') as f:
        f.write('classes= 2\n')
        f.write('train  = {}\n'.format(trainP))
        f.write('valid = {}\n'.format(testP))
        f.write('names  ={}\n'.format(vocNameP))
        f.write('backup = backup')


    with open(vocNameP,'w') as f:
        f.write('none\n')
        f.write('text\n')
       
    ## 生成训练脚本
    chineseocrP=os.getcwd()
    textCfg = os.path.join(chineseocrP,'models/text.cfg')
    
    with open(os.path.join(chineseocrP,'train/darknet/','train.sh'),'w') as f:
        f.write('cd {}\n'.format(darknetRoot))
        f.write('./darknet detector train {} {} {}'.format(vocDatap,textCfg,wP))

    ## 训练模型