Matplotlib - plot face landmarks with no background image - python

The code bellow provides face-aligment to any photo I give as input.
Face-aligment link: https://github.com/1adrianb/face-alignment
import face_alignment
import numpy as np
from mpl_toolkits.mplot3d import Axes3D
import matplotlib.pyplot as plt
from skimage import io
# Run the 3D face alignment on a test image, without CUDA.
fa = face_alignment.FaceAlignment(face_alignment.LandmarksType._3D, device='cpu', flip_input=False)
input = io.imread('datasets/cloud_faces/trainB/00000566.AN.jpg')
preds = fa.get_landmarks(input)[-1]
#TODO: Make this nice
fig = plt.figure(figsize=plt.figaspect(.5))
ax = fig.add_subplot(1, 2, 1)
ax.imshow(input)
ax.plot(preds[0:17,0],preds[0:17,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.plot(preds[17:22,0],preds[17:22,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.plot(preds[22:27,0],preds[22:27,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.plot(preds[27:31,0],preds[27:31,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.plot(preds[31:36,0],preds[31:36,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.plot(preds[36:42,0],preds[36:42,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.plot(preds[42:48,0],preds[42:48,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.plot(preds[48:60,0],preds[48:60,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.plot(preds[60:68,0],preds[60:68,1],marker='o',markersize=6,linestyle='-',color='w',lw=2)
ax.axis('off')
ax = fig.add_subplot(1, 2, 2, projection='3d')
surf = ax.scatter(preds[:,0]*1.2,preds[:,1],preds[:,2],c="cyan", alpha=1.0, edgecolor='b')
ax.plot3D(preds[:17,0]*1.2,preds[:17,1], preds[:17,2], color='blue' )
ax.plot3D(preds[17:22,0]*1.2,preds[17:22,1],preds[17:22,2], color='blue')
ax.plot3D(preds[22:27,0]*1.2,preds[22:27,1],preds[22:27,2], color='blue')
ax.plot3D(preds[27:31,0]*1.2,preds[27:31,1],preds[27:31,2], color='blue')
ax.plot3D(preds[31:36,0]*1.2,preds[31:36,1],preds[31:36,2], color='blue')
ax.plot3D(preds[36:42,0]*1.2,preds[36:42,1],preds[36:42,2], color='blue')
ax.plot3D(preds[42:48,0]*1.2,preds[42:48,1],preds[42:48,2], color='blue')
ax.plot3D(preds[48:,0]*1.2,preds[48:,1],preds[48:,2], color='blue' )
ax.view_init(elev=90., azim=90.)
ax.set_xlim(ax.get_xlim()[::-1])
plt.show()
Like so:
However, I need to train a model "from face landmarks to face", and thus I need an image only with face landmarks.
How do I tweak the code above in order to plot an image with face landmarks only, with no background?

may be you can try something like that:
markersize = 3
output = np.zeros(shape=[width, height, 3], dtype=np.uint8) #black
#output = 255 * np.ones(shape=[512, 512, 3], dtype=np.uint8) # white
def DrawElement(input, points, color, isClosed = False, showHidden = False):
if( showHidden == False):
hide_threshold = -3.0
hidden = []
for i in range(0,len(points)):
if( points[i][2] < hide_threshold ):
hidden.append(i)
if( len(hidden) > 0):
points = np.delete(points, hidden, axis=0)
size = len(points)
if( size > 0):
x_points = points[0:size,0]
y_points = points[0:size,1]
for i in range(0,len(x_points)):
cv2.circle(input, (x_points[i], y_points[i]), 1, color, markersize)
pts = np.array([np.column_stack((x_points,y_points))], np.int32)
pts = pts.reshape((-1,1,2))
cv2.polylines(input, [pts],isClosed,color)
def DrawFace(input, points):
DrawElement(input, preds[0:17], (0,255,0)) # face
DrawElement(input, preds[17:22], (255,0,0)) # eyebrows dx
DrawElement(input, preds[22:27], (0,0,255)) # eyebrows sx
DrawElement(input, preds[27:31], (255,255,0),False, True) # nose
DrawElement(input, preds[31:36], (255,255,100)) # nostrils
DrawElement(input, preds[36:42], (0,255,255), True) # eye dx
DrawElement(input, preds[42:48], (0,128,255), True) # eye sx
DrawElement(input, preds[48:60], (255,255,255), True) # mouth
DrawElement(input, preds[60:68], (255,100,255), True) # mouth opening
DrawFace(output, preds)
cv2.imwrite('output.jpg',output)

Related

Find and split shelves from an image

I need to find and split each shelves from planogram image. In this case split using blue(it can be black, gray, etc.) lines so it should give me 8 shelves. I tried using Hough lines, contours to recognize the shelf. Tell me, please, how to recognize shelves.
Here's my code:
import cv2
import numpy as np
# Load the image
img = cv2.imread("display.jpg")
# Convert to grayscale
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Threshold the image to binarize it
threshold, binary = cv2.threshold(gray, 128, 255, cv2.THRESH_BINARY_INV)
# Find contours
contours, _ = cv2.findContours(binary, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
# Loop through each contour
for i, contour in enumerate(contours):
# Get bounding box of contour
x, y, w, h = cv2.boundingRect(contour)
# Crop the image to the bounding box
shelf_img = img[y:y+h, x:x+w]
# Save the cropped image
cv2.imwrite(f"shelf_{i}.jpg", shelf_img)
Here's the second approach:
#!/usr/bin/env python
# coding: utf-8
# In[5]:
from skimage.transform import (hough_line, hough_line_peaks,probabilistic_hough_line)
from skimage.feature import canny
from skimage import data
from skimage.io import imread
import numpy as np
import matplotlib.pyplot as plt
from skimage.filters import roberts, sobel, scharr
# In[6]:
image = imread("display.jpg", as_gray=True) * 255
# In[7]:
edges = canny(image, 4, 1, 25)
lines = probabilistic_hough_line(edges, threshold=2, line_length=10, line_gap=3)
fig2, ax = plt.subplots(1, 3, figsize=(20, 8))
ax[0].imshow(image, cmap=plt.cm.gray)
ax[0].set_title('Input image')
ax[0].axis('image')
ax[1].imshow(edges, cmap=plt.cm.gray)
ax[1].set_title('Canny edges')
ax[1].axis('image')
ax[2].imshow(edges * 0)
for line in lines:
p0, p1 = line
ax[2].plot((p0[0], p1[0]), (p0[1], p1[1]))
ax[2].set_title('Probabilistic Hough')
ax[2].axis('image')
# In[8]:
height, width = image.shape
# In[9]:
def proj_x(line):
p0, p1 = line
return np.abs(p0[0] - p1[0])
def proj_y(line):
p0, p1 = line
return np.abs(p0[1] - p1[1])
# In[10]:
s_h = np.zeros(height)
s_v = np.zeros(height)
for line in lines:
p0, p1 = line
y1 = max(p0[1], p1[1])
y0 = min(p0[1], p1[1])
if proj_y(line) > 2:
s_v[y0: y1] += proj_y(line)
if proj_x(line) > 2:
s_h[y0: y1] += proj_x(line)
# In[11]:
#fig2, ax = plt.subplots(1, 2, figsize=(8, 8))
plt.figure(figsize=(20, 10))
plt.imshow(image, cmap=plt.cm.gray)
plt.title('Input image')
plt.plot(s_h, np.arange(height), linewidth=2, label="s_h")
plt.plot(s_v, np.arange(height), linewidth=2, label="s_v")
plt.ylim([height, 0])
plt.xlim([0, width])
plt.legend()
# In[13]:
import scipy.ndimage
s_f = 20 * s_h / (s_v + 1)
s_f = scipy.ndimage.gaussian_filter1d(s_f, 1)
# In[14]:
plt.figure(figsize=(20, 10))
plt.imshow(image, cmap=plt.cm.gray)
plt.title('Input image')
plt.plot(s_f, np.arange(height), linewidth=2, label="s_h")
plt.ylim([height, 0])
plt.xlim([0, width])
# In[15]:
import scipy.signal
# In[26]:
cands = scipy.signal.find_peaks_cwt(s_f, np.arange(1, 50))
print(zip(cands, s_f[cands]))
# In[18]:
pairs = []
for c in cands:
for d in cands:
if (c < d) and (d -c < 100):
pairs.append((c, d))
# In[19]:
y_tagets = pairs[np.argmax([s_f[a] * s_f[b] for a, b in pairs])]
# In[20]:
y0, y1 = y_tagets
# In[21]:
import matplotlib.patches as patches
fig = plt.figure(figsize=(10, 10))
ax = fig.add_subplot(111, aspect='equal')
ax.imshow(image, cmap=plt.cm.gray)
ax.set_title('Input image')
ax.add_patch(
patches.Rectangle(
(0, y0),
width,
y1 - y0,
facecolor="red",
alpha=0.4 # remove background
)
)

How to fill a circle with a gradient?

Is there a way to fill a circle created by :
ax.add_patch(ptc.Circle(.....)
with a vertical gradient from a colormap :
grad = cm.get_cmap('plasma', 100)
The expected output:
I don't know how to do this, but according to the picture someone got it with imshow ().
Thanks
You can draw an image and set a clip path:
import numpy as np
import matplotlib.pyplot as plt
x, y, r = 0, 35, 25
fig, ax = plt.subplots()
img = ax.imshow(np.linspace(0, 1, 256).reshape(-1, 1), cmap='plasma',
extent=[x - r, x + r, y - r, y + r], origin='lower')
circle = plt.Circle((x, y), r, transform=ax.transData)
img.set_clip_path(circle)
ax.use_sticky_edges = False
ax.margins(x=0.05, y=0.05)
plt.show()

How can I add images to bars in axes (matplotlib)

I want to add flag images such as below to my bar chart:
I have tried AnnotationBbox but that shows with a square outline. Can anyone tell how to achieve this exactly as above image?
Edit:
Below is my code
ax.barh(y = y, width = values, color = r, height = 0.8)
height = 0.8
for i, (value, url) in enumerate(zip(values, image_urls)):
response = requests.get(url)
img = Image.open(BytesIO(response.content))
width, height = img.size
left = 10
top = 10
right = width-10
bottom = height-10
im1 = img.crop((left, top, right, bottom))
print(im1.size)
im1
ax.imshow(im1, extent = [value - 6, value, i - height / 2, i + height / 2], aspect = 'auto', zorder = 2)
Edit 2:
height = 0.8
for j, (value, url) in enumerate(zip(ww, image_urls)):
response = requests.get(url)
img = Image.open(BytesIO(response.content))
ax.imshow(img, extent = [value - 6, value - 2, j - height / 2, j + height / 2], aspect = 'auto', zorder = 2)
ax.set_xlim(0, max(ww)*1.05)
ax.set_ylim(-0.5, len(yy) - 0.5)
plt.tight_layout()
You need the images in a .png format with a transparent background. (Software such as Gimp or ImageMagick could help in case the images don't already have the desired background.)
With such an image, plt.imshow() can place it in the plot. The location is given via extent=[x0, x1, y0, y1]. To prevent imshow to force an equal aspect ratio, add aspect='auto'. zorder=2 helps to get the image on top of the bars. Afterwards, the plt.xlim and plt.ylim need to be set explicitly (also because imshow messes with them.)
The example code below used 'ada.png' as that comes standard with matplotlib, so the code can be tested standalone. Now it is loading flags from countryflags.io, following this post.
Note that the image gets placed into a box in data coordinates (6 wide and 0.9 high in this case). This box will get stretched, for example when the plot gets resized. You might want to change the 6 to another value, depending on the x-scale and on the figure size.
import numpy as np
import matplotlib.pyplot as plt
# import matplotlib.cbook as cbook
import requests
from io import BytesIO
labels = ['CW', 'CV', 'GW', 'SX', 'DO']
colors = ['crimson', 'dodgerblue', 'teal', 'limegreen', 'gold']
values = 30 + np.random.randint(5, 20, len(labels)).cumsum()
height = 0.9
plt.barh(y=labels, width=values, height=height, color=colors, align='center')
for i, (label, value) in enumerate(zip(labels, values)):
# load the image corresponding to label into img
# with cbook.get_sample_data('ada.png') as image_file:
# img = plt.imread(image_file)
response = requests.get(f'https://www.countryflags.io/{label}/flat/64.png')
img = plt.imread(BytesIO(response.content))
plt.imshow(img, extent=[value - 8, value - 2, i - height / 2, i + height / 2], aspect='auto', zorder=2)
plt.xlim(0, max(values) * 1.05)
plt.ylim(-0.5, len(labels) - 0.5)
plt.tight_layout()
plt.show()
PS: As explained by Ernest in the comments and in this post, using OffsetImage the aspect ratio of the image stays intact. (Also, the xlim and ylim stay intact.) The image will not shrink when there are more bars, so you might need to experiment with the factor in OffsetImage(img, zoom=0.65) and the x-offset in AnnotationBbox(..., xybox=(-25, 0)).
An extra option could place the flags outside the bar for bars that are too short. Or at the left of the y-axis.
The code adapted for horizontal bars could look like:
import numpy as np
import requests
from io import BytesIO
import matplotlib.pyplot as plt
from matplotlib.offsetbox import OffsetImage, AnnotationBbox
def offset_image(x, y, label, bar_is_too_short, ax):
response = requests.get(f'https://www.countryflags.io/{label}/flat/64.png')
img = plt.imread(BytesIO(response.content))
im = OffsetImage(img, zoom=0.65)
im.image.axes = ax
x_offset = -25
if bar_is_too_short:
x = 0
ab = AnnotationBbox(im, (x, y), xybox=(x_offset, 0), frameon=False,
xycoords='data', boxcoords="offset points", pad=0)
ax.add_artist(ab)
labels = ['CW', 'CV', 'GW', 'SX', 'DO']
colors = ['crimson', 'dodgerblue', 'teal', 'limegreen', 'gold']
values = 2 ** np.random.randint(2, 10, len(labels))
height = 0.9
plt.barh(y=labels, width=values, height=height, color=colors, align='center', alpha=0.8)
max_value = values.max()
for i, (label, value) in enumerate(zip(labels, values)):
offset_image(value, i, label, bar_is_too_short=value < max_value / 10, ax=plt.gca())
plt.subplots_adjust(left=0.15)
plt.show()
To complete #johanC answer, it's possible to use flags from iso-flags-png under GNU/linux and the iso3166 python package:
import matplotlib.pyplot as plt
from iso3166 import countries
import matplotlib.image as mpimg
def pos_image(x, y, pays, haut):
pays = countries.get(pays).alpha2.lower()
fichier = "/usr/share/iso-flags-png-320x240"
fichier += f"/{pays}.png"
im = mpimg.imread(fichier)
ratio = 4 / 3
w = ratio * haut
ax.imshow(im,
extent=(x - w, x, y, y + haut),
zorder=2)
plt.style.use('seaborn')
fig, ax = plt.subplots()
liste_pays = [('France', 10), ('USA', 9), ('Spain', 5), ('Italy', 5)]
X = [p[1] for p in liste_pays]
Y = [p[0] for p in liste_pays]
haut = .8
r = ax.barh(y=Y, width=X, height=haut, zorder=1)
y_bar = [rectangle.get_y() for rectangle in r]
for pays, y in zip(liste_pays, y_bar):
pos_image(pays[1], y, pays[0], haut)
plt.show()
which gives:

Overlay rotated Images on plot with Matplotlib

I've currently constructed a plot using rectangle Patches to display a sequence of positions.
EDIT: Code used to generate this (built off of the RLPy library)-
def visualize_trajectory(self, trajectory=[[0,0,0,0], [0.1,0.1,0,0]]):
domain_fig = plt.figure()
for i, s in enumerate(trajectory):
x, y, speed, heading = s[:4]
car_xmin = x - self.REAR_WHEEL_RELATIVE_LOC
car_ymin = y - self.CAR_WIDTH / 2.
car_fig = matplotlib.patches.Rectangle(
[car_xmin,
car_ymin],
self.CAR_LENGTH,
self.CAR_WIDTH,
alpha=(0.8 * i) / len(trajectory) )
rotation = Affine2D().rotate_deg_around(
x, y, heading * 180 / np.pi) + plt.gca().transData
car_fig.set_transform(rotation)
plt.gca().add_patch(car_fig)
Is there any way to overlay each of these patches with images? Ideally, there would be a car image instead of a rectangle at each of the positions.
I've played around with AnnotationBbox and TransformedBbox, but both seem to be inflexible when dealing with rotations.
Take a look at
demo_affine_image
from the matplotlib gallery. It shows how
to rotate an image.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.transforms as mtransforms
import matplotlib.cbook as cbook
def get_image():
fn = cbook.get_sample_data("necked_tensile_specimen.png")
arr = plt.imread(fn)
# make background transparent
# you won't have to do this if your car image already has a transparent background
mask = (arr == (1,1,1,1)).all(axis=-1)
arr[mask] = 0
return arr
def imshow_affine(ax, z, *args, **kwargs):
im = ax.imshow(z, *args, **kwargs)
x1, x2, y1, y2 = im.get_extent()
im._image_skew_coordinate = (x2, y1)
return im
N = 7
x = np.linspace(0, 1, N)
y = x**1.1
heading = np.linspace(10, 90, N)
trajectory = list(zip(x, y, heading))
width, height = 0.3, 0.3
car = get_image()
fig, ax = plt.subplots()
for i, t in enumerate(trajectory, start=1):
xi, yi, deg = t
im = imshow_affine(ax, car, interpolation='none',
extent=[0, width, 0, height], clip_on=True,
alpha=0.8*i/len(trajectory))
center_x, center_y = width//2, height//2
im_trans = (mtransforms.Affine2D()
.rotate_deg_around(center_x, center_y, deg)
.translate(xi, yi)
+ ax.transData)
im.set_transform(im_trans)
ax.set_xlim(-0.5, 1.5)
ax.set_ylim(-0.5, 1.7)
plt.show()

Border around a upper/lower triangle of imshow

This question relates to #bgbg's question about how to visualize only the upper or lower triangle of a symmetric matrix in matplotlib. Using his code (shown at the end), we can generate a figure like this:
Now my question: how can we draw a dark border around just this set of blocks? I ask, because I want to plot two sets of correlation data and put them next to each other as an upper and lower triangle. We can then draw a dark border around each triangle independently, to separate out the two triangles and show they are different metrics. So, like this, but not confusing:
How to do it?
#Figure 1
import numpy as NP
from matplotlib import pyplot as PLT
from matplotlib import cm as CM
A = NP.random.randint(10, 100, 100).reshape(10, 10)
mask = NP.tri(A.shape[0], k=-1)
A = NP.ma.array(A, mask=mask) # mask out the lower triangle
fig = PLT.figure()
ax1 = fig.add_subplot(111)
cmap = CM.get_cmap('jet', 10) # jet doesn't have white color
cmap.set_bad('w') # default value is 'k'
ax1.imshow(A, interpolation="nearest", cmap=cmap)
ax1.grid(True)
axis('off')
#Figure 2
A = NP.random.randint(10, 100, 100).reshape(10, 10)
mask = NP.tri(A.shape[0], k=-1)
mask = NP.zeros_like(A)
mask[NP.arange(10), NP.arange(10)] = 1
A = NP.ma.array(A, mask=mask) # mask out the lower triangle
fig = PLT.figure()
ax1 = fig.add_subplot(111)
cmap = CM.get_cmap('jet', 10) # jet doesn't have white color
cmap.set_bad('w') # default value is 'k'
ax1.imshow(A, interpolation="nearest", cmap=cmap)
title("Correlation Data 1")
ylabel("Correlation Data 2")
yticks([])
xticks([])
You could draw a border using patches.Polygon:
import numpy as NP
from matplotlib import pyplot as PLT
import matplotlib.patches as patches
N = 10
A = NP.random.randint(10, 100, N * N).reshape(N, N)
mask = NP.tri(A.shape[0], k=-1)
mask = NP.zeros_like(A)
mask[NP.arange(N), NP.arange(N)] = 1
A = NP.ma.array(A, mask=mask) # mask out the lower triangle
fig, ax = PLT.subplots()
cmap = PLT.get_cmap('jet', 10) # jet doesn't have white color
cmap.set_bad('w') # default value is 'k'
ax.imshow(A, interpolation="nearest", cmap=cmap, extent=[0, N, 0, N])
line = ([(0, N - 1), (0, 0), (N - 1, 0)] +
[(N - 1 - i - j, i + 1) for i in range(N - 1) for j in (0, 1)])
lines = [line, [(N - x, N - y) for x, y in line]]
for line in lines:
path = patches.Polygon(line, facecolor='none', edgecolor='black',
linewidth=5, closed=True, joinstyle='round')
ax.add_patch(path)
ax.set_xlabel("Correlation Data 1")
ax.xaxis.set_label_position('top')
ax.set_ylabel("Correlation Data 2")
ax.set_yticks([])
ax.set_xticks([])
margin = 0.09
ax.set_xlim(-margin, N + margin)
ax.set_ylim(-margin, N + margin)
ax.set_frame_on(False)
PLT.show()

Categories

Resources