pantallas-led/GenPoster/notebooks/generate_image_bus.ipynb

520 lines
19 KiB
Plaintext

{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import requests\n",
"import io\n",
"from io import BytesIO\n",
"from PIL import ImageDraw, ImageFont\n",
"from PIL import Image, ImageDraw, ImageFont\n",
"import matplotlib.pyplot as plt\n",
"import numpy as np"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class MyDraw():\n",
" def __init__(self, height, width):\n",
" self.height = height\n",
" self.width = width\n",
" pass\n",
"\n",
" def save_image(self, filename):\n",
" self.image.save(filename)\n",
" pass\n",
"\n",
" def start_draw(self, background_color=None):\n",
" if background_color is None:\n",
" background_color = self.theme_params['background_color']\n",
"\n",
" self.image = Image.new(\"RGB\", (self.width, self.height), background_color)\n",
" self.draw = ImageDraw.Draw(self.image)\n",
" pass\n",
"\n",
" def add_image(self, obj, position):\n",
" image_to_add = obj.get_image() # Obtiene la imagen del objeto pasado como argumento\n",
" if image_to_add:\n",
" self.image.paste(image_to_add, position)\n",
" pass\n",
"\n",
" def get_draw(self):\n",
" return self.draw\n",
" \n",
" def get_image(self):\n",
" return self.image\n",
"\n",
" def set_params(self,params):\n",
" self.prms = params\n",
" pass\n",
"\n",
" def set_theme(self,mode='day'):\n",
" if(mode=='day'):\n",
" self.start_day_mode()\n",
" else:\n",
" self.start_night_mode()\n",
" pass\n",
"\n",
" def start_day_mode(self):\n",
" self.theme_params = {\n",
" 'background_color': 'white',\n",
" 'text_color': 'black',\n",
" 'poster_line_color': 'black',\n",
" }\n",
" pass\n",
"\n",
" def start_night_mode(self):\n",
" self.theme_params = {\n",
" 'background_color': 'black',\n",
" 'text_color': 'white',\n",
" 'poster_line_color': 'gray',\n",
" }\n",
" pass\n",
" \n",
" def load_barlow(self, font_size=None):\n",
" # Ruta a la fuente TTF personalizada\n",
" font_path = \"/app/data/Barlow-Medium.ttf\"\n",
" # Carga la fuente\n",
" if font_size is None:\n",
" self.font = ImageFont.truetype(font_path, self.prms['font_size'])\n",
" else:\n",
" self.font = ImageFont.truetype(font_path, font_size)\n",
" pass\n",
"\n",
" def preview(self):\n",
" plt.imshow(self.image)\n",
" plt.axis('off')\n",
" plt.show()\n",
"\n",
" def crop_image(self, top_cut, bottom_cut):\n",
" width, height = self.image.size\n",
" self.image = self.image.crop((0, top_cut, width, height - bottom_cut))\n",
" pass\n",
"\n",
" def resize_image(self, target_width=None, target_height=None):\n",
" \"\"\"\n",
" Ajusta el tamaño de la imagen mientras mantiene las proporciones.\n",
" \n",
" Args:\n",
" image (PIL.Image.Image): La imagen a redimensionar.\n",
" target_width (int, opcional): El ancho objetivo deseado.\n",
" target_height (int, opcional): La altura objetivo deseada.\n",
" \n",
" Returns:\n",
" PIL.Image.Image: La imagen redimensionada.\n",
" \"\"\"\n",
" width, height = self.image.size\n",
" aspect_ratio = width / height\n",
" \n",
" if target_width is None and target_height is None:\n",
" raise ValueError(\"Debes proporcionar al menos una de las dimensiones objetivo.\")\n",
" \n",
" if target_width is not None and target_height is None:\n",
" new_width = target_width\n",
" new_height = int(target_width / aspect_ratio)\n",
" elif target_width is None and target_height is not None:\n",
" new_width = int(target_height * aspect_ratio)\n",
" new_height = target_height\n",
" else:\n",
" new_width = target_width\n",
" new_height = target_height\n",
" \n",
" self.image = self.image.resize((new_width, new_height))\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class BusPoster(MyDraw):\n",
"\n",
" def start_draw(self):\n",
" return super().start_draw()\n",
"\n",
" def set_colors(self):\n",
" width_border = self.prms['width_border']\n",
" proportion = self.prms['proportion']\n",
" fill_color_l = self.prms['letter_background_color']\n",
" fill_color_n = self.prms['number_background_color']\n",
"\n",
"\n",
" self.draw.rounded_rectangle(\n",
" (0, 0, self.width-width_border, self.height-width_border),\n",
" fill=fill_color_l,\n",
" outline=self.theme_params['poster_line_color'],\n",
" width=width_border,\n",
" radius=5)\n",
" \n",
" self.draw.rounded_rectangle(\n",
" (0, 0, proportion*self.width-width_border, self.height-width_border),\n",
" fill=fill_color_n,\n",
" outline=self.theme_params['poster_line_color'],\n",
" width=width_border,\n",
" radius=5)\n",
" pass\n",
"\n",
" def set_bus_number(self, bus_number=\"11\"):\n",
" text_color = 'black'\n",
" width_border = self.prms['width_border']\n",
" text_bbox = self.font.getbbox(str(bus_number))\n",
" font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
" offset_width = np.round((self.prms['proportion']*self.width-width_border)/2) - np.round(font_width/2)\n",
" text_position = (offset_width,0)\n",
" self.draw.text(\n",
" text_position,\n",
" bus_number,\n",
" fill=text_color,\n",
" font=self.font,\n",
" # align =\"center\"\n",
" )\n",
" pass\n",
"\n",
" def set_bus_letter(self, bus_letter=\"E\"):\n",
" proportion = self.prms['proportion']\n",
" width_border = self.prms['width_border']\n",
" text_color = 'white'\n",
" text_bbox = self.font.getbbox(str(bus_letter))\n",
" font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
" offset_width = np.round((proportion*self.width-width_border)) + 0.75*np.round(font_width/2)\n",
" text_position = (offset_width,0)\n",
" self.draw.text(\n",
" text_position,\n",
" bus_letter,\n",
" fill=text_color,\n",
" font=self.font,\n",
" # align =\"center\"\n",
" )\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class TimeAnnouncement(MyDraw):\n",
"\n",
" def start_draw(self):\n",
" super().start_draw()\n",
" self.border = 1\n",
"\n",
" def set_background(self):\n",
" self.draw.rounded_rectangle(\n",
" (0, 0, self.width-0.5*self.border, self.height-0.5*self.border),\n",
" fill=\"#dcdcdc\",\n",
" outline=\"gray\",\n",
" width=self.border,\n",
" radius=1)\n",
" pass\n",
"\n",
" def set_base_text(self):\n",
" text = \"Tiempo aprox\"\n",
" text_color = self.theme_params['text_color']\n",
" self.load_barlow(font_size=11)\n",
" text_bbox = self.font.getbbox(text)\n",
" font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
" # print(font_width, font_height)\n",
" offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2\n",
" text_position = (offset_width,5)\n",
" # text_position = (0, 0)\n",
" self.draw.text(\n",
" text_position,\n",
" text,\n",
" fill=text_color,\n",
" font=self.font,\n",
" align =\"center\"\n",
" )\n",
" pass\n",
"\n",
" def set_min_max_text(self, min_time, max_time):\n",
"\n",
" text = \"Tiempo aprox\"\n",
" text_color = self.theme_params['text_color']\n",
" self.load_barlow(font_size=11)\n",
" text_bbox = self.font.getbbox(text)\n",
" base_font_width, base_font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
"\n",
" if (int(max_time) <= 1):\n",
" text = f\"< 1 min\"\n",
" else:\n",
" text = f'{min_time} a {max_time} min'\n",
" \n",
" self.load_barlow(font_size=18)\n",
" text_bbox = self.font.getbbox(text)\n",
" font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
" # print(font_width, font_height)\n",
" offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2\n",
" offset_height = (np.round((self.height-self.border)) - np.round(base_font_height))/2\n",
" text_position = (offset_width,5+offset_height)\n",
" # text_position = (0, 0)\n",
" self.draw.text(\n",
" text_position,\n",
" text,\n",
" fill=text_color,\n",
" font=self.font,\n",
" align =\"center\"\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class DistanceAnnouncement(MyDraw):\n",
"\n",
" def start_draw(self):\n",
" super().start_draw()\n",
" self.border = 1\n",
"\n",
" def set_background(self):\n",
" self.draw.rounded_rectangle(\n",
" (0, 0, self.width-0.5*self.border, self.height-0.5*self.border),\n",
" fill=\"#dcdcdc\",\n",
" outline=\"gray\",\n",
" width=self.border,\n",
" radius=1)\n",
" pass\n",
"\n",
" def set_base_text(self):\n",
" text = \"Distancia\"\n",
" text_color = self.theme_params['text_color']\n",
" self.load_barlow(font_size=11)\n",
" text_bbox = self.font.getbbox(text)\n",
" font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
" # print(font_width, font_height)\n",
" offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2\n",
" text_position = (offset_width,5)\n",
" # text_position = (0, 0)\n",
" self.draw.text(\n",
" text_position,\n",
" text,\n",
" fill=text_color,\n",
" font=self.font,\n",
" align =\"center\"\n",
" )\n",
" pass\n",
"\n",
" def set_distance_text(self, distance):\n",
"\n",
" text = \"Distancia\"\n",
" text_color = self.theme_params['text_color']\n",
" self.load_barlow(font_size=11)\n",
" text_bbox = self.font.getbbox(text)\n",
" base_font_width, base_font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
"\n",
"\n",
" text = f'{distance} km'\n",
" self.load_barlow(font_size=18)\n",
" text_bbox = self.font.getbbox(text)\n",
" font_width, font_height = text_bbox[2] - text_bbox[0], text_bbox[3] - text_bbox[1]\n",
" # print(font_width, font_height)\n",
" offset_width = (np.round((self.width-self.border)) - np.round(font_width))/2\n",
" offset_height = (np.round((self.height-self.border)) - np.round(base_font_height))/2\n",
" text_position = (offset_width,5+offset_height)\n",
" # text_position = (0, 0)\n",
" self.draw.text(\n",
" text_position,\n",
" text,\n",
" fill=text_color,\n",
" font=self.font,\n",
" align =\"center\"\n",
" )"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"\n",
"class BusPlate():\n",
" def __init__(self, url=None) -> None:\n",
" if url is None:\n",
" self.url = \"https://matriculasdelmundo.com/gRCH1.php\"\n",
" pass\n",
"\n",
" def set_url(self, url):\n",
" self.url = url\n",
" pass\n",
"\n",
" def get_image(self):\n",
" if hasattr(self, 'image'):\n",
" return self.image\n",
" else:\n",
" print(\"Error: No se ha generado ninguna imagen aún.\")\n",
" return None\n",
"\n",
" def request_bus_plate(self, bus_plate=None):\n",
" if bus_plate is None:\n",
" self.bus_plate = \"AABB11\"\n",
" else:\n",
" self.bus_plate = bus_plate\n",
"\n",
" params = {\n",
" \"textRCH1\": self.bus_plate[0:2],\n",
" \"textRCH1A\": self.bus_plate[2:4],\n",
" \"textRCH1B\": self.bus_plate[4:],\n",
" \"textRCH1C\": \"\"\n",
" }\n",
"\n",
" self.response = requests.get(self.url, params=params)\n",
" pass\n",
"\n",
" def save_bus_plate_image(self):\n",
" if self.response.status_code == 200:\n",
" filename = f\"/app/data/output/plate_{self.bus_plate}.png\" \n",
" with open(filename, \"wb\") as f:\n",
" f.write(self.response.content)\n",
" print(f\"Imagen generada guardada como '{filename}'\")\n",
" else:\n",
" print(\"Error al guardar la imagen generada\")\n",
" pass\n",
"\n",
" def generate_image(self):\n",
" image_bytes = io.BytesIO(self.response.content)\n",
" self.image = Image.open(image_bytes)\n",
" self.image = self.image.convert(\"RGBA\") # Convertir a formato RGBA\n",
" pass\n",
"\n",
" def resize_image(self, new_size):\n",
" proportion = np.min([self.image.size[0]/new_size[0], self.image.size[1]/new_size[1]])\n",
" nx, ny = int(np.round(image.size[0]/proportion)), int(np.round(image.size[1]/proportion))\n",
" self.image = self.image.resize((nx, ny))\n",
" pass\n",
"\n",
" def preview(self):\n",
" plt.imshow(self.image)\n",
" plt.axis('off')\n",
" plt.show()\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"class BusImage(MyDraw):\n",
" def __init__(self):\n",
" pass\n",
" \n",
" def load_image_from_url(self, url=None):\n",
" if(url is None):\n",
" # URL de la imagen en línea\n",
" url = \"https://img.freepik.com/iconos-gratis/autobus_318-574563.jpg\"\n",
"\n",
" # Descarga la imagen desde la URL\n",
" response = requests.get(url)\n",
" image_data = response.content\n",
" # Crea un objeto Image desde los datos descargados\n",
" self.image = Image.open(BytesIO(image_data))\n",
" # Crear una imagen en blanco del mismo tamaño que loaded_image con fondo blanco\n",
" background = Image.new(\"RGB\", self.image.size, self.theme_params['background_color'])\n",
"\n",
" # Pega la loaded_image en la imagen en blanco\n",
" background.paste(self.image, (0, 0), self.image)\n",
" \n",
" self.image = background\n",
"\n",
" # Calcula la posición para agregar la imagen cargada\n",
" image_position = (0, 0) # Cambia esto según tu diseño\n",
"\n",
" # Agrega la imagen a la imagen creada\n",
" self.image.paste(background, image_position)\n",
" pass"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"theme = 'night'\n",
"\n",
"panel_height, panel_width = 40, 80\n",
"n_panels = 3\n",
"full_panel = MyDraw(n_panels*panel_height, n_panels*panel_width)\n",
"full_panel.set_theme(theme)\n",
"full_panel.start_draw()\n",
"# full_panel.preview()\n",
"\n",
"bp = BusPlate()\n",
"plate = \"WXYZ88\"\n",
"bp.request_bus_plate(bus_plate=plate)\n",
"bp.generate_image()\n",
"bp.resize_image((40,80))\n",
"\n",
"dist_anmc = DistanceAnnouncement(50, 80)\n",
"dist_anmc.set_theme(theme)\n",
"dist_anmc.start_draw()\n",
"# dist_anmc.set_background()\n",
"dist_anmc.set_base_text()\n",
"dist_anmc.set_distance_text(distance=5)\n",
"\n",
"time_anmc = TimeAnnouncement(50, 80)\n",
"time_anmc.set_theme(theme)\n",
"time_anmc.start_draw()\n",
"# time_anmc.set_background()\n",
"time_anmc.set_base_text()\n",
"time_anmc.set_min_max_text(min_time=2, max_time=3)\n",
"\n",
"poster = BusPoster(30, 60)\n",
"poster.set_theme(theme)\n",
"poster.start_draw()\n",
"\n",
"poster_params = {\n",
" 'proportion': 0.6,\n",
" 'width_border': 1,\n",
" 'font_size': 25,\n",
" 'number_background_color': 'yellow',\n",
" 'letter_background_color': 'green',\n",
"}\n",
"\n",
"poster.set_params(poster_params)\n",
"poster.load_barlow()\n",
"poster.set_colors()\n",
"poster.set_bus_number(bus_number=\"20\")\n",
"poster.set_bus_letter(bus_letter=\"L\")\n",
"\n",
"bm = BusImage()\n",
"bm.set_theme(theme)\n",
"bm.load_image_from_url()\n",
"bm.crop_image(top_cut=165, bottom_cut=165)\n",
"bm.resize_image(target_width=80)\n",
"\n",
"full_panel.add_image(bp, (120, 80))\n",
"full_panel.add_image(dist_anmc, (90, 10))\n",
"full_panel.add_image(time_anmc, (160, 10))\n",
"full_panel.add_image(poster, (40, 80))\n",
"full_panel.add_image(bm, (5,20))\n",
"full_panel.get_image()\n",
"full_panel.save_image('/app/data/output.png')"
]
}
],
"metadata": {
"language_info": {
"name": "python"
},
"orig_nbformat": 4
},
"nbformat": 4,
"nbformat_minor": 2
}