{"componentChunkName":"component---src-templates-post-template-js","path":"/posts/finding-waldo-with-ai","result":{"data":{"markdownRemark":{"id":"b59dd238-f189-5a35-ad2b-f375e8d43efd","html":"<h3 id=\"visión-computacional-y-deep-learning\" style=\"position:relative;\"><a href=\"#visi%C3%B3n-computacional-y-deep-learning\" aria-label=\"visión computacional y deep learning 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>Visión computacional y deep learning</h3>\n<p>El campo de la visión computacional es un gran beneficiario del deep learnig, ya que los método basados en esta rama de la inteligencia artificial han superado los métodos estadísticos clásicos llegando a superar de manera sustancial a los métodos tradicionales.</p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQFOGzAgjkRJxg/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=tFrMyRC4RzeNPVU4GcVM4px-0UvVcIgmLQzxhAz4qKs\">\n\t<figcaption>http://www.cs.toronto.edu/~urtasun/courses/CSC2541_Winter17/detection.pdf</figcaption>\n</figure>\n<p>Existen algunos algoritmos en la actualidad desempeñando una competencia de precisión, por lo cual actualmente estos son los principales:</p>\n<ul>\n<li>You Only Look Once (YOLO)</li>\n<li>Single Shot Detector (SSD)</li>\n<li>Faster Region CNN (F R-CNN)</li>\n</ul>\n<h3 id=\"darknet-yolo\" style=\"position:relative;\"><a href=\"#darknet-yolo\" aria-label=\"darknet yolo 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>Darknet YOLO</h3>\n<p>Para este ejemplo se usará YOLO que presenta una gran capacidad predicativa y rapidez en las predicciones catalogado para uso en tiempo real.</p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQEKG7JkD0hkiQ/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=lWc9Ko7T5_XF3ercR6Rwh4FH2270LdjanTifi8z3eDw\">\n\t<figcaption>https://pjreddie.com/darknet/yolo/ </figcaption>\n</figure>\n<p>Se enseñara al algoritmo de detección de objetos a buscar a Waldo mediante ejemplos obtenidos de Google Images y entrenando el algoritmo de YOLO en su versión 2 y 3. Para esto es necesario tener las librerías darknet para ejecutar la red neuronal convolucional. El código fuente del framework darknet se lo encuentra en la siguiente dirección:</p>\n<p>(<a href=\"https://github.com/pjreddie/darknet\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://github.com/pjreddie/darknet</a>)</p>\n<p>Se compila para el sistema operativo respectivo, adicionalmente tener en cuenta el poder las GPU, un entrenamiento sin estas puede demorar hasta semanas dependiendo de la cantidad de clases e imágenes es por eso que es muy recomendable tener GPU con CUDA. Para este ejemplo se usa nodos en Linux con 4 GPU Tesla K80.</p>\n<h3 id=\"manos-a-la-obra\" style=\"position:relative;\"><a href=\"#manos-a-la-obra\" aria-label=\"manos a la obra 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>Manos a la obra</h3>\n<p>Inicialmente se procede a clonar el repositorio con el código de este articulo, con los scripts para aumentar el conjunto de imágenes mediante transformaciones y generar los archivos de configuración para la red YOLO v2 y v3:</p>\n<p>(<a href=\"https://github.com/FabianBG/finding_waldo_yolo\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://github.com/FabianBG/finding_waldo_yolo</a>)</p>\n<p>En este repositorio se encuentran los siguientes scripts e imágenes usadas para el entrenamiento del buscador de Waldo:</p>\n<ul>\n<li>image_augmentation.py: Script en Python3 para aumentar el tamaño del conjunto inicial de imágenes. Basado en imgaug, si se quiere hacer uso de este script instalar el paquete (<a href=\"https://imgaug.readthedocs.io/en/latest/source/installation.html\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://imgaug.readthedocs.io/en/latest/source/installation.html</a>)</li>\n<li>proccess_dataset.py: Script en Python3 que genera los conjuntos de pruebas y entrenamiento para la red.</li>\n<li>waldo.data: Archivo con la meta-data de la red a entrenar.</li>\n<li>waldo.names: Archivo con los nombres de las clases</li>\n<li>yolov2-waldo.cfg: Configuración de la red YOLOv2.</li>\n<li>yolov3-waldo.cfg: Configuración con la red YOLOv3.</li>\n<li>data/: Carpeta con las imágenes de Waldo, para este ejemplo se trabajará solo con imagenes *.jpg en caso de agregar mas mantener el formato.</li>\n<li>data/aug: Carpeta de salida del script de aumentación de imágenes.</li>\n<li>validate/ : Carpeta con imágenes de validación del modelo.</li>\n<li>test/: Imágenes para pruebas</li>\n</ul>\n<p>Lo primero que necesitamos son los pesos de la red pre-entrenados:</p>\n<ul>\n<li>YOLOv2 (<a href=\"https://pjreddie.com/media/files/darknet19_448.conv.23\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://pjreddie.com/media/files/darknet19_448.conv.23</a>)</li>\n<li>YOLOv3 (<a href=\"https://pjreddie.com/media/files/darknet53.conv.74\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://pjreddie.com/media/files/darknet53.conv.74</a>)</li>\n</ul>\n<p>El segundo paso es opcional ya que consiste en generar mas imágenes mediante transformaciones, para esto se ejecuta la siguiente linea:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># finding_waldo_yolo/\npython ./image_augmentation.py</code></pre></div>\n<p>Lo que genera las imágenes en la carpeta aug dentro de data. Si se desea aplicar diferentes transformaciones referirse al manual de imgaug (<a href=\"https://imgaug.readthedocs.io/en/latest/\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://imgaug.readthedocs.io/en/latest/</a>)</p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C5612AQHzTPpakOttLg/article-inline_image-shrink_1500_2232/0?e=1587600000&amp;v=beta&amp;t=p6d15ZTMEcRnbC-goUwD9_ZVOcWvCpS4ljx3ChcoKb0\">\n</figure>\n<p>Ahora se configura las conjuntos de entrenamiento:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># finding_waldo_yolo/\npython ./proccess_dataset.py [images_location_dir] [darknet_search_path]</code></pre></div>\n<p>Lo que generará dos archivos de texto train.txt y test.txt que contienen las rutas de las imágenes separadas en conjuntos de entrenamiento y pruebas.</p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQE-hflEymoGoA/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=TVlTZbtPk-gZMw4m6GlFxwvjA72NhMneIyP3THSsMek\">\n</figure>\n<p>Bien ahora se tiene los datos, se realiza la configuración e la meta-data de la red mediante al archivo waldo.data</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># finding_waldo_yolo/waldo.data\n\nclasses = 1 # Número de clases\ntrain  = ./train.txt # Path de el archivo train.txt\nvalid  = ./test.txt # Path de el archivo test.txt\nnames = ./waldo.names # Path del archivo waldo.names\nbackup = ./backup/ # Directorio donde se almacenaran los pesos del entrenamiento</code></pre></div>\n<p>Finalmente se empieza el entrenamiento ejecutando el siguiente comando:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># finding_waldo_yolo\n# YOLOv3\ndarknet detector train ./waldo.data ./yolov3-waldo.cfg ./darknet53.conv.74\n\n# YOLOv2\ndarknet detector train ./waldo.data ./yolov2-waldo.cfg ./darknet19_448.conv.23</code></pre></div>\n<p>Cada versión imprime un salida diferente, pero cada una imprime una salida después da cada epoch iteración similar, en este caso se filtro la salida para solo mostrar el resultado de cada iteración mediante </p>\n<p><code class=\"language-text\">[comando] | grep images:</code></p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQGKtmiZHOOP_w/article-inline_image-shrink_1500_2232/0?e=1587600000&amp;v=beta&amp;t=BtmYq2hnAX-K22921Ddjiu2K_KycXYv57d4F1k2Suts\">\n</figure>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># Salida de entrenamiento\n\n11203: 0.097249, 0.100165 avg, 0.004000 rate, 25.083207 seconds, 2867968 images</code></pre></div>\n<p>En donde los 3 primeros números especifican:</p>\n<ul>\n<li>11203 Numero de iteración</li>\n<li>0.097249 Perdida general</li>\n<li>0.100165 Perdida promedio</li>\n</ul>\n<p>Entre menor la perdida mayor precisión, pero tener en cuenta posibles sobre ajustes de la red por esto probar con diferentes pesos generados automáticamente, YOLO genera por defecto pesos para 400, 800 y de ahí en adelante de 10000 en 10000 y el archivo de backup donde esta en la iteración actual. Con esto es posible detener el entrenamiento y empezarlo nuevamente haciendo uso de este archivo de backup.</p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C5612AQHpRETDOJX6xg/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=QMVuFBKSdhYuYk9XDfhPlqmSEEtDTWm3ZD0seFbDDTU\">\n</figure>\n<p>Se obtendrá una serie de archivos de pesos con los cuales podremos probar cuales son los mejores y mas estables sin sobre ajuste mediante pruebas con las imágenes en a carpeta de validate/ para probar los pesos simplemente se ejecuta el siguiente comando:</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># Darknet instalation folder\n./darknet detector test cfg/waldo.data \\ \n                        cfg/yolov2-waldo.cfg \\\n                        [path_weights]/yolov2-waldo_10000.weights \\\n                        [path_finding_waldo_yolo]/validate/1.jpg</code></pre></div>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C5612AQFJKCG1jKS7dQ/article-inline_image-shrink_1000_1488/0?e=1587600000&amp;v=beta&amp;t=hHuZfg1OJ5ACe_IwPM8JIUAxy2jQ3mtYiosnHF-piSo\">\n</figure>\n<p>Se mostrará en una ventana la imagen con la predicción, mediante esta prueba se puede saber que pesos son los mas adecuados para generalizar mejor la búsqueda. Peor existe un problema YOLO no tiene mucha capacidad predictiva sobre objetos pequeños ya que se re dimensiona la imagen de entrada a predecir por lo que se pierden detalles en imágenes sumamente grandes por lo que se genero un script que genera una ventana de recorrido y genera predicciones por cada ventana mostrando los resultados finales.</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># finding_waldo.py\nimport os\nimport sys\nimport cv2 as cv\nimport python.darknet as dn\nimport pdb\n\nsys.path.append(os.path.join(&#39;/home/mbastidas/git/darknet/python/&#39;))\n\nweigths = sys.argv[2]\nconfig = sys.argv[3]\nfile_dir = sys.argv[1]\nparts = int(sys.argv[4])\n\ndef convert_axis(size, box):\n    dw = 1./size[0]\n    dh = 1./size[1]\n    x = box[0]/dw\n    w = box[2]/dw\n    y = box[1]/dh\n    h = box[3]/dh\n\n    b1 = x + (w/2)\n    b3 = y + (h/2)\n    b0 = x + (w/2) - w\n    b2 = y + (h/2) - h\n\n    return (b0, b1, b2, b3)\n\ndef divide_image(file_dir, parts):\n    padding = 50\n    names = []\n    image = cv.imread(file_dir)\n    shape = image.shape\n    partes = int(int(parts / 2))\n    print(&quot;Dividiendo imagen&quot;, file_dir, shape, &quot;en&quot;, partes*2)\n    for i in range(0, partes):\n        w = int(shape[1] / partes)\n        x = int(i * w)\n        w = x + w + padding\n        for j in range(0, partes):\n            h = int(shape[0] / partes)\n            y = int(j * h)\n            h = y + h + padding\n            print(x, w, y, h)\n            if i == partes and j == partes:\n                part = image[y:shape[0], x:shape[1]]\n            else:\n                part = image[y:h, x:w]\n            name = &quot;./&quot; + str(x) + &quot;_&quot; + str(y) + &quot;.jpg&quot;\n            cv.imwrite(name, part)\n            names.append(name)\n    return image, names, (int(shape[1] / partes), int(shape[0] / partes))\n\ndef draw_box(image, points, part_shape, acc, color=(0, 0, 255)):\n      x = int(points[0] + part_shape[0] - (points[2]/2))\n      y = int(points[1] + part_shape[1] - (points[3]/2))\n      x1 = int(points[2] + x)\n      y1 = int(points[3] + y)\n      print((x,y), (x1,y1))\n      cv.rectangle(image, (x,y), (x1,y - 25), color, -1)\n      cv.putText(image,&quot;%.2f&quot; % round(acc,2),(x,y-15), cv.FONT_HERSHEY_SIMPLEX,\n      1,(255,255,255),2,cv.LINE_AA)\n      cv.rectangle(image, (x,y), (x1,y1), color, 3)\n      print(&quot;save on find_waldo_&quot; + file_dir.split(&quot;/&quot;)[-1])\n      cv.imwrite(&quot;./find_waldo_&quot; + file_dir.split(&quot;/&quot;)[-1], image)\n\n\nimage, names, part_shape = divide_image(file_dir, parts)\nprint(names)\nnet = dn.load_net(config.encode(&#39;utf-8&#39;), weigths.encode(&#39;utf-8&#39;), 0)\nmeta = dn.load_meta(&quot;cfg/waldo.data&quot;.encode(&#39;utf-8&#39;))\ninsight = None\nboxes = []\npart = None\nfor name in names:\n    #[class, prob, (x,y, w,h)]\n    predictions = dn.detect(net, meta, name.encode(&#39;utf-8&#39;))\n    print(&quot;Parte&quot;, name)\n    os.remove(name)\n    print(&quot;Pred&quot;, str(len(predictions)))\n    if len(predictions) &gt; 0:\n        if insight == None:\n            insight = predictions[0] \n            part = name[2:].split(&quot;.&quot;)[0].split(&quot;_&quot;)\n        for pred in predictions:\n            print(pred)\n            boxes.append({\n            &quot;part&quot;: name[2:].split(&quot;.&quot;)[0].split(&quot;_&quot;),\n            &quot;insight&quot;: pred\n            })\n            if pred[1] &gt; insight[1]:\n                insight = pred\n                part = name[2:].split(&quot;.&quot;)[0].split(&quot;_&quot;)\nprint(&quot;Best result&quot;, insight, part_shape)\nif insight != None:\n    for box in boxes:\n        draw_box(image, box[&quot;insight&quot;][2], (int(box[&quot;part&quot;][0]), int(box[&quot;part&quot;][1])),\n        box[&quot;insight&quot;][1], color=(255, 0, 0))\n    draw_box(image, insight[2], (int(part[0]), int(part[1])), insight[2])\nelse:\n    print(&quot;No se encontro nada.&quot;)</code></pre></div>\n<p>El script debe estar sobre la carpeta de instalación de darknet ya que hace uso de el wrapper de python para darknet. Este corta una imagen en 4 u 8 secciones y las analiza una por una, graficando todas las predicciones en azul y la mas alta con verde. Este script es especialmente eficaz en imágenes grandes.</p>\n<h3 id=\"pruebas-finales\" style=\"position:relative;\"><a href=\"#pruebas-finales\" aria-label=\"pruebas finales 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 finales</h3>\n<p>Se ejecuta el siguiente comando para ejecutar el script anterior sobre las imágenes en la carpeta de validación</p>\n<div class=\"gatsby-highlight\" data-language=\"text\"><pre class=\"language-text\"><code class=\"language-text\"># Darknet folder\n#! bin/sh\nfor filename in /home/mbastidas/git/yolo-models/waldo/validate/*\ndo\n        python find_waldo.py $filename results/yolov3-waldo_10000.weights cfg/yolov3-waldo.cfg 4\ndone</code></pre></div>\n<p>Este comando ejecuta el script de python de segmentación y genera un imagen con los resultados marcando con verde la mejor predicción y con azul otras predicciones, cada archivo generado se marca con el prefijo </p>\n<p><code class=\"language-text\">find_waldo_[nombre_imagen_validacion].jpg</code></p>\n<p>El resultado se lo puede ver por YOUTUBE (<a href=\"https://www.youtube.com/watch?v=6NKX1Bk8TJg\" target=\"_blank\" rel=\"nofollow noopener noreferrer\">https://www.youtube.com/watch?v=6NKX1Bk8TJg</a>)</p>\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>YOLO es una forma relativamente simple de generar detectores de objectos con una buena tasa de precisión y de forma rápida (<strong>GPU required</strong>). YOLOv2 es mas rápido pero tiende a tener menor precisión al contrario que YOLOv3 que presenta una mejoría de precisión pero a su ver aumenta la complejidad de la red lo cual aumenta los tiempos de predicción y entrenamiento.</p>\n<figure>\n\t<img src=\"https://media-exp1.licdn.com/dms/image/C4E12AQG1p9JlHLE2Vw/article-inline_image-shrink_1500_2232/0?e=1587600000&amp;v=beta&amp;t=PNcntOUAe7-qRwy36bzf0iB-Nq1HAnTZR06fTe_5GVA\">\n</figure>\n<p>Existen casos en los que que erra completamente en la búsqueda porque generalmente para el entrenamiento de algoritmos de deep learning siempre es necesario grandes cantidades de datos, entre mas imágenes se tenga mejor sera la precisión del mismo, como mejoría la trabajo actual se propone aumentar el conjunto de imágenes y probar con mas datos.</p>\n<p>Finalmente no solo YOLO existe actualmente, hay algoritmos que están compitiendo constantemente por mejorar su tasa de precisión por lo que vale estar al día en la evolución e las arquitecturas y mejoras de todos.</p>","fields":{"slug":"/posts/finding-waldo-with-ai","tagSlugs":["/tag/tutorial/","/tag/yolo/","/tag/ai/","/tag/image-detection/"]},"frontmatter":{"date":"2018-09-21T10:40:32.169Z","description":"Tutorial para reconocer placas mediante inteligencia artificial","tags":["Tutorial","YOLO","AI","Image detection"],"title":"Buscando a Waldo con Deep Learning","socialImage":"https://media-exp1.licdn.com/dms/image/C5612AQGIlCs485VfeA/article-cover_image-shrink_600_2000/0?e=1587600000&v=beta&t=OVpDTxmesVpY1bXQ53hAcxmaCtTG3r1CMz4eiHJ-c4s"}}},"pageContext":{"slug":"/posts/finding-waldo-with-ai"}}}