Skip to content

Instantly share code, notes, and snippets.

@soswow
Created May 25, 2025 02:53
Show Gist options
  • Save soswow/29b488cd33c1a563ac14ceee56a6cbcc to your computer and use it in GitHub Desktop.
Save soswow/29b488cd33c1a563ac14ceee56a6cbcc to your computer and use it in GitHub Desktop.
Script to find vertical lines in the middle of the image and convert location to a distance on sensor from center
import sys
import os
from PIL import Image, ImageDraw
def find_vertical_lines_by_gradient(image_path, gradient_threshold=10, min_width=1, max_width=7):
"""
Find vertical lines by detecting strong contrast changes in the middle row, scanning from center to left.
Returns a list of center X positions for each detected line.
min_width and max_width now refer to the actual pixel width of the detected line.
"""
img = Image.open(image_path).convert('L') # Convert to grayscale
width, height = img.size
middle_row = height // 2
pixels = img.load()
# Get the pixel values for the middle row
row = [pixels[x, middle_row] for x in range(width)]
# Compute the gradient (difference between neighbors)
center = width // 2
x = center
edge_positions = []
while x > 0:
diff = abs(row[x] - row[x-1])
if diff >= gradient_threshold:
edge_positions.append(x)
x -= 1
# Group close edges as a single line, using actual pixel width
edge_positions.sort()
lines = []
group = []
for pos in edge_positions:
if not group:
group = [pos]
elif pos - group[-1] <= max_width:
group.append(pos)
else:
group_width = group[-1] - group[0] + 1
if min_width <= group_width <= max_width:
center_x = (group[0] + group[-1]) / 2
lines.append(center_x)
group = [pos]
# Handle last group
if group:
group_width = group[-1] - group[0] + 1
if min_width <= group_width <= max_width:
center_x = (group[0] + group[-1]) / 2
lines.append(center_x)
return lines
def main():
if len(sys.argv) < 2:
print("Usage: python find_vertical_lines.py <image1.png> [image2.png ...]")
return
image_paths = sys.argv[1:]
DEBUG_IMAGE = True # Set to False to disable debug image output
sensor_width_px = 5496
sensor_width_mm = 35.9
sensor_middle_mm = sensor_width_mm / 2
px_to_mm = sensor_width_mm / sensor_width_px
results = {}
max_lines = 0
for image_path in image_paths:
lines = find_vertical_lines_by_gradient(image_path)
lines_mm = [sensor_middle_mm - (center_x * px_to_mm) for center_x in lines]
lines_mm.sort()
base = os.path.splitext(os.path.basename(image_path))[0]
results[base] = lines_mm
if len(lines_mm) > max_lines:
max_lines = len(lines_mm)
if DEBUG_IMAGE:
orig_img = Image.open(image_path).convert('RGB')
width, height = orig_img.size
debug_img = orig_img.copy()
draw = ImageDraw.Draw(debug_img)
for center_x in lines:
x = int(round(center_x))
draw.line([(x, 0), (x, height - 1)], fill=(255, 0, 0), width=1)
debug_img.save(f"{base}_lines.png")
print(f"Debug image with detected lines saved as {base}_lines.png")
# Write tab-separated CSV
bases = list(results.keys())
with open("lines_output.tsv", "w") as f:
f.write("\t".join(bases) + "\n")
for i in range(max_lines):
row = []
for base in bases:
if i < len(results[base]):
row.append(f"{results[base][i]:.2f}")
else:
row.append("")
f.write("\t".join(row) + "\n")
print("Tab-separated results written to lines_output.tsv")
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment