我需要对弯曲的矩形进行透视变换

时间:2021-03-24 20:34:20

标签: c# rotation transform

有没有办法以某种方式旋转图像?我会向你解释:我不想要PictureBox1.Image.RotateFlip(RotateFlipType.Rotate90FlipNone)意义上的旋转,或者如图所示的here,而是将一个弯曲的矩形定向到相机(被与相机齐平)。

我做了一个 drawing for this。这是从上面的视图。想象一下,您是站在 (0, 0) 点并正在拍摄身体照片的人 (“Rechteck”)。我想将该图像旋转某个角度,例如 35°,因为您与身体成 35°(透视)。

我已经准备了一些源代码并进行了计算。 代码的工作原理如下:用户必须输入他到矩形中心的距离以及角度。在附图中,这是红线。您还需要知道矩形的宽度。该程序计算 x 和 y 坐标(x_vy_v)。程序计算从矩形的左边缘到右边缘的所有距离和角度。

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace schiefes_Rechteck
{
    public partial class Form_Main : Form
    {
        /// <summary>
        /// entered angle in degrees 
        /// </summary>
        private Int16 eingegebener_Winkel_in_Grad;
        private UInt16 Radius_in_mm;
        /// <summary>
        /// Length of the rectangle in mm 
        /// </summary>
        private UInt16 Laenge_des_Rechtecks_in_mm;
        /// <summary>
        /// all distances [mm]
        /// </summary>
        private List<double> alle_Entfernungen = new List<double>();
        /// <summary>
        /// all angles [°]
        /// </summary>
        private List<double> alle_Winkel = new List<double>();
        public Form_Main()
        {
            InitializeComponent();
        }

        private void Form_Main_Load(object sender, EventArgs e)
        {
            this.BackColor = Color.FromArgb(148, 148, 109);
            this.Location = new Point(0, 0);
            Button_Start.BackColor = Color.FromArgb(194, 194, 165);
            TextBox_Entfernung.Text = "1300";
            TextBox_Winkel.Text = "35";
            TextBox_Rechtecklaenge.Text = "503";
            if (System.IO.File.Exists(Application.StartupPath + "\\schiefes_Rechteck_Grafik.PNG"))
            {
                PictureBox1.Image = Image.FromFile(Application.StartupPath + "\\schiefes_Rechteck_Grafik.PNG");
            }
        }

        private void Form_Main_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (PictureBox1.Image != null)
            {
                PictureBox1.Image.Dispose();
            }
            if (PictureBox2.Image != null)
            {
                PictureBox2.Image.Dispose();
            }
        }

        private void TextBox_Entfernung_TextChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(TextBox_Entfernung.Text))
            {
                bool erfolgreich = UInt16.TryParse(TextBox_Entfernung.Text, out Radius_in_mm);
                if (erfolgreich)
                {
                    TextBox_Entfernung.ForeColor = Color.FromArgb(0, 163, 0);
                }
                else
                {
                    TextBox_Entfernung.ForeColor = Color.FromArgb(163, 0, 0);
                }
            }
        }

        private void TextBox_Winkel_TextChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(TextBox_Winkel.Text))
            {
                bool erfolgreich = Int16.TryParse(TextBox_Winkel.Text, out eingegebener_Winkel_in_Grad);
                if (erfolgreich)
                {
                    TextBox_Winkel.ForeColor = Color.FromArgb(0, 163, 0);
                }
                else
                {
                    TextBox_Winkel.ForeColor = Color.FromArgb(163, 0, 0);
                }
            }
        }
      
        private void TextBox_Rechtecklaenge_TextChanged(object sender, EventArgs e)
        {
            if (!string.IsNullOrEmpty(TextBox_Rechtecklaenge.Text))
            {
                bool erfolgreich = UInt16.TryParse(TextBox_Rechtecklaenge.Text, out Laenge_des_Rechtecks_in_mm);
                if (erfolgreich)
                {
                    TextBox_Rechtecklaenge.ForeColor = Color.FromArgb(0, 163, 0);
                }
                else
                {
                    TextBox_Rechtecklaenge.ForeColor = Color.FromArgb(163, 0, 0);
                }
            }
        }

        private async void Button_Start_Click(object sender, EventArgs e)
        {
            ListBox1.Items.Clear();
            await Task.Run(() => Berechnung_aller_Werte());
        }

        private void Berechnung_aller_Werte()
        {
            alle_Entfernungen.Clear();
            alle_Winkel.Clear();

            double x_v, y_v; // Mitte des Rechtecks, davon die x-Koordinate und die y-Koordinate. 
            x_v = Radius_in_mm * Math.Cos((90.0 - (double)eingegebener_Winkel_in_Grad) * Math.PI / 180.0); //richtig
            y_v = Radius_in_mm * Math.Sin((90.0 - (double)eingegebener_Winkel_in_Grad) * Math.PI / 180.0); //richtig

            double alpha_in_Grad = 0.0;
            double Entfernung = 0.0;
            double halbe_Rechteckbreite = Laenge_des_Rechtecks_in_mm / 2.0;
            double Position_linker_Rand = x_v - halbe_Rechteckbreite; //richtig

            double Zaehler = 0.0;

            while (Zaehler < Laenge_des_Rechtecks_in_mm)
            {
                alpha_in_Grad = Math.Atan((Position_linker_Rand + Zaehler) / y_v) * 180.0 / Math.PI;
                alle_Winkel.Add(alpha_in_Grad);
                Entfernung = Math.Sqrt(Math.Pow(Position_linker_Rand + Zaehler, 2) + Math.Pow(y_v, 2));
                alle_Entfernungen.Add(Entfernung);
                Zaehler += 1.0;
            }
            this.BeginInvoke((Action)(() => { ListBox1.Items.Add(Math.Round(alle_Entfernungen.Last(), 0).ToString() + " mm"); ListBox1.Items.Add(Math.Round(alle_Winkel.Last(), 0).ToString() + " °"); }));
        }
    }//Form
}

1 个答案:

答案 0 :(得分:0)

如果您想要一个不弯曲的图像,即所有线条保持垂直或水平,那么图像所发生的一切就是从侧面看它的宽度会显得更小,而其高度将保持不变。

通过将您看到图像的视在角度(即图像中黄绿色线和粉红色线之间的角度)除以您站立时的相应角度来获得 x 轴的缩放因子就在图像的前面。这个因素的一个非常好的近似值就是 cos(36° * PI / 180),其中角度是与图像中红线的角度。

xScale = Math.Cos(angleToMidLineInDegrees * Math.PI / 180);
yScale = 1;

或者干脆

xScale = Math.Cos(angleToMidLineInRadians);
yScale = 1;

哪里

angleToMidLineInRadians = Math.ATan(x_redLine / y_v);

或一步

xScale = y_v / Math.Sqrt(x_redLine * x_redLine + y_v * y_v);

参见:关于 WolframAlpha 的 cos(arctan(x/y))


但是,在变换图像时,您必须进行逆变换(如 video 末尾所述),因为您要确定变换图像的像素。即,您将使用伪代码进行操作(其中 t 表示转换,没有 t 是原始坐标):

for (yt = 0 to height_t - 1; yt++) {
    for (xt = 0 to width_t - 1; xt++) {
        (x, y) = inverse_transformation(xt, yt);
        color_t = get_color(picture, x, y);
        draw(picture_t, xt, yt, color_t);
    }
}
相关问题