123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199 |
- from fastapi import FastAPI, HTTPException
- from pydantic import BaseModel
- from typing import List, Optional, Dict, Any
- from datetime import datetime
- import traceback
- import numpy as np
- import matplotlib.pyplot as plt
- from mpl_toolkits.mplot3d import Axes3D
- from io import BytesIO
- import os
- import uvicorn
- import sys
- root_path = os.getcwd()
- sys.path.append(root_path)
- plt.rcParams['font.sans-serif'] = ['SimHei']
- plt.rcParams['axes.unicode_minus'] = False
- app = FastAPI()
- # BRDF data item model
- class BRDFItem(BaseModel):
- sampleModel: str
- temperatureK: float
- wavelengthUm: str
- thetaIncident: float
- phiIncident: float
- thetaReflected: float
- phiReflected: float
- measurement1: float
- measurement2: float
- measurement3: float
- measurement4: float
- measurement5: float
- meanValue: float
- repeatabilityPct: float
- # Request payload model
- class RequestPayload(BaseModel):
- filePath: str
- brdfList: List[BRDFItem]
- # Response model
- class ResponseModel(BaseModel):
- status: int
- msg: str
- filePath: Optional[str] = None
- # Global exception handler
- @app.exception_handler(Exception)
- async def global_exception_handler(request, exc):
- print(f"Unhandled exception: {str(exc)}")
- print(traceback.format_exc())
- return {"status": 500, "msg": "Failed"}
- def sph2cart(theta: float, phi: float):
- x = np.sin(theta) * np.cos(phi)
- y = np.sin(theta) * np.sin(phi)
- z = np.cos(theta)
- return x, y, z
- def plot_all_hemispheres(brdf_list: List[BRDFItem], save_path: str):
- os.makedirs(os.path.dirname(save_path), exist_ok=True)
- # Generate hemisphere surface
- phi = np.linspace(0, 2 * np.pi, 60)
- theta = np.linspace(0, np.pi / 2, 30)
- PHI, THETA = np.meshgrid(phi, theta)
- X = np.sin(THETA) * np.cos(PHI)
- Y = np.sin(THETA) * np.sin(PHI)
- Z = np.cos(THETA)
- # Create 3D plot
- fig = plt.figure(figsize=(10, 8))
- ax = fig.add_subplot(111, projection='3d')
- ax.plot_surface(X, Y, Z, rstride=5, cstride=5, color='lightblue', alpha=0.1, edgecolor='k')
- # Draw vectors for each BRDF data item
- for i, item in enumerate(brdf_list):
- # Convert spherical to cartesian coordinates
- xi, yi, zi = sph2cart(np.deg2rad(item.thetaIncident), np.deg2rad(item.phiIncident))
- xr, yr, zr = sph2cart(np.deg2rad(item.thetaReflected), np.deg2rad(item.phiReflected))
- # Assign colors
- color_i = plt.cm.tab10(i % 10) # Incident light color
- color_r = plt.cm.tab10((i + 5) % 10) # Reflected light color
- # Incident light - points inward (toward center)
- ax.quiver(xi, yi, zi, -xi, -yi, -zi, color=color_i, linewidth=2, arrow_length_ratio=0.1)
- # Reflected light - points outward (away from center)
- ax.quiver(0, 0, 0, xr, yr, zr, color=color_r, linewidth=2, arrow_length_ratio=0.1)
- # Set labels and limits
- ax.set_xlim([-1, 1])
- ax.set_ylim([-1, 1])
- ax.set_zlim([0, 1])
- ax.set_xlabel('sinθ·cosφ')
- ax.set_ylabel('sinθ·sinφ')
- ax.set_zlabel('Z')
- ax.set_title('BRDF扫描轨迹图')
- plt.tight_layout()
- plt.savefig(save_path, format='png', dpi=300)
- plt.close(fig)
- buf = BytesIO()
- plt.savefig(buf, format='png', dpi=300)
- buf.seek(0)
- return buf
- @app.post("/process_brdf", response_model=ResponseModel)
- async def process_brdf(payload: RequestPayload):
- """
- Process BRDF data endpoint
- Parameters:
- - filePath: File save path
- - brdfList: List of BRDF data items
- Returns:
- - status: Status code
- - msg: Message
- - filePath: Saved file path
- """
- try:
- if not payload.filePath:
- return {"status": 500, "msg": "File path is empty"}
- if not payload.brdfList:
- return {"status": 500, "msg": "BRDF list is empty"}
- # Plot all BRDF data
- buf = plot_all_hemispheres(payload.brdfList, payload.filePath)
- return {
- "status": 200,
- "msg": "Success",
- "filePath": payload.filePath
- }
- except Exception as e:
- print(f"Processing error: {str(e)}")
- print(traceback.format_exc())
- return {"status": 500, "msg": "Failed"}
- # if __name__ == "__main__":
- # import uvicorn
- #
- # uvicorn.run(app, host="0.0.0.0", port=8081)
- # if __name__ == "__main__":
- # uvicorn.run("apiServer:app", host="0.0.0.0", port=8081, reload=False)
- if __name__ == "__main__":
- # 更可靠的获取模块名方式
- name_app = os.path.splitext(os.path.basename(__file__))[0]
- # 更完整的日志配置
- log_config = {
- "version": 1,
- "disable_existing_loggers": False,
- "formatters": {
- "default": {
- "()": "uvicorn.logging.DefaultFormatter",
- "fmt": "%(levelprefix)s %(asctime)s %(message)s",
- "datefmt": "%Y-%m-%d %H:%M:%S",
- }
- },
- "handlers": {
- "default": {
- "formatter": "default",
- "class": "logging.StreamHandler",
- "stream": "ext://sys.stderr",
- },
- "file_handler": {
- "class": "logging.FileHandler",
- "filename": "logfile.log",
- "formatter": "default",
- },
- },
- "root": {
- "handlers": ["default", "file_handler"],
- "level": "INFO",
- },
- }
- uvicorn.run(
- app,
- host="0.0.0.0",
- port=8081,
- reload=False,
- log_config=log_config
- )
|