nagasurendra commited on
Commit
f267527
·
verified ·
1 Parent(s): 544c4d7

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +109 -29
app.py CHANGED
@@ -1,30 +1,110 @@
1
- import gradio as gr
2
- import numpy as np
3
  import cv2
4
- from core_pipeline import extract_frames, detect_trees, plot_detections
5
-
6
- def process_video(video_file):
7
- if video_file is None:
8
- return None
9
-
10
- video_path = video_file.name # fix for NamedString input
11
- frames = extract_frames(video_path)
12
- results = []
13
-
14
- for i, frame in enumerate(frames[:3]): # Show top 3 sample frames
15
- detected, bboxes, confs, labels = detect_trees(frame)
16
- annotated = plot_detections(detected, bboxes)
17
- results.append(annotated)
18
-
19
- if results:
20
- preview = np.hstack(results)
21
- return preview
22
- return None
23
-
24
- gr.Interface(
25
- fn=process_video,
26
- inputs=gr.File(label="Upload Drone Video", file_types=[".mp4"]),
27
- outputs=gr.Image(label="Tree Detections (Sample Frames)"),
28
- title="🌳 Drone Tree Detection App",
29
- description="Upload top-down drone footage (.mp4). This app detects trees using YOLOv8 and shows sample frames with bounding boxes."
30
- ).launch()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
  import cv2
2
+ import numpy as np
3
+ import gradio as gr
4
+ from ultralytics import YOLO
5
+ from transformers import AutoImageProcessor, AutoModelForDepthEstimation
6
+ from PIL import Image
7
+
8
+ # Load YOLO model for tree detection
9
+ # Replace "path_to_your_yolo_model.pt" with your model path (e.g., local or Hugging Face Hub)
10
+ yolo_model = YOLO("path_to_your_yolo_model.pt") # Update with your YOLO model path
11
+
12
+ # Load depth estimation model and processor from Hugging Face
13
+ processor = AutoImageProcessor.from_pretrained("Intel/dpt-large")
14
+ depth_model = AutoModelForDepthEstimation.from_pretrained("Intel/dpt-large")
15
+
16
+ # Function to process image and estimate tree heights
17
+ def process_image(image, focal_length_mm=3.6, sensor_height_mm=4.8, depth_scale=100):
18
+ """
19
+ Process an input image to detect trees and estimate their heights.
20
+ Args:
21
+ image: PIL Image from Gradio
22
+ focal_length_mm: Camera focal length in millimeters (default: 3.6)
23
+ sensor_height_mm: Camera sensor height in millimeters (default: 4.8)
24
+ depth_scale: Scaling factor to convert depth map to centimeters (default: 100)
25
+ Returns:
26
+ Annotated image and JSON with tree heights
27
+ """
28
+ # Convert PIL image to OpenCV format (BGR)
29
+ image_cv = cv2.cvtColor(np.array(image), cv2.COLOR_RGB2BGR)
30
+ image_height = image_cv.shape[0] # Image height in pixels
31
+
32
+ # Step 1: Run YOLO to detect trees
33
+ results = yolo_model(image_cv)
34
+ boxes = results[0].boxes.xyxy.cpu().numpy() # Bounding boxes [x_min, y_min, x_max, y_max]
35
+
36
+ # Step 2: Prepare image for depth estimation
37
+ # Convert OpenCV image (BGR) to PIL for transformers
38
+ image_pil = Image.fromarray(cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB))
39
+ # Preprocess image for depth model
40
+ inputs = processor(images=image_pil, return_tensors="pt")
41
+
42
+ # Step 3: Run depth estimation
43
+ with torch.no_grad():
44
+ outputs = depth_model(**inputs)
45
+ predicted_depth = outputs.predicted_depth
46
+
47
+ # Resize depth map to match input image size
48
+ depth_map = torch.nn.functional.interpolate(
49
+ predicted_depth.unsqueeze(1),
50
+ size=(image_cv.shape[0], image_cv.shape[1]),
51
+ mode="bicubic",
52
+ align_corners=False,
53
+ ).squeeze().cpu().numpy()
54
+
55
+ # Step 4: Process each detected tree
56
+ output = []
57
+ for box in boxes:
58
+ x_min, y_min, x_max, y_max = map(int, box)
59
+ h_pixel = y_max - y_min # Bounding box height in pixels
60
+
61
+ # Extract depth for the tree’s bounding box
62
+ depth_region = depth_map[y_min:y_max, x_min:x_max]
63
+ avg_depth = np.mean(depth_region) # Average depth (relative units)
64
+
65
+ # Convert depth to centimeters using scaling factor
66
+ distance_cm = avg_depth * depth_scale # Tune depth_scale based on testing
67
+
68
+ # Calculate tree height in centimeters
69
+ # Formula: H = (h_pixel * D * sensor_height) / (focal_length * image_height)
70
+ tree_height_cm = (h_pixel * distance_cm * sensor_height_mm) / (focal_length_mm * image_height)
71
+ tree_height_cm = round(tree_height_cm, 2) # Round to 2 decimal places
72
+
73
+ output.append({
74
+ "box": (x_min, y_min, x_max, y_max),
75
+ "height_cm": tree_height_cm
76
+ })
77
+
78
+ # Step 5: Draw results on the image
79
+ for item in output:
80
+ x_min, y_min, x_max, y_max = item["box"]
81
+ # Draw bounding box
82
+ cv2.rectangle(image_cv, (x_min, y_min), (x_max, y_max), (0, 255, 0), 2)
83
+ # Add height text
84
+ cv2.putText(image_cv, f"Height: {item['height_cm']} cm", (x_min, y_min - 10),
85
+ cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 255, 0), 2)
86
+
87
+ # Convert back to RGB for Gradio
88
+ image_rgb = cv2.cvtColor(image_cv, cv2.COLOR_BGR2RGB)
89
+
90
+ return image_rgb, output
91
+
92
+ # Create Gradio interface
93
+ iface = gr.Interface(
94
+ fn=process_image,
95
+ inputs=[
96
+ gr.Image(type="pil", label="Upload Image"),
97
+ gr.Number(label="Focal Length (mm)", value=3.6),
98
+ gr.Number(label="Sensor Height (mm)", value=4.8),
99
+ gr.Number(label="Depth Scale Factor", value=100)
100
+ ],
101
+ outputs=[
102
+ gr.Image(label="Detected Trees with Heights"),
103
+ gr.JSON(label="Tree Heights (cm)")
104
+ ],
105
+ title="Tree Detection and Height Estimation",
106
+ description="Upload an image to detect trees and estimate their heights in centimeters. Adjust camera parameters and depth scale as needed."
107
+ )
108
+
109
+ # Launch the interface
110
+ iface.launch()