I want to detect a laser line for an autonomous system.
My work till now: 1. I split the image in rgb channels 2. use only the red channel because of using a red laser line 3. get threshold value manually 4.searching the binary image for a value != 0
I can't threshold it manually for the use case of an automous system any ideas how to solve the problem ?
And only searching of the highest peak in an image isn't good enough because of incidence of sunlight.
Maybe I can search for short peaks.. Because in the region of the laser line the brightness increase fast and then decrease fast after the laser line.
How can I realize that in opencv?
Updated
Ok, I have had a look at your updated picture. My algorithm comes down to the following steps.
Find Brightest Column (i.e. laser line) in Image
Find Dark Gap in Brightest Column
Find Neighbouring Column That is Brightest in Gap in laser line
Step 1 - Find Brightest Column (i.e. laser line) in Image
The easiest way to do this is to squidge the image down so it is still its original width, but just one pixel high effectively averaging the pixels in each vertical column of the image. Then apply an -auto-level
to contrast stretch that to the full range of 0-255 and threshold it at 95% to find all columns that are within 5% of the brightest. Then look for pixels that have thresholded out to white (#ffffff). This is one line in ImageMagick, as follows:
convert http://i.stack.imgur.com/1P1zj.jpg -colorspace gray \
-resize x1! \
-auto-level \
-threshold 95% text: | grep -i ffffff
Output:
297,0: (255,255,255) #FFFFFF white
298,0: (255,255,255) #FFFFFF white
299,0: (255,255,255) #FFFFFF white
So, I now know that columns 297-299 are the the ones where the laser line is. Note that if the picture is slightly rotated, or the laser is not vertical, the bright column will be split across multiple columns. To counteract this, you could shrink the width of the image by a factor of two or three so that adjacent columns tend to get merged into one in the smaller image, then just multiply up the column by the shrink factor to find the original position.
That completes Step 1, but an alternative method follows before Step 2.
I split the image into columns 1 pixel wide with:
convert input.png -crop 1x +repage line%d.png
Now I find the brightest column (one with highest mean brightness) with:
for f in line*; do m=$(convert -format "%[fx:mean]" $f info:);echo $m:$f ;done | sort -g
which gives this
...
...
0.559298:line180.png
0.561051:line185.png
0.561337:line306.png
0.562527:line184.png
0.562939:line183.png
0.584523:line295.png
0.590632:line299.png
0.644543:line296.png
0.671116:line298.png
0.71122:line297.png <--- brightest column = 297
Step 2 - Find Dark Gap in Brightest Column
Now I take column 297 and auto-level it so the darkest part becomes zero and the lightest part becomes white, then I negate it.
convert line297.png -colorspace gray -auto-level -threshold 20% -negate txt:
...
0,100: (0,0,0) #000000 black
0,101: (0,0,0) #000000 black
0,102: (0,0,0) #000000 black
0,103: (0,0,0) #000000 black
0,104: (0,0,0) #000000 black
0,105: (0,0,0) #000000 black
0,106: (0,0,0) #000000 black
0,107: (0,0,0) #000000 black
0,108: (255,255,255) #FFFFFF white <- gap in laser line
0,109: (255,255,255) #FFFFFF white <- gap in laser line
0,110: (255,255,255) #FFFFFF white <- gap in laser line
0,111: (255,255,255) #FFFFFF white <- gap in laser line
0,112: (0,0,0) #000000 black
0,113: (0,0,0) #000000 black
...
0,478: (0,0,0) #000000 black
0,479: (0,0,0) #000000 black
Step 3 - Find Neighbouring Column That is Brightest in Gap in laser line
Now if I multiply this column with each of the columns either side of it, all parts of the other columns that are not in the gap in the laser line will become zero and all parts that are in the gap in the laser line will be multiplied and totalled up as I run through the columns either side of column 297.
So, I check columns 240 to 340, multiplying each column with the mask from the previous step and seeing which one is brightest in the gap in the laser line:
for i in {240..340} ;do n=$(convert line${i}.png mask.png -compose multiply -composite -format "%[mean]" info:);echo $n:$i ;done | sort -g
The output is as follows:
458.495:248
466.169:249
468.668:247
498.294:260
502.756:250
536.844:259
557.726:258
564.508:251
624.117:252
627.508:253 <--- column 253 is brightest
Then I can see that column 253 is the brightest in the area where the laser line is darkest. So the displaced line is in column 253.
I am sure this technique could be done fairly easily in opencv
.
Original Answer
I can tell you a way to do it, but not give you any code for opencv
as I tend to use ImageMagick. I split the image into a series of vertical images, each 1 pixel wide - i.e. single pixel columns. Then I get the average of the brightnesses in all columns and can immediately see the brightest column. It works pretty well, here is how I tested the algorithm:
Split image into single pixel columns
convert http://i.stack.imgur.com/vMiU1.jpg -crop 1x +repage line%04d.png
See what we got:
ls line*
line0000.png line0128.png line0256.png line0384.png line0512.png
line0001.png line0129.png line0257.png line0385.png line0513.png
...
line0126.png line0254.png line0382.png line0510.png line0638.png
line0127.png line0255.png line0383.png line0511.png line0639.png
Yes, 640 vertical lines. Check size of one...
identify line0639.png
line0639.png PNG 1x480 1x480+0+0 8-bit sRGB 1.33KB 0.000u 0:00.000
Yes, it's 1 pixel wide and 480 pixels high.
Now get mean brightness of all lines and sort by brightness:
for f in line*; do m=$(convert -format "%[fx:mean]" $f info:);echo $m:$f ;done | sort -g
Output
0.5151:line0103.png
0.521621:line0104.png
0.527829:line0360.png
0.54699:line0356.png
0.567822:line0355.png
0.752827:line0358.png <--- highest brightness
0.76616:line0357.png <--- highest brightness
Columns 357 and 358 seem to be readily identifiable as your answer.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With