Skip to content

Commit

Permalink
test_copy_geo_exiftool success once
Browse files Browse the repository at this point in the history
  • Loading branch information
cnpcshangbo committed Feb 29, 2024
1 parent eac705c commit a3e4e97
Show file tree
Hide file tree
Showing 11 changed files with 256 additions and 71 deletions.
Binary file added TestImages/S1078749.JPG_original
Binary file not shown.
Binary file added TestImages/S1078749_ori.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added TestImages/raw/S1078749.JPG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
51 changes: 51 additions & 0 deletions TestImages/raw/print_gps_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
#!/home/roboticslab/Developer/image2overlays/.conda/bin/python
from PIL import Image
import piexif

# Load the image from file path
jpg_image_path = '/home/roboticslab/Developer/image2overlays/TestImages/raw/S1078749.JPG'

# Open the image using PIL
jpg_image = Image.open(jpg_image_path)

# Extract EXIF data
exif_data = piexif.load(jpg_image.info['exif'])

# GPS information is in the 'GPSInfo' field of the EXIF data
gps_info = exif_data.get('GPSInfo')

# Define a helper function to convert GPS coordinates to a human-readable format
def convert_to_degrees(value):
"""Convert GPS coordinates stored in the EXIF to degrees"""
d, m, s = value
return d + (m / 60.0) + (s / 3600.0)

# Check if GPSInfo tag is present
if gps_info:
# Extract latitude and longitude in EXIF's specific format
latitude = gps_info.get(2)
longitude = gps_info.get(4)

# Convert latitude and longitude to degrees if they are present
if latitude and longitude:
lat_degrees = convert_to_degrees(latitude)
long_degrees = convert_to_degrees(longitude)

# Determine the sign based on the reference value
lat_ref = gps_info.get(1)
long_ref = gps_info.get(3)

if lat_ref == 'S':
lat_degrees = -lat_degrees
if long_ref == 'W':
long_degrees = -long_degrees

# Print out the geolocation information
geolocation = {
'Latitude': lat_degrees,
'Longitude': long_degrees
}
else:
geolocation = "No geolocation information found."

print(geolocation)
10 changes: 5 additions & 5 deletions config.ini
Original file line number Diff line number Diff line change
@@ -1,25 +1,25 @@
[Settings]
image_path = /home/roboticslab/Downloads/2d_up_9_to_8_test/images
output_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-26_23-06-36
output_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-28_21-23-17
output_dir_message = output_directory is auto-generated.

[CrackSegmentation]
config = config_bo.json
model = saved/UperNet/01-17_12-55/best_model.pth
mask_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-26_23-06-36/crackmask
mask_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-28_21-23-17/crackmask
mask_dir_message = mask_directory is auto-generated.

[StainSegmentation]
config = config_stain.json
model = saved/StainNet/2024-02-26_00-05/best_model.pth
mask_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-26_23-06-36/stainmask
mask_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-28_21-23-17/stainmask
mask_dir_message = mask_directory is auto-generated.

[CrackOverlay]
overlay_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-26_23-06-36/crackoverlay/images
overlay_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-28_21-23-17/crackoverlay/images
overlay_dir_message = overlay_directory is auto-generated.

[StainOverlay]
overlay_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-26_23-06-36/stainoverlay/images
overlay_directory = /home/roboticslab/Downloads/2d_up_9_to_8_test/images_out/2024-02-28_21-23-17/stainoverlay/images
overlay_dir_message = overlay_directory is auto-generated.

33 changes: 33 additions & 0 deletions copy_geo_exiftool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/home/roboticslab/Developer/image2overlays/.conda/bin/python
import subprocess
import json

def get_geolocation_with_exiftool(image_path):
command = ["exiftool", "-json", "-GPSLatitude", "-GPSLongitude", "-GPSAltitude", image_path]
process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
output = process.stdout

try:
data = json.loads(output)[0]
return data.get('GPSLatitude'), data.get('GPSLongitude'), data.get('GPSAltitude')
except json.JSONDecodeError:
return None, None, None

def write_geolocation_with_exiftool(dest_image, latitude, longitude, altitude):
command = [
"exiftool",
f"-GPSLatitude={latitude}",
f"-GPSLongitude={longitude}",
f"-GPSAltitude={altitude}",
dest_image
]
subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)

def process_single_image(raw_image_path, mask_image_path):
lat, lon, alt = get_geolocation_with_exiftool(raw_image_path)

if lat and lon and alt:
write_geolocation_with_exiftool(mask_image_path, lat, lon, alt)
print(f"Geolocation information copied from {raw_image_path} to {mask_image_path}.")
else:
print("No geolocation information found in the source image.")
60 changes: 24 additions & 36 deletions copy_geolocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,36 @@
from PIL import Image


def main(raw_images_dir=None, mask_images_dir=None):
# If directories are not provided directly, parse them from command-line arguments
if raw_images_dir is None or mask_images_dir is None:
parser = argparse.ArgumentParser(
description="Copy geolocation data from raw images to mask images."
)
parser.add_argument(
"raw_images_dir",
type=str,
help="Path to the directory containing the raw images.",
)
parser.add_argument(
"mask_images_dir",
type=str,
help="Path to the directory containing the mask images.",
)

# Parse command-line arguments
args = parser.parse_args()
raw_images_dir = args.raw_images_dir
mask_images_dir = args.mask_images_dir

# Loop through raw images
for raw_image_name in os.listdir(raw_images_dir):
raw_image_path = os.path.join(raw_images_dir, raw_image_name)
def process_single_image(raw_image_path, mask_image_path):
"""Copies geolocation (EXIF) data from a raw image to a corresponding mask image.
# Assuming mask images have a similar naming convention
mask_image_name = raw_image_name.replace(
"raw", "mask"
) # Example transformation
mask_image_path = os.path.join(mask_images_dir, mask_image_name)
Args:
raw_image_path (str): Path to the raw image file.
mask_image_path (str): Path to the mask image file.
"""

if os.path.exists(mask_image_path):
# Extract EXIF data from the raw image
if os.path.exists(mask_image_path):
try:
with Image.open(raw_image_path) as raw_image:
exif_data = piexif.load(raw_image.info.get("exif", {}))

# Load the mask image
with Image.open(mask_image_path) as mask_image:
mask_image.save(mask_image_path, "JPEG", exif=piexif.dump(exif_data))
# Handle cases where no EXIF data exists
if exif_data:
with Image.open(mask_image_path) as mask_image:
mask_image.save(mask_image_path, "JPEG", exif=piexif.dump(exif_data))
print(f"Geolocation copied to: {mask_image_path}")

except (piexif.InvalidImageDataError, OSError) as e:
print(f"Error processing images: {e}")

def main(raw_images_dir=None, mask_images_dir=None):
# ... (argument parsing - same as before) ...

for raw_image_name in os.listdir(raw_images_dir):
raw_image_path = os.path.join(raw_images_dir, raw_image_name)
# ... (construct mask_image_path - same as before) ...

print("Geolocation information copied to mask images.")
process_single_image(raw_image_path, mask_image_path)


if __name__ == "__main__":
Expand Down
23 changes: 23 additions & 0 deletions copy_geolocation_crack.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!~/anaconda3/bin/python
#Copy geolocation data to crack overlay images
import configparser
import subprocess
from main import log_message, call_in_conda_env

# Load the configuration file
config = configparser.ConfigParser()
config.read('config.ini')

# Extract image_path and mask_directory from the config file
image_path = config['Settings']['image_path']
# Assuming you want to use CrackSegmentation's mask_directory for this example
mask_directory = config['CrackSegmentation']['mask_directory']

# The path to your copy_geolocation.py script
copy_geolocation_script_path = 'copy_geolocation.py'

# Call copy_geolocation.py with the extracted paths
# subprocess.run(['python', copy_geolocation_script_path, image_path, mask_directory], check=True)
call_in_conda_env(f"python {copy_geolocation_script_path} {image_path} {mask_directory}", "/home/roboticslab/Developer/image2overlays/.conda")

log_message("Copying geolocation info to stain overlay...")
72 changes: 42 additions & 30 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,38 +10,50 @@ def log_message(message):
timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
print(f"{timestamp}: {message}")

def call_in_conda_env(script_command):
# Define the Conda environment name
conda_env_name = "visualinspection113"
def call_in_conda_env(script_command, conda_env_name="visualinspection113"):
"""
Executes a given script command within a specified Conda environment.
Parameters:
- script_command: The command to run within the Conda environment.
- conda_env_name: Optional. The name of the Conda environment to activate. Defaults to "visualinspection113".
"""
# Construct the command to run the script within the Conda environment
command = f"/bin/bash -c 'source /home/roboticslab/anaconda3/bin/activate {conda_env_name} && {script_command}'"
# Execute the command
subprocess.call(command, shell=True)

# Replace your existing subprocess.call lines with call_in_conda_env function calls

# Create output folder, run_timestamp subfolder
log_message("Create output folder, run_timestamp subfolder.")
call_in_conda_env("python CreateRunTimestampDirectory.py")

# UpdateRawMaskOverlayConfigs.py
log_message("Create raw, mask, overlay, mvs folders in run_timestamp folder.")
call_in_conda_env("python UpdateRawMaskOverlayConfigs.py")

if process_crack:
# Run crack related processing
log_message("Running crack segmentation...")
call_in_conda_env("python cracksegmentation.py")

log_message("Running crack overlay...")
call_in_conda_env("python crackoverlay.py")

if process_stain:
# Run stain related processing
log_message("Running stain segmentation...")
call_in_conda_env("python stainsegmentation.py")

log_message("Running stain overlay...")
call_in_conda_env("python stainoverlay.py")

log_message("Script sequence completed.")
def main():
# Replace your existing subprocess.call lines with call_in_conda_env function calls

# Create output folder, run_timestamp subfolder
log_message("Create output folder, run_timestamp subfolder.")
call_in_conda_env("python CreateRunTimestampDirectory.py")

# UpdateRawMaskOverlayConfigs.py
log_message("Create raw, mask, overlay, mvs folders in run_timestamp folder.")
call_in_conda_env("python UpdateRawMaskOverlayConfigs.py")

if process_crack:
# Run crack related processing
log_message("Running crack segmentation...")
call_in_conda_env("python cracksegmentation.py")

log_message("Running crack overlay...")
call_in_conda_env("python crackoverlay.py")

if process_stain:
# Run stain related processing
log_message("Running stain segmentation...")
call_in_conda_env("python stainsegmentation.py")

log_message("Running stain overlay...")
call_in_conda_env("python stainoverlay.py")

log_message("Copying geolocation info to crack overlay...")
call_in_conda_env("python copy_geolocation_crack.py")

log_message("Script sequence completed.")

if __name__ == "__main__":
main()
52 changes: 52 additions & 0 deletions test_copy_geo_exiftool.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import unittest
import subprocess
import json
import os
from copy_geo_exiftool import process_single_image

class TestImageProcessing(unittest.TestCase):

@classmethod
def setUpClass(cls):
# Backup the original mask image
cls.test_raw_image_path = '/home/roboticslab/Developer/image2overlays/TestImages/raw/S1078749.JPG'
cls.test_mask_image_path = '/home/roboticslab/Developer/image2overlays/TestImages/S1078749.JPG'
cls.backup_mask_image_path = f"{cls.test_mask_image_path}_original"
if not os.path.exists(cls.backup_mask_image_path):
os.rename(cls.test_mask_image_path, cls.backup_mask_image_path)

def get_geolocation_with_exiftool(self, image_path):
# Run exiftool and parse latitude, longitude, and altitude
command = ["exiftool", "-json", "-GPSLatitude", "-GPSLongitude", "-GPSAltitude", image_path]
process = subprocess.run(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
output = process.stdout
data = json.loads(output)[0]
return data.get('GPSLatitude'), data.get('GPSLongitude'), data.get('GPSAltitude')

def test_process_single_image(self):
# Call the function with the test paths
process_single_image(self.test_raw_image_path, self.test_mask_image_path)

# Get the geolocation data from the mask image after processing
lat, lon, alt = self.get_geolocation_with_exiftool(self.test_mask_image_path)

# Assert that the geolocation data is not None (indicating that it's been written)
self.assertIsNotNone(lat, "Latitude was not written to the mask image")
self.assertIsNotNone(lon, "Longitude was not written to the mask image")
self.assertIsNotNone(alt, "Altitude was not written to the mask image")

# Additional assertions can be added to compare with expected values
# For example:
# self.assertEqual(lat, expected_lat)
# self.assertEqual(lon, expected_lon)
# self.assertEqual(alt, expected_alt)

@classmethod
def tearDownClass(cls):
# Clean up: Restore the original state of the mask image
if os.path.exists(cls.backup_mask_image_path):
os.remove(cls.test_mask_image_path)
os.rename(cls.backup_mask_image_path, cls.test_mask_image_path)

if __name__ == '__main__':
unittest.main()
26 changes: 26 additions & 0 deletions test_copy_geolocation.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,32 @@ def setUp(self):

exif_bytes = piexif.dump(exif_dict)
piexif.insert(exif_bytes, raw_image_path)

# Assuming you have an image file at this path
image_path = raw_image_path
print(image_path)
# Load the image
img = Image.open(image_path)

# Prepare dummy GPS data
# Format: (degrees, minutes, seconds)
# Note: For simplicity, using (10, 0, 0.0) for latitude and (20, 0, 0.0) for longitude as an example
gps_ifd = {
piexif.GPSIFD.GPSLatitudeRef: b'N',
piexif.GPSIFD.GPSLatitude: [(10, 1), (0, 1), (0, 1)],
piexif.GPSIFD.GPSLongitudeRef: b'E',
piexif.GPSIFD.GPSLongitude: [(20, 1), (0, 1), (0, 1)]
}

# Create an EXIF data structure, inserting the GPS data
exif_dict = {"GPS": gps_ifd}
exif_bytes = piexif.dump(exif_dict)

# Insert the EXIF data back into the image
img.save(image_path, "jpeg", exif=exif_bytes)

# Close the image file
img.close()

def test_geolocation_copy(self):
# Run the script
Expand Down

0 comments on commit a3e4e97

Please sign in to comment.