Splitting Contour Into Subcontours Based on Straigtness - python

I have a program which gets the contours of an image. I want to break these contours into sub-contours based on relative straightness. If the straightness of an extra pixel is less than a threshold value times the previous straightness, the contour should be split. When I put an image of a square drawn in paint, the program output should be 4 sub-contours. However, for some reason I get 3 sub-contours with 2 of them including 2-3 points and one including most of the original contour. Note, the 0.85 in my code is a threshold derived from a research paper. dPreious is the pixel distance of the edge(Not Including the Current Pixel). v is the euclidean distance between end-points.
def split(contour):
new = [];
toAdd = [];
sCurrent = 0.0
sPrevious = 0.0
v = 0.0
dPrevious = 1
for points in range(0,len(contour)):
currentX = float(contour[points][0])
currentY = float(contour[points][1])
if points != 0:
v = math.sqrt(math.pow(float(toAdd[0][0])-currentX,2) + math.pow(float(toAdd[0][1])-currentY,2))
dPrevious = len(toAdd)
sCurrent = v/dPrevious
if sCurrent < 0.85*sPrevious:
new.append(toAdd)
toAdd = []
toAdd.append(contour[points])
sPrevious = sCurrent
new.append(toAdd)
return new

If you use OpenCV for finding the contours you can use the functionality of the cv2.approxPolyDP() function to do the job for you:
cv2.approxPolyDP. You might have to experiment with the epsilon parameter to have succes with it.

Related

The area & center of gravity of a polygon having non-uniform density of vertices? (in Python)

I would like to calculate the COG of a polygon shaped exactly like the contour map of my town. However, using the available database of borderpoints would produce a rigged result, since some places have much bigger density of borderpoints than others, so the center of gravity would be skewed towards these regions. I tried to equalise the density of vertices by producing this Python code:
import numpy as np
punkty = open("borderpoints.txt","r", encoding = "utf8")
tempp = []
a = []
for line in punkty:
for c in line:
if c != " ":
tempp.append(c)
else:
p = "".join(tempp)
a.append(p)
tempp = []
i = 0
x= []
y = []
fx = open("outx1.txt", "w")
fy = open("outy1.txt", "w")
while i<len(a)-1:
x.append(a[i])
fx.write(a[i])
fx.write("\n")
y.append(a[i+1])
fy.write(a[i+1])
fy.write("\n")
i= i+2
j = 0
jump = 20
newxs = []
newys = []
fnx = open("newxs.txt","w")
fny = open("newys.txt", "w")
while j<len(x):
L = np.sqrt(pow((float(y[j+1])-float(y[j])),2)+pow((float(x[j+1])-float(x[j])),2))
n = jump*L
interval = (float(y[j+1])-float(y[j]))/n
k = 1
slope = (float(x[j+1])-float(x[j]))/(float(y[j+1])-float(y[j]))
inters = float(x[j+1])-slope*float(y[j+1])
while k<n+1:
g = float(y[j])+k*interval
newxs.append(g)
fnx.write(str(g))
fnx.write("\n")
g = (slope*(float(y[j])+k*interval)+inters)
newys.append(g)
fny.write(str(g))
fny.write("\n")
k = k+1
j = j+2
k=1
newxs.append(x)
newys.append(y)
but in the result, the points were denser everywhere except places that were previously empty and were supposed to get populated by the algorithm.
The graphs of the map before the application of the algorithm and
after (some proportions may vary but the main problem is the empty spot).
What is the approach that I could use in solving this problem? How to make the points distributed equally or maybe it's possible to calculate the COG with some other method?
My aim is that the amount of points shouldn't determine the COG, but rather determine the position of polygon sides - these are most important here, but obviously there is no database for them and it's harder to calculate the COG having a lot of linear functions and their ranges.

How to measure size of gear using OpenCV

I am working on project where I have to measure size of gear. I have already find contour of the gear and teeth of the gear. Now I want to measure its real world size with 100 percent accuracy real world dimension of gear is 53mm in outer diameter and 43mm in inner diameter and teeth size is 5mm, and also if I remove this gear and place large gear it also measure its correct diameter
# count outer teeth
c = max(max_c, key=cv2.contourArea)
hull = cv2.convexHull(c, clockwise=True, returnPoints=False)
hull1 = []
for i in hull:
if len(hull1) == 0:
hull1.append(i)
else:
last = hull1[-1]
diff = c[i] - c[last]
if cv2.norm(diff) > 10:
hull1.append(i)
hull1 = np.asarray(hull1)
hull2 = hull1[:-1].copy()
# count inner teeth
defects = cv2.convexityDefects(c, hull2)
#print(defects)
inner_points = []
for i in range(defects.shape[0]):
s, e, f, d = defects[i, 0]
far = tuple(c[f][0])
#print(far)
inner_points.append(f)

Detect a simple circle in an image and find its radius using numpy

I have an image with a simple circle in it.
How to detect the circle using numpy and find its radius ?
I tried learning but couldnt. I would like to learn from the program that you answer here.
Only thing I know as of now is:
from scipy import misc
f = misc.imread("/path/to/file.png")
# then dont know what to do
Here is the image - https://c2.staticflickr.com/6/5476/14135136623_3973d3f03c_b.jpg
I'm assuming for now that the image always consists of exactly one non-black circle in an otherwise black image.
I'm also assuming you don't care particularly about efficiency of the algorithm.
Loop over all of the pixels in the image, keeping a sum, count, min, and max tally of all white pixel's x- and y-coordinates (min/max needed only for x or y, not both). Something like this (just roughly -- I don't have scipy installed atm):
circle_count = 0
circle_sum_x = 0
circle_sum_y = 0
circle_min_x = 0
circle_max_x = 0
for y in range(image_height):
for x in range(image_width):
if pixel(x,y) is not black:
circle_count += 1
circle_sum_x += x
circle_sum_y += y
circle_min_x = min(circle_min_x, x)
circle_max_x = max(circle_max_y, y)
if circle_count == 0:
print "No circle found!"
else:
circle_center_x = circle_sum_x / circle_count
circle_center_y = circle_sum_y / circle_count
circle_radius = (circle_max_x - circle_min_x) / 2

How to solve...ValueError: cannot convert float NaN to integer

I'm running quite a complex code so I won't bother with details as I've had it working before but now im getting this error.
Particle is a 3D tuple filled with 0 or 255, and I am using the scipy centre of mass function and then trying to turn the value into its closest integer (as I'm dealing with arrays). The error is found with on the last line... can anyone explain why this might be??
2nd line fills Particle
3rd line deletes any surrounding particles with a different label (This is in a for loop for all labels)
Particle = []
Particle = big_labelled_stack[x_start+20:x_stop+20,y_start+20:y_stop+20,z_start+20:z_stop+20]
Particle = np.where(Particle == i ,255,0)
CoM = scipy.ndimage.measurements.center_of_mass(Particle)
CoM = [ (int(round(x)) for x in CoM ]
Thanks in advance. If you need more code just ask but I dont think it will help you and its very messy.
################## MORE CODE
border = 30
[labelled_stack,no_of_label] = label(labelled,structure_array,output_type)
# RE-LABEL particles now no. of seeds has been reduced! LAST LABELLING
#Increase size of stack by increasing borders and equal them to 0; to allow us to cut out particles into cube shape which else might lye outside the border
h,w,l = labelled.shape
big_labelled_stack = np.zeros(shape=(h+60,w+60,l+60),dtype=np.uint32)
# Creates an empty border around labelled_stack full of zeros of size border
if (no_of_label > 0): #Small sample may return no particles.. so this stage not neccesary
info = np.zeros(shape=(no_of_label,19)) #Creates array to store coordinates of particles
for i in np.arange(1,no_of_label,1):
coordinates = find_objects(labelled_stack == i)[0] #Find coordinates of label i.
x_start = int(coordinates[0].start)
x_stop = int(coordinates[0].stop)
y_start = int(coordinates[1].start)
y_stop = int(coordinates[1].stop)
z_start = int(coordinates[2].start)
z_stop = int(coordinates[2].stop)
dx = (x_stop - x_start)
dy = (y_stop - y_start)
dz = (z_stop - z_start)
Particle = np.zeros(shape=(dy,dx,dz),dtype = np.uint16)
Particle = big_labelled_stack[x_start+30:x_start+dx+30,y_start+30:y_start+dy+30,z_start+30:z_start+dz+30]
Particle = np.where(Particle == i ,255,0)
big_labelled_stack[border:h+border,border:w+border,border:l+border] = labelled_stack
big_labelled_stack = np.where(big_labelled_stack == i , 255,0)
CoM_big_stack = scipy.ndimage.measurements.center_of_mass(big_labelled_stack)
C = np.asarray(CoM_big_stack) - border
if dx > dy:
b = dx
else: #Finds the largest of delta_x,y,z and saves as b, so that we create 'Cubic_Particle' of size 2bx2bx2b (cubic box)
b = dy
if dz > b:
b = dz
CoM = scipy.ndimage.measurements.center_of_mass(Particle)
CoM = [ (int(round(x))) for x in CoM ]
Cubic_Particle = np.zeros(shape=(2*b,2*b,2*b))
Cubic_Particle[(b-CoM[0]):(b+dx-CoM[0]),(b-CoM[1]):(b+dy-CoM[1]),(b-CoM[2]):(b+dz-CoM[2])] = Particle
volume = Cubic_Particle.size # Gives volume of the box in voxels
info[i-1,:] = [C[0],C[1],C[2],i,C[0]-b,C[1]-b,C[2]-b,C[0]+b,C[1]+b,C[2]+b,volume,0,0,0,0,0,0,0,0] # Fills an array with label.No., size of box, and co-ords
else:
print('No particles found, try increasing the sample size')
info = []
Ok, so I have a stack full of labelled particles, there are two things I am trying to do, first find the centre of masses of each particle with respect ot the labelled_stack which is what CoM_big_labelled_stack (and C) does. and stores the co-ords in a list (tuple) called info. I am also trying to create a cubic box around the particle, with its centre of mass as the centre (which is relating to the CoM variable), so first I use the find objects function in scipy to find a particle, i then use these coordinates to create a non-cubic box around the particle, and find its centre of mass.I then find the longest dimension of the box and call it b, creating a cubic box of size 2b and filling it with particle in the right position.
Sorry this code is a mess, I am very new to Python

How to extract arbitrary 2D slice from 3D volume using Scipy?

I'm using Scipy for rendering planes from 3D data (vector 200x200x200).
I can specify the wanted plane by 2 vectors or vector and an angle.
I want to extract such an arbitrary slice from this 3D volume.
I found how to do it in Matlab:
http://www.mathworks.com/help/techdoc/ref/slice.html
How do I do it in Scipy?
You can use scipy.ndimage.interpolation.rotate to rotate your 3d array to whatever angle you want (it uses spline interpolation) then you can take a slice out of it.
def extract_slice(data, triplet):
"""
Algorithm:
1. find intersections of the plane with the data box edges
2. for these pts, find axis-oriented b-box
3. find the "back" trans (A,T) from R2 to R3, like X' = AX + T
use (0,0), (0,h), (w,0) which are easy to calculate
4. use the trans (with trilinear-interpolation) for every value in the 2D (w,h) image
"""
I will release the code properly in a few months I believe as a part of this project.
I was addressing a similar task as the OP so I came up with this code based on numpy (not scipy) to extract any given slice from a volume given a position vector of any point of the plane and three orthogonal orientation vectors.
I apologise for the length of my answer, but given the complexity of the problem at hand i thought it would be better to give this amount of detail.
For my particular problem these vectors were defined in mm instead of pixels so the spacing (i.e. distance between two consecutive volume pixels at each direction) was also used as input. I have used a nearest neighbour approach to interpolate the subpixel points of the slice.
reslice_volume (volume, spacing, o1, o2, n, pos)
The main steps behind this algorithm are as follow. Note that i use plane and slice interchangeably:
1. Get intersection lines between the desired plane and the bounds of the volume.
def PlaneBoundsIntersectionsLines (n, pos):
"""Outputs points and vectors defining the lines that the view creates by intersecting the volume's bounds.
Input:
Normal vector of the given plane and the coords of a point belonging to the plane.
Output:
normals_line, points_line
"""
def intersectionPlanePlane(n1,p1,n2,p2):
# Get direction of line
nout = np.cross(n1.reshape((1,3)),n2.reshape((1,3))).reshape(3,1)
nout = normalizeLength(nout)
M = np.concatenate((n1.reshape(1,3),n2.reshape(1,3)), axis=0)
b = np.zeros((2,1))
# print(n1.shape, p1.shape)
b[0,0]=np.dot(n1,p1)
b[1,0]=np.dot(n2,p2)
pout,resid,rank,s = np.linalg.lstsq(M,b, rcond=None)
return pout, nout
# ... For each face
normalFaces = np.concatenate((np.eye(3,3),np.eye(3,3)), axis = 1)
pointsFaces = np.array([[0,0,0],[0,0,0],[0,0,0], [379.9872, 379.9872, 169.5], [379.9872, 379.9872, 169.5], [379.9872, 379.9872, 169.5]]).transpose()
points_line = np.zeros((3,6))
normals_line = np.zeros((3,6))
for face in range(6):
n1 = normalFaces[:,face].reshape(3,)
p1 = pointsFaces[:,face].reshape(3,)
pout, nout = intersectionPlanePlane(n1,p1,n,pos)
points_line[:,face] = pout.reshape((3,))
normals_line[:,face] = nout.reshape((3,))
return normals_line, points_line
2. Get intersection points between these lines that are close enough to the borders of the volume to be considered corners of the intersection between the plane and the volume.
def FindPlaneCorners(normals_line, points_line):
"""Outputs the points defined by the intersection of the input lines that
are close enough to the borders of the volume to be considered corners of the view plane.
Input:
Points and vectors defining lines
Output:
p_intersection, intersecting_lines
"""
def intersectionLineLine(Up,P0,Uq,Q0):
# Computes the closest point between two lines
# Must be column points
b = np.zeros((2,1))
b[0,0] = -np.dot((P0-Q0),Up)
b[1,0] = -np.dot((P0-Q0),Uq)
A = np.zeros((2,2))
A[0,0] = np.dot(Up,Up)
A[0,1] = np.dot(-Uq,Up)
A[1,0] = np.dot(Up,Uq)
A[1,1] = np.dot(-Uq,Uq)
if ( np.abs(np.linalg.det(A)) < 10^(-10) ):
point = np.array([np.nan, np.nan, np.nan]).reshape(3,1)
else:
lbd ,resid,rank,s = np.linalg.lstsq(A,b, rcond=None)
# print('\n')
# print(lbd)
P1 = P0 + lbd[0]*Up;
Q1 = Q0 + lbd[1]*Uq;
point = (P1+Q1)/2;
return point
# ... ... Get closest point for every possible pair of lines and select only the ones inside the box
npts = 0
p_intersection = []
intersecting_lines = []
# ... Get all possible pairs of lines
possible_pairs = np.array(list(itertools.combinations(np.linspace(0,5,6), 2)))
for pair in possible_pairs:
k = int(pair[0])
j = int(pair[1])
Up = normals_line[:,k]
P0 = points_line[:,k]
Uq = normals_line[:,j]
Q0 = points_line[:,j]
closest_point = intersectionLineLine(Up,P0,Uq,Q0)
epsilon = 2.2204e-10
# ... ... Is point inside volume? Is it close to the border?
if closest_point[0] <= 379.9872 + epsilon and closest_point[0] >= 0 - epsilon and \
closest_point[1] <= 379.9872 + epsilon and closest_point[1] >= 0 - epsilon and \
closest_point[2] <= 169.5 + epsilon and closest_point[2] >= 0 - epsilon:
# ... ... Is it close to the border? 25 mm?
th = 25
if 379.9872 - closest_point[0] <= th or closest_point[0] - 0 <= th or \
379.9872 - closest_point[1] <= th or closest_point[1] - 0 <= th or \
169.5 - closest_point[2] <= th or closest_point[2] - 0 <= th:
# print('It is close to teh border')
npts += 1
p_intersection.append(closest_point)
intersecting_lines.append([k,j])
p_intersection = np.array(p_intersection).transpose()
return p_intersection, intersecting_lines
3. Transform the points found into the slice's reference frame (sRF) (we can center the RF arbitrarily within the slice plane).
dim = volume.shape
# ... Get intersection lines between plane and volume bounds
normals_line, points_line = PlaneBoundsIntersectionsLines (n, pos)
# ... Get intersections between generated lines to get corners of view plane
p_intersection, intersecting_lines = FindPlaneCorners (normals_line, points_line)
# ... Calculate parameters of the 2D slice
# ... ... Get corners of slice from volume RF (vrf) to slice RF (srf) - in this case centered in the middle of teh slice
# ... ... ... Define T_vrf2srf
Pose_slice_vrf = M_creater(o1,o2,n,pos)
# ... ... ... Apply transform
p_intersection_slicerf = np.zeros(p_intersection.shape)
for corner in range(p_intersection.shape[1]):
pt_arr = np.concatenate((p_intersection[:,corner],np.ones((1,))) ,axis = 0).reshape((4,1))
p_intersection_slicerf[:,corner] = np.matmul(np.linalg.inv(Pose_slice_vrf), pt_arr)[:-1].reshape((3,))
4. Get minimum x and y coordinates across these points and define a corner point that will be used as origin of the plane/slice. Transform this origin point back to the volume's RF (vRF) and define a new transform matrix that shifts RF from vRF to a sRF but now centered on said origin point.
5. From these inslice points coordinates we can determine the size of the slice and then use it to generate all possible inslice indexes of the target slice.
# ... ... Get slice size based on corners and spacing
spacing_slice = [1,1,8]
min_bounds_slice_xy = np.min(p_intersection_slicerf,axis=1)
max_bounds_slice_xy = np.max(p_intersection_slicerf,axis=1)
size_slice_x = int(np.ceil((max_bounds_slice_xy[0] - min_bounds_slice_xy[0] - 1e-6) / spacing_slice[0]))
size_slice_y = int(np.ceil((max_bounds_slice_xy[1] - min_bounds_slice_xy[1] - 1e-6) / spacing_slice[1]))
slice_size = [size_slice_x, size_slice_y, 1]
print('slice_size')
print(slice_size)
# ... ... Get corner in slice coords and redefine transform mat - make corner origin of the slice
origin_corner_slice = np.array([min_bounds_slice_xy[0],min_bounds_slice_xy[1],0])
pt_arr = np.concatenate((origin_corner_slice,np.ones((1,))) ,axis = 0).reshape((4,1))
origin_corner_slice_vrf = np.matmul(Pose_slice_vrf, pt_arr)[:-1].reshape((3,))
Pose_slice_origin_corner_vrf = M_creater(o1,o2,n,origin_corner_slice_vrf)
# ... ... Get every possible inslice coordinates
xvalues = np.linspace(0,size_slice_x-1,size_slice_x)
yvalues = np.linspace(0,size_slice_y-1,size_slice_y)
zvalues = np.linspace(0,0,1)
xx, yy = np.meshgrid(xvalues, yvalues)
xx = xx.transpose()
yy = yy.transpose()
zz = np.zeros(xx.shape)
inslice_coords = np.concatenate((xx.reshape(-1,1), yy.reshape(-1,1), zz.reshape(-1,1)), axis = 1)
6. Next step is to use the newly defined transform matrix (step 4) to map every possible inslice index to the volume's reference frame.
# ... ... Map every point of slice into volume's RF
inslice_coords_vrf = np.zeros(inslice_coords.shape)
for coord_set in range(inslice_coords.shape[0]):
pt_arr = np.concatenate((inslice_coords[coord_set,:],np.ones((1,))) ,axis = 0).reshape((4,1))
inslice_coords_vrf[coord_set,:] = np.matmul(Pose_slice_origin_corner_vrf, pt_arr)[:-1].reshape((3,))
7. We now have all the vRF coordinates that the slice encompasses that should be promptly converted into pixel values by dividing them by the respective spacing. At this step we find that we end up with non-integer pixel values as the slices passes through subpixel locations of the volume. We round the pixel value to its nearest integer - nearest neighbour interpolation.
# ... ... ... Convert to pixel coord - here we used teh resampled spacing
inslice_coords_vrf_px = inslice_coords_vrf.copy()
inslice_coords_vrf_px[:,0] = inslice_coords_vrf[:,0] / spacing[0]
inslice_coords_vrf_px[:,1] = inslice_coords_vrf[:,1] / spacing[1]
inslice_coords_vrf_px[:,2] = inslice_coords_vrf[:,2] / spacing[2]
# ... ... Interpolate pixel value at each mapped point - nearest neighbour int
# ... ... ... Convert pixel value to its closest existing value in the volume
inslice_coords_vrf_px = np.round(inslice_coords_vrf_px, 0).astype(int)
8. Next, we determine which pixels of the slice are actually within the bounds of the volume and get their values. Pixels outside volume are padded to 0.
# ... ... Find slice voxels within volume bounds
in_mask = np.zeros((inslice_coords_vrf_px.shape[0], 1))
idx_in = []
for vox in range(in_mask.shape[0]):
if not np.any(inslice_coords_vrf_px[vox,:]<0) and \
inslice_coords_vrf_px[vox,0]<dim[0] and \
inslice_coords_vrf_px[vox,1]<dim[1] and \
inslice_coords_vrf_px[vox,2]<dim[2]:
in_mask[vox] = 1
idx_in.append(vox)
idx_in = np.array(idx_in)
# ... ... Get pixel value from volume based on interpolated pixel indexes
extracted_slice = np.zeros((inslice_coords_vrf_px.shape[0], 1))
for point in range(inslice_coords_vrf_px.shape[0]):
if point in idx_in:
vol_idx = inslice_coords_vrf_px[point,:]
extracted_slice[point] = volume[vol_idx[0], vol_idx[1], vol_idx[2]]
# ... ... Reshape to slice shape
extracted_slice = extracted_slice.reshape((slice_size[0], slice_size[1]))
I added a plot for extra clarity. Here the volume is defined by the bounding box in black. Line intersections of the slice with the planes defined by the faces of the box/volume in dotted orange. In blue the intersection points between the previous lines. Points in pink belong to the slice and the orange ones belong to the slice and are within the volume.
In my case i was dealing with MRI volumes, so as example I added my resulting slice from the volume.

Categories