Realización de PyTorch | Reconocimiento OCR de matrículas, "Detección de objetivos de aprendizaje profundo de PyTorch"

Nota: este artículo se seleccionó del libro "PyTorch Deep Learning Target Detection" publicado por China Water Resources and Hydropower Press, con cambios

¡Bienestar! ¡Envía libros gratis! !

d866fcb432037fbdc017aaa8de10f7a1.png

Respuesta de antecedentes de la cuenta oficial [Aprendizaje automático y generación de IA] : 168 . Puedes participar en la actividad de entrega gratuita de libros, y la fecha límite para la actividad es el 26-02-2023 a las 22:00 horas.

Respuesta desde el fondo de la cuenta oficial: 168 (recomendado seleccionar y copiar)

También puedes comprarlo tú mismo:

90dd9d293ee86540b978d5e76ae551d7.jpeg

0d72ecd43391c75beb66cd01358d64cf.jpeg

b102a6d56b3921a4433deb33943f9811.jpeg

457a90a6c962e442297b6f6b61e785ce.jpeg

01 Análisis del principio OCR

      La parte de reconocimiento de matrículas de vehículos utilizada en este documento se compone de CNN+LSTM+CTC. Toda la parte de la red se puede dividir en tres partes. En primer lugar, la red troncal CNN se utiliza para extraer la información característica de los caracteres y, en segundo lugar, una Se utiliza una red LSTM bidireccional profunda. Sobre la base de características convolucionales, se extraen las características de secuencia de texto o caracteres y, finalmente, se introduce la estructura CTC para resolver el problema de que los caracteres no se pueden alinear durante el entrenamiento. La estructura de combinación detallada se muestra en la Figura 1.

e4c8fc31cd89377d25d0131629fd9033.png

Figura 1 Diagrama de estructura del algoritmo OCR

      (1) La red troncal CNN extrae características. Dado que la red solo extrae toda la información de las características de la placa de matrícula en forma de convolución para reconocer los caracteres de la placa de matrícula, la entrada del algoritmo es la imagen completa de la placa de matrícula.

      (2) LSTM extrae información de secuencia. LSTM como red de memoria a corto plazo es una estructura RNN especial, que puede evitar el problema de la dependencia a largo plazo. A diferencia de la red neuronal convolucional cíclica (RNN), la RNN puede guardar el estado en diferentes momentos, y la estructura de red única de LSTM puede guardar las características de cuatro estados diferentes.La unidad de estructura de red LSTM se compone principalmente de tres tipos: la puerta de olvido, la puerta de entrada y la puerta de salida Combinadas, el diagrama de estructura de la unidad se muestra en la Figura 2 a continuación.

5f605ee0f4ae47d386628d264e1c06c4.png

Figura 2 unidad de red LSTM

     La puerta de olvido decide principalmente descartar y retener algunas de las características de la red. El proceso de implementación consiste en leer el parámetro de entrada de la red Xt y el estado de salida ht-1 de la capa anterior, y normalizarlo a 0-1 a través del Sigmoid función En el intervalo de rango, 0 significa características descartadas y 1 significa características que deben conservarse. La fórmula de implementación de la puerta de olvido se muestra en la fórmula 1:

132d0868f3262c165a41a05d295875a2.png

       La estructura de la puerta de entrada es diferente a la de la puerta de olvido. Esta parte se divide en dos partes, una parte es similar a la puerta de olvido y la otra parte se basa en la puerta de olvido. La función está asignada entre: 1 y 1 a través de la función tanh, donde -1 representa diferentes partes actualizadas, 1 representa la parte característica que debe actualizarse, como se muestra en las fórmulas 2 y 3:

1cde54bcd1cca9dea0d661d9f848b161.png

      La función sigmoidea en la puerta de salida determina qué parte de la función debe emitirse. Las características de la parte de salida pasan la función tanh y la multiplican con la salida de la función sigmoidea para finalmente determinar las características de la parte de salida. La parte de realización de la fórmula se muestra en las fórmulas 4 y 5:

ea1faef77b451bac4c4163a491d6325e.png

     (3) Estructura del CTC. La estructura CTC es una solución para la alineación automática en el reconocimiento de voz.La aplicación de la estructura de red CTC al reconocimiento de caracteres resuelve el problema de cortar caracteres artificialmente, mejorando así la precisión de todo el algoritmo.

02 Producción del conjunto de datos del número de matrícula

El conjunto de datos utilizado en esta parte de este capítulo se obtiene al interceptar la matrícula en la imagen sobre la base de la primera parte del conjunto de datos.Escribir un script Python en una máquina equipada con un entorno Python para leer el conjunto de datos y analizarlo desde el archivo xml marcado La posición de la imagen donde se encuentra la placa de matrícula. Para garantizar la integridad de la imagen recortada, también se adopta el método de ampliar el valor del píxel. Para la posición de la matrícula guardada en el archivo xml, los puntos de coordenadas de la esquina superior izquierda se reducen respectivamente en 5 píxeles, y el los puntos de coordenadas de la esquina inferior derecha se incrementan en 5 píxeles respectivamente. A diferencia de la detección de objetivos, además de la imagen de datos, el texto de la placa de matrícula debe modificarse de acuerdo con el texto de la placa de matrícula. La figura 3 muestra el conjunto de datos de matrícula entrenable procesado.

4161e7dc7d15a1af89c0331dd8390afc.png

Figura 3 Conjunto de datos de matrícula

      Los datos después del procesamiento preliminar son solo la imagen de la matrícula específica, y la imagen aún no se ha marcado, por lo que no se puede usar directamente como un conjunto de datos para entrenar el algoritmo OCR. Esta parte realiza principalmente el reconocimiento del texto. en la placa de matrícula. YOLOv3 ha sido aprobado en la sección 3.2 de este capítulo. El algoritmo realiza el posicionamiento y clasificación de todas las placas de matrícula bloqueadas, placas de matrícula descolgadas y otros tipos de placas de matrícula. Sobre la base del algoritmo de detección de objetivos, otros tipos de imágenes en los resultados de reconocimiento se seleccionan para su posterior procesamiento. Además de la calidad de la anotación de la imagen, el número de imágenes también afecta directamente si el modelo final tiene una mejor capacidad de generalización. Además de la matrícula normal, el vehículo las imágenes de matrículas en el conjunto de datos también tienen matrículas medio ocluidas. Antes de la capacitación, las imágenes deben procesarse y el proceso de implementación se muestra en la Figura 4.

61a4fe72ee7770b8bb2e00a7607448c6.png

Figura 4 Diagrama de flujo del procesamiento de datos del conjunto de entrenamiento

      (1) Modifique el nombre de la imagen como el texto de la placa de matrícula. A diferencia del método de etiquetado de detección de objetivos, el etiquetado de la matrícula debe cambiarse al nombre de la imagen de acuerdo con el texto de la imagen real, y el sufijo de la imagen permanece sin cambios. completado, el conjunto de datos debe modificarse de acuerdo con el formato requerido por el proyecto real, de acuerdo con el uso del programa Python script para dividir la imagen en un conjunto de entrenamiento y un conjunto de prueba de acuerdo con la proporción de 6: 1, cree un nuevo directorio de datos en el proyecto, y cree dos rutas de tren/texto y prueba/texto respectivamente en la carpeta de datos, donde tren/texto se usa para almacenar las imágenes procesadas del conjunto de entrenamiento, el archivo binario procesado train.pkl se almacena en tren, la prueba la imagen del conjunto se almacena en test/text, y el archivo binario test.pkl del conjunto de prueba se almacena en test.

       (2) Genere un archivo pkl. El archivo pkl es un formato de archivo para almacenar contenido binario.Durante el proceso de entrenamiento, la red lee la información de texto y las imágenes correspondientes del archivo pkl para el entrenamiento. Almacene los nombres de las imágenes en el conjunto de entrenamiento y el conjunto de verificación en secuencia en el archivo pkl recién creado, denominado train.pkl y test.pkl, y almacene los nombres de imágenes correspondientes como números de serie.

03 Modificar el archivo de pre-peso

      En este capítulo, el entrenamiento se realiza sobre la base de pesos preentrenados. La ventaja de usar pesos preentrenados es que no solo puede garantizar una rápida convergencia del modelo, reducir el tiempo de entrenamiento del modelo, sino también evitar la explosión de gradiente. y gradientes durante el proceso de entrenamiento causados ​​por el entrenamiento desde cero. Ocurre la desaparición. Los pesos previos al entrenamiento almacenan datos a través del módulo de subclase OrderedDict en las colecciones de módulos de terceros de Python. OrderedDict es un diccionario ordenado que puede almacenar elementos en el orden de entrada y garantizar que el orden no cambie. Por lo tanto, OrderedDict Use puede garantizar que Los parámetros en el archivo de peso se almacenan de acuerdo con el nivel y el orden de la estructura de la red de entrenamiento. Además de garantizar el orden de formato del almacenamiento de datos en el archivo de peso, el almacenamiento del archivo de peso también está relacionado con el equipo, método de almacenamiento y la estructura de la red durante el proceso de entrenamiento, por lo que el uso de pesas preentrenadas requiere una comprensión de la estructura de las pesas preentrenadas y cómo almacenan el entrenamiento. La modificación del peso previo al entrenamiento en este capítulo incluye analizar el archivo de peso y modificar las dimensiones del archivo de peso para lograr el propósito de modificar el archivo de peso previo al entrenamiento en conjunto. En la figura 5 se muestra una forma de implementación específica.

f3f300fe7d1ce0f93d0893995c97b63e.png

Figura 5 Modificando la implementación del archivo de pesos

     (1) Analizar la estructura de pesos. El método de almacenamiento de los pesos previos al entrenamiento se puede dividir en entrenamiento con CPU, entrenamiento con GPU única y entrenamiento con varias GPU según su método de entrenamiento. La estructura del modelo guardada en el caso de CPU y GPU única es la misma, según el método de almacenamiento. , se puede dividir en una estructura de red para guardar el modelo y los parámetros de entrenamiento en el archivo de peso, y solo guardar los parámetros de entrenamiento en el archivo de peso; según el método de almacenamiento, se puede dividir en guardar los lotes de entrenamiento en el proceso de entrenamiento, parámetros de entrenamiento, archivos de peso de la estructura intermedia de la estructura de red y solo guardar los parámetros de entrenamiento finales Hay dos métodos para el archivo de peso; según la estructura de red, la estructura de red del archivo de peso se puede imprimir a través de un script de Python , y los parámetros de red que deben modificarse en el peso se pueden modificar según los requisitos.

     (2) Modificar las dimensiones de los pesos pre-entrenados. Modificar los parámetros en la red hará que la red cambie, por lo tanto, para modificar el archivo de peso de preentrenamiento para adaptarlo a la red actual, hay dos soluciones: eliminar los parámetros de entrenamiento del nodo de la red inadecuada; modificar los nodos de la red que no son adecuados para el entrenamiento, llene sus nodos. Este capítulo usa el segundo método para hacer coincidir la red, usando Python para leer el nombre del nodo y la información de dimensión en el peso, modificar la dimensión del parámetro de la primera capa en el archivo de peso y el parámetro del nodo de red correspondiente al número de tipos en el fin al número modificado de tipos. Guarde el archivo de pesos modificado como un nuevo archivo de pesos.

04 Configuración de parámetros del modelo y proceso de entrenamiento

       Antes del proceso de entrenamiento del reconocimiento OCR, se deben establecer parámetros específicos de acuerdo con el conjunto de datos de entrenamiento y la configuración del hardware. La configuración de parámetros específicos es la siguiente.

     (1) La ubicación del conjunto de datos cargado está en el directorio del proyecto cnn+lstm, abra el archivo trian_crnn.py, modifique la ruta relativa de la imagen y el archivo pkl en la función de carga de inicialización en la clase OCRIter, y la ruta de la imagen del conjunto de entrenamiento es ./data/train/text , el archivo pkl de la etiqueta del conjunto de entrenamiento ./data/train, la ruta de la imagen del conjunto de prueba ./data/test/text, el archivo pkl de la etiqueta del conjunto de prueba ./data/test , establezca el parámetro train_flag en True y modifique la lectura en el nombre del archivo pkl del código de ingeniería. El código para el conjunto de datos es el siguiente:

if train_flag:
    self.data_path = os.path.join(os.getcwd(), "data", "train", "text")
    self.label_path = os.path.join(os.getcwd(), "data", "train")
else:
    self.data_path = os.path.join(os.getcwd(), "data", "test", "text")
    self.label_path = os.path.join(os.getcwd(), "data", "test")

       El código para generar el archivo pkl es el siguiente:

def _label_path_from_index(self):
    label_file = os.path.join(self.label_path, "train_pkl")
    assert os.path.exists(label_file, "path dose not exits:{}".format(label_file))
    gt_file = open(label_file, "rb")
    label_file = cPickle.load(gt_file)
    gt_file.close()
    return label_file

      Nota: Las funciones de Python que comienzan con un guión bajo en el código de Python representan funciones privadas, donde los métodos o las variables que comienzan con un solo guión bajo inicial _ solo se pueden acceder dentro de la clase y las subclases, y las instancias de la clase no pueden acceder a este atributo y método. Similar al guión bajo inicial único es el guión bajo inicial doble __, las variables y los métodos que comienzan con esto solo se pueden acceder dentro de la clase, y ni las instancias de clase ni las clases derivadas pueden acceder a este atributo y método.

     (1) Modificar el número de etiquetas reconocidas. Los caracteres reconocidos incluyen números, letras y caracteres chinos. El principio de reconocimiento OCR es equivalente a un algoritmo de clasificación múltiple. Por lo tanto, la configuración de categoría incluye números 0-1, incluidos los caracteres chinos A-Hai y las abreviaturas regionales Beijing, Tianjin, Shanxi, Hebei, Mongolia, Liao, Ji, Hei, Shanghái, Jiangsu, Zhejiang, Anhui, Fujian, Shandong, Henan, Hubei, Hunan, Guangdong, Guangxi, Qiong, Sichuan, Gui, Yun, Tíbet, Shaanxi, Gansu, Qing, Ning, Yu, Jiangxi, Singapur, Taiwán, Hong Kong y Macao. Los parámetros de modificación específicos se muestran en la Figura 6.

0e419d8f8601d016b997173f22d03b8a.png

Figura 6 Configuración del código del conjunto de datos

    (2) Modifique num_epoch=6000, BATCH_SIZE=64, configure para usar GPU-0 training, contexts = [mx.context.gpu(0)], la ruta predeterminada para generar y guardar pesos es la carpeta del modelo en el proyecto.

05 Análisis de umbral

       En aplicaciones prácticas, el reconocimiento de matrículas borradas y ocluidas no solo está relacionado con la tasa de reconocimiento del algoritmo, sino que también está estrechamente relacionado con la calidad de las imágenes del vehículo recopiladas y la calidad de la matrícula real. La placa afecta directamente el rendimiento de reconocimiento final. Por ejemplo, la placa de matrícula estará sujeta a factores subjetivos como la cubierta del vehículo, el bloqueo de la placa de matrícula, múltiples placas de matrícula, etc., también se verá afectada por factores objetivos como el óxido, el desprendimiento de fuentes pintura, inclinación de matrícula, etc. Además, también se verá afectado por varios factores, como el clima durante el proceso de disparo, y la diferencia en estos factores también afectará el efecto de reconocimiento final en diversos grados.

     El algoritmo de reconocimiento OCR realiza la clasificación de matrículas normales y matrículas semiocluidas mediante el reconocimiento del texto de la matrícula. Por lo tanto, el algoritmo OCR generará un nivel de confianza para cada carácter reconocido, y cada carácter es independiente entre sí. Puede describir el grado de confianza de toda la placa del número de identificación, y el grado de confianza de la placa del número se obtiene multiplicando el grado de confianza de cada carácter reconocido. confi representa el grado de confianza del i-ésimo carácter, y conf representa el total grado de confianza de la placa de matrícula, la fórmula se muestra en la Ecuación 6:

50033748122438957d9519d70171acad.png

      Se puede ver en la fórmula que cualquier carácter con un nivel de confianza bajo entre los caracteres reconocidos reducirá directamente el nivel de confianza de toda la matrícula. Por lo tanto, puede optar por filtrar directamente según el nivel de confianza de toda la matrícula. De esta manera, se puede lograr el propósito de distinguir entre placas de matrícula normales y placas de matrícula semiocluidas.El proceso de implementación detallado se muestra en la figura 7.

021719ed255d3151630a68abb421bfc5.png

Figura 7 Diagrama de flujo de análisis de umbral

      (1) Preparar datos. Prepare cien placas de matrícula claras y placas de matrícula borrosas o semiocluidas.Los tipos de placas de matrícula también deben incluir datos de matrícula de varios colores y tipos. Entre ellos, la matrícula normal se denomina matrícula normal + número de serie, el número de serie es del 1 al 100, la matrícula semiocluida se denomina matrícula semiocluida + número de serie, y el número de serie también es 1-100, y los datos procesados ​​se colocan en la carpeta conjunto de datos.

      (2) Escribir código. Los datos procesados ​​calculan la tasa de precisión bajo diferentes umbrales a través del programa, guardan la tasa de precisión calculada después de cada modificación del valor del umbral y finalmente generan un gráfico de líneas. En el proceso de implementación, se juzga si la confianza de la placa de matrícula es mayor que el valor umbral establecido. Si la placa de matrícula es más alta que el umbral y se considera que se denomina placa de matrícula normal, se considera normal Si es inferior al umbral y se considera que contiene una placa de matrícula semiocluida Igual que el reconocimiento correcto, calcule la tasa de precisión de la placa de matrícula en estas dos condiciones.

      (3) Seleccione el umbral. La confianza de toda la matrícula se puede obtener a través de la fórmula 3-6, por lo que usar la confianza de la matrícula para aumentar el umbral de filtrado puede lograr el efecto de la clasificación. Escriba un script para contar la precisión de los datos de verificación establecidos bajo diferentes umbrales. La matrícula por debajo del umbral establecido se considera una matrícula semiocluida, de lo contrario, es una matrícula normal. Establezca el valor inicial en 0.5 y auméntelo. a una velocidad de 0,02, para probar El valor de umbral más adecuado se encuentra en el caso de la tasa de precisión más alta. En la figura se puede ver que el valor de umbral es aproximadamente proporcional a la tasa de precisión. Cuando el valor de umbral es alrededor de 0,95, tiende a ser estable, alcanzando una tasa de precisión del 96%, por lo tanto, se selecciona el valor de umbral apropiado que es 0,95, la gráfica de aumento de la parte experimental se muestra en la Figura 8.

6542dba247a3e8f297da43e8952beac7.png

Figura 8 Mapa de umbrales

06 Resultados experimentales

     Después de configurar los parámetros del modelo, inicie el modelo para comenzar el entrenamiento. El entrenamiento del modelo OCR se divide principalmente en dos partes: generar el archivo de peso intermedio y verificar los resultados experimentales. El proceso detallado es el siguiente.

      (1) Generar un archivo de peso. En este capítulo, está configurado para generar un archivo de peso cada época, y el archivo de peso se guarda en la ruta del modelo del proyecto. El nombre del archivo de peso contiene el valor de época, que se utiliza para registrar el número de iteraciones. En el caso de explosión sin gradiente, con entrenamiento continuo, el valor de pérdida se reduce continuamente y el efecto de aprendizaje es mejor.Debido a la gran cantidad de datos y la lenta velocidad de convergencia, se puede establecer en un valor mayor al configurar el intervalo de guardado. El intervalo establecido en este artículo es de 1000 iteraciones. Guarde el archivo de peso una vez.

      (2) Seleccione el modelo. Cuanto menor sea el valor de pérdida durante el proceso de entrenamiento, mejor será el efecto de ajuste en el conjunto de entrenamiento, pero eso no significa que el efecto en el conjunto de verificación también sea bueno. Por lo tanto, además de requerir que el valor de pérdida disminuya continuamente durante el proceso de entrenamiento, también se requiere guardar el peso generado en el archivo intermedio, para garantizar que el modelo en la prueba del conjunto de validación pueda aprender suficientes características y mantener una mejor capacidad de generalización. Se ha comprobado que cuando la época es 4500, la tasa de precisión es mayor y la pérdida es menor. Los resultados de salida de algunas imágenes de prueba se muestran en la Figura 9.

7566a898e0cae712afeb6b02598c3f7b.png

Figura 9 Resultado de salida de OCR

      Seleccione 200 conjuntos de verificación de los datos para calcular la tasa de precisión, incluidas 100 matrículas normales y 100 matrículas medio ocluidas, incluidas matrículas en diversas situaciones, como tarjetas azules, tarjetas amarillas, nueva energía, etc. espere. Las métricas para verificar el algoritmo OCR son las mismas que para la detección de objetos.

      El umbral utilizado en este experimento es 0,95 para calcular la tasa de precisión, la tasa de recuperación y otros indicadores en el conjunto de verificación. Los datos para calcular los indicadores se dividen en dos partes, una parte usa matrículas normales y la otra parte está semiocluida. placas de matrícula, y las otras partes se calculan por separado. Exactitud (exactitud), recuperación (recuperación), precisión (precisión) y F1score son tres partes. De la siguiente tabla, se puede concluir que la precisión de identificar placas de matrícula normales en términos de precisión es tan alta como 94.90%, que es mucho más alta que la media oclusión La tasa de precisión de la placa de matrícula, pero la tasa de precisión de la placa de matrícula de media oclusión puede alcanzar el 100% de reconocimiento basado en la tasa de recuperación de 91.61%. Según el cálculo final de F1score, el efecto de reconocimiento de la placa de matrícula de media oclusión es mejor que el de la placa de matrícula normal. Los resultados calculados se muestran en la Tabla 1.

66f527a7e95cef677cd026ee76fceb63.png

       Las diferentes plataformas de prueba también afectarán su eficiencia operativa.Para reducir el impacto de otros factores y hacer que el efecto de comparación sea más creíble, el sistema operativo utilizado esta vez es Ubuntu 16.04. La plataforma de prueba GPU es la tarjeta gráfica NVIDIA GeForce GTX 1080 Ti, la versión Cuda utilizada es 10.0 y la biblioteca de aceleración Cudnn está instalada en la plataforma GPU probada, y la plataforma CPU es el procesador AMD 3550H. Para realizar la prueba de velocidad en diferentes plataformas, los entornos se configuran en diferentes plataformas, y la prueba de ejecución del código necesita volver a compilar el código además de instalar el entorno de acuerdo con las necesidades de las instalaciones de hardware. La velocidad de carrera después de la prueba se muestra en la Tabla 2.

b937f3ece025799c4f9ff2515f9f7f57.png

Supongo que te gustará:

Explicación simple de difusión estable: Interpretación del modelo de difusión potencial detrás de la tecnología de pintura AI

e452fda5ed4995ba556a0ee207a74499.png ¡Haz clic en mí para ver los álbumes de la serie de GAN~!

¡Tome un almuerzo, conviértase en la vanguardia de la visión de CV!

¡El último y más completo resumen de 100! Generar modelos de difusión Modelos de difusión

ECCV2022 | Resumen de algunos trabajos sobre la generación de redes de confrontación GAN

CVPR 2022 | Más de 25 direcciones, los últimos artículos de 50 GAN

 ICCV 2021 | Resumen de los artículos de GAN sobre 35 temas

¡Más de 110 artículos! CVPR 2021 peinado de papel GAN ​​más completo

¡Más de 100 artículos! El peinado de papel GAN ​​más completo de CVPR 2020

Desmantelando la nueva GAN: representación desacoplada MixNMatch

StarGAN Versión 2: Generación de imágenes de diversidad multidominio

Descarga adjunta | Versión en chino de "Aprendizaje automático explicable"

Descarga adjunta | "Algoritmos de aprendizaje profundo de TensorFlow 2.0 en la práctica"

Descarga adjunta | "Métodos Matemáticos en Visión por Computador" compartir

"Una revisión de los métodos de detección de defectos superficiales basados ​​en el aprendizaje profundo"

Una encuesta de clasificación de imágenes de disparo cero: una década de progreso

"Una encuesta de aprendizaje de pocos disparos basada en redes neuronales profundas"

El "Libro de los ritos · Xue Ji" tiene un dicho: "Aprender solo sin amigos es solitario e ignorante".

¡Bienvenido a unirse al grupo WeChat de comunicación modelo GAN/difusión!

Escanee el código QR a continuación, agregue amigas de la operación y llévela al grupo. Al enviar la solicitud, tenga en cuenta que el formato es: dirección de investigación + región + escuela/empresa + nombre . Como  modelo de difusión + Beijing + Beihang + Wu Yanzu

a1f03074b38751e8f555a9bf0433f410.jpeg

Tenga en cuenta el formato: dirección de investigación + región + escuela/empresa + nombre

¡Haga clic para  recibir un almuerzo a domicilio y conviértase en la frontera de la visión CV! , recibe cupones y únete  al planeta de la creación generada por IA y  el conocimiento de la visión artificial.

Supongo que te gusta

Origin blog.csdn.net/lgzlgz3102/article/details/129210978#comments_27544030
Recomendado
Clasificación