Search code examples
pythonopencvfeature-detection

How to isolate the bars with cv2?


How do I isolate the bars with no white fill, in the image below that represents a bar chart?

I'm looking for a solution which will work for any variation of this image. You can assume that the format will be the same, but some features like the gaps in the gridlines/axis line might be in different places.

I've tried detecting various features of the bars, like the 26x3px ends of the bars, or the top-left and bottom-right corners. For example, using masks like the following for top-left:

bar_top_kernel: Numpy = np.array([
    [1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 1, 1, 1, 1, 1, 1, 1],
    [1, 1, 0, 0, 0, 0, 0, 0, 0],
], dtype='uint8')

or

bar_top_kernel: Numpy = np.array([
    [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
    [0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [0, 1, 1, 1, 1, 1, 1, 1, 1, 1],
    [0, 1, 1, 0, 0, 0, 0, 0, 0, 0],
], dtype='uint8')

But depending on what I try, I either get missed corners or false positives because of how the ends of the bars interact with the gridlines.

I've tried removing the gridlines first. But due to the interaction of the bar ends and the gridlines, I tend to get pieces left over which interfere with the feature detection.

I'm starting to think this might not be possible without some kind of ML approach, but I'm hoping someone will spot a clever trick to achieve this.

(please click to see full size) binary image representing a bar chart


Solution

  • I ended up figuring out a simple trick:

    1. Invert the colours.
    2. Find and fill any contour with: bounding box width > bar width. This basically isolates the bars.
    3. Find contours again and find pairs of contours whose centres of mass are close together. This gives the bars small bars that are split in 2 by the tick marks.
    4. Fill rectangle that encompasses each pair to fix the splits.