for loop to remove values not in an array in python - python

I have a set of images in a numpy array. After some processing and applying a threshold I turned them into images that have either value 0 or 1 in each xy coordinate. I want to use a for loop and nonzero to turn the xy coordinates of the original image that are not in the nonzero array to zero and leave the pixels in the nonzero array with their original intensity. Im a complete noob in programming and I have been given this task.
This is what I have so far but the last part doesn't work:
import cv2
# Taking the first image of the data
image = series_copy2[0,:,:]
# Mean total background of the image
print('Mean total background = ' +str(np.mean(image)) + ' counts.')
# Threshold for background removal
threshold =30
# Setting all pixels below a threshold to zero to remove the background
image[image[:,:] < threshold] = 0
image[image[:,:]>threshold]=1
# Plotting the result for checking
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111)
data = image
plt.tight_layout()
im = plt.imshow(data, interpolation = 'nearest')
np.transpose(np.nonzero(data))
nz_arrays=np.transpose(np.nonzero(data))
#this doesn't work
for x in data:
if image[image[:,:] not in nz_arrays]=0
# Plotting the result for checking
fig = plt.figure(figsize=(10,10))
ax = fig.add_subplot(111)
data = image
plt.tight_layout()
im = plt.imshow(data, interpolation = 'nearest')

#this doesn't work
for x in data:
if image[image[:,:] not in nz_arrays] is 0:
# What is this doing?
When using if, you need to end it with a colon and then write the function.

Related

Want to Apply Blur to a Specific Filled Object on Python Plot

I am using Python 3.8.10 on Linux Mint 20.3 Una. I am making a series of animations with a multitude (potentially thousands) of fish shapes, each of which are produced by specifying a 2D profile with points, and is then filled in using the Pyplot fill function.
What I would like to be able to do is to apply a unique blur to each of these individual filled regions based on a computed distance to mimic image depth. An added complication is that these filled regions frequently overlap.
In theory, this could be done by exporting SVG files and manually applying the blurs in Inkscape or some other package, but there are potentially thousands of fish and hundreds of frames, so a way to achieve this in code really is the only realistic way to accomplish it, if it is possible.
Here is the minimal code that produces two filled profiles that I would like to blur individually:
import matplotlib.pyplot as plt
from scipy.ndimage import gaussian_filter
#define profile of object with points
x_profile = [0.5,0.485951301332915,0.423371700761206,0.358237605529776,0.281609306290982,0.23180095266422,0.152618567550257,0.053001860296735,-0.005746611462221,-0.060663545623872,-0.05683323438022,-0.257343937095579,-0.317369329156755,-0.345466399463283,-0.469348762061393,-0.492337251833031,-0.5,-0.439974607938825,-0.418263242861681,-0.415709156986512,-0.461686095651334,-0.492337415346851,-0.483397419850022,-0.466794594429313,-0.363346513092306,-0.342912313588113,-0.31864669912198,-0.289272544999412,-0.236909860226751,-0.210090037250083,-0.183269887245775,-0.146233189348514,-0.078544599457363,0.086206203027589,0.210088361233424,0.310982111424531,0.418261893872663,0.478287408569203,0.493612741389321]
y_profile = [-0.019156461632871,0.002554903444271,0.031928934931474,0.051085805348896,0.065134504015981,0.07024308455087,0.071518492350251,0.067688181106599,0.158365179012477,0.068965632828735,0.049808353626761,0.028096988549618,0.025542085105346,0.03192770857782,0.10217038434414,0.104725287788412,0.091954040843463,0.00255449465972,-0.00255449465972,-0.017879827479838,-0.067688181106599,-0.148148017942698,-0.158365179012477,-0.151979555540003,-0.061302557634125,-0.047254267751592,-0.040868235494567,-0.042143643293948,-0.080457792913345,-0.084288104156997,-0.079179523622108,-0.097059759886497,-0.111108049769031,-0.127710834311284,-0.126435426511903,-0.107278556094481,-0.076627072885143,-0.045975589675805,-0.031927299793271]
#this just makes a second object and offsets it down 0.5 units
n_objects = 2
n_points = len(y_profile)
x_points = np.zeros((n_objects, n_points))
y_points = np.zeros((n_objects, n_points))
for i in range(n_objects):
for j in range(n_points):
x_points[i,j] = x_profile[j]
y_points[i,j] = y_profile[j] - i*0.5
#make plot
fig = plt.figure(frameon=False)
fig.set_size_inches(6.5, 6.5)
ax = plt.axes()
ax.set_facecolor((0,0,1.0))
ax.set_xlim(-1,+1)
ax.set_ylim(-1,+1)
ax.set_aspect('equal', adjustable='box')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
#create filled regions defined by copies of profile points (I want to be able to apply a blur these fills individually)
for i in range(n_objects):
plt.fill(x_points[i,:], y_points[i,:], color = (0, 0, 0.5))
#tried the following, but does not work at all.
#handle = plt.fill(x_profile, y_profile, color = (0, 0, 0.5))
#blurred = gaussian_filter(handle, sigma=1)
#show plot (normally exporting PNG frames for animation)
plt.show()
which should yield this image:
Fish Profiles
If this is not possible in Python, I'm open to suggestions as to how this could be implemented dynamically in some other way.
I've seen examples of SciPy Gaussian blur applied to regions of static images, but the blur that I want to achieve is specific to the filled "object" which isn't a neat rectangle. I note that when this image is exported as an SVG the individual filled objects appear as distinct entities in that file, but I don't see a way to assign a handle to it within Python and to apply a blur to it. I've tried variations of 'handle = plt.fill(x,y)' and 'gaussian_filter(handle, sigma=1)' but with no success.
I think i was able to do what you are asking for using convolution but it is not optimized for speed at all. plus, it is kind of hard to tell how well it will translate to your bigger code.
Going off of whay you posted, I converted the graphs to rgb arrays and convolved each dimension separately with a from scratch convolution function (not my own,1). this code will output the first fish image and then a few seconds later it will output the blurred fish image.
import matplotlib.pyplot as plt
import numpy as np
import cv2
import plotly.express as px
def Convolve(img, kernel):
(imgX, imgY) = img.shape[:2]
(kernelX, kernelY) = kernel.shape[:2]
#print(imgX,imgY, kernelX,kernelY)
pad = (kernelX - 1) // 2
img = cv2.copyMakeBorder(img, pad, pad, pad, pad, cv2.BORDER_REPLICATE) #top, bottom, left, right
#the above line prevents error with convolution: operands could not be broadcast together with shapes (23,22) (23,23)
output = np.zeros((imgX, imgY), dtype="float32")
#shift kernel vertical and horizontal across image, pad prevents kernel from going out of bounds
for y in np.arange(pad, imgY + pad):
for x in np.arange(pad, imgX + pad):
#locate specific pixel
roi = img[y - pad:y + pad + 1, x - pad:x + pad + 1]
#print(roi)
#perform convolution
k = (roi * kernel).sum()
#populate the result into the previously created np.zeroes array
output[y - pad, x - pad] = k
return output
# define profile of object with points
x_profile = [0.5, 0.485951301332915, 0.423371700761206, 0.358237605529776, 0.281609306290982, 0.23180095266422,
0.152618567550257, 0.053001860296735, -0.005746611462221, -0.060663545623872, -0.05683323438022,
-0.257343937095579, -0.317369329156755, -0.345466399463283, -0.469348762061393, -0.492337251833031, -0.5,
-0.439974607938825, -0.418263242861681, -0.415709156986512, -0.461686095651334, -0.492337415346851,
-0.483397419850022, -0.466794594429313, -0.363346513092306, -0.342912313588113, -0.31864669912198,
-0.289272544999412, -0.236909860226751, -0.210090037250083, -0.183269887245775, -0.146233189348514,
-0.078544599457363, 0.086206203027589, 0.210088361233424, 0.310982111424531, 0.418261893872663,
0.478287408569203, 0.493612741389321]
y_profile = [-0.019156461632871, 0.002554903444271, 0.031928934931474, 0.051085805348896, 0.065134504015981,
0.07024308455087, 0.071518492350251, 0.067688181106599, 0.158365179012477, 0.068965632828735,
0.049808353626761, 0.028096988549618, 0.025542085105346, 0.03192770857782, 0.10217038434414,
0.104725287788412, 0.091954040843463, 0.00255449465972, -0.00255449465972, -0.017879827479838,
-0.067688181106599, -0.148148017942698, -0.158365179012477, -0.151979555540003, -0.061302557634125,
-0.047254267751592, -0.040868235494567, -0.042143643293948, -0.080457792913345, -0.084288104156997,
-0.079179523622108, -0.097059759886497, -0.111108049769031, -0.127710834311284, -0.126435426511903,
-0.107278556094481, -0.076627072885143, -0.045975589675805, -0.031927299793271]
# make plot
fig = plt.figure(frameon=False)
fig.set_size_inches(6.5, 6.5)
ax = plt.axes()
ax.set_facecolor((0, 0, 1.0))
ax.set_xlim(-1, +1)
ax.set_ylim(-1, +1)
ax.set_aspect('equal', adjustable='box')
ax.get_xaxis().set_visible(False)
ax.get_yaxis().set_visible(False)
ax.spines['top'].set_visible(False)
ax.spines['bottom'].set_visible(False)
ax.spines['left'].set_visible(False)
ax.spines['right'].set_visible(False)
# create filled region defined by profile points (I want to be able to apply a blur to this)
plt.fill(x_profile, y_profile, color=(0, 0, 0.5))
fig.canvas.draw()
data = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
data = data.reshape(fig.canvas.get_width_height()[::-1] + (3,))
array=np.array(data)
R=array[:,:,0]
G=array[:,:,1]
B=array[:,:,2]
fig=px.imshow(array, color_continuous_scale="gray")
fig.show()
_1DKern = cv2.getGaussianKernel(33, 2) # first value is dimensions, second is sigma
_2DKern = np.outer(_1DKern, _1DKern.transpose())
convR = Convolve(R, _2DKern)
convG=Convolve(G,_2DKern)
convB=Convolve(B,_2DKern)
conv=np.stack([convR,convG,convB],2)
fig = px.imshow(conv, color_continuous_scale="gray")
fig.show()

How to morph two grid-like images seamlessly?

I have two images that consist of colored squares with different grid step (10x10 and 12x12).
What I want is to make the first image to be smoothly transformed into the second one.
When I use a plain image overlay with cv2.addWeighted() function, the result (left) is not good because of the intersected grid spaces. I suppose it would be better to shift remaining grid cells to the borders and clear out the rest (right).
Is there any algorithm to deal with this task?
Thanks.
You can interpolate each pixel individually between different images.
import numpy as np
from scipy import interpolate
import matplotlib.pyplot as plt
np.random.seed(200)
num_images = 2
images = np.random.rand(num_images, 8,8)
for index, im in enumerate(images):
print(f'Images {index}')
fig = plt.imshow(im)
plt.show()
Interpolating these images:
n_frames = 4
x_array = np.linspace(0, 1, int(n_frames))
def interpolate_images(frame):
intermediate_image = np.zeros((1, *images.shape[1:]))
for lay in range(images.shape[1]):
for lat in range(images.shape[2]):
tck = interpolate.splrep(np.linspace(0, 1, images.shape[0]), images[:, lay, lat], k = 1)
intermediate_image[:, lay, lat] = interpolate.splev(x_array[frame], tck)
return intermediate_image
for frame in range(n_frames):
im = interpolate_images(int(frame))
fig = plt.imshow(im[0])
plt.show()

Python/numpy points list to black/white image area

I'm trying to convert a continuous list points (between 0 and 1) into black and white image, representing area under/over list points.
plt.plot(points)
plt.ylabel('True val')
plt.show()
print("Points shape-->", points.shape)
I can save the image produced by matplotlib but i think this could be a nasty workaround
At the end i would like to obtain and image with shape of (224,224) where white zone represent area under line and black zone represent are over line...
image_area = np.zeros((points.shape[0],points.shape[0],))
# ¿?
Any ideas or suggestions how to approach it are welcome! Thanks experts
Here is a basic example of how you could do it. Since the slicing requires integers, you may have to scale your raw data first.
import numpy as np
import matplotlib.pyplot as plt
# your 2D image
image_data = np.zeros((224, 224))
# your points. Here I am just using a random list of points
points = np.random.choice(224, size=224)
# loop over each column in the image and set the values
# under "points" equal to 1
for col in range(len(image_data[0])):
image_data[:points[col], col] = 1
# show the final image
plt.imshow(image_data, cmap='Greys')
plt.show()
Thank you Eric, here the solution with your proposal, thank you very much!
def to_img(points):
shape = points.shape[0]
# your 2D image
image_data = np.zeros((shape, shape))
# your points. Here I am just using a random list of points
# points = np.random.choice(224, size=224)
def minmax_norm_img(data, xmax, xmin):
return (data - xmin) / (xmax - xmin)
points_max = np.max(points)
points_min = np.min(points)
points_norm = minmax_norm_img(points,points_max , points_min)
# loop over each column in the image and set the values
# over "points" equal to 1
for col in range(len(image_data[0])):
image_data[shape-int(points_norm[col]*shape):, col] = 1
return image_data

How to display pixel variance over time from a set of images? (brightness changing object imaged over time)

I would like to display with a heat map the change in intensity/brightness over time of a set of images. These are images of a brightness-changing object imaged over time. This would be useful to see which parts of the object (which pixels) have the highest variance in brightness.
I'm currently using OpenCV to manipulate these images, but cannot find any straightforward way of getting this heatmap. In addition to this, if anyone could suggest a way of calculating the variance without having to create a separate array for the values for each pixel (maybe calculating it directly from the stack of images?) it would be helpful too.
This in an example of what one of the images looks like
Generate some synthetic data:
All pixes change with std of 3
Some pixes change (in shape X) with std of 5
Code:
import cv2
lena = cv2.imread("lena.png", 0)
lena = cv2.resize(dices, (100,100))
images = np.zeros((30, *lena.shape))
images[0] = lena.astype('float64')
mask = np.rot90(np.eye(100)) + np.eye(100)
for i in range(1,30):
img = images[i-1]
img += np.random.randn(*lena.shape)*3
img += mask*5
images[i] = img
The set of images created look like below
code to render images:
plt.close('all')
plt.figure(figsize=(25,25))
for i in range(25):
plt.subplot(5,5,i+1)
plt.imshow(images[i],cmap='gray')
plt.xticks([])
plt.yticks([])
plt.tight_layout()
plt.show()
Finally, heatmap to find the portions of the image which change at a different speed.
import seaborn as sns; sns.set()
ax = sns.heatmap(images.std(axis=0))
plt.show()
We got our mask back.

Given a 2D numpy array of real numbers, how to generate an image depicting the intensity of each number?

I have a 2D numpy array and would like to generate an image such that the pixels corresponding to numbers that have a high value (relative to other pixels) are coloured with a more intense colour. For example if the image is in gray scale, and a pixel has value 0.4849 while all the other pixels correspond to values below 0.001 then that pixel would probably be coloured black, or something close to black.
Here is an example image, the array is 28x28 and contains values between 0 and 1.
All I did to plot this image was run the following code:
import matplotlib.pyplot as plt
im = plt.imshow(myArray, cmap='gray')
plt.show()
However, for some reason this only works if the values are between 0 and 1. If they are on some other scale which may include negative numbers, then the image does not make much sense.
You can use different colormaps too, like in the example below (note that I removed the interpolation):
happy_array = np.random.randn(28, 28)
im = plt.imshow(happy_array, cmap='seismic', interpolation='none')
cbar = plt.colorbar(im)
plt.show()
And even gray is going to work:
happy_array = np.random.randn(28, 28)
im = plt.imshow(happy_array, cmap='gray', interpolation='none')
cbar = plt.colorbar(im)
plt.show()
You can normalize the data to the range (0,1) by dividing everything by the maximum value of the array:
normalized = array / np.amax(a)
plt.imshow(normalized)
If the array contains negative values you have two logical choices. Either plot the magnitude:
mag = np.fabs(array)
normalized = mag / np.amax(mag)
plt.imshow(normalized)
or shift the array so that everything is positive:
positive = array + np.amin(array)
normalized = positive / np.amax(positive)
plt.imshow(normalized)

Categories

Resources