Inverted pixel coordinates of segmentation mask - python-3.x

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.

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.

Subtracting a line from an image using OpenCV

So being new to OpenCV, I'm trying to detect a part of the image ( the thick line between the "PSSU" and "356750 / 22G1" characters ) and then subtract it from the original image. I want to have a clean final image that I can then put through OCR.
I've managed to detect the line ( red highlights ).
The code for line detection is shown below :
import cv2
import numpy as np
# Reading the required image in
# which operations are to be done.
# Make sure that the image is in the same
# directory in which this python program is
img = cv2.imread('c:\\ml\\test.jpg')
# Convert the img to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply edge detection method on the image
edges = cv2.Canny(gray, 50, 150, apertureSize=3)
# This returns an array of r and theta values
# 4th value ( 400 ) is threshold of how thick the line is thats to be detected. Higher value = thicker line to be detected.
lines = cv2.HoughLines(edges, 1, np.pi/180, 400)
# The below for loop runs till r and theta values
# are in the range of the 2d array
for r_theta in lines:
arr = np.array(r_theta[0], dtype=np.float64)
r, theta = arr
# Stores the value of cos(theta) in a
a = np.cos(theta)
# Stores the value of sin(theta) in b
b = np.sin(theta)
# x0 stores the value rcos(theta)
x0 = a*r
# y0 stores the value rsin(theta)
y0 = b*r
# x1 stores the rounded off value of (rcos(theta)-1000sin(theta))
x1 = int(x0 + 1000*(-b))
# y1 stores the rounded off value of (rsin(theta)+1000cos(theta))
y1 = int(y0 + 1000*(a))
# x2 stores the rounded off value of (rcos(theta)+1000sin(theta))
x2 = int(x0 - 1000*(-b))
# y2 stores the rounded off value of (rsin(theta)-1000cos(theta))
y2 = int(y0 - 1000*(a))
# cv2.line draws a line in img from the point(x1,y1) to (x2,y2).
# (0,0,255) denotes the colour of the line to be
# drawn. In this case, it is red.
cv2.line(img, (x1, y1), (x2, y2), (0, 0, 255), 2)
# All the changes made in the input image are finally
# written on a new image houghlines.jpg
cv2.imwrite('linesDetected.jpg', img)
So how do I now subtract the line ( red highlights ) from the original image?
Thank you.
Having (x1, y1) and (x2, y2) you can slice the image in two parts like:
img_left = img[0:x1, 0:y1]
img_right = img[0:x2, 0:y2]
And then join them back:
final_img = np.concatenate((img_left, img_right), axis=1)

create a 3d cylinder inside 3d volume

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

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()

Using Matplotlib to create a simple schedule

I am trying to create a simple schedule using Matplotlib. I have the following start. I want to remove the y-axis numbers and display "a=1", "a=2", "a=3" instead. Indeed, y1 and y2 defined in my function are sort of fake because the thickness of the box does not really have to be some number. As long as I have an equal thickness for each "a," it is all fine. I want to plot a box with a shade for a=1 in between 370 and 560, for a=2 in between 550 and 980 and so on. The x-axis shows the time in minutes. I checked horizontal bar plots, but they all start from 0 and I couldn't figure out a way to convert them into a scheduling type. Any suggestions?
import matplotlib.pyplot as plt
x = [(370, 560), (550,980), (380,440)]
def activity_filler(x,y1,y2):
# Shade the area between y1 and y2
plt.fill_between(x, y1, y2,
facecolor="grey", # The fill color
color='grey', # The outline color
alpha=0.4, hatch = 'X\/|-') # Transparency of the fill
activity_filler(x[0],[1],[2])
activity_filler(x[1],[2],[3])
activity_filler(x[2],[3],[4])
plt.show()
x = [(370, 560), (550,980), (380,440)]
fig, ax = plt.subplots()
for i,evt in enumerate(x):
ax.barh(i,width=evt[1]-evt[0],left=evt[0])
ax.set_yticks(range(len(x)))
ax.set_yticklabels([f'a={i+1}' for i in range(len(x))])

Resources