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

前端 未结 2 422
梦谈多话
梦谈多话 2021-02-01 09:09

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

\"\"

Both areas are defined by

相关标签:
2条回答
  • 2021-02-01 09:55

    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:

    source image

    Result:

    resulting image

    b) Various sides

    Source:

    source 2

    Result:

    result 2

    c) Perspective

    Source:

    source 3

    Result:

    result 3

    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;
    }
    
    0 讨论(0)
  • 2021-02-01 10:02

    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.

    0 讨论(0)
提交回复
热议问题