Как определить лазерную линию в изображении с помощью Python

Какой самый быстрый и надежный способ обнаружения горизонтальной красной лазерной линии в изображении с помощью Python? Я работаю над небольшим проектом, связанным с 3D-лазерным сканированием, и мне нужно иметь возможность обнаруживать лазер в изображении, чтобы рассчитать расстояние от его искажения.

Для начала, у меня есть два изображения, опорное изображение А известно, не содержат лазерной линии, и изображение B, который, безусловно, содержит лазерную линию, возможно, искаженное. например.

Пример изображения A:

введите описание изображения здесь

Пример изображения B:

введите описание изображения здесь

Так как это RGB, но лазер красный, я удаляю некоторый шум, удаляя синие и зеленые каналы, используя эту функцию:

from PIL import Image
import numpy as np

def only_red(im):
    """
    Strips out everything except red.
    """
    data = np.array(im)
    red, green, blue, alpha = data.T
    im2 = Image.fromarray(red.T)
    return im2

Это дает мне следующие изображения:

введите описание изображения здесь

введите описание изображения здесь

Затем я пытаюсь устранить больше шума, используя разницу этих двух изображений с помощью PIL.ImageChops.difference(). В идеальном случае экспозиция между двумя изображениями будет одинаковой, в результате чего разница не будет содержать ничего, кроме лазерной линии. К сожалению, поскольку лазер добавляет свет, экспозиция и общая яркость каждого изображения существенно различаются, что приводит к различию, которое по-прежнему имеет значительный шум. например.

введите описание изображения здесь

Мой последний шаг - сделать "лучшее предположение" относительно того, где находится линия. Поскольку я знаю, что линия будет грубо горизонтальной, а лазерная линия должна быть самой яркой на изображении, я просматриваю каждый столбец и нахожу строку с самым ярким пикселем, который, как я полагаю, является лазерной линией. Код для этого:

import os
from PIL import Image, ImageOps
import numpy as np

x = Image.open('laser-diff.png', 'r')
x = x.convert('L')

out = Image.new("L", x.size, "black")
pix = out.load()

y = np.asarray(x.getdata(), dtype=np.float64).reshape((x.size[1], x.size[0]))
print y.shape
for col_i in xrange(y.shape[1]):
    col_max = max([(y[row_i][col_i], row_i) for row_i in xrange(y.shape[0])])
    col_max_brightness, col_max_row = col_max
    print col_i, col_max
    pix[col_i, col_max_row] = 255

out.save('laser-line.png')

Все, что мне нужно для выполнения вычисления расстояния, - это массив значений col_max, но laser-line.png помогает мне визуализировать успех и выглядит так:

введите описание изображения здесь

Как вы можете видеть, оценка довольно близка, но у нее все еще есть некоторый шум, в основном на левой стороне изображения, где лазерная линия поглощается матовой черной отделкой.

Что я могу сделать, чтобы улучшить свою точность и/или скорость? Я пытаюсь запустить это на платформе ARM, такой как Raspberry Pi, поэтому я беспокоюсь, что мой код может быть слишком неэффективным, чтобы хорошо работать.

Я не полностью знаком с матричными функциями Numpy, поэтому мне пришлось успеть на медленный цикл, чтобы сканировать каждый столбец, а не что-то более эффективное. Есть ли быстрый способ найти строку с самым ярким пикселем на столбец в Numpy?

Кроме того, существует ли надежный способ выравнивания изображений перед выполнением разности без затемнения лазерной линии?

Ответ 1

Сначала вы можете, вероятно, перемасштабировать интенсивность своего негативного изображения, прежде чем вычесть его из своего положительного, чтобы удалить больше шума. Например, возможно, перемасштабирование по отношениям средней интенсивности может быть хорошей первой попыткой?

Вы также можете попробовать поставить порог: если ваш максимум ниже любого хорошего значения, то это, вероятно, не ваш лазер, а шумная точка...

Тогда yes numpy может найти лучшую строку /col с функцией argmax.

Ответ 2

Сначала введите цвет, который является лазером, и оставляет только красный цвет (в данном случае). Затем примените те же эффекты и проверьте результат.

В этом случае у вас будет гораздо менее загрязненный результат. Результат

Проблема возникает при анализе красного на двери, который был потерян.

Ответ 3

Я пытался что-то сделать. Я не думаю, что это абсолютно здорово. Но на вашем примере это работает относительно хорошо.

Я использовал определение canny edge для обнаружения края в вашем "разностном" изображении. А затем применил преобразование линии Hough, как в этот учебник. Поэтому я начал с обработанного изображения (которое я называю lineDetection.jpg в коде).

enter image description here

Вот окончательный script

import cv2
import numpy as np

img = cv2.imread('lineDetection.jpg')
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
edges = cv2.Canny(gray,10,100)
minLineLength = 50
maxLineGap = 20
lines = cv2.HoughLinesP(edges,0.05,np.pi/5000,10,minLineLength,maxLineGap)
print(len(lines))
for i in range(len(lines)):
    x1,y1,x2,y2 = lines[i][0]
    cv2.line(img,(x1,y1),(x2,y2),(0,255,0),2)

cv2.imwrite('houghlines5.jpg',img)

В зеленой строке, обнаруженной на обработанном изображении. (Вы можете добавить его к исходному изображению для лучшего эффекта)

введите описание изображения здесь

Надеюсь, что это поможет.