-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
first version of ASCII generator command
- Loading branch information
1 parent
991774d
commit f6fc219
Showing
11 changed files
with
329 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
"""Cmd submodules | ||
""" | ||
from .ascii_generator import ascii_generator |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
"""ASCII Art generator Click command | ||
""" | ||
import click | ||
|
||
from ascii_art.generator import generate_ascii_art_from_img | ||
from ascii_art.io import get_img_from_url | ||
from ascii_art.io import get_img_url_list_from_keyword | ||
|
||
|
||
@click.command() | ||
@click.argument("keyword") | ||
@click.option("-c", "--cols", default=79, help="Number of columns for ASCII Art.") | ||
@click.option("-s", "--scale", default=0.43, help="Height scale for ASCII Art.") | ||
@click.option( | ||
"-m", | ||
"--more-levels", | ||
default=True, | ||
help="Whether you want a grayscale of 70 or 10.", | ||
) | ||
def ascii_generator(keyword, cols, scale, more_levels): | ||
"""Shows differents ASCII Art given a keyword | ||
You can change the number of columns and the scale. | ||
There are 2 gray scale available 10 and 70 (default is 70) | ||
""" | ||
|
||
img_list = get_img_url_list_from_keyword(keyword) | ||
|
||
for title, url in img_list: | ||
img = get_img_from_url(url) | ||
ascii_img = generate_ascii_art_from_img(img, cols, scale, more_levels) | ||
|
||
print("Image name : %s" % title) | ||
print(ascii_img) | ||
|
||
ans = input("Do you want to keep this image ? (y/n)").lower() | ||
|
||
if ans == "y": | ||
print("Awesome !") | ||
exit(0) | ||
|
||
|
||
if __name__ == "__main__": | ||
ascii_generator() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
""" Generator submodule | ||
""" | ||
from .generate_ascii_art import generate_ascii_art_from_img |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
"""ASCII Art generator functions | ||
Based on https://www.geeksforgeeks.org/converting-image-ascii-image-python/ | ||
""" | ||
import numpy as np | ||
|
||
GRAY_SCALE_70 = ( | ||
"$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. " | ||
) | ||
GRAY_SCALE_10 = "@%#*+=-:. " | ||
|
||
|
||
def get_average(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 generate_ascii_art_from_img(image, cols=70, scale=0.43, more_levels=True) -> str: | ||
"""Generate a string of a ASCII Art given an image | ||
Parameters | ||
---------- | ||
image : Image | ||
PIL Image (gray scale) | ||
cols : int, optional | ||
Number of columns for the output, by default 70 | ||
scale : float, optional | ||
Scale for the height, by default 0.43 | ||
more_levels : bool, optional | ||
Whether you want 70 gray scales or 10, by default True | ||
Returns | ||
------- | ||
str | ||
ASCII Art string | ||
""" | ||
# 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(get_average(img)) | ||
|
||
# look up ascii char | ||
if more_levels: | ||
gsval = GRAY_SCALE_70[int((avg * 69) / 255)] | ||
else: | ||
gsval = GRAY_SCALE_10[int((avg * 9) / 255)] | ||
|
||
# append ascii char to string | ||
aimg[j] += gsval | ||
|
||
# Convert as string | ||
aimg = "\n".join(aimg) | ||
|
||
# return txt image | ||
return aimg |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
"""Input output submodule | ||
""" | ||
from .img import get_img_from_url | ||
from .scrap import get_data_from_url | ||
from .scrap import get_img_url_list_from_keyword | ||
from .scrap import scrap_img_on_bing |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
from io import BytesIO | ||
|
||
from PIL import Image | ||
|
||
from .scrap import get_data_from_url | ||
|
||
|
||
def get_img_from_url(url: str) -> Image: | ||
"""Returns a gray image given an url | ||
Parameters | ||
---------- | ||
url : str | ||
Url where the image is | ||
Returns | ||
------- | ||
Image | ||
Gray image | ||
""" | ||
ans = get_data_from_url(url) | ||
# Open as grayscale | ||
img = Image.open(BytesIO(ans.content)).convert("L") | ||
|
||
return img |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
"""Scrap functions | ||
""" | ||
import re | ||
|
||
import requests | ||
from bs4 import BeautifulSoup | ||
|
||
|
||
DEFAULT_HEADER = { | ||
"User-Agent": "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/73.0.3683.86 Safari/537.36", | ||
"Accept-Language": "en-GB,en;q=0.5", | ||
} | ||
BING_IMG_URL = "https://www.bing.com/images/search?q=" | ||
|
||
|
||
class RequestException(Exception): | ||
pass | ||
|
||
|
||
def create_header(url: str) -> dict: | ||
"""Create a header dictionnary if it need | ||
to be changed (e.g. for an API) | ||
Parameters | ||
---------- | ||
url : str | ||
url to request (needed to get the host) | ||
Returns | ||
------- | ||
dict | ||
header dictionnary | ||
""" | ||
header = DEFAULT_HEADER | ||
|
||
url_split = url.split("//") | ||
http = url_split[0] | ||
host = url_split[1].split("/")[0] | ||
|
||
header["Host"] = host | ||
header["Referer"] = http + "//" + host | ||
header["Origin"] = http + "//" + host | ||
|
||
return header | ||
|
||
|
||
def get_data_from_url(url: str) -> requests.Response: | ||
"""Return answer of a request get | ||
from a wanted url | ||
Parameters | ||
---------- | ||
url : str | ||
url to request | ||
Returns | ||
------- | ||
requests.Response | ||
answer from requests.get function | ||
Raises | ||
------ | ||
TypeError | ||
url must be a string | ||
ValueError | ||
url must start with 'http' | ||
""" | ||
|
||
if not isinstance(url, str): | ||
raise TypeError("url must be a string") | ||
if not url.startswith("http"): | ||
raise ValueError("url must start with 'http'") | ||
|
||
headers = create_header(url) | ||
r = requests.get(url, headers=headers) | ||
|
||
return r | ||
|
||
|
||
def scrap_img_on_bing(keyword: str) -> list: | ||
"""Scrap images on first page of Bing image page | ||
Parameters | ||
---------- | ||
keyword : str | ||
kerword for the research | ||
Returns | ||
------- | ||
list | ||
List of tuple (title, image url) | ||
Raises | ||
------ | ||
RequestException | ||
Request response is not valid | ||
""" | ||
|
||
url = BING_IMG_URL + keyword | ||
ans = get_data_from_url(url) | ||
|
||
if ans.status_code != 200: | ||
raise RequestException( | ||
"Request response is not valid (status code %s)" % ans.status_code | ||
) | ||
|
||
soup = BeautifulSoup(ans.text, "html.parser") | ||
images = soup.find_all("img") | ||
|
||
img_urls = [] | ||
|
||
# regex for height and width on url | ||
regex = re.compile(r"&w=[0-9]*&h=[0-9]*", re.IGNORECASE) | ||
for item in images: | ||
if not item.has_attr("src2"): | ||
continue | ||
|
||
img_url = item.get("src2") | ||
img_url = regex.sub("&w=200&h=200", img_url) | ||
img_ = (item.get("alt"), img_url) | ||
|
||
img_urls.append(img_) | ||
|
||
return img_urls | ||
|
||
|
||
def get_img_url_list_from_keyword(keyword: str, engine="bing") -> list: | ||
|
||
if engine not in ["bing"]: | ||
raise ValueError("engine muste be in ['bing']") | ||
|
||
if engine == "bing": | ||
img_list = scrap_img_on_bing(keyword) | ||
|
||
return img_list |
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,8 @@ | ||
"""Setup.py file | ||
""" | ||
from distutils.core import setup | ||
from setuptools import find_packages | ||
from setuptools import setup | ||
|
||
|
||
with open("version", "r") as f: | ||
version = f.read() | ||
|
@@ -14,5 +16,10 @@ | |
author="Nathan LAUGA", | ||
author_email="[email protected]", | ||
url=url, | ||
packages=["ascii_art"], | ||
packages=find_packages(), | ||
entry_points={ | ||
"console_scripts": [ | ||
"ascii-generator = ascii_art.cmd:ascii_generator", | ||
], | ||
}, | ||
) |