0

For the past few days, I've been trying to implement a simple filling tool for graphic regions. The scenario that I am currently working on:

Picture1

It has the same region but split into two subregions.

Here is where the problem begins. I get the region scans and loop over them in order to differentiate between the rectangles that belong to one subregion and the ones that belong to the other. This works quite well but, of course, it is an approximation, and when I finish the filling process I get something like this:

Picture2

As you can see, there is still color information of the previous region, so I need a more robust approximation to get rid of those unexpected pixels. Does anyone know how can I improve this method to split accurately a region into subregions?

Thank you in advance!

Code:

List<Region> regionSplits = new List<Region>(); 
RectangleF[] rectanglesReg = regSelected.GetRegionScans(new Matrix()); 
foreach (var rect in rectanglesReg.OrderBy(item => (item.Y)))
{
     if (regionSplits.Count == 0)
         regionSplits.Add(new Region(rect));
     else
     {
         bool added = false;
         foreach (var reg in regionSplits)
         {
              RectangleF rectExpanded = new RectangleF(rect.X - 2, rect.Y - 2, rect.Width + 4, rect.Height + 4);
              if (reg.IsVisible(rectExpanded))
              {
                   reg.Union(rect);
                   added = true;
              }
         }
         if (!added)
             regionSplits.Add(new Region(rect));
     } 
}

To sum up, when I talk about filling a region, I am talking about a feature that I need to add to my app that behaves kinda like a floodfill tool. I mean, if the user clicks over a graphics region, that region has to change its category from for example yellow to blue. So in the sample pictures I have posted, when I click over the left yellow region, I need to change its category to another color. Internally I have to consider the two yellow regions as part of a unique region, so I need to find an algorithm that splits it into two (the code I provide is the one I am currently using).

Anurag Dabas
  • 23,866
  • 9
  • 21
  • 41
MarioAF
  • 23
  • 4
  • _I get the region scans_ Can you explain what that means? Are you talking about gdi Regions and their Rectangles? - What are you targetting: Winforms, WPF, ASP..? YOU should __always__ TAG your questions correctly so one can see it on the questions page! - Finally did you look into floodfilling? [Example](https://stackoverflow.com/questions/45290317/how-can-i-fill-part-of-image-with-color/45299760#45299760) – TaW Mar 31 '21 at 11:27
  • First of all, thank you TaW for answering. I am new to question posting in StackOverflow and I appreciate any advice. When I talk about get region scans, I mean GetRegionScans() method from Winforms' Region object. Regarding considering flood filling approaches, I believe the problem is still the same: I need to split accurately the yellow region into two subregions, so I am able to identify which pixels correspond to the target subregion. – MarioAF Mar 31 '21 at 12:34
  • Besides, I do not have those regions painted over the image, they are graphic objects drawn in a PictureBox's paint event, so a classic floodfill algorithm that checks colors in pixel neighborhood and acts accordingly, would not be appropriate. I would need something that performs this action at graphics level. – MarioAF Mar 31 '21 at 12:42
  • Hm, if you alreay have a correct Region, why not use [Graphics.FillRegion(Brush, Region)](https://learn.microsoft.com/en-us/dotnet/api/system.drawing.graphics.fillregion?view=net-5.0) ?? If the result is not ok, then the Region is not correct either. Probably you have antialiasing pixels.. – TaW Mar 31 '21 at 12:51
  • Yeah, but the problem I am having is that I need a way to split the yellow region (if you see the first attached image, there are two yellow regions, but in fact, in my code, they are treated as a single one because they belong to the same category, the yellow color). The case of study covers a scenario where the user wants to change the category of one of those two, so I need a way to split by code that yellow region, into two yellow subregions, and then change the category of one of them. – MarioAF Mar 31 '21 at 13:45
  • And that is when the problem arises. I use GetRegionScans() to approximate the subregion by rectangles, but the accuracy of this process is poor, as you can see in the second attached picture, where in the left subregion, there are still yellow pixels. – MarioAF Mar 31 '21 at 13:46
  • I do not think the question is all to clear. What is the actual representation of the regions? pixels? Vector graphics / graphics objects? – JonasH Mar 31 '21 at 14:23
  • @JonasH graphics objects. Maybe talking about pixels was indeed not very accurate. – MarioAF Mar 31 '21 at 14:27
  • I still do not think it is all to clear. 'Filling' graphics regions seem poorly defined, since they already are an exact description. Or do you want to associate overlapping regions and do some segmentation of the resulting graph? – JonasH Mar 31 '21 at 14:40
  • The code you don't show us must be wrong. The GetRegionScans rectangles will not just approximate the region but fill it completely. What did you use for the Matrix? Example: `g_p = new GraphicsPath(); g_p.AddClosedCurve(allPoints[0].ToArray()); Console.WriteLine("Path from " + allPoints[0].Count + " points"); reg = new Region(g_p); Matrix M = new Matrix(); var rex = reg.GetRegionScans(M); using (Graphics g = pictureBox2.CreateGraphics()) { g.FillRegion(Brushes.HotPink, reg); foreach (var r in rex) { g.FillRectangle(Brushes.DarkGoldenrod, r); } }` – TaW Mar 31 '21 at 14:41
  • Btw, you may want to read [this](https://stackoverflow.com/questions/43139118/region-isvisiblepointf-has-very-slow-performance-for-large-floating-point-valu/43180365#43180365) wrt RegionScan rectangles.. – TaW Mar 31 '21 at 14:54
  • @TaW I've updated the post with my region split code. Basically what I do is iterate over regSelected (main region) rectangles, an check for possible overlappings (I expand a bit the original rectangles just to guarantee that the process works as expected, but I preserve the original rectangle while creating or expanding the existing region) – MarioAF Mar 31 '21 at 17:45
  • @JonasH when I talk about filling a region, I am talking about a feature that I need to add to my app that behaves kinda like a floodfill tool. I mean, if the user clicks over a graphics region, that region has to change its category from for example yellow to blue. So in the sample pictures I have posted, when I click over the left yellow region, I need to change its category to another color. Internally I have to consider the two yellow regions as part of a unique region, so I need to find an algorithm that splits it into two (the code I provide is the one I am currently using). – MarioAF Mar 31 '21 at 17:47
  • I'm sorry but I still have no idea what you are doing and why. The Contains method will tell you if the user clicked into a region. I don't understand what you want to achieve with the rectangles??? Maybe you can give us a higher level description of the goals. All I read is about filling and determining if a point is in a region, both of which is already built-in.. – TaW Mar 31 '21 at 17:56
  • @TaW when I talk about filling a region, I am talking about a feature that I need to add to my app that behaves kinda like a floodfill tool. I mean, if the user clicks over a graphics region, that region has to change its category from for example yellow to blue. So in the sample pictures I have posted, when I click over the left yellow region, I need to change its category to another color. Internally I have to consider the two yellow regions as part of a unique region, so I need to find an algorithm that splits it into two (the code I provide is the one I am currently using) – MarioAF Mar 31 '21 at 17:57
  • Hm, are you maybe talking about having a non-connected region, which you want to split into two or more contiguous regions? – TaW Mar 31 '21 at 18:02
  • @TaW that's the problem, I do not have any idea of how to split a region into separated subregions in the case they do not intersect. There is not any built-in functionality, at least that is what I believe, that allows me to do that. That's why I have developed that method that gets region rectangles and tries to place them in different subregions following the isVisble (intersection) condition. – MarioAF Mar 31 '21 at 18:05
  • @YaW exactly, that's my problem. – MarioAF Mar 31 '21 at 18:06
  • OK, I'll give it some thought. The issue you are seeing most likely comes from your expanding the rectangles. I gues you do that to include rectangles that are edge on edge or corner..? – TaW Mar 31 '21 at 18:08
  • Exactly, that's the main purpose o expanding them. – MarioAF Mar 31 '21 at 18:15
  • First lets be clear: The title is quite misleading and probably the reason the post got closed. I have voted to re-open. Second: Here is an idea: Step one remove the clicked area (cr) form the full region (fr) resulting in the non-clicked portions (ncr). Then (2) XOR with the original region (fr). Done. But how to to do (1) ? - A workaround would be to create a dummy bitmap, floodfill at the clicked position; then test a point in each rectangle for the color at that point in the dummy.. – TaW Mar 31 '21 at 18:16
  • Of couse it would be easier if the regions had never been lumped into one :-) – TaW Mar 31 '21 at 18:17
  • I will give it a try. Thanks TaW! – MarioAF Mar 31 '21 at 18:18
  • Forget about the XOR part! - I gave it a try myself and it works ok here. Note that there is a small flaw in the Fill4 function when comparing colors. I haved fixed it. Here is my function to extrace one contiguous Region containing a Point. It could/should be improved by testing that at least one rectangle actually contains the point... `static Region ExtractRegion(Region r, Point pt, Graphics g, Size sz) { Region reg = new Region();` ... – TaW Apr 01 '21 at 13:31
  • `using (Bitmap b = new Bitmap(sz.Width, sz.Height)) using (Graphics gr = Graphics.FromImage(b)) { gr.Clear(Color.White); gr.FillRegion(Brushes.Black, r); Fill4(b, pt, Color.Black, Color.Red); using (Matrix M = new Matrix()) { var rex = r.GetRegionScans(M); foreach (var rectF in rex) { var rect = Rectangle.Round(rectF); if (b.GetPixel((int)rect.X, rect.Y) == Color.Red) reg.Union(rect); } } } return reg; }` – TaW Apr 01 '21 at 13:31
  • I understand the procedure, but I have one question. The Fill4 function what does exactly do? I cannot see its definition in the code provided. I guess it performs a floodfill algorithm around pt? – MarioAF Apr 01 '21 at 14:02
  • Yes, it is in the link in my 1st comment. Oh, and I just noticed that the function doesn't need the Graphics object I pass into it. And I didn't do any test wrt the rectangle rounding .. – TaW Apr 01 '21 at 14:03
  • Ok! Again, thank you very much TaW. I really appreciate your hardwork. – MarioAF Apr 01 '21 at 14:05

0 Answers0