23 files changed, 79 insertions(+), 373 deletions(-)
D .github/workflows/ci.yml
M .gitignore
D Dockerfile
D Dockerfile.sh
D Dokumentacja.docx
M LICENSE
M README.md
D app.py
D configuration.py
D data/.DS_Store
D data/.placeholder
D managers/ImageProcess.py
A pgridweb/__init__.py
R managers/Equirec2Perspec.py => pgridweb/equirec2perspec.py
R generate_map.py => pgridweb/map.py
R wwwroot/compass.png => pgridweb/static/compass.png
R wwwroot/styles.css => pgridweb/static/styles.css
R templates/base.html => pgridweb/templates/base.html
R templates/index.html => pgridweb/templates/index.html
D requirements.txt
A setup.py
D test_equirec2perspec.py
D waitress_server.py
D .github/workflows/ci.yml => .github/workflows/ci.yml +0 -30
@@ 1,30 0,0 @@
-name: Deploy project
-
-on:
- push:
- branches: [ "main" ]
-
-jobs:
- deploy:
- runs-on: ubuntu-latest
- steps:
- -
- name: Set up QEMU
- uses: docker/setup-qemu-action@v2
- -
- name: Set up Docker Buildx
- uses: docker/setup-buildx-action@v2
- -
- name: Login to Docker Hub
- uses: docker/login-action@v2
- with:
- username: ${{ secrets.DOCKERHUB_USERNAME }}
- password: ${{ secrets.DOCKERHUB_TOKEN }}
- -
- name: Build and push
- uses: docker/build-push-action@v4
- with:
- platforms: linux/amd64,linux/arm64/v8
- image: clovers1254/360-webview
- push: true
- tags: clovers1254/360-webview:latest
M .gitignore => .gitignore +2 -162
@@ 1,163 1,3 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-# Usually these files are written by a python script from a template
-# before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-cover/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-.pybuilder/
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-# For a library or package, you might want to ignore these files since the code is
-# intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-# However, in case of collaboration, if having platform-specific dependencies or dependencies
-# having no cross-platform support, pipenv may install dependencies that don't work, or not
-# install all needed dependencies.
-#Pipfile.lock
-
-# poetry
-# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
-# This is especially recommended for binary packages to ensure reproducibility, and is more
-# commonly ignored for libraries.
-# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
-#poetry.lock
-
-# pdm
-# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
-#pdm.lock
-# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
-# in version control.
-# https://pdm.fming.dev/#use-with-ide
-.pdm.toml
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
-
-# Cython debug symbols
-cython_debug/
-
-# PyCharm
-# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
-# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
-# and can be added to the global gitignore or merged into this file. For a more nuclear
-# option (not recommended) you can uncomment the following to ignore the entire idea folder.
-#.idea/
-
-/data/img
-/data/points.json>
\ No newline at end of file
+data/
+**/__pycache__/
D Dockerfile => Dockerfile +0 -13
@@ 1,13 0,0 @@
-# syntax=docker/dockerfile:1
-
-FROM python:3.8-slim-buster
-RUN apt-get update && apt-get install ffmpeg libsm6 libxext6 -y
-WORKDIR /python-docker
-
-COPY requirements.txt requirements.txt
-RUN pip3 install -r requirements.txt
-RUN pip3 install waitress
-
-COPY . .
-
-CMD [ "python3", "waitress_server.py" ]>
\ No newline at end of file
D Dockerfile.sh => Dockerfile.sh +0 -13
@@ 1,13 0,0 @@
-#!/bin/sh
-
-docker stop 360-webview && docker rm 360-webview
-docker build . -t 360-webview
-docker run -d -it --name 360-webview --restart always -p 9001:9001 360-webview
-
-# mkdir 360-webview
-# cd 360-webview
-# wget https://files.krystianch.com/pgrid-feit-012-v1.tar.gz
-# tar -xvf pgrid-feit-012-v1.tar.gz
-# rm pgrid-feit-012-v1.tar.gz
-# docker stop 360-webview && docker rm 360-webview
-# docker run -d -it --name 360-webview --restart always -p 9001:9001 -v $(pwd)/img:/python-docker/data/img clovers1254/360-webview>
\ No newline at end of file
D Dokumentacja.docx => Dokumentacja.docx +0 -0
M LICENSE => LICENSE +1 -0
@@ 1,6 1,7 @@
MIT License
Copyright (c) 2023 Minh Nguyen Cong
+Copyright (c) 2023 Krystian Chachuła
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
M README.md => README.md +6 -53
@@ 1,56 1,9 @@
-# Streetview
+# Pgrid Web NoJS
-## Introduction
+Spherical photo web viewer, without JavaScript.
-### Purpose of the project
-The aim of the project is to create free and open-source software that allows viewing a set of spherical photos in a web browser, including devices with low computing power, mobile devices, devices with limited Internet access and browsers without JavaScript support.
+This is a fork of a MIT-licensed course project [360-webview-nojs][1] by three
+students from Warsaw University of Technology: Spyro313 on GitHub,
+Minh Nguyen Cong <mcong@box.com> and AndriiDemk on GitHub.
-### Initial vision of the project
-As the project is not to use javascript, the following buttons will be used for "rotating": up, down, right, left. Clicking the button will render the image slightly rotated in the desired direction.
-
-### The nature of the problem
-The problem of this project is to convert the input photo (equidistant projection) to the normal field of view (Both photos are in jpg format).
-
-## Installation guide
-Instructions for the administrator how to install the project:
-1. Clone the repository from https://github.com/congminh1254/360-webview-nojs to your local machine.
-2. Download and unzip the 360 degree photo dataset on your host computer.
-3. Open a terminal and navigate to the root directory of the cloned repository.
-4. To clear the previous Docker container, run the following command:
-```
-docker stop 360-webview && docker rm 360-webview
-```
-5. To compile the current version of the project into a Docker image, run the following command:
-```
-docker build . -t 360-webview
-```
-6. To deploy a Docker container with the dataset directory mounted in /python-docker/data, run the following command:
-```
-docker run -d -it --name 360-webview --restart always --mount type=bind,source=$HOME/data_folder,target=/python-docker/data -p 9001:9001 360-webview
-```
-This command will start a Docker container named `360-webview` which will automatically restart after rebooting the system (`--restart always`). It will also mount the dataset directory located in `$HOME/data_folder` in the `/python-docker/data` directory of the container where the application will look for 360 degree photos.
-
-Finally, it will map port 9001 of the container to port 9001 of the host (`-p 9001:9001`) where the application will be accessible from the web browser.
-
-7. Optional: To configure Nginx to proxy traffic from the Internet to port 9001, follow these steps:
- - Install Nginx if it is not already installed on your system.
- - Open the Nginx configuration file, usually located in `/etc/nginx/nginx.conf`, with a text editor.
- - Add the following code block in the `http` block:
- ```
- server {
- listen 80;
- server_name example.com;
- location / {
- proxy_pass http://localhost:9001;
- proxy_set_header Host $host;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
- }
- }
- ```
- - Replace `example.com` with your domain name or IP address.
- - Save and close the file, then restart Nginx with the following command:
- ```
- sudo systemctl restart nginx
- ```
- - Now you should be able to access the app from your web browser by going to "http://example.com" or your IP address.
+[1]: https://github.com/congminh1254/360-webview-nojs
D app.py => app.py +0 -45
@@ 1,45 0,0 @@
-from flask import Flask, render_template, request
-from configuration import get_configuration
-from generate_map import generate_map
-from managers.ImageProcess import ImageProcess
-import json
-import os.path as path
-import os
-import random
-
-app = Flask(__name__, static_folder='wwwroot',
- static_url_path='/static', template_folder='templates')
-app.config['FLASK_APP'] = get_configuration('flask_app')
-app.config['FLASK_ENV'] = get_configuration('flask_env')
-
-img_folder = 'data/img'
-if (path.exists(img_folder) == False):
- raise Exception('No image folder found')
-
-map_file = 'data/points.json'
-if (path.exists(map_file) == False):
- generate_map('data')
-
-@app.route('/')
-def index():
- img = request.args.get('img')
- if img == None:
- img = random.choice(os.listdir(img_folder))
- img.replace('/', '')
- print(img)
- x = int(request.args.get('x', '180'))
- y = int(request.args.get('y', '0'))
- zoom = int(request.args.get('zoom', '120'))
- processor = ImageProcess(f'{img_folder}/'+img)
- image = processor.get_image(zoom, x, y, 720, 1080)
- image = "data:image/jpeg;base64, "+image
- maps = json.load(open(map_file))
- for i in range(len(maps)):
- for j in range(len(maps[i])):
- if maps[i][j] != None:
- maps[i][j][0] = maps[i][j][0].replace('img/', '')
- return render_template('index.html', image=image, zoom=zoom, x=x, y=y, step=10, maps=maps, img=img)
-
-
-if __name__ == '__main__':
- app.run(host=get_configuration('host'), port=get_configuration('port'))
D configuration.py => configuration.py +0 -11
@@ 1,11 0,0 @@
-import os
-
-default_config = {
- 'flask_app': '360 Webview',
- 'flask_env': 'development',
- 'host': 'localhost',
- 'port': 5432,
-}
-
-def get_configuration(name):
- return os.environ.get(name, default_config[name])
D data/.DS_Store => data/.DS_Store +0 -0
D data/.placeholder => data/.placeholder +0 -0
D managers/ImageProcess.py => managers/ImageProcess.py +0 -15
@@ 1,15 0,0 @@
-import cv2
-import managers.Equirec2Perspec as E2P
-import base64
-
-class ImageProcess():
- def __init__(self, file_path) -> None:
- self.equirec = E2P.Equirectangular(file_path)
-
- def get_image(self, FOV, theta, phi, height, width):
- image = self.equirec.GetPerspective(FOV, theta, phi, height, width)
- retval, buffer = cv2.imencode('.jpg', image)
- jpg_as_text = base64.b64encode(buffer).decode("utf-8")
- return jpg_as_text
-
-
A pgridweb/__init__.py => pgridweb/__init__.py +51 -0
@@ 0,0 1,51 @@
+import base64
+import json
+import os
+import pathlib
+import random
+
+import cv2
+from flask import Flask, current_app, render_template, request
+
+from .equirec2perspec import Equirectangular
+from .map import generate_map
+
+def index():
+ img_dir = current_app.config["IMG_DIR"]
+
+ img = request.args.get("img")
+ img_path = img_dir / img if img else random.choice(list(img_dir.iterdir()))
+
+ x = int(request.args.get("x", "180"))
+ y = int(request.args.get("y", "0"))
+ zoom = int(request.args.get("zoom", "120"))
+
+ image = Equirectangular(str(img_path)).GetPerspective(zoom, x, y, 720, 1080)
+ ret, buffer = cv2.imencode(".jpg", image)
+ b64 = base64.b64encode(buffer).decode()
+ assert ret, "Encoding failed"
+ image = "data:image/jpeg;base64, " + b64
+
+ # FIXME
+ maps = json.load(open(current_app.config["MAP_FILE"]))
+ for i in range(len(maps)):
+ for j in range(len(maps[i])):
+ if maps[i][j] is not None:
+ maps[i][j][0] = maps[i][j][0].replace("img/", "")
+
+ return render_template("index.html", image=image, zoom=zoom, x=x, y=y,
+ step=10, maps=maps, img=img_path.name)
+
+def create_app():
+ app = Flask(__name__)
+
+ app.config["IMG_DIR"] = pathlib.Path("data/img")
+ if not os.path.exists(app.config["IMG_DIR"]):
+ raise Exception("No image folder found")
+ app.config["MAP_FILE"] = "data/points.json"
+ if not os.path.exists(app.config["MAP_FILE"]):
+ generate_map("data")
+
+ app.add_url_rule("/", view_func=index)
+
+ return app
R managers/Equirec2Perspec.py => pgridweb/equirec2perspec.py +2 -0
@@ 8,9 8,11 @@
import os
import sys
+
import cv2
import numpy as np
+
def xyz2lonlat(xyz):
atan2 = np.arctan2
asin = np.arcsin
R generate_map.py => pgridweb/map.py +2 -2
@@ 43,10 43,10 @@ def generate_map(folder):
while len(points[x]) <= y:
points[x].append(None)
points[x][y] = coordinate
-
+
min_x = 100000
min_y = 100000
max_x = 0
max_y = 0
- json.dump(points, open(f'{folder}/points.json', 'w'))>
\ No newline at end of file
+ json.dump(points, open(f'{folder}/points.json', 'w'))
R wwwroot/compass.png => pgridweb/static/compass.png +0 -0
R wwwroot/styles.css => pgridweb/static/styles.css +0 -0
R templates/base.html => pgridweb/templates/base.html +0 -0
R templates/index.html => pgridweb/templates/index.html +0 -0
D requirements.txt => requirements.txt +0 -3
@@ 1,3 0,0 @@
-flask
-opencv-python
-pytest>
\ No newline at end of file
A setup.py => setup.py +15 -0
@@ 0,0 1,15 @@
+#!/usr/bin/env python3
+
+from distutils.core import setup
+
+setup(
+ packages=["pgridweb"],
+ package_data={"pgridweb": [
+ "static/*",
+ "templates/*",
+ ]},
+ requires=[
+ "flask",
+ "numpy",
+ ],
+)
D test_equirec2perspec.py => test_equirec2perspec.py +0 -22
@@ 1,22 0,0 @@
-import pytest
-import cv2
-import numpy as np
-from managers.Equirec2Perspec import xyz2lonlat, lonlat2XY, Equirectangular
-
-def test_xyz2lonlat():
- xyz = np.array([[1, 0, 0], [0, 1, 0], [0, 0, 1]])
- expected_output = np.array([[np.pi/2, 0], [0, np.pi/2], [0, 0]])
-
- result = xyz2lonlat(xyz)
- np.testing.assert_almost_equal(result, expected_output)
-
-
-def test_lonlat2XY():
- lonlat = np.array([[np.pi/2, 0], [0, np.pi/2], [0, 0]])
-
- shape = (100, 200)
- expected_output = np.array([[150, 49], [99, 99], [99, 50]])
-
- result = lonlat2XY(lonlat, shape)
- np.testing.assert_almost_equal(result, expected_output, 0)
- >
\ No newline at end of file
D waitress_server.py => waitress_server.py +0 -4
@@ 1,4 0,0 @@
-from waitress import serve
-import app
-
-serve(app.app, host='0.0.0.0', port=9001)>
\ No newline at end of file