整合去雾网页工具
This commit is contained in:
248
web_dehaze/server.py
Normal file
248
web_dehaze/server.py
Normal file
@@ -0,0 +1,248 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import argparse
|
||||
import json
|
||||
import mimetypes
|
||||
import threading
|
||||
import traceback
|
||||
from http import HTTPStatus
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from itertools import count
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from urllib.parse import parse_qs, unquote, urlparse
|
||||
|
||||
import pipeline
|
||||
|
||||
|
||||
STATIC_DIR = Path(__file__).resolve().parent / "static"
|
||||
JOBS: dict[str, dict[str, Any]] = {}
|
||||
JOB_IDS = count(1)
|
||||
JOB_LOCK = threading.Lock()
|
||||
|
||||
|
||||
def _json_bytes(payload: Any) -> bytes:
|
||||
return json.dumps(payload, ensure_ascii=False, indent=2).encode("utf-8")
|
||||
|
||||
|
||||
def _new_job(kind: str, target, *args, **kwargs) -> dict[str, Any]:
|
||||
job_id = str(next(JOB_IDS))
|
||||
job = {
|
||||
"id": job_id,
|
||||
"kind": kind,
|
||||
"status": "running",
|
||||
"logs": [],
|
||||
"result": None,
|
||||
"error": None,
|
||||
}
|
||||
with JOB_LOCK:
|
||||
JOBS[job_id] = job
|
||||
|
||||
def log(message: str) -> None:
|
||||
with JOB_LOCK:
|
||||
job["logs"].append(message)
|
||||
job["logs"] = job["logs"][-400:]
|
||||
|
||||
def wrapped() -> None:
|
||||
try:
|
||||
job["result"] = target(log, *args, **kwargs)
|
||||
job["status"] = "done"
|
||||
except Exception as exc:
|
||||
job["status"] = "error"
|
||||
job["error"] = str(exc)
|
||||
log(traceback.format_exc())
|
||||
|
||||
threading.Thread(target=wrapped, daemon=True).start()
|
||||
return job
|
||||
|
||||
|
||||
def _run_dehaze_job(log, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
filename = payload["image"]
|
||||
methods = payload.get("methods") or []
|
||||
options = payload.get("options") or {}
|
||||
postprocessors = payload.get("postprocessors") or []
|
||||
post_sources = payload.get("post_sources") or []
|
||||
reference = payload.get("reference") or filename
|
||||
|
||||
completed = []
|
||||
failed = []
|
||||
for method in methods:
|
||||
try:
|
||||
result = pipeline.run_dehaze_method(filename, method, options.get(method, options), log)
|
||||
completed.append({"method": method, "path": pipeline.relpath(result)})
|
||||
except Exception as exc:
|
||||
failed.append({"method": method, "error": str(exc)})
|
||||
log(f"[{method}] 失败: {exc}")
|
||||
|
||||
if postprocessors:
|
||||
sources = post_sources or [item["method"] for item in completed]
|
||||
for source in sources:
|
||||
try:
|
||||
pipeline.run_postprocessors_for_source(
|
||||
filename,
|
||||
source,
|
||||
postprocessors,
|
||||
params=payload.get("post_params") or {},
|
||||
reference_filename=reference,
|
||||
log=log,
|
||||
)
|
||||
except Exception as exc:
|
||||
failed.append({"postprocess_source": source, "error": str(exc)})
|
||||
log(f"[后处理/{source}] 失败: {exc}")
|
||||
|
||||
return {"completed": completed, "failed": failed, "results": pipeline.get_results(filename)}
|
||||
|
||||
|
||||
def _run_post_job(log, payload: dict[str, Any]) -> dict[str, Any]:
|
||||
filename = payload["image"]
|
||||
source = payload.get("source") or "DCP"
|
||||
processors = payload.get("processors") or []
|
||||
reference = payload.get("reference") or filename
|
||||
outputs = pipeline.run_postprocessors_for_source(
|
||||
filename,
|
||||
source,
|
||||
processors,
|
||||
params=payload.get("params") or {},
|
||||
reference_filename=reference,
|
||||
log=log,
|
||||
)
|
||||
return {
|
||||
"outputs": [pipeline.relpath(path) for path in outputs],
|
||||
"results": pipeline.get_results(filename),
|
||||
}
|
||||
|
||||
|
||||
class DehazeRequestHandler(BaseHTTPRequestHandler):
|
||||
server_version = "DehazeWeb/1.0"
|
||||
|
||||
def log_message(self, fmt: str, *args) -> None:
|
||||
print("[%s] %s" % (self.log_date_time_string(), fmt % args))
|
||||
|
||||
def _send(self, status: int, body: bytes, content_type: str = "application/json; charset=utf-8") -> None:
|
||||
self.send_response(status)
|
||||
self.send_header("Content-Type", content_type)
|
||||
self.send_header("Content-Length", str(len(body)))
|
||||
self.end_headers()
|
||||
self.wfile.write(body)
|
||||
|
||||
def _send_json(self, payload: Any, status: int = HTTPStatus.OK) -> None:
|
||||
self._send(int(status), _json_bytes(payload))
|
||||
|
||||
def _send_error_json(self, status: int, message: str) -> None:
|
||||
self._send_json({"error": message}, status)
|
||||
|
||||
def do_HEAD(self) -> None:
|
||||
parsed = urlparse(self.path)
|
||||
target = STATIC_DIR / "index.html" if parsed.path == "/" else STATIC_DIR / parsed.path.removeprefix("/static/")
|
||||
if parsed.path == "/" or parsed.path.startswith("/static/"):
|
||||
target = target.resolve()
|
||||
static_root = STATIC_DIR.resolve()
|
||||
if static_root not in target.parents and target != static_root:
|
||||
self.send_response(HTTPStatus.FORBIDDEN)
|
||||
self.end_headers()
|
||||
return
|
||||
if target.exists() and target.is_file():
|
||||
content_type = mimetypes.guess_type(target.name)[0] or "application/octet-stream"
|
||||
if target.suffix == ".html":
|
||||
content_type = "text/html; charset=utf-8"
|
||||
self.send_response(HTTPStatus.OK)
|
||||
self.send_header("Content-Type", content_type)
|
||||
self.send_header("Content-Length", str(target.stat().st_size))
|
||||
self.end_headers()
|
||||
return
|
||||
self.send_response(HTTPStatus.NOT_FOUND)
|
||||
self.end_headers()
|
||||
|
||||
def _read_json(self) -> dict[str, Any]:
|
||||
length = int(self.headers.get("Content-Length", "0"))
|
||||
if length <= 0:
|
||||
return {}
|
||||
raw = self.rfile.read(length).decode("utf-8")
|
||||
return json.loads(raw)
|
||||
|
||||
def do_GET(self) -> None:
|
||||
parsed = urlparse(self.path)
|
||||
path = parsed.path
|
||||
query = parse_qs(parsed.query)
|
||||
try:
|
||||
if path == "/":
|
||||
self._serve_static("index.html")
|
||||
elif path.startswith("/static/"):
|
||||
self._serve_static(path.removeprefix("/static/"))
|
||||
elif path == "/asset":
|
||||
rel = unquote(query.get("path", [""])[0])
|
||||
self._serve_asset(rel)
|
||||
elif path == "/api/images":
|
||||
self._send_json({"images": pipeline.list_images()})
|
||||
elif path == "/api/capabilities":
|
||||
self._send_json(pipeline.capabilities())
|
||||
elif path == "/api/results":
|
||||
filename = query.get("image", [""])[0]
|
||||
self._send_json(pipeline.get_results(filename))
|
||||
elif path == "/api/job":
|
||||
job_id = query.get("id", [""])[0]
|
||||
with JOB_LOCK:
|
||||
job = JOBS.get(job_id)
|
||||
payload = dict(job) if job else None
|
||||
if payload is None:
|
||||
self._send_error_json(HTTPStatus.NOT_FOUND, "job not found")
|
||||
else:
|
||||
self._send_json(payload)
|
||||
else:
|
||||
self._send_error_json(HTTPStatus.NOT_FOUND, "not found")
|
||||
except Exception as exc:
|
||||
self._send_error_json(HTTPStatus.INTERNAL_SERVER_ERROR, str(exc))
|
||||
|
||||
def do_POST(self) -> None:
|
||||
parsed = urlparse(self.path)
|
||||
try:
|
||||
payload = self._read_json()
|
||||
if parsed.path == "/api/run":
|
||||
job = _new_job("dehaze", _run_dehaze_job, payload)
|
||||
self._send_json({"job": job})
|
||||
elif parsed.path == "/api/postprocess":
|
||||
job = _new_job("postprocess", _run_post_job, payload)
|
||||
self._send_json({"job": job})
|
||||
else:
|
||||
self._send_error_json(HTTPStatus.NOT_FOUND, "not found")
|
||||
except Exception as exc:
|
||||
self._send_error_json(HTTPStatus.BAD_REQUEST, str(exc))
|
||||
|
||||
def _serve_static(self, name: str) -> None:
|
||||
target = (STATIC_DIR / name).resolve()
|
||||
if STATIC_DIR.resolve() not in target.parents and target != STATIC_DIR.resolve():
|
||||
self._send_error_json(HTTPStatus.FORBIDDEN, "forbidden")
|
||||
return
|
||||
if not target.exists() or not target.is_file():
|
||||
self._send_error_json(HTTPStatus.NOT_FOUND, "static file not found")
|
||||
return
|
||||
content_type = mimetypes.guess_type(target.name)[0] or "application/octet-stream"
|
||||
if target.suffix == ".html":
|
||||
content_type = "text/html; charset=utf-8"
|
||||
elif target.suffix == ".css":
|
||||
content_type = "text/css; charset=utf-8"
|
||||
elif target.suffix == ".js":
|
||||
content_type = "application/javascript; charset=utf-8"
|
||||
self._send(HTTPStatus.OK, target.read_bytes(), content_type)
|
||||
|
||||
def _serve_asset(self, rel: str) -> None:
|
||||
target = pipeline.resolve_asset(rel)
|
||||
content_type = mimetypes.guess_type(target.name)[0] or "application/octet-stream"
|
||||
self._send(HTTPStatus.OK, target.read_bytes(), content_type)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
parser = argparse.ArgumentParser(description="Local dehaze web console")
|
||||
parser.add_argument("--host", default="127.0.0.1")
|
||||
parser.add_argument("--port", type=int, default=7860)
|
||||
args = parser.parse_args()
|
||||
|
||||
server = ThreadingHTTPServer((args.host, args.port), DehazeRequestHandler)
|
||||
print(f"Dehaze web console: http://{args.host}:{args.port}")
|
||||
print(f"Images: {pipeline.IMAGE_DIR}")
|
||||
print(f"Results: {pipeline.RESULTS_DIR}")
|
||||
server.serve_forever()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user