Search code examples
c#imagepicturebox

Is there a better way to handle image click regions?


I have an image that you click 2 points on and it creates a line in between each point. In the end what this department is asking for is the ability to calculate the length of those lines and where those lines occurred. They are currently doing this by hand w/ paper/pen/ruler. This is one of the images I'm working with.

Example Image

Those cracks in the middle are considered "area 7".

So I needed a way to, aside from saving my x & y location to measure the lines later, also add the location they occurred to my list. Below is the only way I knew to do this but its proving to be more of a mess. Because the picture isn't an exact square or rectangle there are a lot of overlapping areas and a lot of areas where there will be voids, that the zones don't get covered very well.

Is there a better way to do this? (currently I'm just using a messagebox to show where I've clicked, I haven't went as far as doing anything with the data yet until I get this right.)

    if (e.Button.Equals(MouseButtons.Left))
    {
        Rectangle zone1 = new Rectangle(35, 30, 770, 30);
        if (zone1.Contains(e.Location))
        {
            MessageBox.Show("Zone1");                  
        }
        Rectangle zone2 = new Rectangle(890, 40, 330, 300);
        if (zone2.Contains(e.Location))
        {
            MessageBox.Show("Zone2");
        }
        Rectangle zone3 = new Rectangle(340, 340, 850, 60);
        if (zone3.Contains(e.Location))
        {
            MessageBox.Show("Zone3");
        }
        Rectangle zone4 = new Rectangle(100, 25, 75, 300);
        if (zone4.Contains(e.Location))
        {
            MessageBox.Show("Zone4");
        }
        //4-1 trying to cover areas missed in zone4
        Rectangle zone41 = new Rectangle(255, 270, 120, 240);
        if (zone41.Contains(e.Location))
        {
            MessageBox.Show("Zone4-1");
        }
        Rectangle zone5 = new Rectangle(310, 100, 180, 150);
        if (zone5.Contains(e.Location))
        {
            MessageBox.Show("Zone5");
        }
        //5-1 trying to cover areas missed in zone5
        Rectangle zone51 = new Rectangle(220, 80, 60, 45);
        if (zone51.Contains(e.Location))
        {
            MessageBox.Show("Zone5-1");
        }
        Rectangle zone6 = new Rectangle(635, 35, 250, 210);
        if (zone6.Contains(e.Location))
        {
            MessageBox.Show("Zone6");
        }
    }

Solution

  • My proposal is that you use a background image as a zone map. This image will not be displayed to the user at all. You load it once and keep it in memory, but continue to display your original image and draw lines on it as usual. Then, when the user clicks on your image, you check the color of the zone map to determine the zone.

    For example, let's say I'm using these two images as my display and and my zone map:

    Zone display Zone map

    Both image are loaded in your code, but only the display map is shown to the user:

    class MyForm
    {
        Bitmap zoneDisplay;
        Bitmap zoneMap;
    
        protected override void OnLoad(EventArgs e)
        {
            base.OnLoad(e);
    
            zoneDisplay = (Bitmap)Image.FromFile(@"c:\temp\zonedisp.png"); // replace with actual path to file
            zoneMap = (Bitmap)Image.FromFile(@"c:\temp\zonemap.png");
    
            // put the display image into the picturebox (or whatever control displays it)
            pictureBox.Image = zoneDisplay;
        }
    

    Then, when the user clicks your image, just check the color on the zone map:

    private void pictureBox_MouseDown(object sender, MouseEventArgs e)
    {
        var color = _zoneMap.GetPixel(e.X, e.Y);
        if (color == Color.FromArgb(0, 0, 255))
            MessageBox.Show("Zone 1");
        else if (color == Color.FromArgb(255, 0, 0))
            MessageBox.Show("Zone 2");
        else if (color == Color.FromArgb(0, 255, 0))
            MessageBox.Show("Zone 3");
        // etc...
    }
    

    If your colors are slightly off then you may need to perform a less exact comparison. Example:

    static int ColorDelta(Color c1, Color c2)
    {
        return Math.Abs(c1.R - c2.R) + Math.Abs(c1.G - c2.G) - Math.Abs(c1.B - c2.B);
    }
    
    private void pictureBox_MouseDown(object sender, MouseEventArgs e)
    {
        var color = _zoneMap.GetPixel(e.X, e.Y);
        if (90 > ColorDelta(color, Color.FromArgb(0, 0, 255)))
            MessageBox.Show("Zone 1");
        else if (90 > ColorDelta(color, Color.FromArgb(255, 0, 0)))
            MessageBox.Show("Zone 2");
        else if (90 > ColorDelta(color, Color.FromArgb(0, 255, 0)))
            MessageBox.Show("Zone 3");
        // etc...
    }