def check_task(image, hashes, RESOLUTION_THRESHOLD, BLURRINESS_THRESHOLD, under_res, too_blurry, hashing=True): if image not in hashes: img = cv2.imread(image, cv2.IMREAD_GRAYSCALE) # Find image size in pixels, if too low, move to under resolution folder. height, width = img.shape val = width * height if val < RESOLUTION_THRESHOLD: move(image, under_res + filename(image)) return # Find blurriness value, if too high, move to blurry folder. val = cv2.Laplacian(img, cv2.CV_64F).var() if val < BLURRINESS_THRESHOLD: move(image, too_blurry + filename(image)) return # Hash image. if hashing: hashes[image] = str(dhash(Image.open(image)))
def get_gps(data_dir, some_gps, good_gps, bad_gps, location, METRES_THR=500): files = glob("{}*.jpg".format(data_dir), recursive=True) images_with_gps = {} nothing = [] for image in tqdm(files): try: lat, lon, alt, bearing = get_exif_location(get_exif_data(image)) if lat and lon: # Not really good but there seem to be a lot of images that just say 0m, have to filter. if alt and alt != 0: images_with_gps[image] = { "lat": lat, "lon": lon, "alt": alt, "bearing": bearing } else: nothing.append(image) except Exception as e: nothing.append((image, e)) print("GPS FOUND FOR:", len(images_with_gps.keys())) print("NOT FOUND FOR:", len(nothing)) incorrect_items, correct_items = {}, {} for image in images_with_gps.keys(): distance = measure(images_with_gps[image]["lat"], images_with_gps[image]["lon"], location.latitude, location.longitude) if distance < METRES_THR: correct_items[image] = distance else: incorrect_items[image] = distance for item in incorrect_items.keys(): move(item, bad_gps + filename(item)) good_gps_to_save = {} some_gps_to_save = {} for item in correct_items.keys(): fname = filename(item) # If image has bearing tag. if images_with_gps[item]["bearing"]: move(item, good_gps + fname) good_gps_to_save[fname] = images_with_gps[item] else: move(item, some_gps + fname) some_gps_to_save[fname] = images_with_gps[item] with open("intermediate/gps_data_from_images.json", "w") as outfile: json.dump(good_gps_to_save, outfile, indent=4) with open("intermediate/some_gps_data_from_images.json", "w") as outfile: json.dump(some_gps_to_save, outfile, indent=4)
def remove_exif(some_gps, cleared_gps): images = glob(some_gps + "/*") # https://stackoverflow.com/questions/19786301/python-remove-exif-info-from-images for image in tqdm(images): try: photo = gpsphoto.GPSPhoto(image) photo.stripData(cleared_gps + filename(image)) except: print(image + "- FALLBACK TO FULL CLEAR") img = Image.open(image) data = list(img.getdata()) img_without_exif = Image.new(img.mode, img.size) img_without_exif.putdata(data) img_without_exif.save(cleared_gps + filename(image))
def select_and_copy_GPS_images(data_dir, good_gps, NUM_GPS_IMAGES, NUM_LARGEST_IMAGES, openMVG_images): # Load previous images used if the file exists. gps_images = [] if os.path.isfile("logs/images_for_georeferencing.json"): with open("logs/images_for_georeferencing.json", "r") as infile: gps_images = json.load(infile) # Sort possible GPS images by size (maximise ability to be included) and set number to be # added to minus what is already there. possible_gps = sorted(glob(good_gps + "*.jpg"), key=os.path.getsize, reverse=True) # Add only enough images to fill the quota, not removing those there. # If there aren't enough, all will be used. for image in possible_gps: if len(gps_images) >= NUM_GPS_IMAGES: break if image not in gps_images: gps_images.append(image) # Save the images for later. with open("logs/images_for_georeferencing.json", "w+") as outfile: json.dump(gps_images, outfile, indent=4) # Move images to folder for image in gps_images: copy(image, openMVG_images + filename(image)) # Sort by size and move the amount needed. sorted_by_size = sorted(glob(data_dir + "*.jpg"), key=os.path.getsize, reverse=True) images_used = [] if os.path.isfile("logs/images_used.json"): with open("logs/images_used.json", "r") as infile: images_used = json.load(infile) for image in sorted_by_size: if len(images_used) >= NUM_LARGEST_IMAGES: break if image not in images_used: images_used.append(image) copy(image, openMVG_images + filename(image)) with open("logs/images_used.json", "w+") as outfile: json.dump(images_used, outfile, indent=4)
def export_gps_to_file(georeference, output="openMVG/"): with open(georeference) as f: data = json.load(f) key_to_fname = { view["value"]["ptr_wrapper"]["data"]["id_pose"]: view["value"]["ptr_wrapper"]["data"]["filename"] for view in data["views"] } fname_to_gps = { key_to_fname[ext["key"]]: ext["value"]["center"] for ext in data["extrinsics"] } recovered = {} for key in fname_to_gps.keys(): x, y, z = fname_to_gps[key][0], fname_to_gps[key][1], fname_to_gps[ key][2] lat, lon, alt = ecef2lla(x, y, z) recovered[key] = {"lat": lat[0][0], "lon": lon[0][0], "alt": alt[0][0]} #print(key_to_filename[key], lat[0][0], lon[0][0]) no_ext = filename(georeference).split(".")[-2] with open(output + no_ext + "_positions.json", "w") as outfile: json.dump(recovered, outfile, indent=4)
def get_duplicate_images(path, duplicate, threshold=5): print("CALCULATING SIZE FOR EACH IMAGE FILE") sizes = {} images = glob(path + "*.jpg", recursive=True) for image in images: sizes[image] = Path(image).stat().st_size print("COMPUTING CLOSE IMAGES:") files = glob(path + "*.jpg", recursive=True) with open("logs/hashes.json", "r") as infile: hashes = json.load(infile) for key, hash in hashes.items(): hashes[key] = hex_to_hash(hash) close = [] from copy import deepcopy comparison = deepcopy(files) for image1 in tqdm(files): comparison = { path: hashes[path] - hashes[image1] for path in comparison if path != image1 } for image2, result in comparison.items(): if result <= threshold: try: # Chose the one with the smaller size to be moved. if sizes[image1] > sizes[image2]: if image2 not in close: close.append(image2) else: if image1 not in close: close.append(image1) except FileNotFoundError: # The offending file was likely moved in a previous pair continue comparison.pop(image1, None) print("MOVING FOUND ITEMS TO 'duplicates' FOLDER") for image in close: move(image, duplicate + filename(image)) return close
def get_accuracy(gps_data, sfm_geo_positions, sfm_expanded_positions, output=None, georeferencing="logs/images_for_georeferencing.json"): # Ground truth with open(gps_data, "r") as infile: actual = json.load(infile) # Position after localisation with open(sfm_expanded_positions, "r") as infile: localised = json.load(infile) # Getting difference to check which images were newly localised. with open(sfm_geo_positions, "r") as infile: geo_positions = json.load(infile) localised_images = [ img for img in localised.keys() if img not in geo_positions.keys() ] with open(georeferencing, "r") as infile: used_for_georeferencing = json.load(infile) used_for_georeferencing = [ filename(img) for img in used_for_georeferencing ] newly_localised = {} sum_error = 0 for key in localised.keys(): # Image not used for georeferencing but as the only images used for localisation were those with GPS cleared... The image has accurate GPS. if key in actual.keys( ) and key in localised_images and key not in used_for_georeferencing: lat1, lat2 = localised[key]["lat"], actual[key]["lat"] lon1, lon2 = localised[key]["lon"], actual[key]["lon"] lat_distance = lat1 - lat2 lon_distance = lon1 - lon2 # Only do altitude for images that have them if "alt" in localised[key]: alt1, alt2 = localised[key]["alt"], actual[key]["alt"] alt_distance = alt1 - alt2 metres_distance = measure(lat1, lon1, lat2, lon2) sum_error += metres_distance newly_localised[key] = { "actual": { "lat": lat2, "lon": lon2 }, "localised": { "lat": lat1, "lon": lon1 }, "change_in_coords": { "lat_change": lat_distance, "lon_change": lon_distance }, "metres_distance_from_actual": metres_distance } if "alt" in localised[key]: newly_localised[key]["actual"]["alt"] = alt2 newly_localised[key]["localised"]["alt"] = alt1 newly_localised[key]["change_in_coords"][ "alt_change"] = alt_distance newly_localised["sum_error"] = sum_error if output: json.dump(newly_localised, open(output, "w+"), indent=4)
def merge_reconstructions(a=None, b=None): reconstructions = glob("reconstructions/*") options = list(combinations(reconstructions, 2)) if not a or not b: option = None if options: while option not in range(-1, len(options)): for num, option in enumerate(options): short = (option[0].replace("reconstructions/", ""), option[1].replace("reconstructions/", "")) print(num, "-", short) print("-1 - EXIT DIALOG") try: option = int(input()) except: option = None if option == -1: return a, b = options[option][0], options[option][1] # Conveniences merge_name = filename(a) + "~" + filename(b) merge_name = merge_name.replace(" ", "_") # Create folders for merging. create_folder("reconstructions/" + merge_name) create_folder("reconstructions/" + merge_name + "/openMVG/") create_folder("reconstructions/" + merge_name + "/openMVG/" + "data/") create_folder("reconstructions/" + merge_name + "/openMVG/" + "localization_images/") create_folder("reconstructions/" + merge_name + "/openMVG/" + "some_gps_localization/") create_folder("reconstructions/" + merge_name + "/openMVG/" + "output/") create_folder("reconstructions/" + merge_name + "/intermediate/") create_folder("reconstructions/" + merge_name + "/logs/") # Copy features and descriptors for one part of the pair features, descriptors = glob(a + "/openMVG/data/*.feat"), glob( a + "/openMVG/data/*.desc") for feature, descriptor in zip(features, descriptors): filename1 = filename(feature) filename2 = filename(descriptor) copy(feature, "reconstructions/" + merge_name + "/openMVG/data/" + filename1) copy(descriptor, "reconstructions/" + merge_name + "/openMVG/data/" + filename2) # Copy features and descriptors for the second part of the pair features, descriptors = glob(b + "/openMVG/data/*.feat"), glob( b + "/openMVG/data/*.desc") for feature, descriptor in zip(features, descriptors): filename1 = filename(feature) filename2 = filename(descriptor) copy(feature, "reconstructions/" + merge_name + "/openMVG/data/" + filename1) copy(descriptor, "reconstructions/" + merge_name + "/openMVG/data/" + filename2) copy( a + "/openMVG/data/image_describer.json", "reconstructions/" + merge_name + "/openMVG/data/image_describer.json") # Copy over the localisation images from both reconstructions. localisation_one = glob(a + "/openMVG/localization_images/*") localisation_two = glob(b + "/openMVG/localization_images/*") for img in localisation_one: copy( img, "reconstructions/" + merge_name + "/openMVG/localization_images/" + filename(img)) for img in localisation_two: copy( img, "reconstructions/" + merge_name + "/openMVG/localization_images/" + filename(img)) some_gps_one = glob(a + "/openMVG/some_gps_localization/*") some_gps_two = glob(b + "/openMVG/some_gps_localization/*") for img in some_gps_one: copy( img, "reconstructions/" + merge_name + "/openMVG/some_gps_localization/" + filename(img)) for img in some_gps_two: copy( img, "reconstructions/" + merge_name + "/openMVG/some_gps_localization/" + filename(img)) with open(a + "/intermediate/gps_data_from_images.json", "r") as infile: gps1 = json.load(infile) with open(b + "/intermediate/gps_data_from_images.json", "r") as infile: gps2 = json.load(infile) merged_gps = {} for key, val in gps1.items(): merged_gps[key] = val for key, val in gps2.items(): merged_gps[key] = val with open( "reconstructions/" + merge_name + "/intermediate/gps_data_from_images.json", "w+") as outfile: json.dump(merged_gps, outfile, indent=4) with open(a + "/intermediate/some_gps_data_from_images.json", "r") as infile: gps1 = json.load(infile) with open(b + "/intermediate/some_gps_data_from_images.json", "r") as infile: gps2 = json.load(infile) merged_gps = {} for key, val in gps1.items(): merged_gps[key] = val for key, val in gps2.items(): merged_gps[key] = val with open( "reconstructions/" + merge_name + "/intermediate/some_gps_data_from_images.json", "w+") as outfile: json.dump(merged_gps, outfile, indent=4) # Merge the actual "sfm_data". from sfm_data import merge_sfm_data merge_sfm_data( a + "/openMVG/output/sfm_data_geo.json", b + "/openMVG/output/sfm_data_geo.json", "reconstructions/" + merge_name + "/openMVG/output/sfm_data_geo.json") # Localise images in reconstruction and make relevant files. commands = [ """ openMVG_main_ConvertSfM_DataFormat \ -i reconstructions/{}/openMVG/output/sfm_data_geo.json \ -o reconstructions/{}/openMVG/output/sfm_data_geo.bin \ """.format(merge_name, merge_name), """ openMVG_main_ConvertSfM_DataFormat \ -i reconstructions/{}/openMVG/output/sfm_data_geo.bin \ -o reconstructions/{}/openMVG/output/sfm_data_geo.ply \ """.format(merge_name, merge_name), """ openMVG_main_SfM_Localization \ -i reconstructions/{}/openMVG/output/sfm_data_geo.bin \ --match_dir reconstructions/{}/openMVG/data \ --out_dir reconstructions/{}/openMVG/localization_output \ --query_image_dir reconstructions/{}/openMVG/localization_images \ --numThreads {} """.format(merge_name, merge_name, merge_name, merge_name, cpu_count()), """ openMVG_main_SfM_Localization \ -i reconstructions/{}/openMVG/output/sfm_data_geo.bin \ --match_dir reconstructions/{}/openMVG/data \ --out_dir reconstructions/{}/openMVG/some_gps_localization_output \ --query_image_dir reconstructions/{}/openMVG/some_gps_localization/ \ --numThreads {} """.format(merge_name, merge_name, merge_name, merge_name, cpu_count()) ] for cmd in commands: os.system(cmd) from gps import export_gps_to_file export_gps_to_file(georeference="reconstructions/" + merge_name + "/openMVG/output/sfm_data_geo.json", output="reconstructions/" + merge_name + "/openMVG/") export_gps_to_file(georeference="reconstructions/" + merge_name + "/openMVG/localization_output/sfm_data_expanded.json", output="reconstructions/" + merge_name + "/openMVG/") export_gps_to_file( georeference="reconstructions/" + merge_name + "/openMVG/some_gps_localization_output/sfm_data_expanded.json", output="reconstructions/" + merge_name + "/openMVG/some_gps_localization_output/") with open(a + "/logs/images_for_georeferencing.json", "r") as infile: used_for_geo1 = json.load(infile) with open(b + "/logs/images_for_georeferencing.json", "r") as infile: used_for_geo2 = json.load(infile) with open( "reconstructions/" + merge_name + "/logs/images_for_georeferencing.json", "w+") as outfile: merged_used_for_geo = used_for_geo1 + used_for_geo2 json.dump(merged_used_for_geo, outfile, indent=4) from gps import get_accuracy get_accuracy("reconstructions/" + merge_name + "/intermediate/gps_data_from_images.json", "reconstructions/" + merge_name + "/openMVG/sfm_data_geo_positions.json", "reconstructions/" + merge_name + "/openMVG/sfm_data_expanded_positions.json", output="reconstructions/" + merge_name + "/openMVG/localised_accuracy.json", georeferencing="reconstructions/" + merge_name + "/logs/images_for_georeferencing.json") get_accuracy( "reconstructions/" + merge_name + "/intermediate/some_gps_data_from_images.json", "reconstructions/" + merge_name + "/openMVG/sfm_data_geo_positions.json", "reconstructions/" + merge_name + "/openMVG/some_gps_localization_output/sfm_data_expanded_positions.json", output="reconstructions/" + merge_name + "/openMVG/some_gps_localised_accuracy.json", georeferencing="reconstructions/" + merge_name + "/logs/images_for_georeferencing.json") from gps import convert_to_kml convert_to_kml(georeference="reconstructions/" + merge_name + "/openMVG/sfm_data_expanded_positions.json", output="reconstructions/" + merge_name + "/openMVG/positions.kml") # Compare accuracy of localisation between reconstructions. acc1 = a + "/openMVG/localised_accuracy.json" acc2 = b + "/openMVG/localised_accuracy.json" acc1 = json.load(open(acc1, "r")) acc2 = json.load(open(acc2, "r")) new_acc = json.load( open( "reconstructions/" + merge_name + "/openMVG/localised_accuracy.json", "r")) acc_changes = {} for img in new_acc.keys(): difference_accuracy = None if img != "sum_error": if img in acc1.keys(): difference_accuracy = acc1[img]["metres_distance_from_actual"] - \ new_acc[img]["metres_distance_from_actual"] if img in acc2.keys(): difference_accuracy = acc2[img]["metres_distance_from_actual"] - \ new_acc[img]["metres_distance_from_actual"] if difference_accuracy: acc_changes[img] = difference_accuracy acc_changes["sum"] = sum(acc_changes.values()) json.dump(acc_changes, open( "reconstructions/" + merge_name + "/accuracy_changes_from_merge.json", "w+"), indent=4)
def handle_choice(choice): if choice == 1: from download import links_from_flickr, download links = links_from_flickr(TOPIC) download(links, "intermediate/images/") elif choice == 2: from images import images_rename images_rename("intermediate/images/") commands = [ # Convert PNGs to JPGs. """mogrify -format jpg {}*.png;""".format("intermediate/images/"), # Remove PNG duplicates """rm {}/*.png;""".format("intermediate/images/"), # Check for faulty JPGs, if so, remove. """jpeginfo -cd {}*.jpg;""".format("intermediate/images/") ] for cmd in commands: os.system(cmd) elif choice == 3: from images import check_images, get_duplicate_images check_images("intermediate/images/", "intermediate/too_small/", "intermediate/too_blurry/", RESOLUTION_THRESHOLD=PIXEL_NUM_THRESHOLD, BLURRINESS_THRESHOLD=BLURRINESS_THRESHOLD) close_images = get_duplicate_images("intermediate/images/", "intermediate/duplicates/", threshold=CLOSE_IMAGE_THRESHOLD) print("IMAGES CLOSE BY HASH:") pprint(close_images) elif choice == 4: from gps import get_gps get_gps("intermediate/images/", "intermediate/some_gps/", "intermediate/good_gps/", "intermediate/bad_gps/", location, METRES_THR=METRES_RADIUS_THRESHOLD) from gps import remove_exif remove_exif("intermediate/good_gps/", "intermediate/cleared_gps/") remove_exif("intermediate/some_gps/", "intermediate/cleared_some_gps/") elif choice == 5: from gps import select_and_copy_GPS_images select_and_copy_GPS_images("intermediate/images/", "intermediate/good_gps/", NUM_GPS_IMAGES, NUM_LARGEST_IMAGES, "openMVG/images/") localization_images = glob("intermediate/good_gps/*.jpg") some_gps_images = glob("intermediate/some_gps/*.jpg") with open("logs/images_for_georeferencing.json", "r") as infile: used_for_georeferencing = json.load(infile) from fileio import copy not_used_for_georeferencing = [] for image in localization_images: if image not in used_for_georeferencing: copy("intermediate/cleared_gps/" + filename(image), "openMVG/localization_images/" + filename(image)) not_used_for_georeferencing.append(image) for image in some_gps_images: copy("intermediate/cleared_some_gps/" + filename(image), "openMVG/some_gps_localization/" + filename(image)) with open("logs/not_used_for_georeferencing.json", "w+") as outfile: json.dump(not_used_for_georeferencing, outfile, indent=4) elif choice == 6: commands = [ """ openMVG_main_SfMInit_ImageListing \ -i openMVG/images \ -d sensor_database.txt \ -o openMVG/init \ | tee logs/image_listing.txt """, """ openMVG_main_ComputeFeatures \ -i openMVG/init/sfm_data.json \ -o openMVG/data \ --describerMethod SIFT \ --describerPreset {} \ --numThreads {} """.format(DESCRIBER_PRESET, cpu_count()), """ openMVG_main_ComputeMatches \ -i openMVG/init/sfm_data.json \ -o openMVG/data/ \ --guided_matching 1 \ --force 1 \ | tee logs/matching.txt """ ] for cmd in commands: os.system(cmd) elif choice == 7: cmd = \ """ openMVG_main_IncrementalSfM \ -i openMVG/init/sfm_data.json \ -m openMVG/data \ -o openMVG/output \ --prior_usage 0 """ os.system(cmd) elif choice == 8: #from sfm_data import remove_images_from_reconstruction cmd = \ """ openMVG_main_ConvertSfM_DataFormat \ -i openMVG/output/sfm_data.bin \ -o openMVG/output/sfm_data.json """ os.system(cmd) LMeds_usage = "" while LMeds_usage not in ["y", "n"]: LMeds_usage = input( "Use of the OpenMVG LMeds model for georegistration [y/n]? ") if LMeds_usage == "y": LMeds_usage = "0" else: LMeds_usage = "1" commands = [ """ openMVG_main_ConvertSfM_DataFormat \ -i openMVG/output/sfm_data.json \ -o openMVG/output/sfm_data_modified.bin """, """ openMVG_main_geodesy_registration_to_gps_position \ -i openMVG/output/sfm_data_modified.bin \ -o openMVG/output/sfm_data_geo.bin \ -m {} \ | tee logs/georegistration.txt """.format(LMeds_usage), """ openMVG_main_ConvertSfM_DataFormat \ -i openMVG/output/sfm_data_geo.bin \ -o openMVG/output/sfm_data_geo.json """, """ openMVG_main_ConvertSfM_DataFormat \ -i openMVG/output/sfm_data_geo.bin \ -o openMVG/output/sfm_data_geo_cloudcompare_viewable.ply """ ] for cmd in commands: os.system(cmd) elif choice == 9: commands = [ """ openMVG_main_SfM_Localization \ -i openMVG/output/sfm_data_geo.bin \ --match_dir openMVG/data \ --out_dir openMVG/localization_output/ \ --query_image_dir openMVG/localization_images/ \ --numThreads {} """.format(cpu_count()), """ openMVG_main_SfM_Localization \ -i openMVG/output/sfm_data_geo.bin \ --match_dir openMVG/data \ --out_dir openMVG/some_gps_localization_output/ \ --query_image_dir openMVG/some_gps_localization/ \ --numThreads {} """.format(cpu_count()) ] for cmd in commands: os.system(cmd) from gps import export_gps_to_file, get_accuracy, convert_to_kml export_gps_to_file(georeference="openMVG/output/sfm_data_geo.json") # Accurate GPS images export_gps_to_file( georeference="openMVG/localization_output/sfm_data_expanded.json") get_accuracy("intermediate/gps_data_from_images.json", "openMVG/sfm_data_geo_positions.json", "openMVG/sfm_data_expanded_positions.json", output="openMVG/localised_accuracy.json") convert_to_kml(georeference="openMVG/sfm_data_expanded_positions.json", gps_data="openMVG/localised_accuracy.json") # Somewhat accurate GPS images export_gps_to_file( georeference= "openMVG/some_gps_localization_output/sfm_data_expanded.json", output="openMVG/some_gps_localization_output/") get_accuracy( "intermediate/some_gps_data_from_images.json", "openMVG/sfm_data_geo_positions.json", "openMVG/some_gps_localization_output/sfm_data_expanded_positions.json", output="openMVG/some_gps_localised_accuracy.json") convert_to_kml( georeference= "openMVG/some_gps_localization_output/sfm_data_expanded_positions.json", output="openMVG/some_gps_positions.kml", gps_data="openMVG/some_gps_localised_accuracy.json") elif choice == 10: from fileio import move, copy create_folder("reconstructions/" + GEO_TOPIC) move("openMVG", "reconstructions/" + GEO_TOPIC + "/") move("intermediate", "reconstructions/" + GEO_TOPIC + "/") move("logs", "reconstructions/" + GEO_TOPIC + "/") copy("./config.py", "reconstructions/" + GEO_TOPIC + "/config.py") elif choice == 11: from sfm_data import merge_reconstructions merge_reconstructions() elif choice == -1: exit(0)