检测矩形在黄色像素上的通过 [英] Detect passing of rectangle over yellow pixel

查看:39
本文介绍了检测矩形在黄色像素上的通过的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个查询的最佳方法,该方法是检测何时移动的并可能旋转的矩形越过面板背景图像的黄色像素.

I have a query regarding the best approach to detect when a moving and potentially rotated rectangle passes over a yellow pixel of a Panel's background image.

我有一个接受Image和Point的方法,如果该点是黄色像素,则返回true.我需要对游戏的功能进行颜色检测,如果汽车(赛车手)越过赛道的黄色边界,则会重置该颜色.此方法如下所示:

I have a method which accepts an Image and a Point, and returns true if that point is that of a yellow pixel. I require this colour detection for the function of my game, which resets the car (player) if it drives over the yellow borders of the track. This method is shown below:

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值检查了矩形的位置.问题在于位置在水平矩形的左上角,这意味着汽车几乎可以完全驶出赛道而不会被发现.

Previously, to detect if the player rectangle passes over yellow, I checked against the location of the rectangle, as provided by the X and Y values of the object. The issue with this is that the location is the top left corner of a horizontal rectangle, meaning the car can drive almost entirely off the track without detection occurring.

我想通过检查矩形所覆盖的所有点来解决此问题.这并非像矩形可能旋转那样简单.我的绘画和运动逻辑如下所示:

I'd like to fix this by checking all points covered by the rectangle. This is not as simple as it may seem as the rectangle is likely to be rotated. My drawing and movement logic is shown below:

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);
}

我尝试但未能创建一种方法来转换角的坐标并找出矩形的中心,但是这种方法不起作用,黄色检测在非常模糊的地方触发:

I tried but failed to create a method which translates the coords of the corner and figures out the middle of the rectangle, but this does not work, and the yellow detection fires in very obscure places:

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));
}

我非常感谢有关如何解决此问题的任何建议.我附上了翻译和黄色检测代码,以防万一我不满意而其他人有更好的主意.

I'd really appreciate any suggestions on how to tackle this. I included the translation and yellow detection code in case I'm miles off in my attempt and someone else has a better idea.

非常感谢您.

推荐答案

我想到了两种方法:

  • 您可以创建沿着汽车矩形倾斜边的循环
  • 或者您也可以将汽车复制到未完成的位图上,然后正常循环遍历.

这是第二种方法的一个例子.

Here is an example of the second approach.

它使用 LockBits 方法,将 Yellow 与您的代码一起检测到 Bitmap 中.

It uses a LockBits method that detects Yellow with your code in a Bitmap.

它通过从原始的 BackgroundImage 不旋转的位置复制该位图来准备该位图.

And it prepares that bitmap by copying it from the original BackgroundImage un-rotated.

这里是结果,包括显示未完成的矩形的控件 Panel :

Here is the result, including a control Panel that shows the untilted Rectangle:

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

Here is the yellow finder function. It uses Lockbits for speed:

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 中.为了进一步控制,我还以白色显示了旋转的汽车矩形.

I prepare the Bitmap to compare in the MouseMove. The variables w, h, w2, h2 hold the width, height and halves of that of the car's size. The source bitmap is in drawPanel1.BackgroundImage. The current angle is in a TrackBar tr_a.Value. For further control I also display the rotated car rectangle in White.

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坐标.这些数据应根据轿厢尺寸或角度的每次变化而准备.代码稍长一些,但也应该更快一些.

The first approach would use a similar LockBits method, but with loops inside that go along the rotated sides of the car rectangle, using floats wth the loop variables to calculate the x-coordinates. Those data should be prepared on each change of car size or angle. The code is a little longer but should be a bit faster, too.

第二种方法的优点是,通过在 Graphics 对象上使用 ClippingRegion ,可以检查任意形状,而第一种方法可以很容易地修改为凹多边形但不适用于弯曲形状.

The advantage if the second approach is that by using a ClippingRegion on the Graphics object one could check an arbitrary shape while the first method can be easily modified for concave polygons but not for curved shapes.

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

Here is the adapted version of the checking code for the first version:

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 可以是任何东西,它将在方法中设置.这些余弦是相对于汽车的,因此它们在汽车行驶时不会改变.

You need to feed in the four corners as a List<PointF> in any order; the top can be anything, it will be set in the method. The coodinates are relative to the car, so they don't change when the car moves..

这篇关于检测矩形在黄色像素上的通过的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆