Using python framework we are able to create image segments as shown in attachment. Now, based on the mouse click in the image segment we need to highlight the segment with specific color.
Based on the mouse click I am able to get x/y coordinates of the specific location. Please suggest me how can I check on which image segment the coordinates belongs to?
The following is the code snippet:
from skimage.segmentation import felzenszwalb, slic,quickshift
from skimage.segmentation import mark_boundaries
from skimage.segmentation import find_boundaries
from skimage.util import img_as_float
from skimage import io
import matplotlib.pyplot as plt
from skimage import measure
from skimage import restoration
from skimage import img_as_float
import numpy as np
coords = []
def find_nearest(array,value):
idx = (np.abs(array-value)).argmin()
return array[idx]
def onclick(event):
global ix, iy
ix, iy = event.xdata, event.ydata
print ('ix ',ix)
print ("iy ",iy)
color = np.float64([1,0,1]) # red color
image[segments == 14] = color
mark_boundaries(image, segments)
ax.imshow(mark_boundaries(image, segments))
coords.append((ix, iy))
return
image=img_as_float(io.imread("amazon.jpg"))
segments = quickshift(image, ratio=1.0, kernel_size=20, max_dist=10,
return_tree=False, sigma=0, convert2lab=True, random_seed=42)
fig = plt.figure("Superpixels -- %d segments" % (500))
ax = fig.add_subplot(1, 1, 1)
fig,ax = plt.subplots()
color = np.float64([1,0,0])
image[segments == 14] = color # desired segment to be colored
fig.canvas.mpl_connect('button_press_event', onclick)
ax.imshow(mark_boundaries(image, segments))
plt.axis("off")
plt.show()
According to the documentation, quickshift returns an integer mask indicating segment labels. If you know what pixel the user clicked on, you can check the value of that pixel in your variable segments to get the segment number.
In you click handler, you can use
clicked_segment = segments[event.xdata, event.ydata]
Related
!pip install numpy
!pip install matplotlib
!pip install opencv-python
import cv2
import matplotlib
import matplotlib.pyplot as plt
import matplotlib.image as mpimg
from PIL import Image
import numpy as np
import glob
import os
import matplotlib.image as mpimg
import pickle
import_images
#images = # join all file .jpg in sample_chessboard dir to list
images = glob.glob('C:\Users\tamim\Downloads\Checkerboard8x11-20220609T003559Z-001\Checkerboard8x11/*.jpg')
# Define the chess board rows and columns
ChessboardSize = (11,8)
# Set the termination criteria for the corner sub-pixel algorithm
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER, 25, 0.001)
# Prepare the object points: (0,0,0), (1,0,0), (2,0,0), ..., (6,5,0). They are the same for all images
objectPoints = np.zeros((ChessboardSize[0] *ChessboardSize1 , 3), np.float32)
objectPoints[:, :2] = np.mgrid[0:ChessboardSize[0], 0:ChessboardSize1].T.reshape(-1, 2)
# Create the arrays to store the object points and the image points
objectPointsArray = []
imgPointsArray = []
for image in images:
print(image)
img = cv2.imread(image)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
**## Find the chaseboard corners**
ret, corners = cv2.findChessboardCorners(gray, ChessboardSize, None)
**## if found, add objectpoints, imagepoints (after refining them)**
if ret == true:
objectPointsArray.append(objectPoints)
corners2 = cv2.cornerSubPix(gray, corners, (11,11),(-1,-1), criteria)
imgPointsArray.append(corners)
image_with_corner = cv2.drawChessboardCorners(gray, ChessboardSize, corners2, ret)
**## Draw and display the corners**
img = cv2.drawChessboardCorners(img, ChessboardSize, coners2, ret)
cv2.imshow('image:',img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# Calibrate a camera
ret, mtx, dist, rvecs, tvecs = cv2.calibrateCamera(objectPointsArray, imgPointsArray, gray.shape[::-1],None,None)
enter image description here
I have a Seaborn displot with a hued variable:
For each hued variable, I want to extract the mode of the density estimate and then plot each hue variable versus its mode, like so:
How do I do this?
You can use scipy.stats.gaussian_kde to create the density estimation function. And then call that function on an array of x-values to calculate its maximum.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.random.normal(0.001, 1, 1300).cumsum() + 30,
'hue': np.repeat(np.arange(0.08, 0.20001, 0.01), 100).round(2)})
g = sns.displot(df, x='x', hue='hue', palette='turbo', kind='kde', fill=True, height=6, aspect=1.5)
plt.show()
from scipy.stats import gaussian_kde
from matplotlib.cm import ScalarMappable
fig, ax = plt.subplots(figsize=(10, 6))
hues = df['hue'].unique()
num_hues = len(hues)
colors = sns.color_palette('turbo', num_hues)
xmin, xmax = df['x'].min(), df['x'].max()
xs = np.linspace(xmin, xmax, 500)
for hue, color in zip(hues, colors):
data = df[df['hue'] == hue]['x'].values
kde = gaussian_kde(data)
mode_index = np.argmax(kde(xs))
mode_x = xs[mode_index]
sns.scatterplot(x=[hue], y=[mode_x], color=color, s=50, ax=ax)
cmap = sns.color_palette('turbo', as_cmap=True)
norm = plt.Normalize(hues.min(), hues.max())
plt.colorbar(ScalarMappable(cmap=cmap, norm=norm), ax=ax, ticks=hues)
plt.show()
Here is another approach, extracting the kde curves. It uses the legend of the kde plot to get the correspondence between the curves and the hue values. sns.kdeplot is the axes-level function used by sns.displot(kind='kde'). fill=False creates lines instead of filled polygons for the curves, for which the values are easier to extract. (ax1.fill_between can fill the curves during a second pass). The x and y axes of the second plot are switched to align the x-axes of both plots.
import matplotlib.pyplot as plt
import seaborn as sns
import pandas as pd
import numpy as np
df = pd.DataFrame({'x': np.random.normal(0.007, 0.1, 1300).cumsum() + 30,
'hue': np.repeat(np.arange(0.08, 0.20001, 0.01), 100).round(2)})
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(12, 10), sharex=True)
sns.kdeplot(data=df, x='x', hue='hue', palette='turbo', fill=False, ax=ax1)
hues = [float(txt.get_text()) for txt in ax1.legend_.get_texts()]
ax2.set_yticks(hues)
ax2.set_ylabel('hue')
for hue, line in zip(hues, ax1.lines[::-1]):
color = line.get_color()
x = line.get_xdata()
y = line.get_ydata()
ax1.fill_between(x, y, color=color, alpha=0.3)
mode_ind = np.argmax(y)
mode_x = x[mode_ind]
sns.scatterplot(x=[mode_x], y=hue, color=color, s=50, ax=ax2)
sns.despine()
plt.tight_layout()
plt.show()
Background
I'm optimizing a project.
Profiling the code I found that 50% of the time is spend on a function in which a set of circles (different radii, colors and locations) are drawn to a choosen sector of fixed size (white canvas) if their center corrdinates is within the sector bounds. Depending on the usage the function saves the figure as a png and returns the path or returns the image as an numpy array.
The build-in method matplotlib._png.write_png from savefig is the most expensive But there is also some overhead from creating the figures, etc.
Generally the code is used with multiprocessing / parallel programming.
Example output
Code
import matplotlib.pyplot as plt
import cv2
import os
def get_top_view(sector, circles, file_path, save_image_flag):
# get the sector bounds.
x_low, y_low, x_high, y_high = get_sector_bounds(sector)
# init figure
fig, ax = plt.subplots()
ax.set_xlim(y_low, y_high)
ax.set_ylim(x_low, x_high)
ax.set_yticklabels([])
ax.set_xticklabels([])
ax.set_yticks([])
ax.set_xticks([])
ax.set_aspect('equal')
ax.axis('off')
# c is a circle object with all relevant data (center coordinates,
# radius, RGB color tuple)
for c in circles:
if x_low <= c.x_coord <= x_high and y_low <= c.y_coord <= y_high:
shape = plt.Circle((c.x_coord, c.y_coord), c.radius, color=c.color)
shape_plot = ax.add_artist(shape)
shapes.append(shape_plot)
plt.gca().invert_yaxis()
if save_image_flag:
plt.savefig(file_path + '_cc.png', bbox_inches='tight', pad_inches=0.02)
plt.close()
return file_path
else:
ax.margins(0)
fig.tight_layout()
fig.canvas.draw()
image_from_plot = np.frombuffer(fig.canvas.tostring_rgb(), dtype=np.uint8)
image_from_plot = image_from_plot.reshape(
fig.canvas.get_width_height()[::-1] + (3,))
image_from_plot = image_from_plot[:, 13:-14]
resized = cv2.resize(image_from_plot, (499, 391))
cropped = resized[78:-78]
plt.close()
return cropped
Questions
There is the issue that the array version and the png image is slightly different. I think that relates to the DPI of the image. I want to fix that and I'm thinking about different options who to speedup this function.
Speedup the process and keeping matplotlib, similar to this example from Github.
Get rid of matplotlib and draw it with Pillow, e.g. some thing like:
from PIL import Image, ImageDraw
def get_top_view(sector, circles, file_path, save_image_flag):
# get the sector bounds.
x_low, y_low, x_high, y_high = get_sector_bounds(sector)
im = Image.new('RGB', (499, 235), (255, 255, 255))
draw = ImageDraw.Draw(im)
# there needs to be some rescaling that the corrdinates match which
# I don't account for at the moment.
for c in circles:
if x_low <= c.x_coord <= x_high and y_low <= c.y_coord <= y_high:
draw.ellipse((c.x_coord - c.radius, c.y_coord - c.radius,
c.x_coord + c.radius, c.y_coord + c.radius),
fill=c.color)
if save_image_flag:
im.save(file_path + '.png')
return file_path
else:
image_as_array = convert_to_array() # have to think about how I'll do that
return image_as_array
A different approach that is faster (and somehow convenient)...
I'd be glad for any feedback on the two issues.
I'll share my findings here. I'm calling the function 2200 times for each output type within the simulation framework. All computation is serial as the multiprocessing code is not part of the example.
There are 220 sectors with 200 circles randomly distributed amongst the sectors. The simulation runs for 10 steps, where the circles radii get updated and new figures are drawn. Hence, 2200 calls to the function.
To generate and save png images it took 293483ms previously.
To generate numpy arrays it took 92715ms previously.
Speedup with Matplotlib
Generating and saving images (save_image_flag = True) now takes 126485ms.
Generating numpy arrays now takes 57029ms.
import matplotlib.pyplot as plt
import os
def get_top_view(sector, circles, file_path, save_image_flag):
# get the sector bounds.
x_low, y_low, x_high, y_high = get_sector_bounds(sector)
# init figure
fig, ax = plt.subplots()
ax.set_xlim(y_low, y_high)
ax.set_ylim(x_low, x_high)
# These were unnecessary
# ax.set_yticklabels([])
# ax.set_xticklabels([])
# ax.set_yticks([])
# ax.set_xticks([])
ax.set_aspect('equal')
ax.axis('off')
# c is a circle object with all relevant data (center coordinates,
# radius, RGB color tuple)
for c in circles:
if x_low <= c.x_coord <= x_high and y_low <= c.y_coord <= y_high:
shape = plt.Circle((c.x_coord, c.y_coord), c.radius, color=c.color)
shape_plot = ax.add_artist(shape)
shapes.append(shape_plot)
plt.gca().invert_yaxis()
# I added this to get the discrepancies between the generated image and
# the numpy array, as bbox_inches='tight' only applies to the saved image.
bbox0 = fig.get_tightbbox(fig.canvas.get_renderer()).padded(0.02)
if save_image_flag:
plt.savefig(file_path + '_cc.png', bbox_inches=bbox0)
plt.close()
return file_path
else:
buf = io.BytesIO()
fig.savefig(buf, format="rgba", dpi=100, bbox_inches=bbox0)
buf.seek(0)
img = np.reshape(np.frombuffer(buf.getvalue(), dtype=np.uint8),
newshape=(235, 499, -1))
img = img[..., :3]
buf.close()
plt.close()
return image
Speedup without Matplotlib
Research is in progress...
I'm trying to create a plot in Python where the data that is being plotted gets updated as my simulation progresses. In MATLAB, I could do this with the following code:
t = linspace(0, 1, 100);
figure
for i = 1:100
x = cos(2*pi*i*t);
plot(x)
drawnow
end
I'm trying to use matplotlib's FuncAnimation function in the animation module to do this inside a class. It calls a function plot_voltage which recalculates voltage after each timestep in my simulation. I have it set up as follows:
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def __init__(self):
ani = animation.FuncAnimation(plt.figure(2), self.plot_voltage)
plt.draw()
def plot_voltage(self, *args):
voltages = np.zeros(100)
voltages[:] = np.nan
# some code to calculate voltage
ax1 = plt.figure(2).gca()
ax1.clear()
ax1.plot(np.arange(0, len(voltages), 1), voltages, 'ko-')`
When my simulation runs, the figures show up but just freeze. The code runs without error, however. Could someone please let me know what I am missing?
Here is a translation of the matlab code into matplotlib using FuncAnimation:
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
t = np.linspace(0, 1, 100)
fig = plt.figure()
line, = plt.plot([],[])
def update(i):
x = np.cos(2*np.pi*i*t)
line.set_data(t,x)
ani = animation.FuncAnimation(fig, update,
frames=np.linspace(1,100,100), interval=100)
plt.xlim(0,1)
plt.ylim(-1,1)
plt.show()
I wish to appear a figure (and certain text) as if they are printed on a page of an open book. Is it possible to transform an jpg image programmatically or in matplotlib to have such an effect?
You can use a background axis along with an open source book image to do something like this,
import numpy as np
import matplotlib.pyplot as plt
fig = plt.figure()
ax1 = fig.add_axes([0.1, 0.1, 0.8, 0.8])
ax2 = fig.add_axes([0.2, 0.3, 0.25, 0.3])
#Plot page from a book
im = plt.imread("./book_page.jpg")
implot = ax1.imshow(im, origin='lower')
# Plot a graph and set background to transparent
x = np.linspace(0,4.*np.pi,40)
y = np.sin(x)
ax2.plot(x,y,'-ro',alpha=0.5)
ax2.set_ylim([-1.1,1.1])
ax2.patch.set_alpha(0.0)
from matplotlib import rc
rc('text', usetex=True)
margin = im.shape[0]*0.075
ytext = im.shape[1]/2.+10
ax1.text(margin, ytext, "The following text is an example")
ax1.text(margin, 90, "Figure 1. Showing a sine function")
plt.show()
Which looks like this,
where I used the following book image.
UPDATE: Added non-affine transformation based on scikit-image warp example, but with Maxwell distribution. The solution saves the matplotlib line as an image in order to apply a pointwise transform. Mapping for vector graphics may be possible but I think this will be more complicated...
import numpy as np
import matplotlib.pyplot as plt
def maxwellian_transform_image(image):
from skimage.transform import PiecewiseAffineTransform, warp
rows, cols = image.shape[0], image.shape[1]
src_cols = np.linspace(0, cols, 20)
src_rows = np.linspace(0, rows, 10)
src_rows, src_cols = np.meshgrid(src_rows, src_cols)
src = np.dstack([src_cols.flat, src_rows.flat])[0]
# add maxwellian to row coordinates
x = np.linspace(0, 3., src.shape[0])
dst_rows = src[:, 1] + (np.sqrt(2/np.pi)*x**2 * np.exp(-x**2/2)) * 50
dst_cols = src[:, 0]
dst_rows *= 1.5
dst_rows -= 1.0 * 50
dst = np.vstack([dst_cols, dst_rows]).T
tform = PiecewiseAffineTransform()
tform.estimate(src, dst)
out_rows = image.shape[0] - 1.5 * 50
out_cols = cols
out = warp(image, tform, output_shape=(out_rows, out_cols))
return out
#Create the new figure
fig = plt.figure()
ax = fig.add_axes([0.1, 0.1, 0.8, 0.8])
#Plot page from a book
im = plt.imread("./book_page.jpg")
implot = ax.imshow(im, origin='lower')
# Plot and save graph as image, will need some manipulation of location
temp, at = plt.subplots()
margin = im.shape[0]*0.1
x = np.linspace(margin,im.shape[0]/2.,40)
y = im.shape[1]/3. + 0.1*im.shape[1]*np.sin(12.*np.pi*x/im.shape[0])
at.plot(x,y,'-ro',alpha=0.5)
temp.savefig("lineplot.png",transparent=True)
#Read in plot as an image and apply transform
plot = plt.imread("./lineplot.png")
out = maxwellian_transform_image(plot)
ax.imshow(out, extent=[0,im.shape[1],0,im.shape[0]])
plt.show()
The figure now looks like,