『OCR_recognition』 chineseocr


1. Proceso de reconocimiento de Chineseocr

paso 1: detección de la dirección del texto

  • Contiene: un algoritmo de clasificación de cuatro de VGG16 (0,90,180,270), detección de ángulo pequeño, estimado_ ángulo de inclinación
  • Este paso se puede omitir porque el algoritmo es robusto dentro de un cierto rango de ángulos

paso 2: detección de texto

  1. Use yolo para detectar el área text_proposals que contiene el cuadro de texto;
    Nota: La entrada de la imagen es fija y comprimida a 608 * 608, el ancho del cuadro se fija en 8 y la altura es 11 ~ 283, un total de 9 anclas. No importa si el cuadro no enmarca el texto. La altura del cuadro principal es Si la superposición de altura del texto supera un cierto umbral, se puede definir como una muestra positiva
  2. text_proposals usa el algoritmo de construcción de líneas de texto para fusionarse en la misma
    línea ; Nota: El método de construcción de líneas de texto es buscar 3 casillas a la izquierda y a la derecha, y si la superposición de la altura es mayor que un cierto umbral, conéctelas juntas . Por supuesto, hay algunas operaciones detalladas, es necesario mirar la lógica del código. En resumen, este paso es conectar las propuestas de la misma línea.
  3. Coloque una línea recta en el centro de la caja en la misma fila, y luego use xmin, xmax más los parámetros de línea recta para obtener los cuatro puntos de la caja (con propiedades de rotación), cajas;
  4. Ordene los cuadros con y, lo que equivale a ordenar las líneas de texto de arriba a abajo;
  5. Ingrese a CRNN uno por uno según cada fila de casillas.

paso 3: reconocimiento de texto

  • Gire la imagen de la región de ROI de acuerdo con el cuadro. Antes de ingresar a la CNN, la altura de la imagen se comprime a 32 y el tamaño de salida es (lote, w, 512), porque la altura se fija en 32. Después de 5 veces de reducción de resolución en CNN, la altura del mapa de características se convierte en 1. A continuación, la salida de CNN debe ingresarse en BLSTM. La dimensión de la salida de LSTM es w, lo que significa que hay una predicción para cada 8 píxeles de la imagen original, porque la reducción de resolución horizontal es solo 3 veces, lo que equivale a todas las propuestas. Hay una predicción, por lo que definitivamente habrá predicciones repetidas, y CTC se usa para deduplicar para generar la secuencia de etiquetas verdadera

Dos, extracto de Darknet text_proposals

El autor extrae text_proposals mejorando el método de yolo

Paso 1: La primera mejora es limitar el ancho a 8, y el resto es el mismo. Hay un total de nueve acors, tres capas de salida y 3 predicciones de anclas para cada capa, que es diferente del cambio de tamaño fijo de la entrada original de yolo 416*416, que se fija aquí 608*608, La clasificación es 2, y se juzga si es texto o no.

# 文字检测引擎 
pwd = os.getcwd()
opencvFlag = 'keras' # keras,opencv,darknet,模型性能 keras>darknet>opencv
IMGSIZE = (608,608)  # yolo3 输入图像尺寸
# keras 版本anchors
keras_anchors = '8,11, 8,16, 8,23, 8,33, 8,48, 8,97, 8,139, 8,198, 8,283'
class_names = ['none','text',]
kerasTextModel=os.path.join(pwd, "models", "text.h5") 	# keras版本模型权重文件

############## darknet yolo  ##############
darknetRoot = os.path.join(os.path.curdir,"darknet") 	# yolo 安装目录
yoloCfg     = os.path.join(pwd,"models","text.cfg")
yoloWeights = os.path.join(pwd,"models","text.weights")
yoloData    = os.path.join(pwd,"models","text.data")

Paso 2: A continuación, de acuerdo con el número total de anclajes y el proceso de post-fabricación, yolo hará predicciones en los tres mapas de características de salida anchors_num/3=9/3y predecirá los anclajes de 3 escalas en cada capa.

def yolo_text(num_classes,anchors,train=False):
    imgInput = Input(shape=(None,None,3))
    darknet = Model(imgInput, darknet_body(imgInput))
    num_anchors = len(anchors)//3
    x, y1 = make_last_layers(darknet.output, 512, num_anchors*(num_classes+5))

    x = compose(DarknetConv2D_BN_Leaky(256, (1,1)),
    			UpSampling2D(2))(x)
    x = Concatenate()([x,darknet.layers[152].output])
    x, y2 = make_last_layers(x, 256, num_anchors*(num_classes+5))

    x = compose(DarknetConv2D_BN_Leaky(128, (1,1)),
    			UpSampling2D(2))(x)
    x = Concatenate()([x,darknet.layers[92].output])
    x, y3 = make_last_layers(x, 128, num_anchors*(num_classes+5))
    
    out = [y1,y2,y3]
    if train:
        num_anchors = len(anchors)
        y_true = [Input(shape=(None, None,num_anchors//3, num_classes+5)) for l in range(3)]
        loss = Lambda(yolo_loss,output_shape=(4,),name='loss',
        			  arguments={
    
    'anchors': anchors,
        			   			 'num_classes': num_classes, 
        			   			 'ignore_thresh': 0.5,})(out+y_true)
        
        def get_loss(loss,index):
            return loss[index]
        
    
        lossName = ['class_loss','xy_loss','wh_loss','confidence_loss']   
        lossList = [Lambda(get_loss,output_shape=(1,),name=lossName[i],arguments={
    
    'index':i})(loss) for i in range(4)]
        textModel = Model([imgInput, *y_true], lossList)
        return textModel
        
    else:
        textModel = Model([imgInput],out)
        return textModel

paso 3: A continuación, cargue el modelo previamente entrenado

textModel.load_weights('models/text.h5')  # 加载预训练模型权重

Paso 4: A continuación, lea y mejore los datos.

trainLoad = data_generator(jpgPath[:num_train], anchors, num_classes,splitW)
testLoad  = data_generator(jpgPath[num_train:], anchors, num_classes,splitW)

Paso 5: Después de obtener la caja con la escala giratoria de la etiqueta, divida las cajas según el ancho de línea de 8.

def get_box_spilt(boxes,im,sizeW,SizeH,splitW=8,isRoate=False,rorateDegree=0):
    """ isRoate:是否旋转box """
    size = sizeW,SizeH
    if isRoate:
        # 旋转box
        im,boxes = get_rorate(boxes,im,degree=rorateDegree)
    # 采用 padding 的方式不改变比例的压缩图像
    newIm,f  = letterbox_image(im, size)
    # 图像压缩后。boxes 也要做相应的压缩
    newBoxes = resize_box(boxes,f)
    #按照行 8 分割 box,一直分割覆盖包含最后
    newBoxes = sum(box_split(newBoxes,splitW),[])
    newBoxes = [box+[1] for box in newBoxes]
    return newBoxes,newIm
    
def box_split(boxes,splitW = 15):
    newBoxes = []
    for box in boxes:
        w = box['w']
        h = box['h']
        cx = box['cx']
        cy=box['cy']
        angle = box['angle']
        x1,y1,x2,y2,x3,y3,x4,y4 = xy_rotate_box(cx,cy,w,h,angle)
        splitBoxes =[]
        i = 1
        tanAngle = tan(-angle)
        
        while True:
            flag = 0 if i==1 else 1
            xmin = x1+(i-1)*splitW
            ymin = y1-tanAngle*splitW*i
            xmax = x1+i*splitW
            ymax = y4-(i-1)*tanAngle*splitW +flag*tanAngle*(x4-x1)
            if xmax>max(x2,x3) and xmin>max(x2,x3):
                break
            splitBoxes.append([int(xmin),int(ymin),int(xmax),int(ymax)])
            i+=1
        
        newBoxes.append(splitBoxes)
    return newBoxes

Paso 6: determina el optimizador

adam = tf.keras.optimizers.Adam(lr=0.0005)

paso 7: ajuste de pérdida

def yolo_loss(args, anchors, num_classes, ignore_thresh=.5):
	# 详细看代码
	pass

Supongo que te gusta

Origin blog.csdn.net/libo1004/article/details/111722863
Recomendado
Clasificación