{"componentChunkName":"component---src-templates-post-template-js","path":"/posts/plate-recognition-tutorial","result":{"data":{"markdownRemark":{"id":"d2a19907-29eb-5524-87cf-6efe4b7327c8","html":"<p>La inteligencia artificial permite actuar sobre muchos campos empresariales, y automatizar de manera eficaz procesos de negocio, en este articulo se propone una tarea simple de extracción de texto de una imagen similar a una placa mediante la aplicación de redes convolucionales y recurrentes que basándose en una imagen de entrada predice el texto en la misma. Para esto es necesario tener un conjunto de datos, que dentro del campo del deep learning es la materia prima para poder generar modelos precisos.</p>\n<p>Todo el código de este articulo se lo puede encontrar en el repositorio: (<a href=\"https://github.com/FabianBG/plate_recognition\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://github.com/FabianBG/plate_recognition</a>)</p>\n<h3 id=\"conjunto-de-datos\" style=\"position:relative;\"><a href=\"#conjunto-de-datos\" aria-label=\"conjunto de datos permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conjunto de datos</h3>\n<p>Como principal materia prima para el modelo que se entrenará, se procede con un generador de imágenes que cumplirá la función de crear un conjunto de imágenes al azar, para esto en nuestro código definimos un generador que cree imágenes basados en el formato de una placa de automóvil (No seremos estrictos con la validación de las mismas).</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># Python3\n# plate_recognition/src/ocr_plate.py\n\ndef random_plate():\n\t    size = randint(plate_min, plate_max)\n\t    letter = &quot;&quot;\n\t    number = &quot;&quot;\n\t    for _ in range(size):\n\t        letter = letter +\n              chr(big_letters[randint(0, len(big_letters) - 1)])\n\t        number = number + chr(digits[randint(0, len(digits) - 1)])\n\t    return letter + &quot;-&quot; + number\n\ndef get_plate(size, empty=False):\n\t    plate = random_plate()\n\t    font = ImageFont.truetype(font_path, 30)\n\t    img = Image.new(&#39;RGB&#39;, size, (0,0,0))\n\t    draw = ImageDraw.Draw(img)\n\t    rounded_rectangle(draw, ((0, 0), size), 20,\n            fill=(255, 255, 255), outline=(255, 255, 255))\n\t    draw.text((25,10), plate, font=font, fill=(0,0,0))\n\t    img = np.array(img.convert(&#39;L&#39;))\n\t    img = img.astype(np.float32) / 255\n\t    img = np.expand_dims(img, 0)\n\t    return img, plate\n\t\n\n\t\n\n\t\n\n\tdef image_generator(size, img_w, img_h, downsample_factor):\n\t    if K.image_data_format() == &#39;channels_first&#39;:\n\t        X_data = np.ones([size, 1, img_w, img_h])\n\t    else:\n\t        X_data = np.ones([size, img_w, img_h, 1])\n\t    labels = np.ones([size, absolute_max_string_len])\n\t    input_length = np.zeros([size, 1])\n\t    label_length = np.zeros([size, 1])\n\t    source_str = []\n\t    for i in range(size):\n\t        data, world = get_plate(image_size, True)\n\t        if K.image_data_format() == &#39;channels_first&#39;:\n\t            X_data[i, 0, 0:img_w, :] = np.array(data).T\n\t        else:\n\t            X_data[i, 0:img_w, :, 0] = data[0, :, :].T\n\t        labels[i] = np.ones(absolute_max_string_len) * -1\n\t        labels[i, 0:len(world)] = text_to_labels(world)\n\t        input_length[i] = img_w // downsample_factor - 2\n\t        label_length[i] = [len(world)]\n\t        source_str.append(world)\n\t    inputs = {&#39;input&#39;: X_data,\n\t                &#39;labels&#39;: labels,\n\t                &#39;input_length&#39;: input_length,\n\t                &#39;label_length&#39;: label_length,\n\t                &#39;source_str&#39;: source_str\n\t                }\n\t    outputs = {&#39;ctc&#39;: np.zeros([size])}\n\t    return (inputs, outputs)</code></pre></div>\n<p>En el fragmento de código anterior se muestra las funciones principales para generar conjuntos de imágenes. Principalmente se obtiene una cadena de texto con el texto de la placa a generar esto mediante la función <strong>random_plate()</strong> esta función proporciona al azar una serie de 3 o 4 letras separadas por un guion y otra serie de 3 o 4 números.</p>\n<p>Una vez proporcionado el texto a dibujar la función <strong>get_plate()</strong> se encarga de pintar una imagen, que a su vez es usada por la función <strong>image_generator()</strong> que se encarga de modelar la estructura de datos para nuestro modelo.</p>\n<p>La estructura de entrada consta de 4 elementos:</p>\n<ul>\n<li>input: Que es la imagen transformada a un vector de datos normalizado entre 1 y 0.</li>\n<li>labels: Que es el texto de la imagen pero transformado a un formato de vector para que sea la salida   esperada de la red neuronal.</li>\n<li>input y label length: Que es el tamaño de cada vector de datos.</li>\n<li>source_str: Que es la cadena de texto con el texto original de la imagen.</li>\n</ul>\n<p>Al final se define un generador que constantemente esta alimentando el modelo:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># Python3\n# plate_recognition/src/ocr_plate.py\n\ndef batch_generator(size):while 1:\n        data = image_generator(size, img_w=200, img_h=50, downsample_factor=4)\n        yield data</code></pre></div>\n<p>Este recibe un tamaño de conjunto para generar el mismo.</p>\n<h3 id=\"modelo\" style=\"position:relative;\"><a href=\"#modelo\" aria-label=\"modelo permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Modelo</h3>\n<p>Una lista la materia prima del modelo, se genera un modelo compuesto principalmente de dos tipos de capas:</p>\n<ul>\n<li>Convolucionales, que son encargadas de extraer características de una imagen.</li>\n<li>Recurrentes, que por medio de una implementación de memoria temporal son capaces de predecir cadenas secuenciales de datos.</li>\n</ul>\n<p>El modelo es generado mediante el siguiente fragmento de codigo:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># Python3\n# plate_recognition/src/ocr_plate.py\n\ndef model(input_shape, output_shape, absolute_max_string_len):# Input Parameters\n    img_h = input_shape[1]\n    img_w = input_shape[0]\n    \n    # Network parameters\n    conv_filters = 16\n    kernel_size = (3, 3)\n    pool_size = 2\n    time_dense_size = 32\n    rnn_size = 512\n\n\n    if K.image_data_format() == &#39;channels_first&#39;:\n        input_shape = (1, img_w, img_h)\n    else:\n        input_shape = (img_w, img_h, 1)\n\n\n    act = &#39;relu&#39;\n    input_data = Input(name=&#39;input&#39;, shape=input_shape, dtype=&#39;float32&#39;)\n    inner = Conv2D(conv_filters, kernel_size, padding=&#39;same&#39;,\n                   activation=act, kernel_initializer=&#39;he_normal&#39;,\n                   name=&#39;conv1&#39;)(input_data)\n    inner = MaxPooling2D(pool_size=(pool_size, pool_size), name=&#39;max1&#39;)(inner)\n    inner = Conv2D(conv_filters, kernel_size, padding=&#39;same&#39;,\n                   activation=act, kernel_initializer=&#39;he_normal&#39;,\n                   name=&#39;conv2&#39;)(inner)\n    inner = MaxPooling2D(pool_size=(pool_size, pool_size), name=&#39;max2&#39;)(inner)\n\n\n    conv_to_rnn_dims = (img_w // (pool_size ** 2),\n                        (img_h // (pool_size ** 2)) * conv_filters)\n    inner = Reshape(target_shape=conv_to_rnn_dims, name=&#39;reshape&#39;)(inner)\n    inner = Dense(time_dense_size, activation=act, name=&#39;dense1&#39;)(inner)\n    gru_1 = GRU(rnn_size, return_sequences=True,\n                kernel_initializer=&#39;he_normal&#39;, name=&#39;gru1&#39;)(inner)\n    gru_1b = GRU(rnn_size, return_sequences=True,\n                 go_backwards=True, kernel_initializer=&#39;he_normal&#39;,\n                 name=&#39;gru1_b&#39;)(inner)\n    gru1_merged = add([gru_1, gru_1b])\n    gru_2 = GRU(rnn_size, return_sequences=True,\n                kernel_initializer=&#39;he_normal&#39;, name=&#39;gru2&#39;)(gru1_merged)\n    gru_2b = GRU(rnn_size, return_sequences=True, go_backwards=True,\n                 kernel_initializer=&#39;he_normal&#39;, name=&#39;gru2_b&#39;)(gru1_merged)\n    inner = Dense(output_shape, kernel_initializer=&#39;he_normal&#39;,\n                  name=&#39;dense2&#39;)(concatenate([gru_2, gru_2b]))\n    \n    y_pred = Activation(&#39;softmax&#39;, name=&#39;softmax&#39;)(inner)\n    Model(inputs=input_data, outputs=y_pred).summary()\n\n\n    labels = Input(name=&#39;labels&#39;,\n                   shape=[absolute_max_string_len], dtype=&#39;float32&#39;)\n    input_length = Input(name=&#39;input_length&#39;, shape=[1], dtype=&#39;int64&#39;)\n    label_length = Input(name=&#39;label_length&#39;, shape=[1], dtype=&#39;int64&#39;)\n    loss_out = Lambda(\n        ctc_lambda_func, output_shape=(1,),\n        name=&#39;ctc&#39;)([y_pred, labels, input_length, label_length])\n    test_func = K.function([input_data], [y_pred])\n    sgd = SGD(lr=0.02, decay=1e-6, momentum=0.9, nesterov=True, clipnorm=5)\n    model = Model(inputs=[input_data, labels, input_length, label_length],\n                  outputs=[loss_out, y_pred])\n    model.compile(loss={&#39;ctc&#39;: ctc, &#39;ctc&#39;: ctc}, optimizer=sgd)\n\n\n    return model, test_func</code></pre></div>\n<p>Que de forma más simplificada consta del siguiente resumen:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># model.summary()\n\nTensorFlow version: 1.12.0\nKeras version: 2.2.4\n__________________________________________________________________________________________________\nLayer (type)                    Output Shape         Param #     Connected to                     \n==================================================================================================\ninput (InputLayer)              (None, 200, 50, 1)   0                                            \n__________________________________________________________________________________________________\nconv1 (Conv2D)                  (None, 200, 50, 16)  160         input[0][0]                      \n__________________________________________________________________________________________________\nmax1 (MaxPooling2D)             (None, 100, 25, 16)  0           conv1[0][0]                      \n__________________________________________________________________________________________________\nconv2 (Conv2D)                  (None, 100, 25, 16)  2320        max1[0][0]                       \n__________________________________________________________________________________________________\nmax2 (MaxPooling2D)             (None, 50, 12, 16)   0           conv2[0][0]                      \n__________________________________________________________________________________________________\nreshape (Reshape)               (None, 50, 192)      0           max2[0][0]                       \n__________________________________________________________________________________________________\ndense1 (Dense)                  (None, 50, 32)       6176        reshape[0][0]                    \n__________________________________________________________________________________________________\ngru1 (GRU)                      (None, 50, 512)      837120      dense1[0][0]                     \n__________________________________________________________________________________________________\ngru1_b (GRU)                    (None, 50, 512)      837120      dense1[0][0]                     \n__________________________________________________________________________________________________\nadd_1 (Add)                     (None, 50, 512)      0           gru1[0][0]                       \n                                                                 gru1_b[0][0]                     \n__________________________________________________________________________________________________\ngru2 (GRU)                      (None, 50, 512)      1574400     add_1[0][0]                      \n__________________________________________________________________________________________________\ngru2_b (GRU)                    (None, 50, 512)      1574400     add_1[0][0]                      \n__________________________________________________________________________________________________\nconcatenate_1 (Concatenate)     (None, 50, 1024)     0           gru2[0][0]                       \n                                                                 gru2_b[0][0]                     \n__________________________________________________________________________________________________\ndense2 (Dense)                  (None, 50, 38)       38950       concatenate_1[0][0]              \n__________________________________________________________________________________________________\nsoftmax (Activation)            (None, 50, 38)       0           dense2[0][0]                     \n==================================================================================================\nTotal params: 4,870,646\nTrainable params: 4,870,646\nNon-trainable params: 0\n__________________________________________________________________________________________________</code></pre></div>\n<p>El modelo hace uso de la implementación de redes recurrentes GRU, que en este caso resultan más eficientes que las LSTM.</p>\n<p>En resumen el modelo recibe una imagen de entrada y mediante las capas de convolución extrae las principales características que a su vez son la entrada de las capas recurrentes las cuales generan las predicciones basándose en un máximo tamaño permitido.</p>\n<h3 id=\"función-de-optimización\" style=\"position:relative;\"><a href=\"#funci%C3%B3n-de-optimizaci%C3%B3n\" aria-label=\"función de optimización permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Función de optimización</h3>\n<p>Dentro de cualquier problema de deep learning tiene que existir un objetivo o función a optimizar, ya sea el caso de una regresión que busca predecir numéricamente un valor o una clasificación que pretende predecir una clase, lo que se busca es optimizar una función, para este caso en particular lo que se busca es que la salida que es una matriz la cual consta de 2 dimensiones, en la que la primera es el tamaño máximo de la cadena que puede predecir el modelo y la segunda el alfabeto.</p>\n<p>En la siguiente imagen se puede apreciar una representación simple de la matriz de salida.</p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQH04kcWT-WgHg/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=UFRHUOCDUQGLPgRX0qywCt3ytjnsA4kM-W4zqvJ2gWk\">\n</figure>\n<p>Para poder optimizar esta matriz y decidir el camino correcto en que los pesos del modelo se ajustan las funciones de perdida comúnmente conocidas no ayudan, pero para este caso en particular se usa una función de perdida especial conocida como CTC(Connectionism Temporal Classification) que en resumen se encarga de definir una especie de camino desde la predicción inicial hasta la final.</p>\n<h3 id=\"entrenamiento\" style=\"position:relative;\"><a href=\"#entrenamiento\" aria-label=\"entrenamiento permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Entrenamiento</h3>\n<p>El entrenamiento del modelo se lo realizó por 20 ciclos o epochs con un batch_size de 64, sobre un equipo que cuenta con tarjeta grafica NVIDIA 1060, aproximadamente el entrenamiento duro 15 a 20 minutos y el modelo se entreno hasta que el error se redujo a menos de 1.</p>\n<p>Para iniciar el entrenamiento del modelo simplemente iniciar dentro del directorio del código:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># Python3\n\npython3 main.py train</code></pre></div>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQE7Eg0K1VR01A/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=syiJB5QpkeKlcZ99QOUTC8Ut2wPp7NC5ZmScgqg48ZI\">\n</figure>\n<h3 id=\"pruebas\" style=\"position:relative;\"><a href=\"#pruebas\" aria-label=\"pruebas permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Pruebas</h3>\n<p>Una vez se entrenaron los modelos, estos se guardan automáticamente cada 2 epochs, en el mismo directorio del código fuente.</p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQErvABpFlU_Rw/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=RtDc9wVNH35-DXDzn5TeVAIzHsnL_Lk0UXom4wl0Ea0\">\n</figure>\nPara las pruebas usaremos estos modelos, para poder ejecutar una prueba ejecutar el siguiente comando:\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># Python3\n\n# python3 main.py [numero_epoch]\n\npython3 main.py 06</code></pre></div>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQHeTQFbdmocOw/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=GkV4GgbipdBa6vjzwZf5rL7x2NgMu7nC2cPCH563GXo\">\n</figure>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQE624rhwaMQqw/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=hT3NEY773bUd4cxpnlwDtul5V-v3l_suWm_BWnSApck\">\n</figure>\n<h3 id=\"conclusiones\" style=\"position:relative;\"><a href=\"#conclusiones\" aria-label=\"conclusiones permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Conclusiones</h3>\n<p>De manera rápida y simple se puede generar un modelo que permita automatizar un proceso y generar valar a una empresa, y gracias a las herramientas actuales muchas partes de la programación están abstraídas y permiten un rápido desarrollo.</p>\n<p>El modelo es una demostración simple ya que para que sea un modelo final que sea viable se tiene que mejorar el conjunto de datos y la variedad de ejemplos ya que como se sabe en el mundo real no todo esta estático y perfectamente retratado.</p>\n<h3 id=\"referencias\" style=\"position:relative;\"><a href=\"#referencias\" aria-label=\"referencias permalink\" class=\"anchor before\"><svg aria-hidden=\"true\" focusable=\"false\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg></a>Referencias</h3>\n<p>Keras OCR Sample (<a href=\"https://keras.io/examples/image_ocr/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://keras.io/examples/image_ocr/</a>)</p>","fields":{"slug":"/posts/plate-recognition-tutorial","tagSlugs":["/tag/tutorial/","/tag/tensorflow-keras/","/tag/ai/","/tag/image-detection/"]},"frontmatter":{"date":"2019-04-10T10:40:32.169Z","description":"Tutorial para reconocer placas mediante inteligencia artificial","tags":["Tutorial","Tensorflow Keras","AI","Image detection"],"title":"Reconocimiento de placas con inteligencia artificial","socialImage":"https://media-exp1.licdn.com/dms/image/C4E12AQE8mRZaamYaWQ/article-cover_image-shrink_720_1280/0?e=1587600000&v=beta&t=xzDzkvKlrdeUh19RQMxvNYx7tPffSRcMzhHWdmbzZZw"}}},"pageContext":{"slug":"/posts/plate-recognition-tutorial"}}}