last(?) commit from hilda
This commit is contained in:
2
requirements.txt
Normal file
2
requirements.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
colorama==0.4.5
|
||||
Pillow==9.2.0
|
||||
114
src/colors.py
Normal file
114
src/colors.py
Normal file
@@ -0,0 +1,114 @@
|
||||
from enum import Enum, auto, unique
|
||||
from colorama import Fore, Back
|
||||
|
||||
class Color:
|
||||
def __init__(self, r, g, b):
|
||||
self.R = r
|
||||
self.G = g
|
||||
self.B = b
|
||||
|
||||
def __str__(self):
|
||||
return str((self.R, self.G, self.B))
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.R == other.R and self.G == other.G and self.B == other.B
|
||||
|
||||
def distance(self, other):
|
||||
return ((self.R - other.R) ** 2
|
||||
+ (self.G - other.G) ** 2
|
||||
+ (self.B - other.B) ** 2) ** .5
|
||||
|
||||
def blend(self, other, weight):
|
||||
oweight = 1 - weight
|
||||
return Color(self.R * weight + other.R * oweight,
|
||||
self.G * weight + other.G * oweight,
|
||||
self.B * weight + other.B * oweight)
|
||||
|
||||
#ubuntu
|
||||
class ColorCode(Enum):
|
||||
Black = Color(1, 1, 1)
|
||||
Red = Color(222, 56, 43)
|
||||
Green = Color(57, 181, 74)
|
||||
Yellow = Color(255, 199, 6)
|
||||
Blue = Color(0, 111, 184)
|
||||
Magenta = Color(118, 38, 113)
|
||||
Cyan = Color(44, 181, 233)
|
||||
White = Color(204, 204, 204)
|
||||
BrightBlack = Color(128, 128, 128)
|
||||
BrightRed = Color(255, 0, 0)
|
||||
BrightGreen = Color(0, 255, 0)
|
||||
BrightYellow = Color(255, 255, 0)
|
||||
BrightBlue = Color(0, 0, 255)
|
||||
BrightMagenta = Color(255, 0, 255)
|
||||
BrightCyan = Color(0, 255, 255)
|
||||
BrightWhite = Color(255, 255, 255)
|
||||
|
||||
def to_fore(color):
|
||||
if color == ColorCode.Black:
|
||||
return Fore.BLACK
|
||||
elif color == ColorCode.Red:
|
||||
return Fore.RED
|
||||
elif color == ColorCode.Green:
|
||||
return Fore.GREEN
|
||||
elif color == ColorCode.Yellow:
|
||||
return Fore.YELLOW
|
||||
elif color == ColorCode.Blue:
|
||||
return Fore.BLUE
|
||||
elif color == ColorCode.Magenta:
|
||||
return Fore.MAGENTA
|
||||
elif color == ColorCode.Cyan:
|
||||
return Fore.CYAN
|
||||
elif color == ColorCode.White:
|
||||
return Fore.WHITE
|
||||
elif color == ColorCode.BrightBlack:
|
||||
return Fore.LIGHTBLACK_EX
|
||||
elif color == ColorCode.BrightRed:
|
||||
return Fore.LIGHTRED_EX
|
||||
elif color == ColorCode.BrightGreen:
|
||||
return Fore.LIGHTGREEN_EX
|
||||
elif color == ColorCode.BrightYellow:
|
||||
return Fore.LIGHTYELLOW_EX
|
||||
elif color == ColorCode.BrightBlue:
|
||||
return Fore.LIGHTBLUE_EX
|
||||
elif color == ColorCode.BrightMagenta:
|
||||
return Fore.LIGHTMAGENTA_EX
|
||||
elif color == ColorCode.BrightCyan:
|
||||
return Fore.LIGHTCYAN_EX
|
||||
elif color == ColorCode.BrightWhite:
|
||||
return Fore.LIGHTWHITE_EX
|
||||
raise ValueError(color)
|
||||
|
||||
def to_back(color):
|
||||
if color == ColorCode.Black:
|
||||
return Back.BLACK
|
||||
elif color == ColorCode.Red:
|
||||
return Back.RED
|
||||
elif color == ColorCode.Green:
|
||||
return Back.GREEN
|
||||
elif color == ColorCode.Yellow:
|
||||
return Back.YELLOW
|
||||
elif color == ColorCode.Blue:
|
||||
return Back.BLUE
|
||||
elif color == ColorCode.Magenta:
|
||||
return Back.MAGENTA
|
||||
elif color == ColorCode.Cyan:
|
||||
return Back.CYAN
|
||||
elif color == ColorCode.White:
|
||||
return Back.WHITE
|
||||
elif color == ColorCode.BrightBlack:
|
||||
return Back.LIGHTBLACK_EX
|
||||
elif color == ColorCode.BrightRed:
|
||||
return Back.LIGHTRED_EX
|
||||
elif color == ColorCode.BrightGreen:
|
||||
return Back.LIGHTGREEN_EX
|
||||
elif color == ColorCode.BrightYellow:
|
||||
return Back.LIGHTYELLOW_EX
|
||||
elif color == ColorCode.BrightBlue:
|
||||
return Back.LIGHTBLUE_EX
|
||||
elif color == ColorCode.BrightMagenta:
|
||||
return Back.LIGHTMAGENTA_EX
|
||||
elif color == ColorCode.BrightCyan:
|
||||
return Back.LIGHTCYAN_EX
|
||||
elif color == ColorCode.BrightWhite:
|
||||
return Back.LIGHTWHITE_EX
|
||||
raise ValueError(color)
|
||||
134
src/picshow.py
Normal file
134
src/picshow.py
Normal file
@@ -0,0 +1,134 @@
|
||||
from PIL import Image
|
||||
import argparse
|
||||
import colorama
|
||||
import sys
|
||||
from io import BytesIO
|
||||
from colors import Color, ColorCode, to_fore, to_back
|
||||
from concurrent.futures import ProcessPoolExecutor
|
||||
|
||||
THRESHOLD1 = 1.5
|
||||
THRESHOLD2 = 2
|
||||
|
||||
SHADE_FULL = u'█'
|
||||
SHADE_LIGHT = u'░'
|
||||
SHADE_MEDIUM = u'▒'
|
||||
SHADE_DARK = u'▓'
|
||||
|
||||
class Block:
|
||||
def __init__(self, char, fore, back):
|
||||
self.char = char
|
||||
self.fore = fore
|
||||
self.back = back
|
||||
|
||||
def extract(self):
|
||||
return self.char, to_fore(self.fore), to_back(self.back)
|
||||
|
||||
def load_image(img):
|
||||
if isinstance(img, Image.Image):
|
||||
return img.convert('RGB')
|
||||
if img:
|
||||
return Image.open(img).convert('RGB')
|
||||
else:
|
||||
data = sys.stdin.buffer.read()
|
||||
return Image.open(BytesIO(data)).convert('RGB')
|
||||
|
||||
def split_image(img, rows, columns, col_index, row_index):
|
||||
width = int(img.size[0] / columns)
|
||||
height = int(img.size[1] / rows)
|
||||
return img.crop((width * col_index, height * row_index,
|
||||
width * (col_index+1), height * (row_index+1)))
|
||||
|
||||
def to_block(img, simple=False):
|
||||
red, green, blue = (0, 0, 0)
|
||||
for pix in img.getdata():
|
||||
red += pix[0]
|
||||
green += pix[1]
|
||||
blue += pix[2]
|
||||
res = img.size
|
||||
size = res[0] * res[1]
|
||||
color = Color(red / size, green / size, blue / size)
|
||||
pairs = [(c, c.value.distance(color)) for c in ColorCode]
|
||||
pairs.sort(key=lambda p : p[1])
|
||||
#cheap fix for divide by zero
|
||||
ratio = pairs[1][1] / (pairs[0][1] or .00001)
|
||||
if simple or ratio > THRESHOLD2:
|
||||
char = SHADE_FULL
|
||||
elif ratio > THRESHOLD1:
|
||||
char = SHADE_DARK
|
||||
else:
|
||||
char = SHADE_MEDIUM
|
||||
return Block(char, pairs[0][0], pairs[1][0])
|
||||
|
||||
def to_block2(img):
|
||||
red, green, blue = (0, 0, 0)
|
||||
for pix in img.getdata():
|
||||
red += pix[0]
|
||||
green += pix[1]
|
||||
blue += pix[2]
|
||||
res = img.size
|
||||
size = res[0] * res[1]
|
||||
color = Color(red / size, green / size, blue / size)
|
||||
match = min(block_list, key=lambda b: b[1].distance(color))
|
||||
return match[0]
|
||||
|
||||
def print_blocks(rows):
|
||||
for row in rows:
|
||||
for block in row:
|
||||
char, fore, back = block.extract()
|
||||
print(fore + back + char + colorama.Fore.RESET + colorama.Back.RESET, end='')
|
||||
print()
|
||||
|
||||
def do_work(file, rows, columns, parallel = False) -> None:
|
||||
with load_image(file) as im:
|
||||
columns = columns or 15
|
||||
rows = rows or 10
|
||||
imgs = []
|
||||
for r in range(rows):
|
||||
row_imgs = []
|
||||
for c in range(columns):
|
||||
row_imgs.append(split_image(im, rows, columns, c, r))
|
||||
imgs.append(row_imgs)
|
||||
if parallel:
|
||||
pool = ProcessPoolExecutor()
|
||||
map_func = pool.map
|
||||
else:
|
||||
map_func = map
|
||||
blocks = map(lambda r: map_func(to_block2, r), imgs)
|
||||
print_blocks(blocks)
|
||||
|
||||
def main(vargs):
|
||||
parser = argparse.ArgumentParser()
|
||||
parser.add_argument('--simple', action='store_true',
|
||||
help='only use full blocks')
|
||||
parser.add_argument('-c', '--columns', type=int,
|
||||
help='the width in chars', default=0)
|
||||
parser.add_argument('-r', '--rows', type=int,
|
||||
help='the height in chars', default=0)
|
||||
parser.add_argument('-p', '--parallel', action='store_true',
|
||||
help='process image in parallel')
|
||||
parser.add_argument('file', nargs='?',
|
||||
help='the file to show')
|
||||
opts = parser.parse_args(vargs)
|
||||
colorama.init()
|
||||
return do_work(opts.file, opts.rows, opts.columns, opts.parallel)
|
||||
|
||||
color_list = list(ColorCode)
|
||||
block_list = [(Block(SHADE_FULL, c, ColorCode.White),
|
||||
c.value) for c in color_list]
|
||||
for i in range(len(color_list)):
|
||||
for j in range(len(color_list)):
|
||||
if i == j:
|
||||
continue
|
||||
#medium shades
|
||||
if i < j:
|
||||
block_list.append((Block(SHADE_MEDIUM,
|
||||
color_list[i], color_list[j]),
|
||||
color_list[i].value.blend(color_list[j].value, .5)))
|
||||
#dark shade
|
||||
block_list.append((Block(SHADE_DARK,
|
||||
color_list[i], color_list[j]),
|
||||
color_list[i].value.blend(color_list[j].value, .75)))
|
||||
|
||||
if __name__ == '__main__':
|
||||
exit(main(sys.argv[1:]))
|
||||
|
||||
Reference in New Issue
Block a user