Skip to content

Commit

Permalink
initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
joelibaceta committed Nov 24, 2023
0 parents commit 936a287
Show file tree
Hide file tree
Showing 5 changed files with 243 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
fixedenv
__pycache__
28 changes: 28 additions & 0 deletions img2ascii.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
from typing import Optional
from PIL import Image
import shutil


def image_to_ascii_art(img: Image.Image, rows: int = 0) -> str:
"""Convert an Image to ASCII Art"""

width, height = img.size
aspect_ratio = height / width
new_width = rows
new_height = aspect_ratio * new_width * 0.55
img = img.resize((new_width, int(new_height)))

pixels = img.getdata()

chars = ["*", "S", "#", "&", "@", "$", "%", "*", "!", ":", "."]
new_pixels = [chars[pixel // 25] for pixel in pixels]
new_pixels = "".join(new_pixels)

new_pixels_count = len(new_pixels)
ascii_image = [
new_pixels[index : index + new_width]
for index in range(0, new_pixels_count, new_width)
]
ascii_image = "\n".join(ascii_image)

return ascii_image
6 changes: 6 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
colorama
pywhatkit
opencv-python
numpy
pillow
setproctitle
120 changes: 120 additions & 0 deletions utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
from PIL import Image

import numpy as np

import os
import pyautogui
import Quartz
import time
import cv2

TERMINAL_COLUMN_WIDTH_CONSTANT = 9.028871391076116
TERMINAL_ROW_HEIGHT_CONSTANT = 17.142857142857142


def get_screen_size() -> tuple[int, int]:
"""
Obtiene el tamaño de la pantalla.
Returns:
tuple: Ancho y alto de la pantalla.
"""
return pyautogui.size()


def get_frame_size(cap: cv2.VideoCapture) -> tuple[int, int]:
"""
Obtiene el tamaño del frame capturado por la webcam.
Args:
cap (cv2.VideoCapture): Objeto de captura de video.
Returns:
tuple: Ancho y alto del frame.
"""
return int(cap.get(cv2.CAP_PROP_FRAME_WIDTH)), int(
cap.get(cv2.CAP_PROP_FRAME_HEIGHT)
)


def get_terminal_size(window_number) -> tuple[int, int, int, int, int, int]:
"""
Obtiene el tamaño de la ventana de la terminal.
Args:
window_number (int): Número de la ventana de la terminal.
Returns:
tuple: Coordenadas x, y, ancho, alto, columnas y filas de la terminal.
"""
window_list = Quartz.CGWindowListCopyWindowInfo(
Quartz.kCGWindowListOptionOnScreenOnly, Quartz.kCGNullWindowID
)
for window_info in window_list:
if (
window_info["kCGWindowNumber"] == window_number
): # Busca la ventana de la terminal
terminal_window = window_info
break

x = int(terminal_window["kCGWindowBounds"]["X"])
y = int(terminal_window["kCGWindowBounds"]["Y"])

terminal_columns, terminal_rows = os.get_terminal_size()

width = int(terminal_columns * TERMINAL_COLUMN_WIDTH_CONSTANT)
height = int(terminal_rows * TERMINAL_ROW_HEIGHT_CONSTANT)

return x, y, width, height, terminal_columns, terminal_rows


def resize_frame(frame, frame_width, frame_height) -> np.ndarray:
"""
Redimensiona el frame para que se ajuste a la pantalla.
Args:
frame (np.ndarray): Frame capturado por la webcam.
frame_width (int): Ancho del frame.
frame_height (int): Alto del frame.
Returns:
np.ndarray: Frame redimensionado.
"""
screen_width, screen_height = get_screen_size()

if frame_height > screen_height:
adjusted_width = int(screen_height / frame_height * frame_width)
adjusted_height = screen_height
else:
adjusted_width = int(screen_width)
adjusted_height = int(screen_width / frame_width * frame_height)

return cv2.resize(frame, (adjusted_width, adjusted_height))


def prepare_image(frame: np.ndarray) -> Image.Image:
"""
Prepara la imagen para la conversión a ASCII Art.
Args:
frame (np.ndarray): Frame capturado por la webcam.
Returns:
Image.Image: Imagen preparada para la conversión a ASCII Art.
"""
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
normalized_frame = cv2.normalize(gray_frame, None, 0, 255, cv2.NORM_MINMAX)
return Image.fromarray(normalized_frame.astype(np.uint8))


def lazy_print(ascii_art: str):
"""
Imprime el ASCII Art en la terminal con un pequeño retraso entre líneas.
Args:
ascii_art (str): ASCII Art a imprimir.
"""
lines = ascii_art.split("\n")
for line in lines:
print(line, end="\r\n")
time.sleep(0.1)
87 changes: 87 additions & 0 deletions webcam-ascii.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
from img2ascii import image_to_ascii_art
from colorama import init, Fore
from utils import (
get_frame_size,
get_terminal_size,
resize_frame,
prepare_image,
lazy_print,
)

import sys
import cv2


class WebcamAscii:
"""
Clase que convierte la entrada de una webcam en ASCII Art
y la muestra en multiples ventanas de la terminal
"""

# Inicializa colorama para imprimir colores en la terminal
init(autoreset=True)

def __init__(self, window_number):
self.window_number = window_number

def open_webcam(self):
"""
Abre la conexión con la webcam y verifica si la conexión fue exitosa.
Returns:
Objeto cv2.VideoCapture si la conexión fue exitosa.
"""

# Abre la conexión con la webcam
cap = cv2.VideoCapture(0)
# Verifica si la conexión fue exitosa
exit() if not cap.isOpened() else print(
Fore.GREEN + "Conexión exitosa con la cámara."
)
return cap

def run(self):
"""
Inicia la captura de la webcam y la muestra en la terminal.
"""

cap = self.open_webcam()
frame_width, frame_height = get_frame_size(cap)
(
terminal_x,
terminal_y,
terminal_width,
terminal_height,
terminal_columns,
_,
) = get_terminal_size(window_number)

while True:
ret, frame = cap.read()

if not ret: # Verifica si la captura fue exitosa
print(Fore.RED + "Error: No se puede leer el frame.")
break

resized_frame = resize_frame(frame, frame_width, frame_height)
cutted_frame = resized_frame[
terminal_y : terminal_y + terminal_height,
terminal_x : terminal_x + terminal_width,
]
pre_processed_image = prepare_image(cutted_frame)
ascii_art = image_to_ascii_art(pre_processed_image, rows=terminal_columns)
lazy_print(ascii_art)

if cv2.waitKey(1) & 0xFF == ord("q"):
break

cap.release()

# Libera la cámara y cierra la ventana
cv2.destroyAllWindows()


if __name__ == "__main__":
window_number = int(sys.argv[1]) # Obtiene el número de la ventana de la terminal
cam = WebcamAscii(window_number)
cam.run()

0 comments on commit 936a287

Please sign in to comment.