检测矩形在黄色像素上的传递

时间:2016-09-10 05:39:36

标签: c# visual-studio graphics

我有一个关于检测移动且可能旋转的矩形何时经过Panel的背景图像的黄色像素的最佳方法的查询。

我有一个接受Image和Point的方法,如果该点是黄色像素,则返回true。我需要对我的游戏功能进行颜色检测,如果它在赛道的黄色边框上行驶,则会重置汽车(玩家)。该方法如下所示:

private Boolean isYellow(Image image, Point point)
{
   Bitmap bitmap = new Bitmap(image);
   Color color = bitmap.GetPixel(point.X, point.Y);

   return (color.R > 220 && color.G > 220 && color.B < 200);
}

以前,为了检测玩家矩形是否经过黄色,我检查了矩形的位置,由对象的X和Y值提供。问题在于位置是水平矩形的左上角,这意味着汽车几乎完全可以在轨道上行驶而不会发生检测。

我想通过检查矩形覆盖的所有点来解决这个问题。这并不像看起来那么简单,因为矩形可能会旋转。我的绘图和运动逻辑如下所示:

public void draw(Graphics g)
{
   int dx = rectangle.X + (rectangle.Height / 2);
   int dy = rectangle.Y + (rectangle.Width / 2);

   g.ScaleTransform(xScale, yScale);
   g.TranslateTransform(dx, dy);
   g.RotateTransform((float) ((180 * angle) / Math.PI));
   g.TranslateTransform(-dx, -dy);
   g.DrawImage(image, rectangle.X, rectangle.Y);
   g.ResetTransform();
}

public void move(uRaceGame game, Panel panel)
{
   double cos = Math.Cos(angle), sin = Math.Sin(angle);
   int xLocation = 200;
   int yLocation = 200;

   xLocation = (int) Math.Floor(rectangle.X + (cos * game.moveDir * 60)); 
   yLocation = (int) Math.Floor(rectangle.Y + (sin * game.moveDir * 60)); 

   angle = (angle + (game.rotateDir * (Math.PI / 128))) % (Math.PI * 2);

   if (xLocation * xScale > panel.Width - (rectangle.Width * cos) || yLocation * yScale > panel.Height - (rectangle.Width * sin) - 5 || xLocation * xScale < 0 || yLocation * yScale < 5) return;

   rectangle.Location = new Point(xLocation, yLocation);
}

我试过但未能创建一个方法来翻译角落的坐标并找出矩形的中间部分,但这不起作用,并且黄色检测在非常模糊的地方发射:

public Point getCentre()
{
    int cX = (int) (rectangle.X + ((rectangle.Width / 2) / xScale)), cY = (int) (rectangle.Y + ((rectangle.Height / 2) / yScale));
    float tempX = (rectangle.X - cX), tempY = (rectangle.Y - cY);

    double rX = (tempX * Math.Cos(angle)) - (tempY * Math.Sin(angle));
    double rY = (tempX * Math.Sin(angle)) - (tempY * Math.Cos(angle));

    return new Point((int) ((rX + cX) * xScale), (int) ((rY + cY) * yScale));
}

我非常感谢有关如何解决这个问题的任何建议。我提供了翻译和黄色检测代码,以防万一我尝试了数英里外,其他人有更好的想法。

非常感谢。

1 个答案:

答案 0 :(得分:3)

我想到了两种方法:

  • 您可以创建沿着汽车矩形倾斜侧面的循环
  • 或者您可以将汽车复制到一个直接位图并正常循环。

以下是第二种方法的示例。

它使用LockBits方法,使用Yellow中的代码检测Bitmap

它通过从原始BackgroundImage未旋转的文件中复制它来准备该位图。

结果如下,包括一个控件Panel,它显示了直到的矩形:

enter image description here

这是黄色取景器功能。它使用Lockbits来提高速度:

using System.Runtime.InteropServices;
using System.Drawing.Imaging;

public bool testForYellowBitmap(Bitmap bmp)
{
    Size s1 = bmp.Size;
    PixelFormat fmt = new PixelFormat();
    fmt = bmp.PixelFormat;
    Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
    BitmapData bmp1Data = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt);
    byte bpp1 = 4;
    if (fmt == PixelFormat.Format24bppRgb) bpp1 = 3;
    else if (fmt == PixelFormat.Format32bppArgb) bpp1 = 4; else return false; // throw!!
    int size1 = bmp1Data.Stride * bmp1Data.Height;
    byte[] data1 = new byte[size1];
    System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1);
    for (int y = 0; y < s1.Height; y++)
    {
        for (int x = 0; x < s1.Width; x++)
        {
            Color c1;
            int index1 = y * bmp1Data.Stride + x * bpp1;
            if (bpp1 == 4)
                c1 = Color.FromArgb(data1[index1 + 3], data1[index1 + 2],
                                    data1[index1 + 1], data1[index1 + 0]);
            else c1 = Color.FromArgb(255, data1[index1 + 2], 
                                          data1[index1 + 1], data1[index1 + 0]);
            if (c1.R > 220 && c1.G > 220 && c1.B < 200) 
               { bmp.UnlockBits(bmp1Data); return true; }
        }
    }
    bmp.UnlockBits(bmp1Data);
    return false;
}

我准备Bitmap进行比较MouseMove。变量w, h, w2, h2保持汽车尺寸的宽度,高度和半度。源位图位于drawPanel1.BackgroundImage中。当前角度为TrackBar tr_a.Value。为了进一步控制,我还以白色显示旋转的汽车矩形。

private void drawPanel1_MouseMove(object sender, MouseEventArgs e)
{
    if (e.Button.HasFlag(MouseButtons.Left))
    {
        Size sz = drawPanel1.BackgroundImage.Size;
        Rectangle rectSrc = new Rectangle(e.X - w2, e.Y - h2, w, h);
        Rectangle rectTgt = new Rectangle(e.X - w, e.Y - h, 2 * w, 2 * h);

        using (Graphics g = drawPanel1.CreateGraphics())  // start optional
        {
            g.TranslateTransform(e.X, e.Y);
            g.RotateTransform(trb_a.Value);
            g.TranslateTransform(-e.X, -e.Y);
            drawPanel1.Refresh();
            g.DrawRectangle(Pens.White, rectSrc);
        }

        using (Graphics g = drawPanel2.CreateGraphics())
        {                                                      // end optional
            using (Bitmap bmp = new Bitmap(sz.Width, sz.Height))
            using (Graphics g2 = Graphics.FromImage(bmp))
            {
                g2.TranslateTransform(e.X, e.Y);
                g2.RotateTransform(-trb_a.Value);
                g2.TranslateTransform(-e.X, -e.Y);
                g2.DrawImage(drawPanel1.BackgroundImage, rectTgt, rectTgt, 
                             GraphicsUnit.Pixel);
                drawPanel2.Refresh();
                g.DrawImage(bmp, rectSrc, rectSrc, GraphicsUnit.Pixel);
                Text = testForYellowBitmap(bmp) ? "!!YELLOW!!" : "";
            }
        }
    }

第一种方法会使用类似的LockBits方法,但内部有循环沿着汽车矩形的旋转边,使用floats循环变量来计算x坐标。应根据汽车尺寸或角度的每次变化准备这些数据。代码有点长,但也应该快一点。

如果第二种方法是通过在ClippingRegion对象上使用Graphics,则可以检查任意形状,而第一种方法可以很容易地修改凹面多边形但不能修改弯曲形状。

以下是第一个版本的检查代码的改编版本:

public bool testForYellowBitmapTilt(Bitmap bmp, List<int> leftPts, 
                                    List<int> rightPts, Point topLeft)
{
    Size s1 = bmp.Size;
    PixelFormat fmt = new PixelFormat();
    fmt = bmp.PixelFormat;
    Rectangle rect = new Rectangle(0, 0, s1.Width, s1.Height);
    BitmapData bmp1Data = bmp.LockBits(rect, ImageLockMode.ReadOnly, fmt);
    byte bpp1 = 4;
    if (fmt == PixelFormat.Format24bppRgb) bpp1 = 3;
    else if (fmt == PixelFormat.Format32bppArgb) bpp1 = 4; 
         else return false; // or throw!!
    if (leftPts.Count != rightPts.Count) return false; // or throw!!

    int size1 = bmp1Data.Stride * bmp1Data.Height;
    byte[] data1 = new byte[size1];
    System.Runtime.InteropServices.Marshal.Copy(bmp1Data.Scan0, data1, 0, size1);

    for (int y = 0; y < (leftPts.Count); y++)
    {
        for (int x = leftPts[y] + topLeft.X; x < rightPts[y] + topLeft.X; x++)
        {
            Color c1;

            int index1 = (y + topLeft.Y) * bmp1Data.Stride + x * bpp1;
            if (index1 > 0)
            {
                if (bpp1 == 4)
                    c1 = Color.FromArgb(data1[index1 + 3], data1[index1 + 2], 
                                        data1[index1 + 1], data1[index1 + 0]);
                else c1 = Color.FromArgb(255, data1[index1 + 2],
                                        data1[index1 + 1], data1[index1 + 0]);

                if (c1.R > 220 && c1.G > 220 && c1.B < 200) 
                   { bmp.UnlockBits(bmp1Data); return true; }
            }
        }
    }
    bmp.UnlockBits(bmp1Data);
    return false;
}

左侧和右侧坐标存储在此处:

List<int> leftPts = new List<int>();
List<int> rightPts = new List<int>();
Point top = Point.Empty;


void getOuterPoints(List<PointF> corners, out List<int> leftPts, 
                    out List<int> rightPts, out Point top)
{
    leftPts = new List<int>();
    rightPts = new List<int>();

    PointF left = corners.Select(x => x).OrderBy(x => x.X).First();
    PointF right = corners.Select(x => x).OrderByDescending(x => x.X).First();
            top = Point.Round(corners.Select(x => x).OrderBy(x => x.Y).First());
    PointF bottom = corners.Select(x => x).OrderByDescending(x => x.Y).First();

    int w1 = -(int)(top.X - left.X);
    int w2 = -(int)(left.X - bottom.X );
    int h1 = (int)(left.Y - top.Y);
    int h2 = (int)(bottom.Y - left.Y);

    float d1 = 1f * w1 / h1;
    float d2 = 1f * w2 / h2;

    for (int y = 0; y < h1; y++) leftPts.Add( (int)(y * d1) );
    for (int y = 0; y < h2; y++) leftPts.Add( (int)(y * d2 + w1));

    for (int y = 0; y < h2; y++) rightPts.Add( (int)(y * d2));
    for (int y = 0; y < h1; y++) rightPts.Add(  (int)(y * d1 + w2));
}

您需要以任意顺序作为List<PointF>以四角进场; top可以是任何东西,它将在方法中设置。 coodinates是相对于汽车,所以当汽车移动时它们不会改变..