/// <summary>
        /// Projects the given image onto a base image.
        /// </summary>
        /// <param name="projectImageFilename">Path to image to be projected.</param>
        /// <param name="baseImageFilename">Path to base image.</param>
        /// <param name="topLeft">Top-left corner of the projection region.</param>
        /// <param name="topRight">Top-right corner of the projection region.</param>
        /// <param name="bottomLeft">Bottom-left corner of the projection region.</param>
        /// <param name="bottomRight">Bottom-right corner of the projection region.</param>
        /// <returns>An image containing the given input projected onto the base image.</returns>
        public static Image <Rgba32> ProjectOnto(string projectImageFilename, string baseImageFilename,
                                                 Point topLeft,
                                                 Point topRight,
                                                 Point bottomLeft,
                                                 Point bottomRight)
        {
            using (var projectImage = Image.Load(projectImageFilename))
                using (var baseImage = Image.Load(baseImageFilename))
                {
                    //declare without using statement (need to return this so can't dispose of it)
                    var outputImage = new Image <Rgba32>(baseImage.Width, baseImage.Height);

                    //compute the transformation matrix based on the destination points and apply it to the input image
                    Matrix4x4 transformMat = TransformHelper.ComputeTransformMatrix(projectImage.Width, projectImage.Height, topLeft, topRight, bottomLeft, bottomRight);

                    //project the image according to the input points (and realign it to fix any EXIF bugs)
                    projectImage.Mutate(x => x.AutoOrient());
                    projectImage.Mutate(x => x.Transform(new ProjectiveTransformBuilder().AppendMatrix(transformMat)));

                    //draw the base image on top of the projected image
                    outputImage.Mutate(x => x.DrawImage(projectImage, 1.0f));
                    outputImage.Mutate(x => x.DrawImage(baseImage, new Point(0, 0), 1.0f));

                    return(outputImage);
                }
        }
        /// <summary>
        /// Projects the given text onto a base image.
        /// </summary>
        /// <param name="text">The text to be projected.</param>
        /// <param name="baseImageFilename">Path to base image.</param>
        /// <param name="topLeft">Top-left corner of the projection region.</param>
        /// <param name="topRight">Top-right corner of the projection region.</param>
        /// <param name="bottomLeft">Bottom-left corner of the projection region.</param>
        /// <param name="bottomRight">Bottom-right corner of the projection region.</param>
        /// <returns>The base image with the given text projected onto it.</returns>
        public static Image <Rgba32> ProjectText(string text, string baseImageFilename,
                                                 Point topLeft,
                                                 Point topRight,
                                                 Point bottomLeft,
                                                 Point bottomRight)
        {
            using (var baseImage = Image.Load(baseImageFilename))
                using (var textImage = new Image <Rgba32>(1920, 1080))
                {
                    var outputImage = new Image <Rgba32>(baseImage.Width, baseImage.Height);

                    int  fontSize = textImage.Width / 10;
                    Font font     = SystemFonts.CreateFont("Impact", fontSize);

                    //determine text location
                    float  padding         = 10f;
                    float  textMaxWidth    = textImage.Width - (padding * 2);
                    PointF topLeftLocation = new PointF(padding, padding * 2);

                    //black brush for text fill
                    SolidBrush brush = new SolidBrush(Color.Black);

                    //wrap and align text before drawing
                    TextGraphicsOptions options = new TextGraphicsOptions()
                    {
                        TextOptions = new TextOptions()
                        {
                            WrapTextWidth       = textMaxWidth,
                            HorizontalAlignment = HorizontalAlignment.Center
                        }
                    };

                    //draw text on the text canvas
                    textImage.Mutate(x => x.BackgroundColor(Color.White));
                    textImage.Mutate(x => x.DrawText(options, text, font, brush, topLeftLocation));

                    //compute the transformation matrix based on the destination points and apply it to the text image
                    Matrix4x4 transformMat = TransformHelper.ComputeTransformMatrix(textImage.Width, textImage.Height, topLeft, topRight, bottomLeft, bottomRight);
                    textImage.Mutate(x => x.Transform(new ProjectiveTransformBuilder().AppendMatrix(transformMat)));

                    //draw the projected text and the base image on the output image
                    outputImage.Mutate(x => x.DrawImage(textImage, new Point(0, 0), 1.0f));
                    outputImage.Mutate(x => x.DrawImage(baseImage, 1.0f));

                    return(outputImage);
                }
        }