Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I fill arbitrary closed regions in Matplotlib?

Let me start with where I am:

enter image description here

I created the above image with the following code:

import matplotlib.pyplot as plt
import numpy as np

color_palette_name = 'gist_heat'
cmap = plt.cm.get_cmap(color_palette_name)
bgcolor = cmap(np.random.rand())

f = plt.figure(figsize=(12, 12), facecolor=bgcolor,)
ax = f.add_subplot(111)
ax.axis('off')

t = np.linspace(0, 2 * np.pi, 1000)

x = np.cos(t) + np.cos(6. * t) / 2.0 + np.sin(14. * t) / 3.0
y = np.sin(t) + np.sin(6. * t) / 2.0 + np.cos(14. * t) / 3.0

ax.plot(x, y, color=cmap(np.random.rand()))
ax.fill(x, y, color=cmap(np.random.rand()))
plt.tight_layout()
plt.savefig("../demo/tricky.png", facecolor=bgcolor, edgecolor=cmap(np.random.rand()), dpi=350)

Is there a way to fill the loops (or the triangle-like regions) that are created when the line crosses itself with some other color? It doesn't have to be matplotlib, it could be scikit-image or some other library.

I'm thinking some pseudo-code like:

for region in regions:
    ax.fill(region, color=cmap(np.random.rand()))

But I have no idea how to get the regions, or how filling it would work.

like image 794
JBWhitmore Avatar asked Mar 28 '16 05:03

JBWhitmore


1 Answers

The problem first seemed straightforward to me, my idea was to use blob analysis to detect the different blobs, to group them by size and use a floodfill algorithm to colour them.

However, I have run into some problems with default values for blob analysis which I had not modified, which has cost some time. Furthermore, I have not found any python code snippets for flood filling or colourizing blobs with OpenCV, and there have been some syntax changes for using SimpleBlobDetection compared to older versions for which I could find only little documentation and sample code. So maybe all this code can also be useful for other users.

Hope that I have properly identified the segments you wanted to find. If you want to not include the large dark outer leaves, there is a line to comment out.

For the sake of visualization, you can resize the image (commented out at the moment, remember to adapt size thresholds accordingly by a factor of 4*4=16)

enter image description here

The code is somewhat lengthy with all these options, but hopefully easy to read. I have learned a lot on blob analysis with OpenCV working on this problem, thank you!

Nice image, by the way.

import numpy as np
import cv2

im = cv2.imread('tricky.png')

# For better visibility, resize image to better fit screen
#im= cv2.resize(im, dsize=(0,0),fx=0.25, fy=0.25)

#convert to gray value for blob analysis
imgray= cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)



#### Blob analysis to find inner white leaves
# SimpleBlobDetector will find black blobs on white surface, this is why type=cv2.THRESH_BINARY_INV is necessary
ret,imthresh = cv2.threshold(imgray,160, 255,type=cv2.THRESH_BINARY_INV)

# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()

# Filter by Area.
params.filterByArea = True
params.minArea = 15000
params.maxArea = 150000

# Create a detector with the parameters
detector = cv2.SimpleBlobDetector_create(params)

# Detect blobs.
keypoints = detector.detect(imthresh)

# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures
# the size of the circle corresponds to the size of blob
im_with_keypoints = cv2.drawKeypoints(imthresh, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# Show blobs
cv2.imshow("Keypoints", im_with_keypoints)




####floodfill inner white leaves with blue
#http://docs.opencv.org/3.0-beta/modules/imgproc/doc/miscellaneous_transformations.html

#Create a black mask for floodfill. Mask needs to be 2 pixel wider and taller
maskborder=imgray.copy()
maskborder[:] = 0
bordersize=1
maskborder=cv2.copyMakeBorder(maskborder, top=bordersize, bottom=bordersize, left=bordersize, right=bordersize, borderType= cv2.BORDER_CONSTANT, value=[255,255,255] )
print imgray.shape[:2]
print maskborder.shape[:2]

#Create result image for floodfill
result = im.copy()

#fill white inner segments with blue color
for k in keypoints:
    print int(k.pt[0]),int(k.pt[1])
    seed_pt = int(k.pt[0]),int(k.pt[1])
    cv2.floodFill(result, maskborder, seed_pt, (255,0, 0))




#### Blob analysis to find small triangles
# SimpleBlobDetector will find black blobs on white surface, this is why type=cv2.THRESH_BINARY_INV is necessary
ret,imthresh2 = cv2.threshold(imgray,150, 255,type=cv2.THRESH_BINARY)
ret,imthresh3 = cv2.threshold(imgray,140, 255,type=cv2.THRESH_BINARY_INV)
imthresh4 = cv2.add(imthresh2,imthresh3)

# Setup SimpleBlobDetector parameters.
params = cv2.SimpleBlobDetector_Params()

# Filter by Area.
params.filterByArea = True
params.minArea = 20
params.maxArea = 1000
params.maxArea = 50000 #Using this line includes the outer dark leaves. Comment out if necessary

# Don't filter by Circularity
params.filterByCircularity = False

# Don't filter by Convexity
params.filterByConvexity = False

# Don't filter by Inertia
params.filterByInertia = False

# Create a detector with the parameters
detector = cv2.SimpleBlobDetector_create(params)

# Detect blobs.
keypoints = detector.detect(imthresh4)

# Draw detected blobs as red circles.
# cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS ensures
# the size of the circle corresponds to the size of blob
im_with_keypoints2 = cv2.drawKeypoints(imthresh4, keypoints, np.array([]), (0,0,255), cv2.DRAW_MATCHES_FLAGS_DRAW_RICH_KEYPOINTS)

# Show blobs
cv2.imshow("Keypoints2", im_with_keypoints2)



####floodfill triangles with green
#http://docs.opencv.org/3.0-beta/modules/imgproc/doc/miscellaneous_transformations.html

#Create a black mask for floodfill. Mask needs to be 2 pixel wider and taller
maskborder=imgray.copy()
maskborder[:] = 0
bordersize=1
maskborder=cv2.copyMakeBorder(maskborder, top=bordersize, bottom=bordersize, left=bordersize, right=bordersize, borderType= cv2.BORDER_CONSTANT, value=[255,255,255] )
print imgray.shape[:2]
print maskborder.shape[:2]

#Create result image for floodfill
result2 = result.copy()

#fill triangles with green color
for k in keypoints:
    print int(k.pt[0]),int(k.pt[1])
    seed_pt = int(k.pt[0]),int(k.pt[1])
    cv2.floodFill(result2, maskborder, seed_pt, (0,255, 0))



#cv2.imshow('main',im)
#cv2.imshow('gray',imgray)
#cv2.imshow('borders',maskborder)
#cv2.imshow('threshold2',imthresh2)
#cv2.imshow('threshold3',imthresh3)
#cv2.imshow('threshold4',imthresh4)
cv2.imshow("Result", result2)
cv2.imwrite("result.png",result2)

cv2.waitKey(0)
cv2.destroyAllWindows()
like image 156
tfv Avatar answered Sep 29 '22 21:09

tfv