commit 075f3d9a24f6ae9312abc241d545660bed162644 Author: Klaus-Uwe Mitterer Date: Tue Sep 18 19:30:00 2018 +0200 Initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..fffc64d --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__ +*.swp diff --git a/worker.py b/worker.py new file mode 100644 index 0000000..7877cae --- /dev/null +++ b/worker.py @@ -0,0 +1,275 @@ +import PIL.Image +import urllib.request +import io +import math +import subprocess + +def tiles_to_equirectangular_blender(back, right, front, left, top, bottom): + left.save("left.png") + front.save("front.png") + right.save("right.png") + back.save("back.png") + bottom.save("bottom.png") + top.save("top.png") + + height = left.size[0] * 2 + width = left.size[0] * 4 + + try: + process = subprocess.Popen( + ['cube2sphere', 'front.png', 'back.png', 'right.png', 'left.png', + 'top.png', 'bottom.png', '-f', 'png', "-r", + str(width), str(height)] + ) + + process.wait() + + except: + print("No imports here.") + +def tiles_to_equirectangular(back, right, front, left, top, bottom): + dim = left.size[0] + + raw = [] + + t_width = dim * 4 + t_height = dim * 2 + + for y in range(t_height): + v = 1.0 - (float(y) / t_height) + phi = v * math.pi + + for x in range(t_width): + u = float(x) / t_width + theta = u * math.pi * 2 + + x = math.cos(theta) * math.sin(phi) + y = math.sin(theta) * math.sin(phi) + z = math.cos(phi) + + a = max(abs(x), abs(y), abs(z)) + + xx = x / a + yy = y / a + zz = z / a + + if yy == -1: + currx = int(((-1 * math.tan(math.atan(x / y)) + 1.0) / 2.0) * dim) + ystore = int(((-1 * math.tan(math.atan(z / y)) + 1.0) / 2.0) * (dim - 1)) + part = left + + elif xx == 1: + currx = int(((math.tan(math.atan(y / x)) + 1.0) / 2.0) * dim) + ystore = int(((math.tan(math.atan(z / x)) + 1.0) / 2.0) * dim) + part = front + + elif yy == 1: + currx = int(((-1 * math.tan(math.atan(x / y)) + 1.0) / 2.0) * dim) + ystore = int(((math.tan(math.atan(z / y)) + 1.0) / 2.0) * (dim - 1)) + part = right + + elif xx == -1: + currx = int(((math.tan(math.atan(y / x)) + 1.0) / 2.0) * dim) + ystore = int(((-1 * math.tan(math.atan(z / x)) + 1.0) / 2.0) * (dim - 1)) + part = back + + elif zz == 1: + currx = int(((math.tan(math.atan(y / z)) + 1.0) / 2.0) * dim) + ystore = int(((-1 * math.tan(math.atan(x / z)) + 1.0) / 2.0) * (dim - 1)) + part = bottom + + else: + currx = int(((-1 * math.tan(math.atan(y / z)) + 1.0) / 2.0) * dim) + ystore = int(((-1 * math.tan(math.atan(x / z)) + 1.0) / 2.0) * (dim - 1)) + part = top + + curry = (dim - 1) if ystore > (dim - 1) else ystore + + if curry > (dim - 1): + curry = dim - 1 + + if currx > (dim - 1): + currx = dim - 1 + + raw.append(part.getpixel((currx, curry))) + + output = PIL.Image.new("RGB", (t_width, t_height)) + output.putdata(raw) + + return output + +def krpano_normalize(url): + ''' + Takes the URL of any image in a krpano panorama and returns a string + with substitutable variables for image IDs. + + :param url: URL of an image contained in a krpano panorama + :return: string with substitutable variables or False if URL invalid + ''' + + try: + with urllib.request.urlopen(url) as res: + assert res.getcode() == 200 + + parts = url.split("/") + + assert "_" in parts[-1] + parts[-1] = "%i_%i.jpg" + parts[-2] = "%i" + parts[-3] = "%i" + + return "/".join(parts) + + except: + return False + +def krpano_getmaxzoom(schema): + ''' + Takes a normalized string from krpano_normalize() and returns the maximum + zoom level available. + + :param schema: normalized URL format output by krpano_normalize() + :return: int value of largest available zoom level + ''' + + l = 0 + + while True: + try: + url = schema % (0, l+1, 0, 0) + with urllib.request.urlopen(url) as res: + assert res.getcode() == 200 + l += 1 + except: + return l + +def krpano_export(schema): + ''' + Takes a normalized string from krpano_normalize() and returns a list of + lists of lists containing all images fit for passing into stitch(). + + :param schema: normalized URL format output by krpano_normalize() + :return: list of lists of lists of PIL.Image() objects for krpano_stitch() + ''' + + maxzoom = krpano_getmaxzoom(schema) + output = [] + + for tile in range(6): + t_array = [] + y = 0 + + while True: + r_array = [] + x = 0 + + while True: + try: + res = urllib.request.urlopen(schema % (tile, maxzoom, y, x)) + assert res.getcode() == 200 + fo = io.BytesIO(res.read()) + img = PIL.Image.open(fo) + r_array.append(img) + x += 1 + except Exception as e: + break + + if not r_array: + break + + t_array.append(r_array) + y += 1 + + output.append(t_array) + + return output + +def krpano_process(url): + ''' + Takes the URL of any image in a krpano panorama and returns a list of + lists of lists containing all images fit for passing into stitch(). + + :param url: URL of an image contained in a krpano panorama + :return: list of lists of lists of PIL.Image() objects for krpano_stitch() + ''' + + schema = krpano_normalize(url) + + if not schema: + raise ValueError("%s does not seem to be a valid krpano URL." % url) + + return krpano_export(schema) + +def krpano_stitch(tiles): + ''' + Takes a list of lists of lists containing PIL Image objects and stitches + them into one. Each box tile (first-order lists), line (second-order) and + column (third-order) must be equal in height and width. + + :param faces: list of lists of lists containing PIL.Image objects + :return: list of stitched PIL.Image objects + ''' + + output = [] + + for tile in tiles: + output.append(stitch(tile)) + + return output + +def krpano_to_equirectangular(url): + images = krpano_process(url) + stitched = krpano_stitch(images) + return tiles_to_equirectangular_blender(*stitched) + +def stitch(images): + ''' + Takes a list of lists containing PIL Image objects and stitches them into + one. Each line (first-order lists) and column (second-order) must be equal + in height and width. + + :param images: list of lists containing PIL.Image objects + :return: stitched PIL.Image object + ''' + + t_height = 0 # Total height of resulting image + t_width = 0 # Total width of resulting image + + '''Calculate height of final image by adding up heights of the first images + of each row.''' + + for row in images: + w, h = row[0].size + t_height += h + + '''Calculate width of final image by adding up widths of the images in the + first row.''' + + for image in images[0]: + w, h = image.size + t_width += w + + '''Generate output Image object using calculated height and width.''' + + output = PIL.Image.new("RGB", (t_width, t_height)) + + curry = 0 # Current y position (top = 0) + + for row in images: + currx = 0 # Current x position (left = 0) + y_offset = 0 # How far down we have to go for the next line + + for image in row: + + '''Paste line of images into the output image.''' + + output.paste(im=image, box=(currx, curry)) + w, h = image.size + currx += w + + if not y_offset: + y_offset = h + + curry += y_offset + + return output