Spaces:
Build error
Build error
| ''' | |
| Refer to https://huggingface.co/spaces/dt/ascii-art/blob/main/app.py | |
| ''' | |
| # Python code to convert an image to ASCII image. | |
| import sys, random, argparse | |
| import numpy as np | |
| import math | |
| import base64 | |
| from PIL import Image, ImageFont, ImageDraw | |
| from moviepy.editor import * | |
| from tqdm.auto import tqdm | |
| import gradio as gr | |
| # 70 levels of gray | |
| gscale1 = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. " | |
| # 10 levels of gray | |
| gscale2 = '@%#*+=-:. ' | |
| font = ImageFont.load_default() | |
| def getAverageL(image): | |
| """ | |
| Given PIL Image, return average value of grayscale value | |
| """ | |
| # get image as numpy array | |
| im = np.array(image) | |
| # get shape | |
| w,h = im.shape | |
| # get average | |
| return np.average(im.reshape(w*h)) | |
| def covertImageToAscii(input_img, cols, scale, moreLevels): | |
| """ | |
| Given Image and dims (rows, cols) returns an m*n list of Images | |
| """ | |
| # declare globals | |
| global gscale1, gscale2 | |
| # open image and convert to grayscale | |
| image = input_img.convert('L') | |
| # store dimensions | |
| # store dimensions | |
| W, H = image.size[0], image.size[1] | |
| # compute width of tile | |
| w = W/cols | |
| # compute tile height based on aspect ratio and scale | |
| h = w/scale | |
| # compute number of rows | |
| rows = int(H/h) | |
| # check if image size is too small | |
| if cols > W or rows > H: | |
| print("Image too small for specified cols!") | |
| exit(0) | |
| # ascii image is a list of character strings | |
| aimg = [] | |
| # generate list of dimensions | |
| for j in range(rows): | |
| y1 = int(j*h) | |
| y2 = int((j+1)*h) | |
| # correct last tile | |
| if j == rows-1: | |
| y2 = H | |
| # append an empty string | |
| aimg.append("") | |
| for i in range(cols): | |
| # crop image to tile | |
| x1 = int(i*w) | |
| x2 = int((i+1)*w) | |
| # correct last tile | |
| if i == cols-1: | |
| x2 = W | |
| # crop image to extract tile | |
| img = image.crop((x1, y1, x2, y2)) | |
| # get average luminance | |
| avg = int(getAverageL(img)) | |
| # look up ascii char | |
| if moreLevels: | |
| gsval = gscale1[int((avg*69)/255)] | |
| else: | |
| gsval = gscale2[int((avg*9)/255)] | |
| # append ascii char to string | |
| aimg[j] += gsval | |
| # return txt image | |
| return aimg | |
| def colorizeTextImage(input_img, text_img): | |
| input_img = np.asarray(input_img) | |
| input_img = input_img.reshape(( | |
| input_img.shape[0]//11, | |
| 11, | |
| input_img.shape[1]//6, | |
| 6, | |
| 3 | |
| )) | |
| input_img = np.float32(input_img) | |
| text_img = np.asarray(text_img) | |
| text_img = text_img.reshape(( | |
| input_img.shape[0], | |
| 11, | |
| input_img.shape[2], | |
| 6, | |
| 3 | |
| )) | |
| alpha = np.float32(text_img)[...,:1] / 255 | |
| alpha[alpha < 0.125] = 0 | |
| alpha[alpha >= 0.125] = 1 | |
| out_img = input_img * alpha | |
| out_colors = out_img.sum((1,3), keepdims=True) / (alpha.sum((1,3), keepdims=True) + 1e-12) | |
| out_img = out_colors * alpha | |
| out_img = out_img.reshape(( | |
| out_img.shape[0] * out_img.shape[1], | |
| out_img.shape[2] * out_img.shape[3], | |
| 3 | |
| )) | |
| out_img = np.clip(out_img, 0, 255) | |
| out_img = np.uint8(out_img) | |
| return out_img | |
| def sepia(input_img, no_colors=False): | |
| input_img = Image.fromarray(input_img).convert('RGB') | |
| aimg = covertImageToAscii(input_img, 200, 6/11, True) | |
| blank_image = Image.new(mode="RGB", size=(len(aimg[0])*6, len(aimg)*11), color=(0, 0, 0)) | |
| my_image = blank_image.copy() | |
| image_editable = ImageDraw.Draw(my_image) | |
| image_editable.text((0, 0), "\n".join(aimg), (255, 255, 255), font=font, spacing=0) | |
| if no_colors: | |
| return np.asarray(my_image) | |
| input_img_resize = input_img.resize((len(aimg[0])*6, len(aimg)*11), Image.BICUBIC) | |
| w, h = input_img.size | |
| scale = 200 * 6 / w | |
| w = 200 * 6 | |
| h = int(round(h*scale)) | |
| input_img = input_img.resize((200 * 6, h), Image.BICUBIC) | |
| input_img_resize.paste(input_img, (0, 0, w, h)) | |
| input_img = input_img_resize | |
| my_image = colorizeTextImage(input_img, my_image) | |
| return my_image | |
| def sepia_video(video_file, no_colors=False): | |
| clip = VideoFileClip(video_file) | |
| audioclip = clip.audio | |
| frames = int(clip.fps * clip.duration) | |
| imgs = [] | |
| for i in tqdm(range(frames)): | |
| imgs.append(sepia(clip.get_frame(i/clip.fps), no_colors)) | |
| video = ImageSequenceClip(imgs, fps=clip.fps) | |
| video = video.set_audio(audioclip) | |
| video.write_videofile("out.mp4", fps=clip.fps) | |
| return "out.mp4" | |
| iface = gr.Interface(sepia_video, | |
| [gr.Video(format=None), gr.Checkbox(label="No Colors")], | |
| "video", | |
| title = "Colorful ASCII Art", | |
| description = "Convert an image to colorful ASCII art based on ascii character density. Click the first output text to download the generated svg.") | |
| iface.launch() | |