diff --git a/3dvistasteal/__init__.py b/3dvistasteal/__init__.py new file mode 100644 index 0000000..d8d2fed --- /dev/null +++ b/3dvistasteal/__init__.py @@ -0,0 +1,133 @@ +import PIL.Image +import urllib.request +import io +import math +import subprocess +import tempfile +import pathlib +import os +import re + +from stitching import stitch, tiles_to_equirectangular_blender + +def steal3dvista_normalize(url): + ''' + Takes the URL of any image in a steal3dvista panorama and returns a string + with substitutable variables for image IDs. + + :param url: URL of an image contained in a steal3dvista 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" + + return "/".join(parts) + + except: + return False + + +def steal3dvista_getmaxzoom(schema): + ''' + Takes a normalized string from steal3dvista_normalize() and returns the maximum + zoom level available. + + :param schema: normalized URL format output by steal3dvista_normalize() + :return: int value of largest available zoom level + ''' + + return 0 + + +def steal3dvista_export(schema): + ''' + Takes a normalized string from steal3dvista_normalize() and returns a list of + lists of lists containing all images fit for passing into stitch(). + + :param schema: normalized URL format output by steal3dvista_normalize() + :return: list of lists of lists of PIL.Image() objects for multistitch() + ''' + + maxzoom = steal3dvista_getmaxzoom(schema) + output = [] + + if True: + y = 0 + while True: + r_array = [] + x = 0 + + while True: + try: + res = urllib.request.urlopen(schema % (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 + + output.append(r_array) + y += 1 + + return output + + +def steal3dvista_make_tiles(url): + ''' + Determines the type of processing needed to build the six tiles, then + creates and returns them. + + :param urL: URL of any image in a steal3dvista panorama + :return: list of stitched PIL.Image objects (back, right, front, left, top, + bottom) + ''' + + parts = url.split("/") + + try: + schema = steal3dvista_normalize(url) + images = steal3dvista_export(schema) + full = stitch(images) + + tiles = [] + + for i in range(6): + tiles.append(full.crop((full.width / 12 * i, 0, (full.width / 12 * (i + 1)) - 1, full.height))) + + return [tiles[4], tiles[0], tiles[5], tiles[1], tiles[2], tiles[3]] + + except: + raise ValueError("%s does not seem to be a valid steal3dvista URL." % url) + + +def steal3dvista_to_equirectangular(url, rotation=[0,0,0], resolution=[3840,1920]): + ''' + Takes the URL of any image in a steal3dvista panorama and returns a finished + stitched image. + + :param url: Image URL + :return: PIL.Image object containing the final image + ''' + + stitched = steal3dvista_make_tiles(url) + function = tiles_to_equirectangular_blender + + rx, ry, rz = rotation + width, height = resolution + return function(*stitched, rx=rx, ry=ry, rz=rz, width=width, height=height) + + +process_url = steal3dvista_to_equirectangular diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..e69de29 diff --git a/README.md b/README.md new file mode 100644 index 0000000..92aba0d --- /dev/null +++ b/README.md @@ -0,0 +1,18 @@ +# Panorama Image Exporter for 360 Content (PIX360) + +PIX360, previously called panosteal, is a Python3 script allowing you to download 360° panorama images and videos from different sources on the Internet. + +It was created as part of a project for one of our clients, with additional sources simply slapped on as required, so currently the code is neither clean nor documented. We are hoping to rewrite the application from scratch soon, but we don't know just how soon. In that rewrite, we would like to implement a couple of convenience features as well. + +The script can be used by passing the URL of a *part* of the image you want to download into _handler.py_ – the script is not currently able to use the URL of a website to find embedded images (with two exceptions: images hosted by Google, and 360 videos on YouTube), so you will need to find the URL by using your browser's developer tools, for example. + +It also comes with a simple HTTP server. To use it, make sure you have _gunicorn_ installed, then run *both* _hallmonitor.sh_ and _run.sh_. + +## Install + +To run PIX360, you need: + +* Python >= 3.8 +* Blender (for stitching images) + +We recommend creating and enabling a venv, then installing the Python requirements using `pip install -r requirements.txt`. \ No newline at end of file diff --git a/handler.py b/handler.py index 1068d67..8e304cc 100755 --- a/handler.py +++ b/handler.py @@ -7,7 +7,7 @@ import subprocess import traceback regs = { - r"\d/\d/\d_\d\.jpg": "krpanosteal", + r"\d+/\d+/\d+_\d+\.jpg": "krpanosteal", r"\_[frblud].jpg": "krpanosteal", r"my.matterport.com/show/": "matterportsteal", r"cdn-1.matterport.com": "matterportsimple", @@ -43,11 +43,12 @@ if __name__ == "__main__": parser.add_argument("--rotation", nargs=3, type=int, help="rotation on x/y/z axes", metavar=("x","y","z")) parser.add_argument("--resolution", type=int, nargs=2, metavar=("w","h")) parser.add_argument("--output", default=".") + parser.add_argument("--module", help="name of module to use") args = parser.parse_args() try: - handler = parse_url(args.url) + handler = importlib.import_module(args.module).process_url if args.module else parse_url(args.url) image = handler(args.url, args.rotation or [0,0,0], args.resolution or [3840, 1920]) if not hasattr(image, "im"): with open(args.output + "/" + args.title + ".mkv", "wb") as video: diff --git a/krpanosteal/__init__.py b/krpanosteal/__init__.py index 8d300b8..bf5f74b 100644 --- a/krpanosteal/__init__.py +++ b/krpanosteal/__init__.py @@ -27,7 +27,7 @@ def krpano_normalize(url): assert "_" in parts[-1] parts[-1] = "%i_%i.jpg" parts[-2] = "%i" - parts[-3] = "%i" + parts[-3] = parts[-3].rstrip("0123456789") + "%i" return "/".join(parts)