I'm trying to find the edges on satellite imagery. While I've tried using canny edge detection, I've found much better results by passing the imagery through a structured random forests model resulting in the grayscale image below.
The problem is that I need to have a vector of the lines as a final output (geojson, shp, etc.). I found this tool: https://pypi.org/project/ridge-detection/ . It seems to give the results I want but so far I've only been able to get an image as an output with red lines I'm trying to extract embedded in an image. Attached is the code I've tried for getting results with the ridge-detection package. Result of this package attached as second image.
So is there a way to extract the line information from a grayscale image as a vector segments?
image_path = "/content/drive/MyDrive/Edges_Test_Landsat.jpg"
from IPython.display import Image, display
import matplotlib.pyplot as plt
from ridge_detection.lineDetector import LineDetector
from ridge_detection.params import Params
from ridge_detection.helper import displayContours, save_to_disk
from datetime import datetime
from PIL import Image
from mrcfile import open as mrcfile_open
# Parameters for using the ridge-detection package
params_dict = {
"path_to_file": image_path,
"mandatory_parameters": {
"Sigma": 3.39,
"Lower_Threshold": 0.5,
"Upper_Threshold": 1.02,
"Maximum_Line_Length": 0,
"Minimum_Line_Length": 150,
"Darkline": "LIGHT",
"Overlap_resolution": "NONE"
},
"optional_parameters": {
"Line_width": 10.0,
"High_contrast": 200,
"Low_contrast": 80
},
"further_options": {
"Correct_position": True,
"Estimate_width": True,
"doExtendLine": True,
"Show_junction_points": False,
"Show_IDs": False,
"Display_results": False,
"Preview": False,
"Make_Binary": True,
"save_on_disk": False
}
}
params = Params(params_dict)
try:
img = mrcfile_open(params_dict["path_to_file"]).data
except ValueError:
img = Image.open(params_dict["path_to_file"])
# Create LineDetector instance
detect = LineDetector(params=params)
# Run ridge detection on the image
result = detect.detectLines(img)
resultJunction = detect.junctions
# Display contours (I think this is where it's taking the line segments and attaching them back into an image)
out_img, img_only_lines = displayContours(params, result, resultJunction)
#Plot the display
plt.figure(figsize=(15, 15))
plt.imshow(img_only_lines)
plt.axis('off')
plt.title('Detected Lines')
plt.show()
For future users that might encounter this issue, I was able to figure this out.
In the code shared in my original question, I was able to get an image with the lines I wanted embedded in the image. I figured those lines must be accessible somewhere and after much digging through the documentation and functions themselves I found that the result variable contained x,y coordinate information about the lines. I used this code to get them out into a vector format.
import matplotlib.pyplot as plt
import cv2
import numpy as np
# Read the original image
original_image = image
# Determine the height of the image
height = original_image.shape[0]
# Display the original image
plt.imshow(cv2.cvtColor(original_image, cv2.COLOR_BGR2RGB))
# Iterate through each line object in the result list
for line_object in result:
x_coords = line_object.col
y_coords = line_object.row
# Plot the rotated line
plt.plot(x_coords, y_coords, color='red')
# Add labels and title
plt.axis('off')
plt.title('Lines Overlay on Original Image')
# Show the plot
plt.show()
Ended up being a pretty simple fix, just took a while for me to find it.