Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Extracting a laser line in an image (using OpenCV)

i have a picture from a laser line and i would like to extract that line out of the image.

enter image description here

As the laser line is red, i take the red channel of the image and then searching for the highest intensity in every row:

enter image description here

The problem now is, that there are also some points which doesnt belong to the laser line (if you zoom into the second picture, you can see these points).

Does anyone have an idea for the next steps (to remove the single points and also to extract the lines)?

That was another approach to detect the line: First i blurred out that "black-white" line with a kernel, then i thinned(skeleton) that blurred line to a thin line, then i applied an OpenCV function to detect the line.. the result is in the below image:enter image description here

NEW:

Now i have another harder situation. I have to extract a green laser light.
The problem here is that the colour range of the laser line is wider and changing.
On some parts of the laser line the pixel just have high green component, while on other parts the pixel have high blue component as well. enter image description here

like image 573
Mirnyy Avatar asked Jun 06 '17 12:06

Mirnyy


2 Answers

Getting the highest value in every row will always output a value, instead of ignoring when the value isn't high enough. Consider using a threshold too, so that you can discard ones that aren't high enough.

However, that's not a very efficient way to do this at all. A much better and easier solution would be to use the OpenCV function inRange(); define a lower and upper bound for the red color in all three channels, and this will return a binary image with white pixels where the image intensity is within that BGR range.

This is in python but it does the job, should be easy to see how to use the function:

import cv2
import numpy as np

img = cv2.imread('image.png')
lowerb = np.array([0, 0, 120])
upperb = np.array([100, 100, 255])
red_line = cv2.inRange(img, lowerb, upperb)

cv2.imshow('red', red_line)
cv2.waitKey(0)

This produces the output: Red line binary iamge

This could be further processed by finding contours or other methods to turn the points into a nice curve.

like image 79
alkasm Avatar answered Oct 22 '22 01:10

alkasm


I'm really sorry for the short answer without any code, but I suggest you take contours and process them.

I dont know exact what you need, so here are two approaches for you:

  • just collect as much as possible contours on single line (use centers and try find straight line with smallest mean)

  • as first way, but trying heuristically combine separated lines.... it's much harder, but this may give you almost full laser line from image.

--

Some example for yours picture:

import cv2
import numpy as np
import math

img = cv2.imread('image.png')
hsv = cv2.cvtColor(img, cv2.COLOR_RGB2HSV)
# filtering red area of hue
redHueArea = 15
redRange = ((hsv[:, :, 0] + 360 + redHueArea) % 360) 
hsv[np.where((2 * redHueArea) > redRange)] = [0, 0, 0] 
# filtering by saturation
hsv[np.where(hsv[:, :, 1] < 95)] = [0, 0, 0]
# convert to rgb
rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2RGB)
# select only red grayscaled channel with low threshold  
gray = cv2.cvtColor(rgb, cv2.COLOR_RGB2GRAY)
gray = cv2.threshold(gray, 15, 255, cv2.THRESH_BINARY)[1]
# contours processing
(_, contours, _) = cv2.findContours(gray.copy(), cv2.RETR_LIST, 1)
for c in contours:
    area = cv2.contourArea(c)
    if area < 8: continue
    epsilon = 0.1 * cv2.arcLength(c, True) # tricky smoothing to a single line
    approx = cv2.approxPolyDP(c, epsilon, True)
    cv2.drawContours(img, [approx], -1, [255, 255, 255], -1)

cv2.imshow('result', img)
cv2.waitKey(0)

In your case it's work perfectly, but, as i already said, you will need to do much more work with contours.

enter image description here

like image 1
Green_Wizard Avatar answered Oct 22 '22 00:10

Green_Wizard