I would like to get if a polyline and rectangle intersect on opencv+Python:
A = cv2.rectangle(frame,(384,0),(510,128),(0,255,0),3)
pts = np.array([[1300,900],[1750,700],[1000,200],[600,200]], np.int32)
pts = pts.reshape((-1,1,2))
B = cv2.polylines(frame,[pts],True,(244,66,66),7)
How can I find if A intersect with B?
Thank you
Opencv and Numpy don't have direct geometry intersection functions.
You can write your own (see Numpy and line intersections) or a common technique is to draw the rectangle filled with a color and then check if the points along the line on the same image are that color.
Related
I'm very new to the image processing and object detection. I'd like to extract/identify the position and dimensions of teeth in the following image:
Here's what I've tried so far using OpenCV:
import cv2
import numpy as np
planets = cv2.imread('model.png', 0)
canny = cv2.Canny(planets, 70, 150)
circles = cv2.HoughCircles(canny,cv2.HOUGH_GRADIENT,1,40, param1=10,param2=16,minRadius=10,maxRadius=80)
circles = np.uint16(np.around(circles))
for i in circles[0,:]:
# draw the outer circle
cv2.circle(planets,(i[0],i[1]),i[2],(255,0,0),2)
# draw the center of the circle
cv2.circle(planets,(i[0],i[1]),2,(255,0,0),3)
cv2.imshow("HoughCirlces", planets)
cv2.waitKey()
cv2.destroyAllWindows()
This is what I get after applying canny filter:
This is the final result:
I don't know where to go from here. I'd like to get all of the teeth identified. How can I do that?
I'd really appreciate any help..
Note that the teeth-structure is more-or-less a parabola (upside-down). If you could somehow guess the parabolic shape that defines the centroids of those blobs (teeth), then your problem could be simplified to a reasonable extent. I have shown a red line that passes through the centers of the teeth.
I would suggest you to approach it as follows:
Binarize your image (background=0, else 1). You could use sklearn.preprocessing.binarize.
Calculate the centroid of all the non-zero pixels. This is the central blue circle in the image. Call this structure_centroid. See this: How to center the nonzero values within 2D numpy array?.
Make polar slices of the entire image, centered at the location of the structure_centroid. I have shown a cartoon image of such polar slices (triangular semi-transparent). Cover complete 360 degrees. See this: polarTransform library.
Determine the position of the centroid of the non-zero pixels for each of these polar slices. See these:
find the distance between a point and a curve python.
Find the minimum distance from a point to a curve.
The array containing these centroids gives you the locus (path) of the average location of the teeth. Call this centroid_path.
Run an elimination/selection algorithm on the circles you were able to detect, that are closest to the centroid_path. Use a threshold distance to drop the outliers.
This should give you a good approximation of the teeth with the circles.
I hope this helps.
I'm currently using skimage.measure.find_contours() to find contours on a surface. Now that I've found the contours I need to able to find the area enclosed within them.
When all of the vertices are within the data set this is fine as a have a fully enclosed polygon.
However, how do I ensure the polygon is fully enclosed if the contour breaches the edge of the surface, either at an edge or at a corner? When this happens I would like to use the edge of the surface as additional vertices to close off the polygon. For example in the following image, with contours shown, you can see that the contours end at the edge of the image, how do I close them up? Also in the example of the brown contour, which is just a single line, I don't think I want an area returned, how would I single out this case?
I know I can check for enclosed contours/polygons by checking if the last vertices of the polygon is the same as the first.
I have code for calculating the area inside a polygon, taken from here
def find_area(array):
a = 0
ox,oy = array[0]
for x,y in array[1:]:
a += (x*oy-y*ox)
ox,oy = x,y
return -a/2
I just need help in closing off the polygons. And checking for the different cases that might occur.
Thanks
Update:
After applying the solution suggested by #soupault I have this code:
import numpy as np
import matplotlib.pyplot as plt
from skimage import measure
# Construct some test data
x, y = np.ogrid[-np.pi:np.pi:100j, -np.pi:np.pi:100j]
r = np.sin(np.exp((np.sin(x)**3 + np.cos(y)**2)))
# Coordinates of point of interest
pt = [(49,75)]
# Apply thresholding to the surface
threshold = 0.8
blobs = r > threshold
# Make a labelled image based on the thresholding regions
blobs_labels = measure.label(blobs, background = 0)
# Show the thresholded regions
plt.figure()
plt.imshow(blobs_labels, cmap='spectral')
# Apply regionprops to charactersie each of the regions
props = measure.regionprops(blobs_labels, intensity_image = r)
# Loop through each region in regionprops, identify if the point of interest is
# in that region. If so, plot the region and print it's area.
plt.figure()
plt.imshow(r, cmap='Greys')
plt.plot(pt[0][0], pt[0][1],'rx')
for prop in props:
coords = prop.coords
if np.sum(np.all(coords[:,[1,0]] == pt[0], axis=1)):
plt.plot(coords[:,1],coords[:,0],'r.')
print(prop.area)
This solution assumes that each pixel is 1x1 in size. In my real data solution this isn't the case so I have also applied the following function to apply linear interpolation to the data. I believe you can also apply a similar function to make the area of each pixel smaller and increase the resolution of the data.
import numpy as np
from scipy import interpolate
def interpolate_patch(x,y,patch):
x_interp = np.arange(np.ceil(x[0]), x[-1], 1)
y_interp = np.arange(np.ceil(y[0]), y[-1], 1)
f = interpolate.interp2d(x, y, patch, kind='linear')
patch_interp = f(x_interp, y_interp)
return x_interp, y_interp, patch_interp
If you need to measure the properties of different regions, it is natural to start with finding the regions (not contours).
The algorithm will be the following, in this case:
Prepare a labeled image:
1.a Either fill the areas between different contour lines with the different colors;
1.b Or apply some image thresholding function, and then run skimage.measure.label (http://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.label);
Execute regionprops using the very labeled image as an input (http://scikit-image.org/docs/dev/api/skimage.measure.html#skimage.measure.regionprops);
Iterate over regions in regionprops and calculate the desired parameters (area, perimeter, etc).
Once you identified the regions in your image via regionprops, you can call .coords for each of them to get the enclosed contour.
If someone will need close open contours by image edges (and make a polygon) here is:
import shapely.geometry as sgeo
import shapely.ops as sops
def close_contour_with_image_edge(contour, image_shape):
"""
this function uses shapely because its easiest way to do that
:param contour: contour generated by skimage.measure.find_contours()
:param image_shape: tuple (row, cols), standard return of numpy shape()
:return:
"""
# make contour linestring
contour_line = sgeo.LineString(contour)
# make image box linestring
box_rows, box_cols = image_shape[0], image_shape[1]
img_box = sgeo.LineString(coordinates=(
(0, 0),
(0, box_cols-1),
(box_rows-1, box_cols-1),
(box_rows-1, 0),
(0, 0)
))
# intersect box with non-closed contour and get shortest line which touch both of contour ends
edge_points = img_box.intersection(contour_line)
edge_parts = sops.split(img_box, edge_points)
edge_parts = list(part for part in edge_parts.geoms if part.touches(edge_points.geoms[0]) and part.touches(edge_points.geoms[1]))
edge_parts.sort(reverse=False, key=lambda x: x.length)
contour_edge = edge_parts[0]
# weld it
contour_line = contour_line.union(contour_edge)
contour_line = sops.linemerge(contour_line)
contour_polygon = sgeo.Polygon(contour_line.coords)
return contour_polygon
I've extracted a Circle shaped mask from an image in OpenCV. I used the following code for the same:
H, W = img.shape
x, y = np.meshgrid(np.arange(W), np.arange(H))**
d2 = (x - xc)**2 + (y - yc)**2**
mask = d2 < r **2**
And, used the mask value to find the average color outside the circle.
outside = np.ma.masked_where(mask, img)**
average_color = outside.mean()**
I want to extract an Ellipse from an image in the same above process in OpenCV Python.
Thank You.
Drawing Ellipse
To draw the ellipse, we need to pass several arguments. One argument is the center location (x,y). Next argument is axes lengths (major axis length, minor axis length). angle is the angle of rotation of ellipse in anti-clockwise direction. startAngle and endAngle denotes the starting and ending of ellipse arc measured in clockwise direction from major axis. i.e. giving values 0 and 360 gives the full ellipse. For more details, check the documentation of cv2.ellipse(). Below example draws a half ellipse at the center of the image.
cv2.ellipse(img,(256,256),(100,50),0,0,180,255,-1)
Taken from Miki's Link in the Question Comments
the blue pen is the contour
and the red pen is the straight line
how could I find the two areas of the intersection of line and contour
Now, I can get the contour area by
area = cv2.contourArea(np.array( [ [i] for i in blue_points ] ))
A simple but perhaps not the most efficient way would be to use cv.drawContours and cv.line to create two images: one with the contour of the blob and one with the contour of the line. Then cv.bitwise_and them together, and any point that is still positive will be points of intersection.
Shapely library makes it quick.
Assuming you have points of contour and line:
from shapely.geometry import Polygon, LineString
poly = Polygon([(5,5), (10,10), (10,0)])
a = LineString([(0, 0), (8, 8)])
print(a.intersects(poly))
There are options to speed up the code. Not checked.
I have a set of boundary points of an object.
I want to draw it using opencv as contour.
I have no idea that how to convert my points to contour representation.
To the same contour representation which is obtained by following call
contours,_ = cv2.findContours(image,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
Any ideas?
Thanks
By looking at the format of the contours I would think something like this should be sufficient:
contours = [numpy.array([[1,1],[10,50],[50,50]], dtype=numpy.int32) , numpy.array([[99,99],[99,60],[60,99]], dtype=numpy.int32)]
This small program gives an running example:
import numpy
import cv2
contours = [numpy.array([[1,1],[10,50],[50,50]], dtype=numpy.int32) , numpy.array([[99,99],[99,60],[60,99]], dtype=numpy.int32)]
drawing = numpy.zeros([100, 100],numpy.uint8)
for cnt in contours:
cv2.drawContours(drawing,[cnt],0,(255,255,255),2)
cv2.imshow('output',drawing)
cv2.waitKey(0)
To create your own contour from a python list of points L
L=[[x1,y1],[x2,y2],[x3,y3],[x4,y4],[x5,y5],[x6,y6],[x7,y7],[x8,y8],[x9,y9],...[xn,yn]]
Create a numpy array ctr from L, reshape it and force its type
ctr = numpy.array(L).reshape((-1,1,2)).astype(numpy.int32)
ctr is our new countour, let's draw it on an existing image
cv2.drawContours(image,[ctr],0,(255,255,255),1)
A contour is simply a curve joining all continuous points so to create your own contour, you can create a np.array() with your (x,y) points in clockwise order
points = np.array([[25,25], [70,10], [150,50], [250,250], [100,350]])
That's it!
There are two methods to draw the contour onto an image depending on what you need:
Contour outline
If you only need the contour outline, use cv2.drawContours()
cv2.drawContours(image,[points],0,(0,0,0),2)
Filled contour
To get a filled contour, you can either use cv2.fillPoly() or cv2.drawContours() with thickness=-1
cv2.fillPoly(image, [points], [0,0,0]) # OR
# cv2.drawContours(image,[points],0,(0,0,0),-1)
Full example code for completeness
import cv2
import numpy as np
# Create blank white image
image = np.ones((400,400), dtype=np.uint8) * 255
# List of (x,y) points in clockwise order
points = np.array([[25,25], [70,10], [150,50], [250,250], [100,350]])
# Draw points onto image
cv2.drawContours(image,[points],0,(0,0,0),2)
# Fill points onto image
# cv2.fillPoly(image, [points], [0,0,0])
cv2.imshow('image', image)
cv2.waitKey()
To add to Cherif KAOUA's answer, I found I had to convert to list and zip my numpy array. Reading in an array of points from a text file:
contour = []
with open(array_of_points,'r') as f:
next(f) // line one of my file gives the number of points
for l in f:
row = l.split()
numbers = [int(n) for n in row]
contour.append(numbers)
ctr = np.array(contour).reshape((-1,1,2)).astype(np.int32)
ctr = ctr.tolist()
ctr = zip(*[iter(ctr)]*len(contour))