Intern-4 commited on
Commit
fc25810
·
1 Parent(s): c02898e

exiftool installation

Browse files
Files changed (2) hide show
  1. apt.txt +1 -0
  2. gradio_with_map.py +57 -90
apt.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ libimage-exiftool-perl
gradio_with_map.py CHANGED
@@ -3,13 +3,13 @@ import gradio as gr
3
  import PIL.Image as Image
4
  import numpy as np
5
  import cv2
6
- from ultralytics import ASSETS, YOLOv10
 
7
  from geopy.distance import geodesic
8
  import folium
9
  import base64
10
  import supervision as sv
11
  import os
12
- from PIL.ExifTags import TAGS, GPSTAGS
13
 
14
  # Constants for image dimensions
15
  IMAGE_WIDTH = 4000
@@ -19,9 +19,13 @@ IMAGE_HEIGHT = 3000
19
  model = YOLOv10("weights/best.pt")
20
 
21
  # Define the directory for saving uploaded images
22
- UPLOAD_DIR = 'uploads' # Or any other directory within your project
23
  os.makedirs(UPLOAD_DIR, exist_ok=True)
24
 
 
 
 
 
25
 
26
  # Function to calculate ground distance from pixel distance
27
  def calculate_ground_distance(altitude, fov_deg, image_dimension, pixel_distance):
@@ -29,14 +33,12 @@ def calculate_ground_distance(altitude, fov_deg, image_dimension, pixel_distance
29
  ground_distance = (2 * altitude * np.tan(fov_rad / 2)) * (pixel_distance / image_dimension)
30
  return ground_distance
31
 
32
-
33
  # Function to get GPS coordinates from offsets
34
  def get_gps_coordinates(lat, lon, north_offset, east_offset):
35
  new_location = geodesic(meters=north_offset).destination((lat, lon), 0)
36
  new_location = geodesic(meters=east_offset).destination(new_location, 90)
37
  return new_location.latitude, new_location.longitude
38
 
39
-
40
  def extract_xmp_metadata(xmp_data):
41
  # Parse the XMP data as an XML tree
42
  root = ET.fromstring(xmp_data)
@@ -57,65 +59,10 @@ def extract_xmp_metadata(xmp_data):
57
 
58
  return relative_altitude, gimbal_yaw_degree, gimbal_pitch_degree
59
 
60
-
61
- def extract_gps_info(exif_data):
62
- """Extract GPS information from the EXIF data."""
63
- gps_info = {}
64
- for tag, value in exif_data.items():
65
- decoded = TAGS.get(tag, tag)
66
- if decoded == 'GPSInfo':
67
- for t in value:
68
- sub_decoded = GPSTAGS.get(t, t)
69
- gps_info[sub_decoded] = value[t]
70
- return gps_info
71
-
72
- def extract_fov_info(exif_data):
73
- """Extract FOV information from the EXIF data."""
74
- fov_info = {}
75
-
76
- for tag, value in exif_data.items():
77
- decoded = TAGS.get(tag, tag)
78
-
79
- # Check for known FOV-related tags
80
- if decoded == 'FocalLength': # Example tag for focal length
81
- focal_length = value
82
- fov_info['FocalLength'] = focal_length
83
-
84
- if decoded == 'FocalLengthIn35mmFilm': # Another example
85
- focal_length_35mm = value
86
- fov_info['FocalLengthIn35mmFilm'] = focal_length_35mm
87
-
88
- # Add additional checks for custom FOV-related tags if known
89
- # if decoded == 'YourCameraSpecificFOVTag':
90
- # fov_degree = value
91
- # fov_info['FOV'] = fov_degree
92
-
93
- # Calculate FOV if needed (e.g., using focal length and sensor size)
94
- if 'FocalLength' in fov_info:
95
- # Example calculation for FOV using a known formula:
96
- # This example uses a hypothetical sensor size; adjust as needed.
97
- sensor_width = 36.0 # mm (example for full-frame sensor)
98
- focal_length = fov_info['FocalLength'][0] / fov_info['FocalLength'][1]
99
-
100
- # Calculate horizontal FOV in degrees
101
- fov_info['FOV'] = 2 * np.arctan(sensor_width / (2 * focal_length)) * (180 / np.pi)
102
-
103
- return fov_info
104
-
105
-
106
- def get_decimal_from_dms(dms, ref):
107
- """Convert DMS coordinates to decimal format."""
108
- degrees, minutes, seconds = dms
109
- decimal = float(degrees) + float(minutes) / 60 + float(seconds) / 3600
110
- if ref in ['S', 'W']:
111
- decimal = -decimal
112
- return decimal
113
-
114
-
115
  def save_image_with_metadata(img, img_path):
116
  # Convert PIL Image to a format that retains EXIF
117
  img_format = img.format or 'JPEG'
118
-
119
  # Save image to a temporary file to preserve metadata
120
  img.save(img_path, format=img_format)
121
 
@@ -139,30 +86,56 @@ def predict_image(img, conf_threshold, iou_threshold):
139
  print(f"Gimbal Pitch Degree: {gimbal_pitch_degree}")
140
  else:
141
  print("XMP data not found in the image.")
142
-
143
  # Extract EXIF data
144
  exif_data = img.info.get("exif")
145
-
146
- if exif_data is not None:
147
- gps_info = extract_gps_info(exif_data)
148
- print("GPS Info:", gps_info)
149
-
150
- # Get latitude and longitude
151
- gps_latitude = get_decimal_from_dms(gps_info['GPSLatitude'], gps_info['GPSLatitudeRef'])
152
- gps_longitude = get_decimal_from_dms(gps_info['GPSLongitude'], gps_info['GPSLongitudeRef'])
153
-
154
- CAMERA_GPS = (gps_latitude, gps_longitude)
155
- print("Camera GPS:", CAMERA_GPS)
156
- else:
157
- print("EXIF data not found in the image.")
158
- return None, "EXIF data not found."
159
 
160
  # Save the image with metadata
161
- img.save(img_path) # Save without EXIF data if not available
162
-
 
 
 
163
  # Convert PIL Image to OpenCV image
164
  img_cv2 = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
165
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
166
  # Perform prediction
167
  results = model.predict(
168
  source=img_cv2,
@@ -174,7 +147,7 @@ def predict_image(img, conf_threshold, iou_threshold):
174
  )
175
 
176
  detections = sv.Detections.from_ultralytics(results[0])
177
-
178
  # Annotate and display image
179
  for r in results:
180
  im_array = r.plot()
@@ -184,7 +157,7 @@ def predict_image(img, conf_threshold, iou_threshold):
184
  building_locations = []
185
  for i, box in enumerate(detections.xyxy): # Correct way to iterate through boxes
186
  # Extract bounding box coordinates and class
187
- # print(box)
188
  x_min, y_min, x_max, y_max = box # Access the first (and only) box
189
  class_id = int(detections.class_id[i]) # Get class ID as an integer
190
 
@@ -194,17 +167,11 @@ def predict_image(img, conf_threshold, iou_threshold):
194
  pixel_distance_x = x_center - IMAGE_WIDTH / 2
195
  pixel_distance_y = IMAGE_HEIGHT / 2 - y_center
196
 
197
- RELATIVE_ALTITUDE = float(relative_altitude)
198
- FOV_HORIZONTAL = float(extract_fov_info(exif_data)[['FOV']])
199
- FOV_VERTICAL = FOV_HORIZONTAL * (IMAGE_HEIGHT / IMAGE_WIDTH)
200
-
201
  ground_distance_x = calculate_ground_distance(RELATIVE_ALTITUDE, FOV_HORIZONTAL, IMAGE_WIDTH, pixel_distance_x)
202
  ground_distance_y = calculate_ground_distance(RELATIVE_ALTITUDE, FOV_VERTICAL, IMAGE_HEIGHT, pixel_distance_y)
203
 
204
- east_offset = ground_distance_x * np.cos(np.radians(gimbal_yaw_degree)) - ground_distance_y * np.sin(
205
- np.radians(gimbal_yaw_degree))
206
- north_offset = ground_distance_x * np.sin(np.radians(gimbal_yaw_degree)) + ground_distance_y * np.cos(
207
- np.radians(gimbal_yaw_degree))
208
 
209
  building_lat, building_lon = get_gps_coordinates(CAMERA_GPS[0], CAMERA_GPS[1], north_offset, east_offset)
210
  building_locations.append((building_lat, building_lon, class_id))
@@ -223,7 +190,7 @@ def predict_image(img, conf_threshold, iou_threshold):
223
 
224
  folium.Marker(
225
  location=(building_lat, building_lon),
226
- popup=f'Building {i + 1}: {building_status}',
227
  icon=folium.Icon(color='red' if class_id == 1 else 'green', icon='home')
228
  ).add_to(m)
229
 
@@ -231,7 +198,7 @@ def predict_image(img, conf_threshold, iou_threshold):
231
  m.save('temp_map.html')
232
  with open('temp_map.html', 'r') as f:
233
  folium_map_html = f.read()
234
-
235
  encoded_html = base64.b64encode(folium_map_html.encode()).decode('utf-8')
236
  data_url = f"data:text/html;base64,{encoded_html}"
237
 
 
3
  import PIL.Image as Image
4
  import numpy as np
5
  import cv2
6
+ from ultralytics import YOLOv10
7
+ from exiftool import ExifToolHelper
8
  from geopy.distance import geodesic
9
  import folium
10
  import base64
11
  import supervision as sv
12
  import os
 
13
 
14
  # Constants for image dimensions
15
  IMAGE_WIDTH = 4000
 
19
  model = YOLOv10("weights/best.pt")
20
 
21
  # Define the directory for saving uploaded images
22
+ UPLOAD_DIR = './uploads/' # Or any other directory within your project
23
  os.makedirs(UPLOAD_DIR, exist_ok=True)
24
 
25
+ # Debugging: Check exiftool path
26
+ exiftool_path = os.popen("which exiftool").read().strip()
27
+ print(f"ExifTool path: {exiftool_path}")
28
+
29
 
30
  # Function to calculate ground distance from pixel distance
31
  def calculate_ground_distance(altitude, fov_deg, image_dimension, pixel_distance):
 
33
  ground_distance = (2 * altitude * np.tan(fov_rad / 2)) * (pixel_distance / image_dimension)
34
  return ground_distance
35
 
 
36
  # Function to get GPS coordinates from offsets
37
  def get_gps_coordinates(lat, lon, north_offset, east_offset):
38
  new_location = geodesic(meters=north_offset).destination((lat, lon), 0)
39
  new_location = geodesic(meters=east_offset).destination(new_location, 90)
40
  return new_location.latitude, new_location.longitude
41
 
 
42
  def extract_xmp_metadata(xmp_data):
43
  # Parse the XMP data as an XML tree
44
  root = ET.fromstring(xmp_data)
 
59
 
60
  return relative_altitude, gimbal_yaw_degree, gimbal_pitch_degree
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  def save_image_with_metadata(img, img_path):
63
  # Convert PIL Image to a format that retains EXIF
64
  img_format = img.format or 'JPEG'
65
+
66
  # Save image to a temporary file to preserve metadata
67
  img.save(img_path, format=img_format)
68
 
 
86
  print(f"Gimbal Pitch Degree: {gimbal_pitch_degree}")
87
  else:
88
  print("XMP data not found in the image.")
89
+
90
  # Extract EXIF data
91
  exif_data = img.info.get("exif")
92
+ try:
93
+ xmp_data = img.info.get("xmp")
94
+ #print(xmp_data)
95
+ except:
96
+ print("error loading xmp data")
97
+ #print(exif_data)
 
 
 
 
 
 
 
 
98
 
99
  # Save the image with metadata
100
+ if exif_data:
101
+ img.save(img_path, exif=exif_data) # Save the image with its EXIF data
102
+ else:
103
+ img.save(img_path) # Save without EXIF data if not available
104
+
105
  # Convert PIL Image to OpenCV image
106
  img_cv2 = cv2.cvtColor(np.array(img), cv2.COLOR_RGB2BGR)
107
 
108
+ # Use ExifTool to extract metadata
109
+ metadata = {}
110
+ tag_list = [
111
+ "Composite:FOV",
112
+ "Composite:GPSLatitude",
113
+ "Composite:GPSLongitude",
114
+ "XMP:AbsoluteAltitude",
115
+ "XMP:RelativeAltitude",
116
+ "XMP:GimbalRollDegree",
117
+ "XMP:GimbalYawDegree",
118
+ "XMP:GimbalPitchDegree"
119
+ ]
120
+
121
+ rel_path = img_path.lstrip("./")
122
+ #print(rel_path)
123
+ with ExifToolHelper() as et:
124
+ for d in et.get_metadata(rel_path):
125
+ metadata.update({k: v for k, v in d.items() if k in tag_list})
126
+
127
+ # Extract necessary metadata
128
+ CAMERA_GPS = (metadata["Composite:GPSLatitude"], metadata["Composite:GPSLongitude"])
129
+ RELATIVE_ALTITUDE = float(relative_altitude)
130
+ GIMBAL_YAW_DEGREE = float(gimbal_yaw_degree)
131
+ FOV_HORIZONTAL = float(metadata["Composite:FOV"])
132
+ FOV_VERTICAL = FOV_HORIZONTAL * (IMAGE_HEIGHT / IMAGE_WIDTH)
133
+ #GIMBAL_PITCH_DEGREE = float(gimbal_pitch_degree)
134
+
135
+ # Convert degrees to radians
136
+ yaw_rad = np.radians(GIMBAL_YAW_DEGREE)
137
+ #pitch_rad = np.radians(GIMBAL_PITCH_DEGREE)
138
+
139
  # Perform prediction
140
  results = model.predict(
141
  source=img_cv2,
 
147
  )
148
 
149
  detections = sv.Detections.from_ultralytics(results[0])
150
+
151
  # Annotate and display image
152
  for r in results:
153
  im_array = r.plot()
 
157
  building_locations = []
158
  for i, box in enumerate(detections.xyxy): # Correct way to iterate through boxes
159
  # Extract bounding box coordinates and class
160
+ #print(box)
161
  x_min, y_min, x_max, y_max = box # Access the first (and only) box
162
  class_id = int(detections.class_id[i]) # Get class ID as an integer
163
 
 
167
  pixel_distance_x = x_center - IMAGE_WIDTH / 2
168
  pixel_distance_y = IMAGE_HEIGHT / 2 - y_center
169
 
 
 
 
 
170
  ground_distance_x = calculate_ground_distance(RELATIVE_ALTITUDE, FOV_HORIZONTAL, IMAGE_WIDTH, pixel_distance_x)
171
  ground_distance_y = calculate_ground_distance(RELATIVE_ALTITUDE, FOV_VERTICAL, IMAGE_HEIGHT, pixel_distance_y)
172
 
173
+ east_offset = ground_distance_x * np.cos(yaw_rad) - ground_distance_y * np.sin(yaw_rad)
174
+ north_offset = ground_distance_x * np.sin(yaw_rad) + ground_distance_y * np.cos(yaw_rad)
 
 
175
 
176
  building_lat, building_lon = get_gps_coordinates(CAMERA_GPS[0], CAMERA_GPS[1], north_offset, east_offset)
177
  building_locations.append((building_lat, building_lon, class_id))
 
190
 
191
  folium.Marker(
192
  location=(building_lat, building_lon),
193
+ popup=f'Building {i+1}: {building_status}',
194
  icon=folium.Icon(color='red' if class_id == 1 else 'green', icon='home')
195
  ).add_to(m)
196
 
 
198
  m.save('temp_map.html')
199
  with open('temp_map.html', 'r') as f:
200
  folium_map_html = f.read()
201
+
202
  encoded_html = base64.b64encode(folium_map_html.encode()).decode('utf-8')
203
  data_url = f"data:text/html;base64,{encoded_html}"
204