Find nearest point horizontal in imagemagick? - imagemagick

I'm trying to find the nearest point to the point (red in this case) in this image. In this image this output find the first line point from right
how I could do this
output
Please help me.

This looks fun! Let's dump the image to text using ImageMagick:
convert image.png txt:
# ImageMagick pixel enumeration: 337,218,65535,srgb
0,0: (65535,65535,65535) #FFFFFF white
1,0: (65535,65535,65535) #FFFFFF white
2,0: (65535,65535,65535) #FFFFFF white
3,0: (65535,65535,65535) #FFFFFF white
4,0: (65535,65535,65535) #FFFFFF white
...
...
221,79: (0,0,0) #000000 black
221,80: (0,0,0) #000000 black
221,81: (0,0,0) #000000 black
221,82: (0,0,0) #000000 black
...
...
Ok, now let's use awk to find all black pixels and print their (x,y) coordinates:
convert image.png txt: | awk -F'[,:]' '/black/{x=$1;y=$2;print x,y}'
221 79
221 80
221 81
221 82
221 83
221 84
...
...
Ok, now let's tell awk where the red pixel is by passing in rx (red x-coordinate) and ry (red y-coordinate). Then also, calculate the sum of the squares of the x-distance and y-distance from red to each black pixel. When it is less (i.e. nearer) than any seen so far, save the location. Print the nearest location at the end.
convert image.png txt: | awk -F'[,:]' -v rx=318 -v ry=127 '
BEGIN{m=999999}
/black/{
x=$1; y=$2; d2=(rx-x)*(rx-x)+(ry-y)*(ry-y)
if(d2<m){m=d2;xm=x;ym=y}
}
END{print xm,ym}'
277 127
So, that is the answer... (277,127). Let's check it by drawing a cyan circle there:
convert image.png -fill cyan -draw "circle 277,127 277,132" check.png
On re-reading the question, I note that you actually only want the horizontally closest point whereas my solution above caters for the general case in any direction. If you just want horizontal offset, and you know the horizontal line is at y-coordinate 127, you can just extract that specific row from the image and simplify things like this:
convert image.png -crop x1+0+127 txt: | awk -F'[,:]' -v rx=318 '
BEGIN{m=999999} /black/{x=$1;d=(rx-x)*(rx-x);if(d<m){m=d;xm=x}} END{print xm}'
277
If you don't like awk, you can just do it by eyeball...
convert image.png -crop x1+0+127 txt: | grep -E "black|red"
221,0: (0,0,0) #000000 black
277,0: (0,0,0) #000000 black <--- nearest black to red
314,0: (65535,0,0) #FF0000 red
315,0: (65535,0,0) #FF0000 red
316,0: (65535,0,0) #FF0000 red
317,0: (65535,0,0) #FF0000 red
318,0: (65535,0,0) #FF0000 red
319,0: (65535,0,0) #FF0000 red
320,0: (65535,0,0) #FF0000 red
How did I find the coordinates of the red pixel? I used ImageMagick's sub-image search looking for a red pixel like this:
compare -metric rmse -subimage-search -dissimilarity-threshold 1 image.png \( -size 1x1 xc:red \) null:
0 (0) # 317,121
Notes:
I just used the sum of the squares rather than the square root of the sum of the squares because it is computationally faster and the results are the same because it holds that if a>b, then a * a > b * b in this case.
I used slightly different rx and ry from those generated by the sub-image search because OP says he had the coordinates and the ones found by sub-image search don't find the exact centre of the rather large red blob, but instead the top-leftmost edge of the red blob.

Related

Find colored box in image and create a mask file

I would like to create a hotfolder for my motion camera, into which I can drop images marking areas, which should be excluded in motion recognition via a *pgm mask. On these images, there is a small area marked with a transparent box with a magenta colored outline. My aim is to replace this box and outline with a black solid box and the rest of the image with white. (Tried to post samples here, but not enough reputation to do so.)
I know how to do this "by foot" using gimp, but I cannot figure out a clever and simple way achieving this with imagemagick.
I tried googling for solutions with -trim and -virtual-pixel, but no luck. Any help would be appreciated.
I'll do this step-by-step so you can see the intermediate parts in case you are on Windows and bash doesn't work.
First, let's make make everything that is not within 10% of your magenta colour, namely rgb(225,75,130), into lime green:
magick source.jpg -fill lime -fuzz 10% +opaque "rgb(225,75,130)" result.png
Ok, now let's get the trim box - i.e. all the constant junk that ImageMagick could trim off to focus on the magenta bit.
magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info:
14x66+426+118
So your magenta box is 14x66pixels and located at offset 426,118 from the top-left. Now we want to get those in bash variables w,h,x,y. We need to change x and + into spaces using tr:
read w h x y < <(magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info: | tr 'x+' ' ')
If we print this we get:
echo $w, $h, $x, $y
14, 66, 426, 118
Now we want to draw a rectangle, but that needs top-left and bottom-right, so we need to do some maths:
((x1=x+w))
((y1=y+h))
Ok, now we can load the original image, make it fully white, then draw our black rectangle:
magick source.jpg -threshold -1 -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm
So, the whole thing boils down to:
#!/bin/bash
read w h x y < <(magick source.jpg -fill black -fuzz 10% +opaque "rgb(225,75,130)" -format '%#' info: | tr 'x+' ' ')
echo $w, $h, $x, $y
((x1=x+w))
((y1=y+h))
magick source.jpg -threshold -1 -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm
There are other (maybe more elegant) ways of doing it, using flood-fills and/or connected components but I didn't want it to rely on your magenta box being "watertight", i.e. not rely on the sides being continuous and complete.
Also, if the size of your images is known and constant, you can avoid reloading the original and making it white by thresholding like I do in the last line and just create a canvas of the known dimensions, i.e.:
magick -size ${W}x${H} xc:white -fill black -draw "rectangle $x,$y $x1,$y1" -depth 8 mask.pgm

ImageMagick count how many identical or almost identical color pixels are vertically by x coordinate

I have this image:
I need to count how many red pixels there are vertically on x 60 which would be the red line on the right containing 4 pixels.
I tried this:
img.png -alpha off -fuzz 2% -fill white -opaque "#FF0000" -fill black +opaque white -gravity west -crop 61x+0+0 -format "%[fx:round(mean*w*h)]\n" info:
but it also counts all the red pixels left to x 60
Try:
magick ... -crop 1x+61+0 ...
to crop a full-height, 1px wide column starting at 61,0.

Imagemagick - Inverse

I've a png image containing transparent pixels and colored pixels (mainly white).
I'd like to transform all transparent pixels to white pixels and all white pixels to transparent pixels within a given rectangle.
My idea would be to
convert the white pixels to red
the transparent colors to white
and the red colors to transparent
. Here' s my code:
1) convert ldl_0.png -fuzz 10% -fill red -opaque white lx.png
2) convert lx.png -background white -alpha remove -alpha off lx2.png
However I can' t figure out how do I transform red colors to transparent. How do I do that?
Also how can I force to do this only within a given rectangle?
Thank You.
-----
Try this in ImageMagick. Negate the alpha channel and turn the whole RGB channels to white.
convert in.png -channel a -negate +channel -fill white -colorize 100 out.png

Laser line detection opencv

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.

imagemagick detect coordinates of transparent areas

I have a PNG image with transparent areas, squares/rectangle areas that contains transparency.
I would like to know if there is some way that i can know the top,lef,width,height of theses transparent areas in the image.
Thanks for any help
Updated Answer
In the intervening years, I have come across a simpler solution, so I thought I would update it so anyone else seeing it can get the benefit of the latest and greatest.
Start off just the same, by extracting the alpha layer to an image of its own and invert it:
convert start.png -alpha extract -negate intermediate.png
Now perform a "Connected Component Analysis" on that:
convert start.png -alpha extract -negate \
-define connected-components:verbose=true \
-define connected-components:area-threshold=100 \
-connected-components 8 -auto-level result.png
Objects (id: bounding-box centroid area mean-color):
0: 256x256+0+0 128.7,130.4 62740 srgb(0,0,0)
3: 146x8+103+65 175.5,68.5 1168 srgb(255,255,255)
2: 9x93+29+42 33.0,88.0 837 srgb(255,255,255)
1: 113x7+4+21 60.0,24.0 791 srgb(255,255,255)
You will see there is a header line and 4 lines of output and each has a colour at the end, the first line is black, and corresponds to the entire shape, and the the last three are white, corresponding to the three transparent areas. It is basically the second field on each of the last three lines that you want. So, 146x8+103+65 means a box 146px wide by 103px tall offset 103px to the right of the top-left corner, and 65px down from the top-left corner.
If I draw those in, in red, you can see what it has identified:
convert result.png -stroke red -fill none -strokewidth 1 \
-draw "rectangle 103,65 249,73" \
-draw "rectangle 29,42 38,135" \
-draw "rectangle 4,21 117,28" result.png
Original Answer
The following may help you get to an answer but I have not developed it all the way through to completion - people often ask questions and then never log in again and there is quite a lot of effort involved...
Let's start with this input image - where the white areas are transparent:
You can extract the alpha channel from an image with ImageMagick like this:
convert input.png -alpha extract -negate alpha.png
which gives this, where white areas are transparent
Ok, one approach is to find the bounding box of the white areas, you can do this with trim and it will give you the bounding box that encloses the white areas:
convert input.png -alpha extract -format "%#" info:
245x114+4+21
So the bounding box is 245px wide and 114px high starting at offset +4+21 from top left. I can draw that on the image to show it:
So, that is a start.
Also, you can get ImageMagick to enumerate the pixels in text format, so you can run this command
convert input.png -alpha extract -negate txt: | more
# ImageMagick pixel enumeration: 256,256,255,gray
0,0: (0,0,0) #000000 gray(0)
1,0: (0,0,0) #000000 gray(0)
2,0: (0,0,0) #000000 gray(0)
which tells you that the image is 256x256 and that the first 3 pixels are all black. If you want the white ones (i.e. transparent ones) you can do this:
convert input.png -alpha extract -negate txt: | grep FFFFFF | more
4,21: (255,255,255) #FFFFFF gray(255)
5,21: (255,255,255) #FFFFFF gray(255)
6,21: (255,255,255) #FFFFFF gray(255)
7,21: (255,255,255) #FFFFFF gray(255)
This tells you that pixel 4,21 is the top left corner of your transparent area - I'm glad it matches the output from the bounding box method above :-)
So, you can easily get a list of all the pixels that are transparent. This approach could be developed, or something similar coded up in Ruby (RMagick) to find contiguous areas of black - but that is beyond the scope of this answer for the moment - as I am not a Ruby programmer :-)
Ok, I have learned some Ruby this afternoon and, no laughter please, this is my first Ruby program. It is probably pretty ugly and more like Perl or C (my preferred languages) but it works and finds the rectangular transparent areas.
#!/usr/bin/ruby
require 'RMagick'
include Magick
infile=ARGV[0]
img = ImageList.new(infile)
w=img.columns
h=img.rows
#Extract alpha channel into pixel array
px=img.export_pixels(0,0,w,h,"A")
for row in 0..h-1
for col in 0..w-1
thispx=px[w*row+col]
if thispx<32768 then
a=row
b=col
# Find extent (c) of rectangle towards right
for r in col..w-1
thispx=px[w*row+r]
if thispx<32768
c=r
else
break
end
end
# Find extent (d) of rectangle towards bottom
for s in row..h-1
thispx=px[w*s+col]
if thispx<32768
d=s
else
break
end
end
# Blank this rectangle as we have located it
for r in row..d
for s in col..c
px[w*r+s]=65535
end
end
# Tell caller about this rectangle
printf "%d,%d %d,%d\n",a,b,d,c
end
end
end
Run it like this:
bounds.rb input.png

Resources