How do I draw an image based on a simple polygon?

微笑、不失礼 提交于 2019-12-03 05:56:58

问题


I'd like to copy a roughly rectangular area to a rectangular area. Example:

Both areas are defined by their corner points. The general direction is kept (no flipping etc).

Simply rotating the source image does not work since opposing sides may be of different length.

So far I found no way to do this in pure C# (except manual pixel copying), so I guess I have to resort to the Windows API or some 3rd party library?


回答1:


Generally speaking, what you want to do is map the destination coordinates to the source coordinates through a transform function:

for (int y = 0; y < destHeight; y++) {
    for (x=0; x < destWidth; x++) {
        Color c = Transform(x, y, sourceImage, sourceTransform);
        SetPixel(destImage, x, y, c);
    }
}

Let's assume that sourceTransform is an object that encapsulates a transformation from source to dest coordinates (and vice versa).

Working in dest coordinates will make it easier to avoid that curve in your retransformed source image and will allow you to better antialias, as you can map the corners of the dest pixel to the source image and sample within it and interpolate/extrapolate.

In your case you're going to have a set of linear equations that do the mapping - in this case this is known as quadrilateral warping - see this previous question.




回答2:


Since I could not find an answer, I wrote a naive implementation myself. It works reasonably well.

Examples

I drew all examples manually in Paint, so they are not very exact - it was just enough to test some basics.

a) Slight rotation.

Source:

Result:

b) Various sides

Source:

Result:

c) Perspective

Source:

Result:

Code

(it's specialized to my use case, but it should be easy to adapt):

// _Corners are, well, the 4 corners in the source image
// _Px is an array of pixels extracted from the source image

public void Rescale ()
{
    RescaleImage (
        _Corners[0],
        _Corners[1],
        _Corners[3],
        _Corners[2],
        100,
        100);
}

private void RescaleImage (PointF TL, PointF TR, PointF LL, PointF LR, int sx, int sy)
{
    var bmpOut = new Bitmap (sx, sy);

    for (int x = 0; x < sx; x++) {
        for (int y = 0; y < sy; y++) {
            /*
             * relative position
             */
            double rx = (double) x / sx;
            double ry = (double) y / sy;

            /*
             * get top and bottom position
             */
            double topX = TL.X + rx * (TR.X - TL.X);
            double topY = TL.Y + rx * (TR.Y - TL.Y);
            double bottomX = LL.X + rx * (LR.X - LL.X);
            double bottomY = LL.Y + rx * (LR.Y - LL.Y);

            /*
             * select center between top and bottom point
             */
            double centerX = topX + ry * (bottomX - topX);
            double centerY = topY + ry * (bottomY - topY);

            /*
             * store result
             */
            var c = PolyColor (centerX, centerY);
            bmpOut.SetPixel (x, y, c);
        }
    }

    bmpOut.Save (_Path + "out5 rescale out.bmp");
}

private Color PolyColor (double x, double y)
{
    // get fractions
    double xf = x - (int) x;
    double yf = y - (int) y;

    // 4 colors - we're flipping sides so we can use the distance instead of inverting it later
    Color cTL = _Px[(int) y + 1, (int) x + 1];
    Color cTR = _Px[(int) y + 1, (int) x + 0];
    Color cLL = _Px[(int) y + 0, (int) x + 1];
    Color cLR = _Px[(int) y + 0, (int) x + 0];

    // 4 distances
    double dTL = Math.Sqrt (xf * xf + yf * yf);
    double dTR = Math.Sqrt ((1 - xf) * (1 - xf) + yf * yf);
    double dLL = Math.Sqrt (xf * xf + (1 - yf) * (1 - yf));
    double dLR = Math.Sqrt ((1 - xf) * (1 - xf) + (1 - yf) * (1 - yf));

    // 4 parts
    double factor = 1.0 / (dTL + dTR + dLL + dLR);
    dTL *= factor;
    dTR *= factor;
    dLL *= factor;
    dLR *= factor;

    // accumulate parts
    double r = dTL * cTL.R + dTR * cTR.R + dLL * cLL.R + dLR * cLR.R;
    double g = dTL * cTL.G + dTR * cTR.G + dLL * cLL.G + dLR * cLR.G;
    double b = dTL * cTL.B + dTR * cTR.B + dLL * cLL.B + dLR * cLR.B;

    Color c = Color.FromArgb ((int) (r + 0.5), (int) (g + 0.5), (int) (b + 0.5));

    return c;
}


来源:https://stackoverflow.com/questions/5728885/how-do-i-draw-an-image-based-on-a-simple-polygon

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!