Directorio de artículos
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
- 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- 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.- 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;
- Ordene los cuadros con y, lo que equivale a ordenar las líneas de texto de arriba a abajo;
- 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/3
y 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