0

I'd like to ask if there's a better or faster alternative way to get the largest rectangle inside an almost rectangular contour.

The rectangle should be aligned to both x and y axis and should be completely inside the rectangular contour. That means it would not contain any external white pixels, yet occupy the largest area in the contour.

Test image is here: img

I've tried these two but I'm looking if there's a faster and neater way to go around this.

I also tried going through the points of a contour and getting the minimum and maximum points like in here but of course, it just shows similar results to what cv2.boundingRect already does.

mashedpotatoes
  • 395
  • 2
  • 20

1 Answers1

1

Maybe this is a bit of lateral thinking, but looking at your examples and spec when not fill out white pikels contiguouys with the outside bounding box instead. (Like a 'paint pot' brush in Paint-type application).

E.g. (red pixels being the ones you would turn black from white):

You could probably even limit the process to the outer N pixels.

Image filled out to outside bounding box

============================

So how might one implement this? It is essentially a version of the "flood fill" algorithm used in pixel graphics programmes, except that you start not from a single seed pixel but checking every point on the edge of the outside bounding rectangle. You start filling in and build a stack of points you need to come back to because you can't necessarily follow every area at once and may need to go back on your self.

You can look that algorithm up, but a 'pure' version will be very stack-heavy if you push every point you can't follow right now, particularly starting with the whole boundary of the shape.

I haven't implemented it this way, but my first thought would be a scan from a boundary inwards, taking a whole line of pixels at a time and mark all the 'white' pixels with a new 3rd colour, then on the next row you fill all the white pixels touching the previously marked pixels and so on. (doesn't matter whether you mark the changed pixels as a 3rd colour, a mask, or alpha-channel or whatever - but you must be able to tell newly filled in pixels from the old black ones.

As you go, you need to check for any 'stranded' areas where you need to work backwards to fill in white areas that are not directly connected to the outside:

Start filling from the edge... Start filling from the edge

Watch out for stranded areas - if you find one, scan backwards to fill before going to where you were before, to carry one (you may need to recurse if you stranded area turns back on itself again, though in your particular application this shouldn't be a huge issue, unlike some graphics applications)

Watch out from stranded areas

And continue, not forgetting to fill in from the other edges if required (see note below) until you come to a row with no further pixels to fill and no more back-filling to do. Then restart at the far side of the image as you need to start a backward pass from the far side to catch anything else on that side.

enter image description here

enter image description here

For a practical implementation there is some thinking to do. Your examples will have a lot of filling at the edge but not much by way of complex internal shapes to follow, which keeps things simple. But you need to work from all 4 sides to do it efficiently - perhaps working in as a series of concentric rectangles rather than one side at a time. More complexity working through the design but massively more efficient in this example.

Food for thought anyhow.

Duke Bouvier
  • 111
  • 4
  • This is an interesting approach! How did you do this? – mashedpotatoes Oct 26 '19 at 13:05
  • 1
    I haven't implemented this as described but did something similar for a computer graphics application back in the days of yore. While you concertually recurse if there the holes turn back on themselves it won't happen often so a simple list/stack of points to go back to would cover it. Can see lots of ways of implementing the fill for each line, but this doesn't seem to intensive so you don't have to be very sophisticated. – Duke Bouvier Apr 14 '20 at 21:12
  • It certainly gave me insight. The ultimate goal was to reliably read the characters while ignoring the noise. I have finished the program with a similar albeit painfully simple method: I just "flooded" the image twice (one for the outside of the box, then another for the inside), and whatever remains, must be the numbers inside. It isn't perfect though, characters that intersect with the box are flooded, causing it to be ignored or be read as two separate characters. Anyhow, thanks! – mashedpotatoes Apr 22 '20 at 13:09