position overlay precisely in matplotlib - python

I am trying to overlay precisely one image patch to a larger patch of the image
I have the coordinates where I'd like to put the patch and overlay the image but I don't know how to do with matplotlib.
I know it's possible with PILLOW (as explained here)but since I am using matplotlib for everything I'd be happy to stick to it.
For this example it would be moving the 'red patch' into the rectangle where 'it's supposed' to be.
Here is the code that I used for that:
temp_k = img_arr[0][
np.min(kernel[:, 1]) : np.max(kernel[:, 1]),
np.min(kernel[:, 0]) : np.max(kernel[:, 0]),
]
temp_w = img_arr[1][
np.min(window[:, 1]) : np.max(window[:, 1]),
np.min(window[:, 0]) : np.max(window[:, 0]),
]
w, h = temp_k.shape[::-1]
res = cv2.matchTemplate(temp_w, temp_k, cv2.TM_CCORR_NORMED)
min_val, max_val, min_loc, max_loc = cv2.minMaxLoc(res)
cv2.rectangle(temp_w, top_left, bottom_right, 255, 1)
plt.imshow(temp_w, cmap="bone")
plt.imshow(temp_k, cmap="magma", alpha=0.6)
plt.plot(max_loc[0], max_loc[1], "yo")
plt.savefig("../images/test.png")
plt.tight_layout()
Does anyone has an idea how to do that ?
Thanks in advance.

Just as with Pillow, you need to tell Matplotlib where to place data. If you omit that, it will assume a default extent of [0,xs,ys,0], basically plotting it in the top-left corner as shown on your image.
Generating some example data:
import matplotlib.pyplot as plt
import numpy as np
n = 32
m = n // 2
o = n // 4
a = np.random.randn(n,n)
b = np.random.randn(m,m)
a_extent = [0,n,n,0]
b_extent = [o, o+m, o+m, o]
# a_extent = [0, 32, 32, 0]
# b_extent = [8, 24, 24, 8]
Plotting with:
fig, ax = plt.subplots(figsize=(5,5), constrained_layout=False, dpi=86, facecolor="w")
ax.imshow(a, cmap="bone", extent=a_extent)
ax.autoscale(False)
ax.imshow(b, cmap="magma", extent=b_extent)
Will result in:

Related

Viewing 3d Field Lines of an unstructured dataset

I have tried to view field lines of an uncomplete regular grid vector field with first pyVista Streamlines and then with plotly without success... I have yet good results with other 2d streamplots :
2d streamplot of the data
Could someone help me with this ? I found no answer... Here is my data : https://wetransfer.com/downloads/7f3c4ae01e5922e753ea708134f956e720230214141330/bf11ab
import pandas as pd
import numpy as np
import pyvista as pv
import plotly.graph_objects as go
df = pd.read_csv("mix_griddata.csv")
X = df['X']
Y = df['Y']
Z = df['Z']
Vx = df['Vx']
Vy = df['Vy']
Vz = df['Vz']
fig = go.Figure(data=go.Streamtube(
x = X,
y = Y,
z = Z,
u = Vx,
v = Vy,
w = Vz,
starts = dict(
x = X.sample(frac=0.01,replace=False),
y = Y.sample(frac=0.01,replace=False),
z = Z.sample(frac=0.01,replace=False)
),
sizeref =1,
colorscale = 'Portland',
showscale = False,
maxdisplayed = 30000000
))
fig.update_layout(
scene = dict(
aspectratio = dict(
x = 1,
y = 1,
z = 1
)
),
margin = dict(
t = 10,
b = 10,
l = 10,
r = 10
)
)
fig.show(renderer="browser")
#Streamlines
mix_FD_grid = np.load("C:/Users/hd377/OneDrive - ensam.eu/0-Thesis/Fibres_Direction_in_allvolume/mix/mix_FD_grid.npy")
origin = (0,0,0)
mesh = pv.UniformGrid(dimensions=mix_FD_grid[:,:,:,0].shape, spacing=(1, 1, 1), origin=origin)
vectors = np.empty((mesh.n_points, 3))
vectors[:, 0] = mix_FD_grid[:,:,:,0].flatten()
vectors[:, 1] = mix_FD_grid[:,:,:,1].flatten()
vectors[:, 2] = mix_FD_grid[:,:,:,2].flatten()
mesh['vectors'] = vectors
stream, src = mesh.streamlines(
'vectors', return_source=True, max_steps = 20000, n_points=200, source_radius=25, source_center=(15, 0, 30)
)
p = pv.Plotter()
p.add_mesh(mesh.outline(), color="k")
p.add_mesh(stream.tube(radius=0.1))
p.camera_position = [(182.0, 177.0, 50), (139, 105, 19), (-0.2, -0.2, 1)]
p.show()
The plotly window does appear in my browser but no tube are visible at all, and the axes values are false.
The pyVista does show something, but in the wrong direction, and clearly not what expected (longitudinal flux circumventing a central cone).
I'll only be tackling PyVista. It's hard to say for sure and I'm only guessing, but your data is probably laid out in the wrong order.
For starters, your data is inconsistent to begin with: your CSV has 1274117 rows whereas your multidimensional array has shape (37, 364, 100, 3), for a total of 1346800 vectors. And your question title says "unstructured", but your PyVista attempt uses a uniform grid with.
Secondly, your CSV doesn't correspond to a regular grid in the first place, e.g. at the end of the file you have 15 rows starting with 368.693,36.971999999999994, then 8 rows starting with 369.71999999999997,36.971999999999994, then a single row starting with 370.74699999999996,36.971999999999994. In a regular grid you'd get the same number of items in each block.
Thirdly, your CSV has an unusual (MATLAB-smelling) layout that the order of axes is z-x-y (rather than either x-y-z or z-y-x). This is a strong clue that your data is mangled due to memory layout issues when flattened. But the previous two point mean that I can't verify how your 4d array was created, I have to take it for granted that it's correct.
Just plotting your raw data makes it obvious that the data is mangled in your original version (with some style cleanup):
import numpy as np
import pyvista as pv
mix_FD_grid = np.load("mix_FD_grid.npy")
origin = (0, 0, 0)
mesh = pv.UniformGrid(dimensions=mix_FD_grid.shape[:-1], spacing=(1, 1, 1), origin=origin)
vectors = np.empty_like(mesh.points)
vectors[:, 0] = mix_FD_grid[..., 0].ravel()
vectors[:, 1] = mix_FD_grid[..., 1].ravel()
vectors[:, 2] = mix_FD_grid[..., 2].ravel()
mesh.point_data['vectors'] = vectors
mesh.plot()
The fragmented pattern you can see is a hallmark of data mangling due to mistaken memory layout.
If we assume the layout is more or less sane, trying column-major layout ("F" for "Fortran", also used by MATLAB) seems to make a lot more sense:
vectors[:, 0] = mix_FD_grid[..., 0].ravel('F')
vectors[:, 1] = mix_FD_grid[..., 1].ravel('F')
vectors[:, 2] = mix_FD_grid[..., 2].ravel('F')
mesh.point_data['vectors'] = vectors
mesh.plot()
So we can try using streamlines using that:
stream, src = mesh.streamlines(
'vectors', return_source=True, max_steps=20000, n_points=200, source_radius=25, source_center=(15, 0, 30)
)
p = pv.Plotter()
p.add_mesh(mesh.outline(), color="k")
p.add_mesh(stream.tube(radius=0.1))
p.show()
It doesn't look great:
So, you said that the streamlines should be longitudinal, but here they are clearly transversal. Can it be that the x and y field components are swapped? I can't tell, so let's try!
import numpy as np
import pyvista as pv
mix_FD_grid = np.load("mix_FD_grid.npy")
origin = (0, 0, 0)
mesh = pv.UniformGrid(dimensions=mix_FD_grid.shape[:-1], spacing=(1, 1, 1), origin=origin)
vectors = np.empty_like(mesh.points)
vectors[:, 0] = mix_FD_grid[..., 1].ravel('F') # swap 0 <-> 1
vectors[:, 1] = mix_FD_grid[..., 0].ravel('F') # swap 0 <-> 1
vectors[:, 2] = mix_FD_grid[..., 2].ravel('F')
mesh.point_data['vectors'] = vectors
stream, src = mesh.streamlines(
'vectors', return_source=True, max_steps=20000, n_points=200, source_radius=25, source_center=(15, 0, 30)
)
p = pv.Plotter()
p.add_mesh(mesh.outline(), color="k")
p.add_mesh(stream.tube(radius=0.1))
p.show()
Now we're talking!
Bonus: y field component on a volumetric plot:
mesh.plot(volume=True, scalars=vectors[:, 1], show_scalar_bar=False)

ImportError: cannot import name 'disk' from 'skimage.draw'

**
I am new to the python interface. I am importing the numpy, scipy , gradient descend libraries . And By using the skimage I am importing imread and rgb2gray .By using the scipy library I am using the signal function .With gradient descent I am converting the image into imgray .Then I am applying the gaussian filter and when I am mapping corner function using harris response function I am getting the error:
It will be really helpful for me if anyone can help me.I have tried to install skimage multiple of time I have installed it and uninstalled it but I couldn't able to find any solution to it.I have also imported all the libraries required but I don't know what is the issue . Please provide me accurate result for the code. Thank you in advance.
Below is my error :
**
**NameError Traceback (most recent call last)
<ipython-input-7-76c3bdecb24d> in <module>
----> 1 corners = corner_peaks(harris_response)
2 fig, ax = plt.subplots()
3 ax.imshow(img, interpolation='nearest', cmap=plt.cm.gray)
4 ax.plot(corners[:, 1], corners[:, 0], '.r', markersize=3)
NameError: name 'corner_peaks' is not defined**
Below is my code :
from skimage.io import imread
from skimage.color import rgb2gray
img = imread('box.jpg')
imggray = rgb2gray(img)
from scipy import signal as sig
import numpy as np
def gradient_x(imggray):
##Sobel operator kernels.
kernel_x = np.array([[-1, 0, 1],[-2, 0, 2],[-1, 0, 1]])
return sig.convolve2d(imggray, kernel_x, mode='same')
def gradient_y(imggray):
kernel_y = np.array([[1, 2, 1], [0, 0, 0], [-1, -2, -1]])
return sig.convolve2d(imggray, kernel_y, mode='same')
I_x = gradient_x(imggray)
I_y = gradient_y(imggray)
from scipy.ndimage import gaussian_filter
Ixx = gaussian_filter(I_x**2, sigma=1)
Ixy = gaussian_filter(I_y*I_x, sigma=1)
Iyy = gaussian_filter(I_y**2, sigma=1)
k = 0.05
# determinant
detA = Ixx * Iyy - Ixy ** 2
# trace
traceA = Ixx + Iyy
harris_response = detA - k * traceA ** 2
img_copy_for_corners = np.copy(img)
img_copy_for_edges = np.copy(img)
for rowindex, response in enumerate(harris_response):
for colindex, r in enumerate(response):
if r > 0:
# this is a corner
img_copy_for_corners[rowindex, colindex] = [255,0,0]
elif r < 0:
# this is an edge
img_copy_for_edges[rowindex, colindex] = [0,255,0]
corners = corner_peaks(harris_response)
fig, ax = plt.subplots()
ax.imshow(img, interpolation='nearest', cmap=plt.cm.gray)
ax.plot(corners[:, 1], corners[:, 0], '.r', markersize=3)
It seems you missed to import the function. I assume it needs something like
from skimage.feature import corner_peaks
at the beginning. (Your indentation in the example above is wrong by the way but I assume that is a copy/paste error.)
Later in the script you will encounter the error that plt is not defined. You'd need to
import matplotlib.pyplot as plt

getting a dark image using make_lupton_rgb

I am using astropy visualization to make a colored image of M66 in this case.
Before doing anything I learnt that I have to cast my RGB .fts array with numpy.float_()
forCasting = np.float_()
### READING
b = fits.open("data/"+"M66-Blue.fts")[0].data
r = fits.open("data/"+"M66-Red.fts")[0].data
g = fits.open("data/"+"M66-Green.fts")[0].data
### CASTING
r = np.array(r,forCasting)
g = np.array(g,forCasting)
b = np.array(b,forCasting)
so that I could proceed with my stretch like :
stretch = SqrtStretch() + ZScaleInterval()
r = stretch(b)
g = stretch(r)
b = stretch(g)
plt.imshow(r, origin='lower')
plt.show()
plt.imshow(g, origin='lower')
plt.show()
plt.imshow(b, origin='lower')
plt.show()
Then I just use the method make_lupton_rgb from astropy.visualizaion as follow, but I have a super dark image that I cannot distinguish anything. Does anybody know why I have a dark final image here? Do you have any suggestions?
### SAVING
# rgb_default = make_lupton_rgb(r, g, b, minimum=1000, stretch=900, Q=100, filename="provafinale.png")
rgb_default = make_lupton_rgb(r, g, b, filename="provafinale.png")
plt.imshow(rgb_default, origin='lower')
plt.show()
Thanks!
It looks like you have to set stretch and Q arguments of make_lupton_rgb.
The default values are stretch=5 and Q=8, that gives dark result.
I have no experience with astropy or with Astronomy.
I just played with the arguments, and got bright image using stretch=1 and Q=0.
rgb_default = make_lupton_rgb(r, g, b, minimum=0, stretch=1, Q=0, filename="provafinale.png")
I tried computing minimum and stretch using np.percentile, for linear stretching the output.
I tested the code using an m8_050507_9i9m image from index_fits.
Here is the code I used for testing:
import numpy as np
from astropy.io import fits
from astropy.visualization import SqrtStretch
from astropy.visualization import ZScaleInterval
from astropy.visualization import make_lupton_rgb
from matplotlib import pyplot as plt
forCasting = np.float_()
### READING
# http://www.mistisoftware.com/astronomy/index_fits.htm
r = fits.open("m8_050507_9i9m_R.FIT")[0].data
g = fits.open("m8_050507_9i9m_G.FIT")[0].data
b = fits.open("m8_050507_9i9m_B.FIT")[0].data
# Crop the top and the right margin (contains black pixels)
r = r[:, :-200]
g = g[:, :-200]
b = b[:, :-200]
### CASTING
r = np.array(r,forCasting)
g = np.array(g,forCasting)
b = np.array(b,forCasting)
stretch = SqrtStretch() + ZScaleInterval()
r = stretch(b)
g = stretch(r)
b = stretch(g)
plt.imshow(r, origin='lower')
plt.imshow(g, origin='lower')
plt.imshow(b, origin='lower')
### SAVING
# https://docs.astropy.org/en/stable/api/astropy.visualization.make_lupton_rgb.html
# astropy.visualization.make_lupton_rgb(image_r, image_g, image_b, minimum=0, stretch=5, Q=8, fil/ename=None)[source]
# Return a Red/Green/Blue color image from up to 3 images using an asinh stretch.
# The input images can be int or float, and in any range or bit-depth.
lo_val, up_val = np.percentile(np.hstack((r.flatten(), g.flatten(), b.flatten())), (0.5, 99.5)) # Get the value of lower and upper 0.5% of all pixels
stretch_val = up_val - lo_val
rgb_default = make_lupton_rgb(r, g, b, minimum=lo_val, stretch=stretch_val, Q=0, filename="provafinale.png")
# Cut the top rows - contains black pixels
rgb_default = rgb_default[100:, :, :]
plt.imshow(rgb_default, origin='lower')
plt.show()
Result:

FFT low-pass filter

Be warned, this is a newbie question.
I acquired some noisy data (a 1x200 pixel sclice from a grayscale image), for which I am trying to build a simple FFT low-pass filter. I do understand the general principle of the Fourier Transform, but I ran into trouble trying to implement it.
My script works well on example data, but behaves in a strange manner on my data.
I think I must be mixing dimensions at some point, but it's been quite a few long hours and I cannot find where! I suspect that, because the output (please see below) of print(signal.shape) is different between the example and real data. Furthermore, scipy.fftpack.rfft(signal) seems to do nothing to my signal instead of computing the function in the frequency domain, as it should.
My script:
(will run out-of-the-box using example data, just by copy-pasting everything below #example data)
import cv2
import numpy as np
from scipy.fftpack import rfft, irfft, fftfreq, fft, ifft
import matplotlib as mpl
import matplotlib.pyplot as plt
#===========================================
#GETTING DATA AND SETTING CONSTANTS
#===========================================
REACH = 100
COURSE = 180
CENTER = (cx, cy)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
gray = cv2.equalizeHist(gray)
gray2 = gray.copy()
#drawing initial vector
cv2.line(gray, (cx, cy + REACH), (cx, cy - REACH), 0, 5)
cv2.circle(gray, (cx, cy + REACH), 10, 0, -1)
cv2.circle(gray, (cx, cy), REACH, 0, 5)
#flooding contour with white
cv2.drawContours(gray2, contours, index, 255, -1)
#real data
signal = gray2[(cy - REACH):(cy + REACH), (cx-0.5):(cx+0.5)]
time = np.linspace(0, 2*REACH, num=200)
#example data
time = np.linspace(0,10,2000)
signal = np.cos(5*np.pi*time) + np.cos(7*np.pi*time)
#=============================================
#THE FFT TRANSFORM & FILTERING
#=============================================
#signal filtering
f_signal = rfft(signal)
W = fftfreq(signal.size, d=time[1]-time[0])
cut_f_signal = f_signal.copy()
cut_f_signal[(W>5)] = 0
cut_signal = irfft(cut_f_signal)
#==================================
#FROM HERE ITS ONLY PLOTTING
#==================================
print(signal.shape)
plt.figure(figsize=(8,8))
ax1 = plt.subplot(321)
ax1.plot(signal)
ax1.set_title("Original Signal", color='green', fontsize=16)
ax2 = plt.subplot(322)
ax2.plot(np.abs(f_signal))
plt.xlim(0,100)
ax2.set_title("FFT Signal", color='green', fontsize=16)
ax3 = plt.subplot(323)
ax3.plot(cut_f_signal)
plt.xlim(0,100)
ax3.set_title("Filtered FFT Signal", color='green', fontsize=16)
ax4 = plt.subplot(324)
ax4.plot(cut_signal)
ax4.set_title("Filtered Signal", color='green', fontsize=16)
for i in [ax1,ax2,ax3,ax4]:
i.tick_params(labelsize=16, labelcolor='green')
plt.tight_layout()
plt.show()
The result on real data:
parameters:
signal = gray2[(cy - REACH):(cy + REACH), (cx-0.5):(cx+0.5)]
time = np.linspace(0, 2*REACH, num=200)
filtering parameter:
cut_f_signal[(W<0.05)] = 0
Output:
output of signal.shape is (200L, 1L)
The result on example data:
parameters:
signal = np.cos(5*np.pi*time) + np.cos(7*np.pi*time)
time = np.linspace(0,10,2000)
filtering parameter:
cut_f_signal[(W>5)] = 0
Output:
output of signal.shape is (2000L,)
So I began to think about that, and after a time I realized the stupidity in my ways. My base data is an image, and I take a slice of it to generate the above signal.
So instead of trying to implement a less-than-satisfying home-brewed FFT filter to smoooth the signal, it is in fact much better and easier to smooth the image with one of the numerous battle-tested filters (gaussian, bilateral, etc.) available in equally numerous image libs (in my case, OpenCV)...
Thanks to the people that took the time to try and help!

Shape recognition with numpy/scipy (perhaps watershed)

My goal is to trace drawings that have a lot of separate shapes in them and to split these shapes into individual images. It is black on white. I'm quite new to numpy,opencv&co - but here is my current thought:
scan for black pixels
black pixel found -> watershed
find watershed boundary (as polygon path)
continue searching, but ignore points within the already found boundaries
I'm not very good at these kind of things, is there a better way?
First I tried to find the rectangular bounding box of the watershed results (this is more or less a collage of examples):
from numpy import *
import numpy as np
from scipy import ndimage
np.set_printoptions(threshold=np.nan)
a = np.zeros((512, 512)).astype(np.uint8) #unsigned integer type needed by watershed
y, x = np.ogrid[0:512, 0:512]
m1 = ((y-200)**2 + (x-100)**2 < 30**2)
m2 = ((y-350)**2 + (x-400)**2 < 20**2)
m3 = ((y-260)**2 + (x-200)**2 < 20**2)
a[m1+m2+m3]=1
markers = np.zeros_like(a).astype(int16)
markers[0, 0] = 1
markers[200, 100] = 2
markers[350, 400] = 3
markers[260, 200] = 4
res = ndimage.watershed_ift(a.astype(uint8), markers)
unique(res)
B = argwhere(res.astype(uint8))
(ystart, xstart), (ystop, xstop) = B.min(0), B.max(0) + 1
tr = a[ystart:ystop, xstart:xstop]
print tr
Somehow, when I use the original array (a) then argwhere seems to work, but after the watershed (res) it just outputs the complete array again.
The next step could be to find the polygon path around the shape, but the bounding box would be great for now!
Please help!
#Hooked has already answered most of your question, but I was in the middle of writing this up when he answered, so I'll post it in the hopes that it's still useful...
You're trying to jump through a few too many hoops. You don't need watershed_ift.
You use scipy.ndimage.label to differentiate separate objects in a boolean array and scipy.ndimage.find_objects to find the bounding box of each object.
Let's break things down a bit.
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
def draw_circle(grid, x0, y0, radius):
ny, nx = grid.shape
y, x = np.ogrid[:ny, :nx]
dist = np.hypot(x - x0, y - y0)
grid[dist < radius] = True
return grid
# Generate 3 circles...
a = np.zeros((512, 512), dtype=np.bool)
draw_circle(a, 100, 200, 30)
draw_circle(a, 400, 350, 20)
draw_circle(a, 200, 260, 20)
# Label the objects in the array.
labels, numobjects = ndimage.label(a)
# Now find their bounding boxes (This will be a tuple of slice objects)
# You can use each one to directly index your data.
# E.g. a[slices[0]] gives you the original data within the bounding box of the
# first object.
slices = ndimage.find_objects(labels)
#-- Plotting... -------------------------------------
fig, ax = plt.subplots()
ax.imshow(a)
ax.set_title('Original Data')
fig, ax = plt.subplots()
ax.imshow(labels)
ax.set_title('Labeled objects')
fig, axes = plt.subplots(ncols=numobjects)
for ax, sli in zip(axes.flat, slices):
ax.imshow(labels[sli], vmin=0, vmax=numobjects)
tpl = 'BBox:\nymin:{0.start}, ymax:{0.stop}\nxmin:{1.start}, xmax:{1.stop}'
ax.set_title(tpl.format(*sli))
fig.suptitle('Individual Objects')
plt.show()
Hopefully that makes it a bit clearer how to find the bounding boxes of the objects.
Use the ndimage library from scipy. The function label places a unique tag on each block of pixels that are within a threshold. This identifies the unique clusters (shapes). Starting with your definition of a:
from scipy import ndimage
image_threshold = .5
label_array, n_features = ndimage.label(a>image_threshold)
# Plot the resulting shapes
import pylab as plt
plt.subplot(121)
plt.imshow(a)
plt.subplot(122)
plt.imshow(label_array)
plt.show()

Categories