Spaces:
Runtime error
Runtime error
Upload 11 files
Browse files- .env +5 -0
- Dockerfile +26 -0
- db_bucket.py +50 -0
- db_conn_testing.ipynb +0 -0
- db_user_info.py +38 -0
- detection.py +135 -0
- docker-compose.yml +16 -0
- entrypoint.sh +8 -0
- main.py +99 -0
- new_yolov8_best.pt +3 -0
- requirements.txt +177 -0
.env
ADDED
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
Database_Password="rB97dJHBQEawiwM2"
|
2 |
+
SUPABASE_URL="https://xfmceiwvmuxyfgsyogct.supabase.co"
|
3 |
+
SUPABASE_KEY="eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InhmbWNlaXd2bXV4eWZnc3lvZ2N0Iiwicm9sZSI6ImFub24iLCJpYXQiOjE3MjE2MzM2NTksImV4cCI6MjAzNzIwOTY1OX0.O8i1TEUeA8xE5ZJpBbn9yw8owjcXQ3mrLvrRrcu_XeQ"
|
4 |
+
USER_EMAIL_1="[email protected]"
|
5 |
+
USER_PASSWORD_1="Timmy8469@@"
|
Dockerfile
ADDED
@@ -0,0 +1,26 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
# Use an official Python runtime as a parent image
|
2 |
+
FROM python:3.10-slim
|
3 |
+
|
4 |
+
# Set the working directory in the container
|
5 |
+
WORKDIR /app
|
6 |
+
|
7 |
+
# Install system dependencies
|
8 |
+
RUN apt-get update && apt-get install -y \
|
9 |
+
gcc \
|
10 |
+
libgl1-mesa-glx \
|
11 |
+
libglib2.0-0
|
12 |
+
|
13 |
+
# Copy only requirements to leverage Docker cache
|
14 |
+
COPY requirements.txt .
|
15 |
+
|
16 |
+
# Install any needed packages specified in requirements.txt
|
17 |
+
RUN pip install --no-cache-dir -r requirements.txt
|
18 |
+
|
19 |
+
# Copy the rest of the application code
|
20 |
+
COPY . /app
|
21 |
+
|
22 |
+
# Make port 8000 available to the world outside this container
|
23 |
+
EXPOSE 8000
|
24 |
+
|
25 |
+
# Run the entrypoint script
|
26 |
+
CMD ["sh", "./entrypoint.sh"]
|
db_bucket.py
ADDED
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from datetime import datetime
|
2 |
+
from dotenv import load_dotenv
|
3 |
+
import os
|
4 |
+
load_dotenv()
|
5 |
+
|
6 |
+
from supabase import create_client, Client
|
7 |
+
|
8 |
+
url: str = os.environ.get("SUPABASE_URL")
|
9 |
+
key: str = os.environ.get("SUPABASE_KEY")
|
10 |
+
supabase: Client = create_client(url, key)
|
11 |
+
|
12 |
+
|
13 |
+
# authentication set up
|
14 |
+
user_email:str = os.environ.get("USER_EMAIL_1")
|
15 |
+
user_password: str = os.environ.get("USER_PASSWORD_1")
|
16 |
+
|
17 |
+
# Sign in the user
|
18 |
+
auth_response = supabase.auth.sign_in_with_password({"email": user_email, "password": user_password})
|
19 |
+
|
20 |
+
# checking if a file exists
|
21 |
+
def file_exists(file_path):
|
22 |
+
return os.path.isfile(file_path)
|
23 |
+
|
24 |
+
# adding detected weed shp files to the bucket
|
25 |
+
def upload_file_to_bucket(file_path: str, storage_path: str = "shapefiles_storage/", bucket_name: str = "shapefiles-bucket") -> None:
|
26 |
+
# firstlt checking if the path exists
|
27 |
+
if not file_exists(file_path):
|
28 |
+
print(f"Error: {file_path} does not exist")
|
29 |
+
return None
|
30 |
+
# extracting the file ame
|
31 |
+
file_name = os.path.basename(file_path)
|
32 |
+
|
33 |
+
# adding today's date and time to the file name
|
34 |
+
file_name = datetime.now().strftime("%Y-%m-%d_%H-%M-%S_") + file_name
|
35 |
+
|
36 |
+
with open(file_path, 'rb') as file:
|
37 |
+
response = supabase.storage.from_(bucket_name).upload(storage_path+file_name, file)
|
38 |
+
|
39 |
+
if response.status_code != 200:
|
40 |
+
return f"Error: {response.json()['message']}"
|
41 |
+
else:
|
42 |
+
return "File uploaded successfully:", response.json()
|
43 |
+
|
44 |
+
if __name__ == "__main__":
|
45 |
+
# Define the relative path to the file from the current script location
|
46 |
+
file_path = os.path.abspath(os.path.join(os.path.dirname(__file__), 'db setup.zip'))
|
47 |
+
|
48 |
+
# upload the file to the bucket
|
49 |
+
response = upload_file_to_bucket(file_path=file_path)
|
50 |
+
print(response)
|
db_conn_testing.ipynb
ADDED
The diff for this file is too large to render.
See raw diff
|
|
db_user_info.py
ADDED
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from dotenv import load_dotenv
|
2 |
+
import os
|
3 |
+
load_dotenv()
|
4 |
+
|
5 |
+
from supabase import create_client, Client
|
6 |
+
|
7 |
+
url: str = os.environ.get("SUPABASE_URL")
|
8 |
+
key: str = os.environ.get("SUPABASE_KEY")
|
9 |
+
supabase: Client = create_client(url, key)
|
10 |
+
|
11 |
+
|
12 |
+
# authentication set up
|
13 |
+
user_email:str = os.environ.get("USER_EMAIL_1")
|
14 |
+
user_password: str = os.environ.get("USER_PASSWORD_1")
|
15 |
+
|
16 |
+
# Sign in the user
|
17 |
+
auth_response = supabase.auth.sign_in_with_password({"email": user_email, "password": user_password})
|
18 |
+
|
19 |
+
|
20 |
+
# creating a function to insert user info
|
21 |
+
def insert_user_info(user_info: dict) -> None:
|
22 |
+
try:
|
23 |
+
response = supabase.table("user-info").insert(user_info).execute()
|
24 |
+
return f"'response': {response}"
|
25 |
+
except Exception as e:
|
26 |
+
print(e)
|
27 |
+
|
28 |
+
|
29 |
+
if __name__ == "__main__":
|
30 |
+
|
31 |
+
new_info = {
|
32 |
+
"Name": "Timothy Afolami",
|
33 |
+
"Address": "Lagos, Nigeria",
|
34 |
+
"Phone Number": "08100450227",
|
35 |
+
"Email" : "[email protected]"
|
36 |
+
}
|
37 |
+
response = insert_user_info(user_info=new_info)
|
38 |
+
print(response)
|
detection.py
ADDED
@@ -0,0 +1,135 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import cv2
|
3 |
+
import zipfile
|
4 |
+
import shapefile
|
5 |
+
import numpy as np
|
6 |
+
from shapely.geometry import Polygon
|
7 |
+
from io import BytesIO
|
8 |
+
from PIL import Image
|
9 |
+
import rasterio
|
10 |
+
from rasterio.windows import Window
|
11 |
+
from ultralytics import YOLO
|
12 |
+
from db_bucket import upload_file_to_bucket # Import the bucket upload function
|
13 |
+
|
14 |
+
# Paths and configurations
|
15 |
+
path_to_store_bounding_boxes = 'detect/'
|
16 |
+
path_to_save_shapefile = 'weed_detections'
|
17 |
+
slice_size = 3000
|
18 |
+
|
19 |
+
# Load YOLO model (update the path to your model)
|
20 |
+
model = YOLO('new_yolov8_best.pt')
|
21 |
+
|
22 |
+
class_names = ["citrus area", "trees", "weeds", "weeds and trees"]
|
23 |
+
|
24 |
+
# Function to initialize directories
|
25 |
+
def initialize_directories():
|
26 |
+
os.makedirs(path_to_store_bounding_boxes, exist_ok=True)
|
27 |
+
os.makedirs("slices", exist_ok=True)
|
28 |
+
|
29 |
+
# Function to slice the GeoTIFF
|
30 |
+
async def slice_geotiff(file_path, slice_size=3000):
|
31 |
+
slices = []
|
32 |
+
with rasterio.open(file_path) as dataset:
|
33 |
+
img_width = dataset.width
|
34 |
+
img_height = dataset.height
|
35 |
+
transform = dataset.transform
|
36 |
+
|
37 |
+
for i in range(0, img_height, slice_size):
|
38 |
+
for j in range(0, img_width, slice_size):
|
39 |
+
window = Window(j, i, slice_size, slice_size)
|
40 |
+
transform_window = rasterio.windows.transform(window, transform)
|
41 |
+
slice_data = dataset.read(window=window)
|
42 |
+
slice_img = Image.fromarray(slice_data.transpose(1, 2, 0)) # Convert to HWC format
|
43 |
+
slice_filename = f"slices/slice_{i}_{j}.png"
|
44 |
+
slice_img.save(slice_filename)
|
45 |
+
slices.append((slice_filename, transform_window))
|
46 |
+
return slices
|
47 |
+
|
48 |
+
# Function to create a shapefile with image dimensions and bounding boxes
|
49 |
+
def create_shapefile_with_latlon(bboxes, shapefile_path="weed_detections.shp"):
|
50 |
+
w = shapefile.Writer(shapefile_path)
|
51 |
+
w.field('id', 'C')
|
52 |
+
|
53 |
+
for idx, (x1, y1, x2, y2, transform) in enumerate(bboxes):
|
54 |
+
top_left = rasterio.transform.xy(transform, y1, x1, offset='center')
|
55 |
+
top_right = rasterio.transform.xy(transform, y1, x2, offset='center')
|
56 |
+
bottom_left = rasterio.transform.xy(transform, y2, x1, offset='center')
|
57 |
+
bottom_right = rasterio.transform.xy(transform, y2, x2, offset='center')
|
58 |
+
|
59 |
+
poly = Polygon([top_left, top_right, bottom_right, bottom_left, top_left])
|
60 |
+
w.poly([poly.exterior.coords])
|
61 |
+
w.record(f'weed_{idx}')
|
62 |
+
|
63 |
+
w.close()
|
64 |
+
|
65 |
+
# Function to detect weeds in image slices
|
66 |
+
async def detect_weeds_in_slices(slices):
|
67 |
+
weed_bboxes = []
|
68 |
+
img_width, img_height = slice_size, slice_size # Assuming fixed slice size
|
69 |
+
|
70 |
+
for slice_filename, transform in slices:
|
71 |
+
img_array = np.array(Image.open(slice_filename))
|
72 |
+
results = model.predict(slice_filename, imgsz=640, conf=0.2, iou=0.4)
|
73 |
+
results = results[0]
|
74 |
+
|
75 |
+
for i, box in enumerate(results.boxes):
|
76 |
+
tensor = box.xyxy[0]
|
77 |
+
x1 = int(tensor[0].item())
|
78 |
+
y1 = int(tensor[1].item())
|
79 |
+
x2 = int(tensor[2].item())
|
80 |
+
y2 = int(tensor[3].item())
|
81 |
+
conf = box.conf[0].item()
|
82 |
+
label = box.cls[0].item()
|
83 |
+
|
84 |
+
if class_names[int(label)] == "weeds":
|
85 |
+
cv2.rectangle(img_array, (x1, y1), (x2, y2), (255, 0, 255), 3)
|
86 |
+
weed_bboxes.append((x1, y1, x2, y2, transform))
|
87 |
+
|
88 |
+
# Save the image with bounding boxes
|
89 |
+
detected_image_path = os.path.join(path_to_store_bounding_boxes, os.path.basename(slice_filename))
|
90 |
+
cv2.imwrite(detected_image_path, cv2.cvtColor(img_array, cv2.COLOR_RGB2BGR))
|
91 |
+
|
92 |
+
create_shapefile_with_latlon(weed_bboxes)
|
93 |
+
|
94 |
+
async def create_zip():
|
95 |
+
# Create a zip file
|
96 |
+
zip_file_path = "weed_detections.zip"
|
97 |
+
with zipfile.ZipFile(zip_file_path, 'w') as zip_file:
|
98 |
+
for ext in ['shp', 'shx', 'dbf']:
|
99 |
+
file_name = f"{path_to_save_shapefile}.{ext}"
|
100 |
+
if os.path.exists(file_name):
|
101 |
+
zip_file.write(file_name, os.path.basename(file_name))
|
102 |
+
|
103 |
+
return zip_file_path
|
104 |
+
|
105 |
+
# Function to clean up created files and directories
|
106 |
+
def cleanup():
|
107 |
+
# Remove the zip file
|
108 |
+
if os.path.exists("weed_detections.zip"):
|
109 |
+
os.remove("weed_detections.zip")
|
110 |
+
|
111 |
+
# Remove the geotiff file
|
112 |
+
if os.path.exists("uploaded_geotiff.tif"):
|
113 |
+
os.remove("uploaded_geotiff.tif")
|
114 |
+
|
115 |
+
# Remove shapefile components
|
116 |
+
for ext in ['shp', 'shx', 'dbf']:
|
117 |
+
file_name = f"{path_to_save_shapefile}.{ext}"
|
118 |
+
if os.path.exists(file_name):
|
119 |
+
os.remove(file_name)
|
120 |
+
|
121 |
+
# Remove slices
|
122 |
+
if os.path.exists("slices"):
|
123 |
+
for file in os.listdir("slices"):
|
124 |
+
file_path = os.path.join("slices", file)
|
125 |
+
if os.path.isfile(file_path):
|
126 |
+
os.remove(file_path)
|
127 |
+
os.rmdir("slices")
|
128 |
+
|
129 |
+
# Remove detected bounding boxes
|
130 |
+
if os.path.exists("detect"):
|
131 |
+
for file in os.listdir("detect"):
|
132 |
+
file_path = os.path.join("detect", file)
|
133 |
+
if os.path.isfile(file_path):
|
134 |
+
os.remove(file_path)
|
135 |
+
os.rmdir("detect")
|
docker-compose.yml
ADDED
@@ -0,0 +1,16 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
version: '3.9'
|
2 |
+
|
3 |
+
services:
|
4 |
+
app:
|
5 |
+
build:
|
6 |
+
context: .
|
7 |
+
dockerfile: Dockerfile
|
8 |
+
ports:
|
9 |
+
- "8000:8000"
|
10 |
+
volumes:
|
11 |
+
- .:/app
|
12 |
+
environment:
|
13 |
+
- SUPABASE_URL=${SUPABASE_URL}
|
14 |
+
- SUPABASE_KEY=${SUPABASE_KEY}
|
15 |
+
- USER_EMAIL_1=${USER_EMAIL_1}
|
16 |
+
- USER_PASSWORD_1=${USER_PASSWORD_1}
|
entrypoint.sh
ADDED
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
#!/bin/sh
|
2 |
+
|
3 |
+
# Exit immediately if a command exits with a non-zero status
|
4 |
+
set -e
|
5 |
+
|
6 |
+
# Step 4: Run FastAPI App
|
7 |
+
echo "Starting FastAPI app..."
|
8 |
+
uvicorn main:app --host 0.0.0.0 --port 8000
|
main.py
ADDED
@@ -0,0 +1,99 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
from fastapi import FastAPI, WebSocket, WebSocketDisconnect, UploadFile, File, Request
|
2 |
+
from fastapi.responses import JSONResponse, HTMLResponse, StreamingResponse
|
3 |
+
from fastapi.staticfiles import StaticFiles
|
4 |
+
from detection import initialize_directories, slice_geotiff, detect_weeds_in_slices, cleanup, create_zip # Import detection functions
|
5 |
+
from db_user_info import insert_user_info
|
6 |
+
from db_bucket import upload_file_to_bucket
|
7 |
+
from typing import List
|
8 |
+
from io import BytesIO
|
9 |
+
|
10 |
+
app = FastAPI()
|
11 |
+
|
12 |
+
# Mount static files
|
13 |
+
app.mount("/static", StaticFiles(directory="static"), name="static")
|
14 |
+
|
15 |
+
# WebSocket manager
|
16 |
+
class ConnectionManager:
|
17 |
+
def __init__(self):
|
18 |
+
self.active_connections: List[WebSocket] = []
|
19 |
+
|
20 |
+
async def connect(self, websocket: WebSocket):
|
21 |
+
await websocket.accept()
|
22 |
+
self.active_connections.append(websocket)
|
23 |
+
|
24 |
+
def disconnect(self, websocket: WebSocket):
|
25 |
+
self.active_connections.remove(websocket)
|
26 |
+
|
27 |
+
async def send_message(self, message: str):
|
28 |
+
for connection in self.active_connections:
|
29 |
+
await connection.send_text(message)
|
30 |
+
|
31 |
+
manager = ConnectionManager()
|
32 |
+
|
33 |
+
@app.get("/", response_class=HTMLResponse)
|
34 |
+
async def read_index():
|
35 |
+
with open("static/index.html") as f:
|
36 |
+
return HTMLResponse(content=f.read())
|
37 |
+
|
38 |
+
@app.get("/app", response_class=HTMLResponse)
|
39 |
+
async def read_app():
|
40 |
+
with open("static/app.html") as f:
|
41 |
+
return HTMLResponse(content=f.read())
|
42 |
+
|
43 |
+
@app.post("/register")
|
44 |
+
async def register_user(request: Request):
|
45 |
+
user_info = await request.json()
|
46 |
+
try:
|
47 |
+
insert_user_info(user_info)
|
48 |
+
return JSONResponse(content={"detail": "User registered successfully"}, status_code=200)
|
49 |
+
except Exception as e:
|
50 |
+
return JSONResponse(content={"detail": str(e)}, status_code=400)
|
51 |
+
|
52 |
+
@app.post("/upload_geotiff/")
|
53 |
+
async def upload_geotiff(file: UploadFile = File(...)):
|
54 |
+
# Initialize directories at the start
|
55 |
+
initialize_directories()
|
56 |
+
|
57 |
+
file_location = f"uploaded_geotiff.tif"
|
58 |
+
with open(file_location, "wb") as f:
|
59 |
+
f.write(file.file.read())
|
60 |
+
|
61 |
+
await manager.send_message("GeoTIFF file uploaded successfully. Slicing started.")
|
62 |
+
slices = await slice_geotiff(file_location, slice_size=3000)
|
63 |
+
await manager.send_message("Slicing complete. Starting weed detection.")
|
64 |
+
weed_bboxes = await detect_weeds_in_slices(slices)
|
65 |
+
await manager.send_message("Weed detection complete. Generating shapefile.")
|
66 |
+
|
67 |
+
# Create zip file
|
68 |
+
zip_file_path = await create_zip()
|
69 |
+
await manager.send_message("Shapefiles Generated. Zipping shapefile.")
|
70 |
+
|
71 |
+
# Upload the zip file to the bucket
|
72 |
+
response = upload_file_to_bucket(zip_file_path)
|
73 |
+
print(response)
|
74 |
+
await manager.send_message("Zip file uploaded to bucket storage.")
|
75 |
+
|
76 |
+
# Read zip file into buffer for download
|
77 |
+
zip_buffer = BytesIO()
|
78 |
+
with open(zip_file_path, 'rb') as f:
|
79 |
+
zip_buffer.write(f.read())
|
80 |
+
zip_buffer.seek(0)
|
81 |
+
|
82 |
+
# Cleanup files and directories
|
83 |
+
cleanup()
|
84 |
+
|
85 |
+
return StreamingResponse(zip_buffer, media_type="application/zip", headers={"Content-Disposition": "attachment; filename=weed_detections.zip"})
|
86 |
+
|
87 |
+
@app.websocket("/ws")
|
88 |
+
async def websocket_endpoint(websocket: WebSocket):
|
89 |
+
await manager.connect(websocket)
|
90 |
+
try:
|
91 |
+
while True:
|
92 |
+
data = await websocket.receive_text()
|
93 |
+
await manager.send_message(data)
|
94 |
+
except WebSocketDisconnect:
|
95 |
+
manager.disconnect(websocket)
|
96 |
+
|
97 |
+
|
98 |
+
if __name__ == "__main__":
|
99 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
new_yolov8_best.pt
ADDED
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
1 |
+
version https://git-lfs.github.com/spec/v1
|
2 |
+
oid sha256:2664fca042b1ead70a9dc27597052b1c137719ab8ae3a870c940bf16cdfc4c0e
|
3 |
+
size 22508569
|
requirements.txt
ADDED
@@ -0,0 +1,177 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
affine==2.4.0
|
2 |
+
altair==5.3.0
|
3 |
+
annotated-types==0.7.0
|
4 |
+
anyio==4.4.0
|
5 |
+
argon2-cffi==23.1.0
|
6 |
+
argon2-cffi-bindings==21.2.0
|
7 |
+
arrow==1.3.0
|
8 |
+
asttokens==2.4.1
|
9 |
+
async-lru==2.0.4
|
10 |
+
attrs==23.2.0
|
11 |
+
Babel==2.15.0
|
12 |
+
beautifulsoup4==4.12.3
|
13 |
+
bleach==6.1.0
|
14 |
+
blinker==1.8.2
|
15 |
+
cachetools==5.3.3
|
16 |
+
certifi==2024.2.2
|
17 |
+
cffi==1.16.0
|
18 |
+
charset-normalizer==3.3.2
|
19 |
+
click==8.1.7
|
20 |
+
click-plugins==1.1.1
|
21 |
+
cligj==0.7.2
|
22 |
+
colorama==0.4.6
|
23 |
+
comm==0.2.2
|
24 |
+
contourpy==1.2.1
|
25 |
+
cycler==0.12.1
|
26 |
+
debugpy==1.8.1
|
27 |
+
decorator==5.1.1
|
28 |
+
defusedxml==0.7.1
|
29 |
+
deprecation==2.1.0
|
30 |
+
dnspython==2.6.1
|
31 |
+
email_validator==2.1.1
|
32 |
+
exceptiongroup==1.2.1
|
33 |
+
executing==2.0.1
|
34 |
+
fastapi==0.111.0
|
35 |
+
fastapi-cli==0.0.4
|
36 |
+
fastjsonschema==2.19.1
|
37 |
+
filelock==3.14.0
|
38 |
+
fiona==1.9.6
|
39 |
+
fonttools==4.52.3
|
40 |
+
fqdn==1.5.1
|
41 |
+
fsspec==2024.5.0
|
42 |
+
geopandas==0.14.4
|
43 |
+
gitdb==4.0.11
|
44 |
+
GitPython==3.1.43
|
45 |
+
gotrue==2.6.0
|
46 |
+
h11==0.14.0
|
47 |
+
h2==4.1.0
|
48 |
+
hpack==4.0.0
|
49 |
+
httpcore==1.0.5
|
50 |
+
httptools==0.6.1
|
51 |
+
httpx==0.27.0
|
52 |
+
hyperframe==6.0.1
|
53 |
+
idna==3.7
|
54 |
+
intel-openmp==2021.4.0
|
55 |
+
ipykernel==6.29.4
|
56 |
+
ipython==8.24.0
|
57 |
+
isoduration==20.11.0
|
58 |
+
jedi==0.19.1
|
59 |
+
Jinja2==3.1.4
|
60 |
+
json5==0.9.25
|
61 |
+
jsonpointer==2.4
|
62 |
+
jsonschema==4.22.0
|
63 |
+
jsonschema-specifications==2023.12.1
|
64 |
+
jupyter_client==8.6.2
|
65 |
+
jupyter_core==5.7.2
|
66 |
+
jupyter-events==0.10.0
|
67 |
+
jupyter-lsp===2.2.5
|
68 |
+
jupyter_server==2.14.0
|
69 |
+
jupyter_server_terminals==0.5.3
|
70 |
+
jupyterlab==4.2.1
|
71 |
+
jupyterlab_pygments==0.3.0
|
72 |
+
jupyterlab_server==2.27.2
|
73 |
+
kiwisolver==1.4.5
|
74 |
+
markdown-it-py==3.0.0
|
75 |
+
MarkupSafe==2.1.5
|
76 |
+
matplotlib==3.9.0
|
77 |
+
matplotlib-inline==0.1.7
|
78 |
+
mdurl==0.1.2
|
79 |
+
mistune==3.0.2
|
80 |
+
mkl==2021.4.0
|
81 |
+
mpmath==1.3.0
|
82 |
+
nbclient==0.10.0
|
83 |
+
nbconvert==7.16.4
|
84 |
+
nbformat==5.10.4
|
85 |
+
nest-asyncio==1.6.0
|
86 |
+
networkx==3.3
|
87 |
+
notebook==7.2.0
|
88 |
+
notebook_shim===0.2.4
|
89 |
+
numpy==1.26.4
|
90 |
+
opencv-python==4.10.0.82
|
91 |
+
opencv-python-headless==4.10.0.82
|
92 |
+
orjson==3.10.3
|
93 |
+
overrides==7.7.0
|
94 |
+
packaging==24.0
|
95 |
+
pandas==2.2.2
|
96 |
+
pandocfilters==1.5.1
|
97 |
+
parso==0.8.4
|
98 |
+
pillow==10.3.0
|
99 |
+
pip==24.0
|
100 |
+
platformdirs==4.2.2
|
101 |
+
postgrest==0.16.9
|
102 |
+
prometheus_client==0.20.0
|
103 |
+
prompt_toolkit==3.0.44
|
104 |
+
protobuf==4.25.3
|
105 |
+
psutil==5.9.8
|
106 |
+
pure-eval==0.2.2
|
107 |
+
py-cpuinfo==9.0.0
|
108 |
+
pyarrow==16.1.0
|
109 |
+
pycparser==2.22
|
110 |
+
pydantic==2.7.3
|
111 |
+
pydantic_core==2.18.4
|
112 |
+
pydeck==0.9.1
|
113 |
+
Pygments==2.18.0
|
114 |
+
pyparsing==3.1.2
|
115 |
+
pyproj==3.6.1
|
116 |
+
pyshp==2.3.1
|
117 |
+
python-dateutil==2.9.0.post0
|
118 |
+
python-dotenv==1.0.1
|
119 |
+
python-json-logger==2.0.7
|
120 |
+
python-multipart==0.0.9
|
121 |
+
rasterio==1.3.10
|
122 |
+
realtime==1.0.6
|
123 |
+
referencing==0.35.1
|
124 |
+
requests==2.32.2
|
125 |
+
rfc3339-validator==0.1.4
|
126 |
+
rfc3986-validator==0.1.1
|
127 |
+
rich==13.7.1
|
128 |
+
rpds-py==0.18.1
|
129 |
+
scipy==1.13.1
|
130 |
+
seaborn==0.13.2
|
131 |
+
Send2Trash==1.8.3
|
132 |
+
setuptools==69.5.1
|
133 |
+
shapely==2.0.4
|
134 |
+
shellingham==1.5.4
|
135 |
+
six==1.16.0
|
136 |
+
smmap==5.0.1
|
137 |
+
sniffio==1.3.1
|
138 |
+
snuggs==1.4.7
|
139 |
+
soupsieve==2.5
|
140 |
+
stack-data==0.6.3
|
141 |
+
starlette==0.37.2
|
142 |
+
storage3==0.7.7
|
143 |
+
streamlit==1.35.0
|
144 |
+
StrEnum==0.4.15
|
145 |
+
supabase==2.5.3
|
146 |
+
supafunc==0.4.7
|
147 |
+
sympy==1.12
|
148 |
+
tbb==2021.12.0
|
149 |
+
tenacity==8.3.0
|
150 |
+
terminado==0.18.1
|
151 |
+
thop==0.1.1.post2209072238
|
152 |
+
tinycss2==1.3.0
|
153 |
+
toml==0.10.2
|
154 |
+
tomli==2.0.1
|
155 |
+
toolz==0.12.1
|
156 |
+
torch==2.3.0
|
157 |
+
torchvision==0.18.0
|
158 |
+
tornado==6.4
|
159 |
+
tqdm==4.66.4
|
160 |
+
traitlets==5.14.3
|
161 |
+
typer==0.12.3
|
162 |
+
types-python-dateutil==2.9.0.20240316
|
163 |
+
typing_extensions==4.12.2
|
164 |
+
tzdata==2024.1
|
165 |
+
ujson==5.10.0
|
166 |
+
ultralytics==8.2.23
|
167 |
+
uri-template==1.3.0
|
168 |
+
urllib3==2.2.1
|
169 |
+
uvicorn==0.30.1
|
170 |
+
watchdog==4.0.1
|
171 |
+
watchfiles==0.22.0
|
172 |
+
wcwidth==0.2.13
|
173 |
+
webcolors==1.13
|
174 |
+
webencodings==0.5.1
|
175 |
+
websocket-client==1.8.0
|
176 |
+
websockets==12.0
|
177 |
+
wheel==0.43.0
|