How to detect a laser line in an image using Python
Question: What's the quickest most reliable method of detecting a roughly horizontal red
laser line in an image using Python? I'm working on a small project related to
3d laser scanning, and I need to be able to detect the laser in an image in
order to calculate distance from its distortion.

To start, I have two images, a reference image A known to contain no laser
line, and an image B that definitely contains a laser line, possibly
distorted. e.g.

Sample image A:

[![enter image description
here](http://i.stack.imgur.com/ZLPrA.jpg)](http://i.stack.imgur.com/ZLPrA.jpg)

Sample image B:

[![enter image description
here](http://i.stack.imgur.com/t5gkr.jpg)](http://i.stack.imgur.com/t5gkr.jpg)

Since these are RGB, but the laser is red, I remove some noise by stripping
out the blue and green channels using this function:

    
    
    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
    

That gets me these images:

[![enter image description
here](http://i.stack.imgur.com/08Lp0.jpg)](http://i.stack.imgur.com/08Lp0.jpg)

[![enter image description
here](http://i.stack.imgur.com/YRnYp.jpg)](http://i.stack.imgur.com/YRnYp.jpg)

Next, I try and eliminate more noise by taking the difference of these two
images using `PIL.ImageChops.difference()`. Ideally, the exposure between the
two images would be identical, causing the difference to contain nothing
except the laser line. Unfortunately, because the laser adds light, the
exposure and overall brightness of each image is significantly different,
resulting in a difference that still has considerable noise. e.g.

[![enter image description
here](http://i.stack.imgur.com/28BlP.jpg)](http://i.stack.imgur.com/28BlP.jpg)

My final step is to make a "best guess" as to where the line is. Since I know
the line will be roughly horizontal and the laser line should be the brightest
thing in the image, I scan each column and find the row with the brightest
pixel, which I assume to be the laser line. The code for this is:

    
    
    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')
    

All I really need to perform my distance calculation is the array of `col_max`
values, but the `laser-line.png` helps me visualize the success, and looks
like:

[![enter image description
here](http://i.stack.imgur.com/LdTNZ.jpg)](http://i.stack.imgur.com/LdTNZ.jpg)

As you can see, the estimate is pretty close, but it still has some noise,
mostly on the left-hand side of the image where the laser line is absorbed by
a matte black finish.

What can I do to improve my accuracy and/or speed? I'm trying to run this on
an ARM platform like the Raspberry Pi, so I'm worried my code might to be too
inefficient to run well.

I'm not fully familiar with Numpy's matrix functions, so I had to settle for a
slow for loop to scan each column instead of something more efficient. Is
there a fast way to find the row with the brightest pixel per column in Numpy?

Also, is there a reliable way to equalize the images prior to performing the
difference without dimming the laser line?


Answer: First you can probably rescale the intensity of your negative image before
subtracting it from your positive, to remove more noise. For example maybe
rescaling by the ratios of the average intesity might be a good first try?

You can also try to put a threshold: if your max in below whatever good value,
then it is probably not your laser but a noisy point...

Then yes numpy can find the best row / col with the argmax function.

