Extracting positive and negative shapes from a BitmapData

The first step in generating the "vector text effects" which I blogged about in my previous post is to grab a BitmapData of a character, then seperate the "characters components" into individual BitmapData instances.

So let's take a capital B :

The B only contains one "positive shape", that is, the "filled in" part. The B contains 3 "negative shapes" or the transparent bits. In this case we've got the surrounding area and the two "holes".

If we take a percent symbol "%"

We've got 3 positive shapes and 3 negative ones.

So how does one go about extracting these? I'm sure there are several approaches, I tried a few, this was the speediest of the ones I came up with.

Step 1 : Convert the captured BitmapData into one containing just two colors, a solid and a transparent. *Note:* this has the side effect that the extracted shapes have "pixelated" , "jagged" or "aliased" edges. For my purposes this is sufficient, a different approach would be needed to keep anti-aliasing.

Actionscript:
  1. public static function toMonoChrome(source:BitmapData,mono_color:uint=0xFF000000):BitmapData{
  2.     var bmd:BitmapData=source.clone();
  3.     bmd.threshold(bmd,bmd.rect,new Point(),">",0x00000000,mono_color);
  4.     return bmd;
  5. }

Step 2 : Create an "inverse" of the "monochrome" image:

Actionscript:
  1. //create the monochrome for extracting "positive shapes"
  2. var positive_two_color:BitmapData=new BitmapData(source_bmd.width,source_bmd.height,true,0x00);
  3. positive_two_color.draw(source_bmd);
  4. positive_two_color=BitmapDataUtil.toMonoChrome(positive_two_color,0xFFFF0000);
  5.  
  6. //create the monochrome for extracting "negative shapes"
  7. var temp:BitmapData=new BitmapData(source_bmd.width,source_bmd.height,true,0xFF0000FF);
  8. temp.draw(positive_two_color);
  9. var negative_two_color:BitmapData=new BitmapData(source_bmd.width,source_bmd.height,true,0xFFFF0000);
  10. negative_two_color.copyChannel(temp,positive_two_color.rect,new Point(), BitmapDataChannel.BLUE, BitmapDataChannel.ALPHA);

Again, I'm sure there are other ways to skin this cat. I created a BLUE "temp" BitmapData, then drew the "positive shape" on it. I then copy the BLUE Channel into the ALPHA channel of a RED BitmapData, which creates an image with inverted RED and ALPHA from the "positive shape".

Step 3 : Extract the "component" BitmapDatas from the positive and negative BitmapDatas. This requires a few steps:

Create a loop which:

  1. finds the first non transparent pixel more info
  2. Does a FloodFill with Blue on that pixel
  3. Create a new BitmapData, and copy the BLUE Channel into it, add this to the "found" items
  4. Remove the Blue from the original by using a transparent FloodFill

Do this for both the "positive" and inverse "negative" BitmapDatas and store the discovered "component shapes" in a vector or an array.

Here's my code for doing so:

Actionscript:
  1. protected static function extractShapesFromMonochrome(bmd:BitmapData):Vector.<BitmapData>{
  2.     var scan_y:uint=0;
  3.     var non_trans:Point;
  4.     var found:Vector.<BitmapData>=new Vector.<BitmapData>();
  5.     var copy_bmd:BitmapData;
  6.     while(scan_y<bmd.height){
  7.         non_trans=BitmapDataUtil.getFirstNonTransparentPixel(bmd,scan_y);
  8.         if(non_trans==null)return found;
  9.         bmd.floodFill(non_trans.x,non_trans.y,0xFF0000FF);//fill with blue
  10.         copy_bmd=new BitmapData(bmd.width,bmd.height,true,0xFFFF0000);
  11.         copy_bmd.copyChannel(bmd,bmd.rect,new Point(),BitmapDataChannel.BLUE , BitmapDataChannel.ALPHA);
  12.         bmd.floodFill(non_trans.x,non_trans.y,0x00000000);//fill with blue
  13.         found.push(copy_bmd);
  14.         scan_y=non_trans.y;
  15.     }
  16.     return found;
  17. }

That pretty much does it. The next step is to analyze the discovered shapes and generate vector points to work with... but that's another blog post.

Tags:

4 Responses to “Extracting positive and negative shapes from a BitmapData”

  1. Gaspy Says:

    I’d be very interested in the approach you use for vectorizing bitmaps.

  2. sakri Says:

    I’m gonna blog about a “simple shape extruder with the drawing API” next, then that should be followed by a post about the “vectorizing” process… hopefully sometime next week :)

  3. nodename » The Name of the Node: Image Fill with Spanning Trees Says:

    [...] used my logo ? (as in ?? “node”) as the input image, run Sakri Rosenstrom’s image segmentation algorithm on it, dropped 10,000 random points into the segments, and drawn the minimum spanning tree of each [...]

  4. Blair Says:

    About the ‘pixel’ ‘jagged’ edges …

    I think the ‘mask’ value might help….

    var mask = 0x00FF0000;

    bmd.threshold(bmd,bmd.rect,new Point(),”>”,0×00000000,mono_color,mask);

    this code is not tested, but something like this , It worked for me when I was trying something very similar, and whatever it was , I gave up around the time that I was trying to make something just like this function that finds the first non transparent pixel ! wow thanx great help for this!

Leave a Reply