last(?) commit from hilda

This commit is contained in:
2022-11-21 13:50:07 -06:00
commit 2064965072
3 changed files with 250 additions and 0 deletions

114
src/colors.py Normal file
View 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
View 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:]))