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.
Related
Referring this video by andrew ng
https://youtu.be/XuD4C8vJzEQ?list=PLkDaE6sCZn6Gl29AoE31iwdVwSG-KnDzF
From this video I conclude that for detecting vertical edges in an image there should be some BRIGHTER followed by DARKER regions starting from the left side, then only this [[1,0,-1],[1,0,-1],[1,0,-1]] will act as a vertical edge detector otherwise not.
Is my conclusion correct ?
and
Is the vice versa will also be true ?
If you think about the filter:
1 0 -1
1 0 -1
1 0 -1
you will see that it is just subtracting the pixels to the right from the pixels to the left at each location, i.e. finding the horizontal differences.
As such, it is capable of finding transitions from light to dark and dark to light, it's just that the differences will show up with an opposite sign (plus or minus). So, if you transition from a bright area on the left to a darker area on the right, you will have a large number (bright) minus a small number (dark) and the difference will be positive. Conversely, if you transition from a dark area on the left (small number) to a brighter area on the right (large number), you will end up with a negative difference.
Here is an example, just done in Terminal with ImageMagick. Start with this image:
Apply the filter you are talking about:
magick input.png -morphology convolve '3x3: 1,0,-1 1,0,-1 1,0,-1' result.png
And you can see it finds only dark-to-light edges.
If you want to detect edges from light to dark and dark to light, you need to either:
use a signed number (as opposed to unsigned) so you can hold negative results, or
add a "bias" to your convolution.
If your data was unsigned 8-bit, you could add a 50% bias by dividing all your current values by 2 and adding 127 before convolving, for example.
So, applying a bias, your filter now finds dark-to-light and light-to-dark edges:
magick input.png -define convolve:scale='50%!' -bias 50% -morphology convolve '3x3: 1,0,-1 1,0,-1 1,0,-1' result.png
If you now want to detect horizontal edges transitioning from light-to-dark, rotate the filter to this:
-1 -1 -1
0 0 0
1 1 1
And apply:
magick input.png -morphology convolve '3x3: -1,-1,-1 0,0,0 1,1,1' result.png
Or, if you want to find horizontal edges transitioning from dark-to-light, use:
1 1 1
0 0 0
-1 -1 -1
magick input.png -morphology convolve '3x3: 1,1,1 0,0,0 -1,-1,-1' result.png
And the same again, but with a bias so we can find both light-to-dark and dark-to-light transitions in one fell swoop:
magick image.png -define convolve:scale='50%!' -bias 50% -morphology convolve '3x3: -1,-1,-1 0,0,0 1,1,1' result.png
Anthony Thyssen provides more excellent information about convolution than you could ever hope to need in a very approachable style here.
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.
For every row in an image, I would like to find the first black (or first non-white) pixel in that row. For example, for an image like this:
I would expect output like:
0
1
0
Or something close to that that I can parse. I think there might be a way to do this with subimage-search but I don't quite know how. Any pointers?
You do NOT need subimage-search to achieve your goal. The problem can be reduced to text parsing.
1. Basics
Consider this: you can tell ImageMagick to convert any image to a textual representation, which holds the exact color information for each individual pixel. Example:
convert wizard: textwizard.txt
(wizard: is a builtin image available for all ImageMagick installations for testing purposes.)
Yes, it is that easy! This image "format" is requested by just adding a .txt suffix. Results:
# ImageMagick pixel enumeration: 480,640,255,srgb
0,0: (255,255,255) #FFFFFF white
1,0: (255,255,255) #FFFFFF white
2,0: (255,255,255) #FFFFFF white
[....]
47,638: (246,247,249) #F6F7F9 srgb(246,247,249)
48,638: (246,247,249) #F6F7F9 srgb(246,247,249)
47,639: (236,235,236) #ECEBEC srgb(236,235,236)
48,639: (230,228,218) #E6E4DA srgb(230,228,218)
[....]
476,639: (255,255,255) #FFFFFF white
477,639: (255,255,255) #FFFFFF white
478,639: (255,255,255) #FFFFFF white
479,639: (255,255,255) #FFFFFF white
If you look at the first line of the output, you'll notice that ImageMagick uses it to detail some special info about the image here:
# ImageMagick pixel enumeration: 480,640,255,srgb
It means:
the image is 480 pixels wide,
the image is 640 pixels high,
the image uses a range of 0-255 for color info per channel (that is equivalent to an 8-bit color depth),
the image is build in the sRGB color space
The other lines consist of 4 columns:
the first column in format (N,M) indicates the exact position of the respective pixels as (row_number,column_number). (The index for row and column numbers is zero-based -- row no. 1 is indicated as 0, no. 2 as 1.)
the other three columns, redundantly, each hold the exact same information, each in a different notation: the exact color value for the pixel given in column 1. (The last column will use a human-readable name if ImageMagick knows one for that color value...)
As a side note: you can use such a textual representation of the original image (with or without some extra modifications) to re-create a real image:
convert textwizard.txt wizard.jpg
2. Select a specific row
You should be aware that you can select a specific region of an image with the following syntax:
image.png[WIDTHxHEIGHT+X_OFFSET+Y_OFFSET]
So to select a specific row only, you can set HEIGHT as 1. To get any row completely, set X-OFFSET as 0. To get a specific row, set the Y-OFFSET accordingly.
In order to get the values (for the builtin wizard: image used above) for the row with index 47, we can do:
convert wizard:[640x1+0+47] row47.txt
cat row47.txt
# ImageMagick pixel enumeration: 480,1,255,srgb
0,0: (255,255,255) #FFFFFF white
1,0: (255,255,255) #FFFFFF white
2,0: (255,255,255) #FFFFFF white
[....]
428,0: (82,77,74) #524D4A srgb(82,77,74)
429,0: (169,167,168) #A9A7A8 srgb(169,167,168)
430,0: (232,231,228) #E8E7E4 srgb(232,231,228)
432,0: (246,247,249) #F6F7F9 srgb(246,247,249)
[....]
476,0: (255,255,255) #FFFFFF white
477,0: (255,255,255) #FFFFFF white
478,0: (255,255,255) #FFFFFF white
479,0: (255,255,255) #FFFFFF white
If you do not want the textual output in a file, but printed on the standard output channel, you can do this:
convert wizard:[480x1+0+47] txt:-
3. Stitching it all together
Based on above snippets of information, the approach that can be taken with this task is clear:
Loop through all pixel rows of the image.
Output each pixel's color value as text.
Look for the first non-white pixel and keep its location information.
4. Possible script (OS X, Linux, Unix)
Here is a main part of a Bash script that could be used:
# Define some image specific variables (width, height, ...)
image=${1}
number_of_columns=$(identify -format '%W' ${image})
width=${number_of_columns} # just an alias
number_of_rows=$(identify -format '%H' ${image})
height=${number_of_rows} # just an alias
max_of_indices=$(( ${height} -1 ))
# Loop through all rows and grep for first non-white pixel
for i in $(seq 0 ${max_of_indices}); do
echo -n "Row ${i} : " ;
convert ${image}[${width}x1+0+${i}] txt:- \
| grep -v enumeration \
| grep -v '#FFFFFF' -m 1 \
|| echo "All WHITE pixels in row!"
done
The -v white will de-select all lines which contain the string white.
The -m 1 parameter will return the maximum of 1 matches (i.e. the first match).
It will be slow, but it will work.
I would go with something like this using the built-in checkerboard pattern:
convert -size 100x100 pattern:checkerboard -auto-level board.png
#!/bin/bash
convert wizard: txt: | awk -F'[,: ]' '
/^#/ || /#FFFFFF/ {next}
!($2 in fb) {fb[$2]=$1}
END {r=$2;for(i=0;i<=r;i++){if(i in fb)print i,fb[i]; else print i,"-1"}}'
The -F[,: ] tells awk to split the words on the line by commas, colons or spaces - this helps me get at the row and column at the start of each line. The line with /^#/ skips the comment in the first line of ImageMagick text output and all lines that contain white or #FFFFFF.
Then, I have an array fb[] , indexed by image row, that holds the column of the first black pixel on each row. Each time I find a line with a row not in my array fb[], I save it in the array.
At the end, inside END{}, I run through fb[] printing all rows and indices of first black pixels in those rows. Note that I output -1 in place of any undefined elements (i.e. those with no non-white pixels) - thanks to #KurtPfeifle for the hint.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
Given RGB value's of all pixels of a image , how can we find the probability that the given pixel is of skin color and what percentage of the image is of skin color .
Noodling around on Google tells me that caucasian skin tones often, or maybe generally, or maybe sometimes conform to the following sort of rule:
Blue channel: 140-180
Green channel: Blue * 1.15
Red channel: Blue * 1.5
So, with that in mind, I made some colour swatches that correspond to that with ImageMagick, using this command line:
#!/bin/bash
for b in $(seq 140 5 180); do
g=$(echo "$b * 1.15/1" | bc)
r=$(echo "$b * 1.5/1" | bc)
convert -label "R:$r,G:$g,B:$b" -size 200x200 xc:"rgb($r,$g,$b)" miff:-
done | montage - -frame 5 -tile 3x swatches.png
And got this:
Ok, those look kind of reasonable, now I try to use those to detect skin tones, again with ImageMagick. For the moment, and just so you can see it, I am going to colour lime-green everthing I detect as a skin-tone, using this which is right in the middle of the tonal range identified above:
convert -fuzz 5% face1.jpg -fill lime -opaque "rgb(240,184,160)" out.jpg
Mmmm, not very good. Increase the fuzziness maybe?
Mmmm, still pretty rubbish - picking up only part of the skin and some of the white shirt collar. Different face maybe?
Ok, not bad at detecting him, although notice it completely fails to detect the right side of his face, however there are still a few problems as we can see from the pink cadillac:
and Miss Piggy below...
Maybe we can be a bit more targeted in our search, and, though I can't draw it in 3-D, I can explain in 2-D. Instead of targeting a single large circle (actually sphere in 3-D space) in the middle of our range, maybe we could target some smaller circles spread along our range and thereby include fewer extraneous colours... the magenta represents the degree of fuzz. So rather than this:
we could do this:
using this command:
convert -fuzz 13% face1.jpg -fill lime \
-opaque "rgb(219,168,146)" \
-opaque "rgb(219,168,146)" \
-opaque "rgb(255,198,172)" out.jpg
So, you can see it is pretty hard to find skin-tones just by using RGB values and I haven't even started to address different races, different lighting etc.
Another approach may be to use a different colourspace, such as HSL - Hue Saturation and Lightness. We are not so interested in Lightness because that is just a function of exposure, so we look for hues that match those of skin and some degree of saturation to avoid washed out colours. You can do that with ImageMagick like this:
#!/bin/bash
convert face1.jpg -colorspace hsl -separate \
\( -clone 0 -threshold 7% -negate +write h.png \) \
\( -clone 1 -threshold 30% +write s.png \) \
-delete 0-2 -evaluate-sequence min out.png
That says this... take the image face1.jpg and convert it to HSL colorspace, then separate the layers so we now have 3 images in our stack. image 0 is the Hue, image 1 is the Saturation and image 2 is the Lightness. Next line. Take the Hue layer and threshold it at 7% which means pinky-reds, invert it and save it (just so you can see it) as h.png. Next line. Take the Saturation layer, and say "any saturation over 30% is good enough for me", then save as file s.png. Next line. Delete the 3 original layers (HS&L) from the original image leaving just the thresholded Hue and thresholded Saturation layers. Now put these ontop of each other and choose whichever is the minimum and save that. The point is that either the Hue or the Saturation layer can be used to gate which pixels are selected.
Here are the files, first the Hue (h.png):
next the Saturation (s.png):
and now the combined output file.
Once you have got your algorithm sorted out for deciding which pixels are skin coloured, you will need to count them to work out the percentages you seek. That is pretty easy... all we do is change everything that is not lime-green to black (so it counts for zero in the averaging) and then resize the image to a single pixel and get its colour as text:
convert -fuzz 13% face1.jpg -fill lime \
-opaque "rgb(219,168,146)" \
-opaque "rgb(219,168,146)" \
-opaque "rgb(255,198,172)" \
-fill black +opaque lime -resize 1x1! txt:
# ImageMagick pixel enumeration: 1,1,255,srgb
0,0: (0,92,0) #005C00 srgb(0,92,0)
We can see there is, not surprisingly, no red and no blue and the average colour of the green pixels is 92/255, so 36% of pixels match our description of skin-toned.
If you want to get more sophisticated you may have to look at shapes, textures and contexts, or train a skin classifier and write a whole bunch of stuff in OpenCV or somesuch...
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