-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathapi.py
More file actions
143 lines (120 loc) · 4.84 KB
/
api.py
File metadata and controls
143 lines (120 loc) · 4.84 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
from fastapi import FastAPI, HTTPException, Body
from pydantic import BaseModel, Field
from typing import List
import numpy as np
import cv2
from io import BytesIO
from PIL import Image
import requests
from fastapi.middleware.cors import CORSMiddleware
# Local imports
from geoCalib_client import get_camera_parameters_estimate
from calibration_utils import estimate_camera_params, gps_to_camxy_vasha_fixed
# --- Pydantic Models ---
class CalibrationRequest(BaseModel):
imagePoints: List[List[float]] = Field(..., min_items=4)
worldPoints: List[List[float]] = Field(..., min_items=4)
imageUrl: str
class CalibrationResponse(BaseModel):
k_matrix: List[List[float]]
r_matrix: List[List[float]]
t_vector: List[List[float]]
estimated_image_points: List[List[float]]
# --- FastAPI App ---
app = FastAPI(
title="Camera Calibration API",
description="An API to calculate camera matrix and distortion coefficients using a combination of initial estimation and refinement.",
version="1.1.0",
)
app.add_middleware(
CORSMiddleware,
allow_origins=["*"], # Or specify domains like ["http://localhost:3000"]
allow_credentials=True,
allow_methods=["*"], # Make sure OPTIONS is included
allow_headers=["*"],
)
@app.post("/calibrate", response_model=CalibrationResponse)
async def calibrate_camera(data: CalibrationRequest):
"""
Calibrates a camera by first getting an initial estimate from geocalib,
then refining it using local feature points.
"""
try:
# --- 1. Get Initial Estimate from GeoCalib ---
try:
initial_params, _, _ = get_camera_parameters_estimate(
data.imageUrl)
# Default to 50 deg if not found
focal_length_deg = initial_params.get('vFoV', (50, 0))[0]
except Exception as e:
raise HTTPException(
status_code=503, detail=f"GeoCalib service failed: {e}")
# --- 2. Prepare for Refinement ---
# Fetch image to get its size
try:
response = requests.get(data.imageUrl)
response.raise_for_status()
pil_img = Image.open(BytesIO(response.content))
# (height, width) for utils
frame_size = (pil_img.height, pil_img.width)
except requests.RequestException as e:
raise HTTPException(
status_code=400, detail=f"Failed to fetch image from URL: {e}")
focal_length = 0.5 * frame_size[0] / \
np.tan(0.5 * np.radians(focal_length_deg))
# Create initial K matrix (intrinsics)
initial_k = np.array([
[focal_length, 0, frame_size[1] / 2],
[0, focal_length, frame_size[0] / 2],
[0, 0, 1]
], dtype=np.float32)
# --- 3. Refine with calibration_utils ---
if len(data.imagePoints) != len(data.worldPoints):
raise HTTPException(
status_code=400, detail="Number of image and world points must match.")
poi_xy = np.array(data.imagePoints[1:])
poi_gps = np.array(data.worldPoints[1:])
origin_gps = np.array(data.worldPoints[0])
try:
k_matrix, dist_coeffs, r_matrix, t_vector, cam_ecef_coords = estimate_camera_params(
origin_gps,
poi_gps,
poi_xy,
frame_size,
intrinsics_estimate=initial_k
)
except Exception as e:
raise HTTPException(
status_code=500, detail=f"Calibration refinement failed: {e}")
# --- 4. Calculate Reprojection Error ---
# use gps_to_camxy_vasha_fixed
image_x, image_y, cam_distance = gps_to_camxy_vasha_fixed(
poi_gps[:, 0], # lats
poi_gps[:, 1], # lons
poi_gps[:, 2], # alts
cam_k=k_matrix,
cam_r=r_matrix,
cam_t=t_vector,
cam_ecef=cam_ecef_coords,
camera_gps=origin_gps,
distortion=dist_coeffs
)
# # add input points to estimated points for response
# image_x = np.insert(image_x, 0, data.imagePoints[0][0])
# image_y = np.insert(image_y, 0, data.imagePoints[0][1])
image_x = np.concatenate(([data.imagePoints[0][0]], image_x))
image_y = np.concatenate(([data.imagePoints[0][1]], image_y))
return {
"k_matrix": k_matrix.tolist(),
"r_matrix": r_matrix.tolist(),
"t_vector": t_vector.tolist(),
"estimated_image_points": np.vstack((image_x, image_y)).T.tolist(),
}
except HTTPException as e:
raise e # Re-raise HTTPException to keep status code and detail
except Exception as e:
raise HTTPException(
status_code=500, detail=f"An unexpected error occurred: {str(e)}")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=5001)