imagemagick检测透明区域的坐标

use*_*779 2 transparency alpha ruby-on-rails imagemagick

我有一个PNG图像,包含透明区域,包含透明度的正方形/矩形区域.我想知道是否有某种方式我可以知道图像中这些透明区域的顶部,左侧,宽度,高度.

谢谢你的帮助

Mar*_*ell 5

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
Run Code Online (Sandbox Code Playgroud)

enter image description here

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)
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

enter image description here


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:

enter image description here

You can extract the alpha channel from an image with ImageMagick like this:

convert input.png -alpha extract -negate alpha.png
Run Code Online (Sandbox Code Playgroud)

which gives this, where white areas are transparent

enter image description here

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
Run Code Online (Sandbox Code Playgroud)

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:

enter image description here

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)
Run Code Online (Sandbox Code Playgroud)

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)
Run Code Online (Sandbox Code Playgroud)

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 Code Online (Sandbox Code Playgroud)

Run it like this:

bounds.rb input.png
Run Code Online (Sandbox Code Playgroud)