0

I am writing code in python 2.7.12 using opencv '2.4.9.1'.
I have a 2d numpy array containing values in range [0,255].
My aim is to find largest region containing value in range[x,y]
I found How to use python OpenCV to find largest connected component in a single channel image that matches a specific value? as pretty well-explained . Only, the catch is - it is meant for opencv 3 .

I can try to write a function of this type
[pseudo code]

def get_component(x,y,list):
  append x,y to list
  visited[x][y]=1
 if(x+1<m && visited[x+1][y]==0)
  get_component(x+1,y,list)
 if(y+1<n && visited[x][y+1]==0)
  get_component(x,y+1,list)
 if(x+1<m)&&(y+1<n)&&visited[x+1][y+1]==0
  get_component(x+1,y+1,list)
 return

MAIN
biggest_component = NULL
biggest_component_size = 0
low = lowest_value_in_user_input_range
high = highest_value_in_user_input_range
matrix a = gray image of size mxn
matrix visited = all value '0' of size mxn
for x in range(m):
 for y in range(n):
  list=NULL
  if(a[x][y]>=low) && (a[x][y]<=high) && visited[x][y]==1:
   get_component(x,y,list)
   if (list.size>biggest_component_size)
    biggest_component = list
Get maximum x , maximum y , min x and min y from above list containing coordinates of every point of largest component to make rectangle R .
Mission accomplished !

[/pseudo code]

Such an approach will not be efficient, I think.
Can you suggest functions for doing the same with my setup ?
Thanks.

1 Answer 1

2

Happy to see my answer linked! Indeed, connectedComponentsWithStats() and even connectedComponents() are OpenCV 3+ functions, so you can't use them. Instead, the easy thing to do is just use findContours().

You can calculate moments() of each contour, and included in the moments is the area of the contour.

Important note: The OpenCV function findContours() uses 8-way connectivity, not 4-way (i.e. it also checks diagonal connectivity, not just up, down, left, right). If you need 4-way, you'd need to use a different approach. Let me know if that's the case and I can update..

In the spirit of the other post, here's the general approach:

  1. Binarize your image with the thresholds you're interested in.
  2. Run cv2.findContours() to get the contour of each distinct component in the image.
  3. For each contour, calculate the cv2.moments() of the contour and keep the maximum area contour (m00 in the dict returned from moments() is the area of the contour).
  4. Either keep the contour as a list of points if that's what you need, otherwise draw them on a new blank image if you want it as a mask.

I lack creativity today, so you get the cameraman as our example image as you didn't provide one.

import cv2
import numpy as np

img = cv2.imread('cameraman.png', cv2.IMREAD_GRAYSCALE)

Cameraman

Now, let's binarize to get some separated blobs:

bin_img = cv2.inRange(img, 50, 80)

Binary Cameraman

Now let's find the contours.

contours = cv2.findContours(bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]

# For OpenCV 3+ use: 
# contours = cv2.findContours(bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[1]

Now for the main bit; looping through the contours and finding the largest one:

max_area = 0
max_contour_index = 0
for i, contour in enumerate(contours):
    contour_area = cv2.moments(contour)['m00']
    if contour_area > max_area:
        max_area = contour_area
        max_contour_index = i

So now we have an index max_contour_index of the largest contour by area, so you can access the largest contour directly just by doing contours[max_contour_index]. You could of course just sort the contours list by the contour area and grab the first (or last, depending on sort order). If you want to make a mask of the one component, you can use

cv2.drawContours(new_blank_image, contours, max_contour_index, color=255, thickness=-1)

Note the -1 will fill the contour as opposed to outlining it. Here's an example drawing the contour over the original image:

Labeled Cameraman

Looks about right.

All in one function:

def largest_component_mask(bin_img):
    """Finds the largest component in a binary image and returns the component as a mask."""

    contours = cv2.findContours(bin_img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)[0]
    # should be [1] if OpenCV 3+

    max_area = 0
    max_contour_index = 0
    for i, contour in enumerate(contours):
        contour_area = cv2.moments(contour)['m00']
        if contour_area > max_area:
            max_area = contour_area
            max_contour_index = i

    labeled_img = np.zeros(bin_img.shape, dtype=np.uint8)
    cv2.drawContours(labeled_img, contours, max_contour_index, color=255, thickness=-1)

    return labeled_img
Sign up to request clarification or add additional context in comments.

11 Comments

i will run this and report back . BTW , really lucid .
can i draw this contour over a color image (actually, gray scale image was derived from it) ? Also, i did print contours[max_contour_index].shape and got (924,1,2) . It is actually x,y coordinates of 924 points. How can I extract xmin,xmax,ymin,ymax as it will denote rectangular mask whose top left and bottom right coordinate are of my interest ?
Sure, but you'll need a 3 channel color image to draw over. I read in the image as grayscale in the first lines of code above, so you'd want to read in a color image instead so you can use drawContours() with a color=(b, g, r) tuple for what color you'd like to draw.
@user1371666 you can also simply use boundingRect() with each contour to find the bounding rectangle of the contour. See more here: docs.opencv.org/3.1.0/dd/d49/tutorial_py_contour_features.html
@user1371666 I can't reproduce your error, possibly because I'm using OpenCV 3. When I run this on an image with a single white point, it works as expected. Can you edit your original post to show, in the single point case, what your contours list looks like? Also please print out all the other variables -- what contour_area is when you go through and what max_contour_index is and print the full output of contours.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.