mirror of
https://github.com/placeAtlas/atlas.git
synced 2024-11-18 16:03:23 +01:00
210 lines
5 KiB
Python
210 lines
5 KiB
Python
#!/usr/bin/python
|
|
|
|
from io import TextIOWrapper
|
|
import json
|
|
import traceback
|
|
import numpy
|
|
from PIL import Image, ImageDraw
|
|
import gc
|
|
import tqdm
|
|
|
|
"""
|
|
# 166 to 164 with reference of 165
|
|
shrink
|
|
166
|
|
164
|
|
20
|
|
web\_img\canvas\place30\159.png
|
|
web\_img\canvas\place30\163_159.png
|
|
|
|
# 166 to 165 with reference of 166
|
|
shrink
|
|
166
|
|
165
|
|
20
|
|
web\_img\canvas\place30\159.png
|
|
web\_img\canvas\place30\164_159.png
|
|
|
|
# 164 to 165 with reference of 165
|
|
shrink
|
|
164
|
|
165
|
|
20
|
|
web\_img\canvas\place30\159.png
|
|
web\_img\canvas\place30\163_159.png
|
|
|
|
# 166 to 167 with reference of 167
|
|
expand
|
|
166
|
|
167
|
|
20
|
|
web\_img\canvas\place30\159.png
|
|
web\_img\canvas\place30\165_159.png
|
|
"""
|
|
|
|
class ScaleConfig:
|
|
type = 'expand'
|
|
source = ''
|
|
destination = ''
|
|
threshold = 20
|
|
image1 = ''
|
|
image2 = ''
|
|
|
|
def swap_source_dest(source, destination, image2):
|
|
ScaleConfig.source = source
|
|
ScaleConfig.destination = destination
|
|
ScaleConfig.image2 = image2
|
|
|
|
def remove_white(entry: dict):
|
|
|
|
canvas_ref = Image.new('RGBA', (2000,2000))
|
|
|
|
with Image.open(ScaleConfig.image1).convert('RGBA') as image1:
|
|
if ScaleConfig.image2:
|
|
with Image.open(ScaleConfig.image2).convert('RGBA') as image2:
|
|
canvas_ref.paste(image1, (0, 0), image1)
|
|
canvas_ref.paste(image2, (0, 0), image2)
|
|
else:
|
|
canvas_ref.paste(image1, (0, 0), image1)
|
|
|
|
# uncomment when you need to see the source canvas
|
|
# canvas_ref.show()
|
|
|
|
# print(entry['path'])
|
|
|
|
for (period, polygonList) in entry['path'].items():
|
|
|
|
if not f"-{ScaleConfig.source}" in period: continue
|
|
|
|
# Get bounding rectangle and have a list of tuples for polygon
|
|
|
|
polygon = []
|
|
x_box = 2000
|
|
y_box = 2000
|
|
x_box2 = 0
|
|
y_box2 = 0
|
|
|
|
for point in polygonList:
|
|
x_box = min(x_box, max(point[0] - 1.5, 0))
|
|
y_box = min(y_box, max(point[1] - 1.5, 0))
|
|
x_box2 = max(x_box2, min(point[0] + 1.5, 2000))
|
|
y_box2 = max(y_box2, min(point[1] + 1.5, 2000))
|
|
polygon.append(tuple(point))
|
|
|
|
x_box = int(x_box)
|
|
y_box = int(y_box)
|
|
x_box2 = int(x_box2)
|
|
y_box2 = int(y_box2)
|
|
|
|
# Crop the image based on polygon
|
|
# https://stackoverflow.com/questions/22588074/
|
|
|
|
imArray = numpy.asarray(canvas_ref)
|
|
|
|
with Image.new('L', (imArray.shape[1], imArray.shape[0]), 0) as maskIm:
|
|
ImageDraw.Draw(maskIm).polygon(polygon, outline=1, fill=1)
|
|
mask = numpy.array(maskIm)
|
|
newImArray = numpy.empty(imArray.shape,dtype='uint8')
|
|
|
|
|
|
newImArray[:,:,:3] = imArray[:,:,:3]
|
|
newImArray[:,:,3] = mask*255
|
|
|
|
imArray = newImArray[y_box:y_box2,x_box:x_box2,:]
|
|
|
|
# points = numpy.array([polygon])
|
|
# print(points)
|
|
# print(cv2.boundingRect(points[0]))
|
|
# print(1)
|
|
# print(imArray)
|
|
|
|
colored_pixel_count: int = 0
|
|
all_pixel_count: int = 0
|
|
|
|
# Read the area based on bounding box
|
|
|
|
for x in imArray:
|
|
for pixel in x:
|
|
if pixel[3] == 0: continue
|
|
all_pixel_count += 1
|
|
if (pixel[0] == 255 and pixel[1] == 255 and pixel[2] == 255): continue
|
|
colored_pixel_count += 1
|
|
|
|
if all_pixel_count == 0: break
|
|
|
|
colorness = (100 * colored_pixel_count)/all_pixel_count
|
|
|
|
if (ScaleConfig.type == "shrink" and colorness < ScaleConfig.threshold) or (ScaleConfig.type == "expand" and colorness > ScaleConfig.threshold):
|
|
print(f"[{entry['id']} {period}] {colored_pixel_count}/{all_pixel_count} ({colorness}%)")
|
|
new_period = period.replace(f'-{ScaleConfig.source}', f'-{ScaleConfig.destination}')
|
|
entry['path'][new_period] = entry['path'][period]
|
|
del entry['path'][period]
|
|
entry['center'][new_period] = entry['center'][period]
|
|
del entry['center'][period]
|
|
break
|
|
# newIm = Image.fromarray(newImArray, "RGBA")
|
|
# newIm.show()
|
|
|
|
break
|
|
|
|
return entry
|
|
|
|
def per_line_entries(entries: list, file: TextIOWrapper):
|
|
"""
|
|
Returns a string of all the entries, with every entry in one line.
|
|
"""
|
|
file.write("[\n")
|
|
line_temp = ""
|
|
for entry in tqdm.tqdm(entries):
|
|
if line_temp:
|
|
file.write(line_temp + ",\n")
|
|
line_temp = json.dumps(entry, ensure_ascii=False)
|
|
file.write(line_temp + "\n]")
|
|
|
|
def format_all(entry: dict, silent=False):
|
|
def print_(*args, **kwargs):
|
|
if not silent:
|
|
print(*args, **kwargs)
|
|
|
|
entry = remove_white(entry)
|
|
print_("Completed!")
|
|
return entry
|
|
|
|
def scale_back_entries(entries):
|
|
for i in tqdm.trange(len(entries)):
|
|
try:
|
|
entry_formatted = format_all(entries[i], True)
|
|
entries[i] = entry_formatted
|
|
except Exception:
|
|
print(f"Exception occured when formatting ID {entries[i]['id']}")
|
|
print(traceback.format_exc())
|
|
if not (i % 50):
|
|
print(f"{i} checked.")
|
|
gc.collect()
|
|
|
|
def go(path):
|
|
|
|
print(f"Scaling whiteout for {path}...")
|
|
|
|
with open(path, "r+", encoding='UTF-8') as f1:
|
|
entries = json.loads(f1.read())
|
|
|
|
scale_back_entries(entries)
|
|
|
|
print(f"{len(entries)} checked. Writing...")
|
|
|
|
with open(path, "w", encoding='utf-8', newline='\n') as f2:
|
|
per_line_entries(entries, f2)
|
|
|
|
print("Writing completed. All done.")
|
|
|
|
if __name__ == '__main__':
|
|
|
|
ScaleConfig.type = input("Type (shrink/expand): ")
|
|
ScaleConfig.source = input("Source: ")
|
|
ScaleConfig.destination = input("Destination: ")
|
|
ScaleConfig.threshold = int(input("Threshold (%): "))
|
|
ScaleConfig.image1 = input("Reference canvas layer 1: ")
|
|
ScaleConfig.image2 = input("Reference canvas layer 2: ")
|
|
|
|
go("web/atlas.json")
|