create a 3d cylinder inside 3d volume - python-3.x

I have 3d volume. Which has shape of (399 x 512 x 512). And It has voxel spacing of 0.484704 x 0.484704 x 0.4847
Now, I want to define a cylinder inside this volume with length 5mm, diameter 1mm, intensity 1 inside, intensity 0 outside.
I saw an example to define a cylinder in internet like this code:
from mpl_toolkits.mplot3d import Axes3D
def data_for_cylinder_along_z(center_x,center_y,radius,height_z):
z = np.linspace(0, height_z, 50)
theta = np.linspace(0, 2*np.pi, 50)
theta_grid, z_grid=np.meshgrid(theta, z)
x_grid = radius*np.cos(theta_grid) + center_x
y_grid = radius*np.sin(theta_grid) + center_y
return x_grid,y_grid,z_grid
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
Xc,Yc,Zc = data_for_cylinder_along_z(0.2,0.2,0.05,0.1)
ax.plot_surface(Xc, Yc, Zc, alpha=0.5)
plt.show()
However, I don't know how to define the cylinder inside the 3d volume keeping all the conditions (length 5mm, diameter 1mm, intensity 1 inside, intensity 0 outside) true. I also want to define the center of cylinder automatically. So that I can define the cylinder at any place of inside the 3d volume keeping the other condition true. Can anyone show or provide any example?
Thanks a lot in advance.

One simple way of solving this would be to perform each of the checks individually and then just keep the voxels that satisfy all of your constraints.
If you build a grid with all of the centers of the voxels: P (399 x 512 x 512 x 3), each voxel at (i,j,k) will be associated with its real-world position (x,y,z).
That's a little tricky, but it should look something like this:
np.stack(np.meshgrid(np.arange(0, shape[0]),
np.arange(0, shape[1]),
np.arange(0, shape[2]), indexing='ij'), axis=3)
If you subtract the cylinder's center (center_x,center_y, center_z), you're left with the relative positions of each (i,j,k) voxel P_rel (399 x 512 x 512 x 3)
When you have that, you can apply each of your tests one after the other. For a Z-oriented cylinder with a radius and height_z it would look something like:
# constrain the Z-axis
not_too_high = P_rel[:,:,:,2]<= (0.5*height_z)
not_too_low = P_rel[:,:,:,2]>= (-0.5*height_z)
# constrain the radial direction
not_too_far = np.linalg.norm(P_rel[:,:,:,:2],axis=3)<=radius
voxels_in_cyl = not_too_high & not_too_low & not_too_far
I haven't tested the code, but you get the idea.
If you wanted to have an cylinder with an arbitrary orientation you would have to project P_rel into axial and radial components and then do an analogous check without "hard-coding" the indices as I did in this example

Related

Divide a circle into n number of equal pixels in Python

Question background: In python, I am working on a task in which I have to project the nodes of geometry (X and Y coordinates). I have plotted a graph which shows Geometry and a circle around the geometry as shown in picture below using below code.
from matplotlib import pyplot as plt, patches
plt.rcParams["figure.figsize"] = [9.00, 6.50]
plt.rcParams["figure.autolayout"] = True
fig = plt.figure()
ax = fig.add_subplot()
# other plt.scatter or plt.plot here
plt.scatter(x_new, y_new) # x_new and y_new is a list of coordinates
circle1 = plt.Circle((0, 0), radius=4, fill = False) # (0, 0) is a centre of circle with radius 4
ax.add_patch(circle1)
ax.axis('equal')
plt.show()
My Question: I have to divide the circle into 36 pixels. I do not have clue at the moment what code I should write to do this. I want my result like in the picture below. Kindly help me on this.

How to set HoughCircles parameters automatically to detect different sizes of circles in opencv python?

I want to set HoughCircles parameters automatically to detect all size of circles in an image. And also should detect group of same size circles.
I am trying group of same size circles in one image. And group of same size circles in different image, the sizes of circles in both image are different.
So how to set HoughCircles parameters automatically that can detect group of circles in any image.
please help me.
Thank u
If you're looking to collectively just "bin" same-size circles, the below should serve as a good starting point that can be tweaked for your application.
import cv2
import numpy as np
img = cv2.imread('C:\\Test\\circles.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, dp=2.0, minDist=50, minRadius=20, maxRadius=250)
radius_map = {}
for n in range(20, 250, 1):
radius_map[n] = []
if circles is not None:
circles = np.round(circles[0, :]).astype("int")
for (x, y, r) in circles:
radius_map[r].append((x, y, r))
for key in radius_map:
if len(radius_map[key]) > 0:
output = img.copy()
for x, y, r in radius_map[key]:
cv2.circle(output, (x, y), r, (0, 255, 0), 4)
cv2.imshow(f"Radius {key}", output)
cv2.waitKey(0)
If you require some thresholded band of say, circles with radius 50 and 51 are considered the same size, you can iterate over the radius_map dict object and group radius bins together.
Input Image:
Output Images:

How to use `extent` in matplotlib ax.imshow() without changing the positions of the overlayed ax.text() handles?

I am trying to annotate a heatmap. The matplotlib docs present an example, which suggests creating a helper function to format the annotations. I feel there must be a simpler way to do what I want. I can annotate inside the boxes of the heatmap, but these texts change position when editing the extent of the heatmap. My question is how to use extent in ax.imshow(...) while also using ax.text(...) to annotate the correct positions. Below is an example:
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.colors import Normalize
def get_manhattan_distance_matrix(coordinates):
shape = (coordinates.shape[0], 1, coordinates.shape[1])
ct = coordinates.reshape(shape)
displacement = coordinates - ct
return np.sum(np.abs(displacement), axis=-1)
x = np.arange(11)[::-1]
y = x.copy()
coordinates = np.array([x, y]).T
distance_matrix = get_manhattan_distance_matrix(coordinates)
# print("\n .. {} COORDINATES:\n{}\n".format(coordinates.shape, coordinates))
# print("\n .. {} DISTANCE MATRIX:\n{}\n".format(distance_matrix.shape, distance_matrix))
norm = Normalize(vmin=np.min(distance_matrix), vmax=np.max(distance_matrix))
This is where to modify the value of extent.
extent = (np.min(x), np.max(x), np.min(y), np.max(y))
# extent = None
According to the matplotlib docs, the default extent is None.
fig, ax = plt.subplots()
handle = ax.imshow(distance_matrix, cmap='plasma', norm=norm, interpolation='nearest', origin='upper', extent=extent)
kws = dict(ha='center', va='center', color='gray', weight='semibold', fontsize=5)
for i in range(len(distance_matrix)):
for j in range(len(distance_matrix[i])):
if i == j:
ax.text(j, i, '', **kws)
else:
ax.text(j, i, distance_matrix[i, j], **kws)
plt.show()
plt.close(fig)
One can generate two figures by modifying extent - simply uncomment the commented line and comment the uncommented line. The two figures are below:
One can see that by setting extent, the pixel locations change, which in turn changes the positions of the ax.text(...) handles. Is there a simple solution to fix this - that is, set an arbitrary extent and still have the text handles centered in each box?
When extent=None, the effective extent is from -0.5 to 10.5 in both x and y. So the centers lie on the integer positions. Setting the extent from 0 to 10 doesn't align with the pixels. You'd have to multiply by 10/11 to get them right.
The best approach would be to set extent = (np.min(x)-0.5, np.max(x)+0.5, np.min(y)-0.5, np.max(y)+0.5) to get the centers back at integer positions.
Also note that default an image is displayed starting from the top, and that the y-axis is reversed. If you change the extent, to get the image upright, you need ax.imshow(..., origin='lower'). (The 0,0 pixel should be the blue one in the example plot.)
To put a text in the center of a pixel, you can add 0.5 to the horizontal index, divide by the width in pixels and multiply by the difference of the x-axis. And the similar calculation for the y-axis. To get better readability, the text color can be made dependent on the pixel color.
# ...
extent = (np.min(x), np.max(x), np.min(y), np.max(y))
x0, x1, y0, y1 = extent
fig, ax = plt.subplots()
handle = ax.imshow(distance_matrix, cmap='plasma', norm=norm, interpolation='nearest', origin='lower', extent=extent)
kws = dict(ha='center', va='center', weight='semibold', fontsize=5)
height = len(distance_matrix)
width = len(distance_matrix[0])
for i in range(height):
for j in range(width):
if i != j:
val = distance_matrix[i, j]
ax.text(x0 + (j + 0.5) / width * (x1 - x0), y0 + (i + 0.5) / height * (y1 - y0),
f'{val}\n{i},{j}', color='white' if norm(val) < 0.6 else 'black', **kws)
plt.show()

Inverted pixel coordinates of segmentation mask

The following is an image of a segmentation mask (it appears yellow). Overlaid onto this mask are the pixels/coordinates of the very same segmentation mask (appears blue).
My question is: why are these pixels/coordinates inverted, transparent and split at the diagonal? Why are they not plotted as a complete "fill", such as the mask itself?
My goal is for these coordinates to appear in "normal" (x,y) linear order. Code:
from matplotlib import patches
import numpy as np
# create mask
mask = np.zeros((350, 525), dtype=np.uint8)
# populate region of mask
mask[2:222,42:521] = 1
# get coordinates of populated region
y, x = np.where(mask == 1)
pts = np.column_stack([x, y])
# define figure, axes, title
fig = plt.figure()
ax = fig.add_axes([0,0,1,1])
ax.set_title('Segmentation mask pixel coordinates')
# show mask
plt.imshow(mask, interpolation='none')
# add mask points
poly = patches.Polygon(pts)
ax.add_patch(poly)
plt.show()
In your example len(pts) gives 105380 because pts contains all the points of the mask in row-based order. So poly has a snake-like shape with length=105380 and width=1. The snake starts in the upper left corner and ends in the lower right - that's why you have diagonal line.
To correct the plot you may do the following modification:
# borders
(x1, y1), (x2, y2) = pts.min(axis=0), pts.max(axis=0)
# corners
pts_for_poly = list(zip((x1, x2, x2, x1), (y1, y1, y2, y2)))
# rectangle polygon
poly = patches.Polygon(pts_for_poly)
I hope now it looks kinda like expected or close to that.

How to visualize feasible region for linear programming (with arbitrary inequalities) in Numpy/MatplotLib?

I need to implement a solver for linear programming problems. All of the restrictions are <= ones such as
5x + 10y <= 10
There can be an arbitrary amount of these restrictions. Also , x>=0 y>=0 implicitly.
I need to find the optimal solutions(max) and show the feasible region in matplotlib. I've found the optimal solution by implementing the simplex method but I can't figure out how to draw the graph.
Some approaches I've found:
This link finds the minimum of the y points from each function and uses plt.fillBetween() to draw the region. But it doesn't work when I change the order of the equations. I'm not sure which y values to minimize(). So I can't use it for arbitrary restrictions.
Find solution for every pair of restrictions and draw a polygon. Not efficient.
An easier approach might be to have matplotlib compute the feasible region on its own (with you only providing the constraints) and then simply overlay the "constraint" lines on top.
# plot the feasible region
d = np.linspace(-2,16,300)
x,y = np.meshgrid(d,d)
plt.imshow( ((y>=2) & (2*y<=25-x) & (4*y>=2*x-8) & (y<=2*x-5)).astype(int) ,
extent=(x.min(),x.max(),y.min(),y.max()),origin="lower", cmap="Greys", alpha = 0.3);
# plot the lines defining the constraints
x = np.linspace(0, 16, 2000)
# y >= 2
y1 = (x*0) + 2
# 2y <= 25 - x
y2 = (25-x)/2.0
# 4y >= 2x - 8
y3 = (2*x-8)/4.0
# y <= 2x - 5
y4 = 2 * x -5
# Make plot
plt.plot(x, 2*np.ones_like(y1))
plt.plot(x, y2, label=r'$2y\leq25-x$')
plt.plot(x, y3, label=r'$4y\geq 2x - 8$')
plt.plot(x, y4, label=r'$y\leq 2x-5$')
plt.xlim(0,16)
plt.ylim(0,11)
plt.legend(bbox_to_anchor=(1.05, 1), loc=2, borderaxespad=0.)
plt.xlabel(r'$x$')
plt.ylabel(r'$y$')
This is a vertex enumeration problem. You can use the function lineqs which visualizes the system of inequalities A x >= b for any number of lines. The function will also display the vertices on which the graph was plotted.
The last 2 lines mean that x,y >=0
from intvalpy import lineqs
import numpy as np
A = -np.array([[5, 10],
[-1, 0],
[0, -1]])
b = -np.array([10, 0, 0])
lineqs(A, b, title='Solution', color='gray', alpha=0.5, s=10, size=(15,15), save=False, show=True)
Visual Solution Link

Resources