简体   繁体   中英

How do I get a polygon shape from a bitmap area

So - this is one of those rare times where I'm asking a "how do I" question where I've literally tried nothing because I have no idea where to begin.

Here's my problem.

I have a photo which I have keyed against a green screen and then turned into a mask where I have some number of pixels that are transparent and the rest that are black. Let's say this black shape is the upper chest and head of a photo subject.

I know how to find all the black pixels and I can easily figure out which black pixels are next to transparent ones so I can easily draw an outline of the black shape but that's where I run out of ideas... how do I transform that outline into a sequential array of points that can be used as a polygon?

Finished my analysis, I've created an SVG vector path from a mask bitmap. This gives you an optimised sequence path of all the points in the contour frame.

The process of converting a vector outline to a bitmap is called rendering. The converse process of turning bitmaps into outlines is called tracing.

Other tags that apply: mask, vector-graphics, vectorization, vector, tracing, bezier, bezier-curve ...

Links:
THEORY PDF Potrace: a polygon-based tracing algorithm
Potrace wiki
Potrace Android port and frontend
The first bit is easy, replace the green screen (chromo-key) with alpha. I will create a mask, to parse into a vector SVG .
What I have so far:
Image 1 is the Original image (green screen)[chromo-key]. Image 2 Green screen replaced with Alpha mask.
原版的 α Image 3 Contour Frame (as a bitmap with Alpha) Image 4 Mask.
轮廓 面具 .
Image 5 Here's the contour as an SVG vector file (save the following text to svg.svg file and load it in a browser to verfiy it ;O) [it looks like Image 3]).

<svg width="320" height="240" version="1.1" xmlns="http://www.w3.org/2000/svg" desc="Created by Jon Goodwin maskActivity version 1.1.2" >
<path desc="l 0 p 0" fill="rgb(255,255,255)" stroke="rgb(0,0,0)" stroke-width="1" opacity="1.0"
d="M 125.5 13.0 L 136.0 13.5 L 145.5 15.0 L 157.5 18.0 L 166.5 23.0 L 176.5 30.0 Q 184.4 32.1 188.0 38.5 L 196.0 50.5 Q 194.8 56.3 198.0 57.5 
L 201.5 65.0 L 205.0 73.5 L 207.0 77.5 L 207.0 84.5 L 206.5 86.0 L 210.0 88.5 L 209.0 91.5 L 215.0 93.5 Q 219.7 98.3 219.0 108.5 L 218.0 111.5 
L 218.0 120.5 L 217.0 127.5 L 216.0 129.5 Q 216.5 134.5 212.5 135.0 L 208.0 136.5 L 208.0 149.5 Q 206.8 154.3 209.0 155.5 L 210.5 162.0 
L 214.5 163.0 L 218.0 165.5 Q 219.8 168.8 224.5 169.0 L 238.5 176.0 L 250.5 184.0 L 266.5 193.0 L 278.5 200.0 L 283.5 203.0 L 295.0 212.5 
L 295.5 214.0 L 299.0 216.5 L 306.0 226.5 L 308.5 231.0 L 312.0 239.5 L 66.5 240.0 L 66.0 237.5 L 68.0 234.5 
L 69.0 227.5 Q 72.6 226.1 72.5 221.0 L 78.0 214.5 L 91.5 199.0 L 96.5 198.0 L 102.0 195.5 L 104.5 192.0 L 106.0 191.5 L 103.0 185.5 
L 101.0 180.5 Q 102.0 172.5 97.5 170.0 L 94.0 167.5 L 84.5 132.0 L 84.0 124.5 Q 84.8 120.8 82.5 120.0 Q 80.3 119.3 81.0 115.5 L 82.0 112.5 
L 81.0 109.5 L 81.0 102.5 L 82.0 99.5 L 82.0 93.5 L 81.0 89.5 L 83.0 84.5 L 81.5 85.0 Q 77.5 85.5 77.0 82.5 L 78.0 70.5 Q 77.1 62.1 80.0 57.5 
Q 77.3 56.8 78.0 52.5 Q 79.6 45.5 84.5 42.0 L 86.0 41.5 Q 85.3 37.8 87.5 37.0 L 93.5 31.0 L 95.0 30.5 L 101.5 19.0 Q 106.3 20.3 107.5 18.0 
L 111.5 17.0 L 125.5 13.0 Z" />
</svg>


Notes on SVG: Paths in SVG have a compact coding. For example M (for 'move to') precedes initial numeric x and y coordinates and L (line to) precedes a point to which a line should be drawn. Further command letters (C, S, Q, T and A) precede data that is used to draw various Bézier and elliptical curves. Q is quadratic Bézier, Z is used to close a path. In all cases, absolute coordinates follow capital letter commands and relative coordinates are used after the equivalent lower-case letters.

The contour frame was a tricky bit (I found some coding magic). It is a Bitmap with all the white part of the image as ONE alpha. There are in fact a few colours in the contiguous line contour frame (that's ok).
(we [I], ;O|) may be approaching the nub of the problem now ;O) )
You say:

I can easily draw an outline of the black shape but that's where I run out of ideas... how do I transform that outline into a sequential array of points that can be used as a polygon?

Hmm, Tricky (and depends on what polygon you want, I'm imagining a SVG vector graphic will give us the sequence of points from the mask bitmap), but it's an enclosed contiguous and cyclic (no holes, and one line thick), not too bad.

No external libraries or dependencies.
Here's the changeColor() code ( Image 1 to Image 2 ):
called like this:

Bitmap chromoBmp       = changeColor(origBmp, 0xff00FF00, 0x00000000, false, "chromo");//green to alpha


private Bitmap changeColor(Bitmap src, int colorToReplace, int colorThatWillReplace, boolean not, String log) {
    int width = src.getWidth();
    int height = src.getHeight();
int[] pixels = new int[width * height];
// get pixel array from source
    src.getPixels(pixels, 0, width, 0, 0, width, height);

Bitmap bmOut = Bitmap.createBitmap(width, height, src.getConfig());
int pixel;

 // iteration through pixels
for (int y = 0; y < height; ++y) {
    for (int x = 0; x < width; ++x) {
        // get current index in 2D-matrix
        int index = y * width + x;
        pixel = pixels[index];

        if(not == true)
        {
            if(pixel != colorToReplace)
            {
                pixels[index] = colorThatWillReplace;
            }
        }
        else
        {
            if(pixel == colorToReplace)
            {
                pixels[index] = colorThatWillReplace;
            }
        }
    }//x
}//y

bmOut.setPixels(pixels, 0, width, 0, 0, width, height);
return bmOut;
}

And some quite important (magic) processingBitmap_BlurContour() ( Image2 to Image3 ):
called like this:

Bitmap framContourBmp  = processingBitmap_BlurContour(changedBmp, 1);//thickness of frame


private Bitmap processingBitmap_BlurContour(Bitmap src, int blurValue){
    int width = src.getWidth();
    int height = src.getHeight();

BlurMaskFilter blurMaskFilter;
Paint paintBlur = new Paint();

Bitmap dest = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);//***VERY IMPORTANT Bitmap.Config.RGB_565 **NOT** Bitmap.Config.ARGB_8888
Canvas canvas = new Canvas(dest);

//Create background in Black
Bitmap alpha = src.extractAlpha();
paintBlur.setColor(0xFF00FF00);//green with alpha set to FF
canvas.drawBitmap(alpha, 0, 0, paintBlur);

//Create outer blur, in Black
blurMaskFilter = new BlurMaskFilter(blurValue, BlurMaskFilter.Blur.OUTER);
paintBlur.setMaskFilter(blurMaskFilter);
canvas.drawBitmap(alpha, 0, 0, paintBlur);

//Create inner blur
blurMaskFilter = new BlurMaskFilter(blurValue, BlurMaskFilter.Blur.INNER);
paintBlur.setMaskFilter(blurMaskFilter);
canvas.drawBitmap(src, 0, 0, paintBlur);

return dest;
   }//processingBitmap_BlurContour

To make the Bitmap mask ( Image2 to Image4 ):

Bitmap changedBmp = changeColor(bmp, 0,  0xff000000, true, "mask");//any col NOT trans(black) -> opaque black

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM