Matplotlib: How to Draw a Rectangle on Image

matplotlib: how to draw a rectangle on image

You can add a Rectangle patch to the matplotlib Axes.

For example (using the image from the tutorial here):

import matplotlib.pyplot as plt
import matplotlib.patches as patches
from PIL import Image

im = Image.open('stinkbug.png')

# Create figure and axes
fig, ax = plt.subplots()

# Display the image
ax.imshow(im)

# Create a Rectangle patch
rect = patches.Rectangle((50, 100), 40, 30, linewidth=1, edgecolor='r', facecolor='none')

# Add the patch to the Axes
ax.add_patch(rect)

plt.show()

Sample Image

Matplotlib draw rectangle over image, rectangle specified in image coordinates

If the axis goes from top to bottom, the bottom of the rectangle is actually the top, seen in data coordinates. However, what is always true is that a rectangle

plt.Rectangle((x, y), w, h)

expands from (x,y) to (x+w, y+h).

So when we plot the same rectangle plt.Rectangle((1,2), 2, 1) in axes with different orientations of the respective x and y axes, it will look different.

import matplotlib.pyplot as plt

def plot_rect(ax):
rect = plt.Rectangle((1,2), 2, 1)
ax.add_patch(rect)
ax.scatter([1], [2], s=36, color="k", zorder=3)

fig, axs = plt.subplots(2,2)

xlims = ((-4,4), (-4,4), (4,-4), (4,-4))
ylims = ((-4,4), (4,-4), (-4,4), (4,-4))

for ax, xlim, ylim in zip(axs.flat, xlims, ylims):
plot_rect(ax)
ax.set(xlim=xlim, ylim=ylim)

plt.show()

Sample Image

Is there a way to draw rectangles and/or squares with matplotlib?

Code:

import matplotlib
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.patches import Rectangle



X = np.arange(16).reshape(4, 4)


fig = plt.figure()

ax = fig.add_subplot(111)

plt.title("Rectangles")

rect1 = matplotlib.patches.Rectangle((1.3,1.6), 1.1, 2, color='blue', fc = 'none',lw = 2)
rect2 = matplotlib.patches.Rectangle((1.5,1.8), .65, 1.6, color='orange',fc = 'none', lw = 2)

ax.add_patch(rect1)
ax.add_patch(rect2)

plt.xlim([1.2, 2.5])
plt.ylim([1.5, 3.7])

plt.xlabel("x - axis")
plt.ylabel("y - axis")


plt.show()

Output:

Sample Image

Drawing a rectangle with Matplotlib

The delivery of the x-y coordinates is mixed up in your code. p1 is still in the same form as P1, i.e., an x-y pair. But matplotlib plt.plot() expects a list of x-values followed by a list of y-values. Hence, this misunderstanding - matplotlib interpreted in plt.plot(p1,p2) p1 as two x-values and p2 as two y-values. So, we have to chain the x- and the y-values together before plotting.

import sympy as sp
import matplotlib.pyplot as plt

def draw_rectangle(P1,P2,P3,P4):
p1, p2 = sp.Point(P1[0], P1[1]), sp.Point(P2[0], P2[1])
p3, p4 = sp.Point(P3[0], P3[1]), sp.Point(P4[0], P4[1])
plt.plot(*zip(p1,p2))
plt.plot(*zip(p2,p3))
plt.plot(*zip(p3,p4))
plt.plot(*zip(p4,p1))
#or alternatively
#plt.plot(*zip(p1, p2, p3, p4, p1))

P1=[20,30]
P2=[40,30]
P3=[40,60]
P4=[20,60]

draw_rectangle(P1,P2,P3,P4)
plt.show()

Sample output:

Sample Image

However, it remains unclear why you converted your data into sympy points before plotting. You could have directly used P1, P2,..., but maybe this is required for some other program parts we are not aware of. Or you actually wanted to use another sympy function, not sp.point, but I don't speak fluent sympy, so cannot comment on that.

Draw image in rectangle python

Here is one way using Python/OpenCV/Numpy. Do a perspective warp of the panda image using its 4 corners and the 4 corners of the rectangle. Then make a mask of the excess regions, which are black in the warped image. Finally, blend the warped image and background image using the mask.

Input:

Sample Image

Graph Image:

Sample Image

import numpy as np
import cv2
import math

# read image to be processed
img = cv2.imread("panda.png")
hh, ww = img.shape[:2]

# read background image
bck = cv2.imread("rectangle_graph.png")
hhh, www = bck.shape[:2]

# specify coordinates for corners of img in order TL, TR, BR, BL as x,y pairs
img_pts = np.float32([[0,0], [ww-1,0], [ww-1,hh-1], [0,hh-1]])

# manually pick coordinates of corners of rectangle in background image
bck_pts = np.float32([[221,245], [333,26], [503,111], [390,331]])

# compute perspective matrix
matrix = cv2.getPerspectiveTransform(img_pts,bck_pts)
#print(matrix)

# change black and near-black to graylevel 1 in each channel so that no values
# inside panda image will be black in the subsequent mask
img[np.where((img<=[5,5,5]).all(axis=2))] = [1,1,1]

# do perspective transformation setting area outside input to black
img_warped = cv2.warpPerspective(img, matrix, (www,hhh), cv2.INTER_LINEAR, borderMode=cv2.BORDER_CONSTANT, borderValue=(0,0,0))

# make mask for area outside the warped region
# (black in image stays black and rest becomes white)
mask = cv2.cvtColor(img_warped, cv2.COLOR_BGR2GRAY)
mask = cv2.threshold(mask, 0, 255, cv2.THRESH_BINARY)[1]
mask = cv2.merge([mask,mask,mask])
mask_inv = 255 - mask

# use mask to blend between img_warped and bck
result = ( 255 * (bck * mask_inv + img_warped * mask) ).clip(0, 255).astype(np.uint8)

# save images
cv2.imwrite("panda_warped.png", img_warped)
cv2.imwrite("panda_warped_mask.png", mask)
cv2.imwrite("panda_in_graph.png", result)

# show the result
cv2.imshow("warped", img_warped)
cv2.imshow("mask", mask)
cv2.imshow("result", result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Warped Input:

Sample Image

Mask:

Sample Image

Result:

Sample Image

How to draw a rectangle over a specific region in a matplotlib graph

The most likely reason is that you used data units for the x arguments when calling axhspan. From the function's docs (my emphasis):

y coords are in data units and x coords are in axes (relative 0-1)
units
.

So any rectangle stretching left of 0 or right of 1 is simply drawn off-plot.

An easy alternative might be to add a Rectangle to your axis (e.g., via plt.gca and add_patch); Rectangle uses data units for both dimensions. The following would add a grey rectangle with width & height of 1 centered on (2,3):

from matplotlib.patches import Rectangle
import matplotlib.pyplot as plt

fig = plt.figure()
plt.xlim(0, 10)
plt.ylim(0, 12)

someX, someY = 2, 5
currentAxis = plt.gca()
currentAxis.add_patch(Rectangle((someX - .5, someY - .5), 4, 6, facecolor="grey"))

Sample Image

  • Without facecolor
currentAxis.add_patch(Rectangle((someX - .5, someY - .5), 4, 6, facecolor="none", ec='k', lw=2))

Sample Image

Matplotlib: draw a selection area in the shape of a rectangle with the mouse

Matplotlib provides its own RectangleSelector. There is an example on the matplotlib page, which you may adapt to your needs.

A simplified version would look something like this:

import matplotlib.pyplot as plt
import numpy as np
from matplotlib.widgets import RectangleSelector

xdata = np.linspace(0,9*np.pi, num=301)
ydata = np.sin(xdata)

fig, ax = plt.subplots()
line, = ax.plot(xdata, ydata)

def line_select_callback(eclick, erelease):
x1, y1 = eclick.xdata, eclick.ydata
x2, y2 = erelease.xdata, erelease.ydata

rect = plt.Rectangle( (min(x1,x2),min(y1,y2)), np.abs(x1-x2), np.abs(y1-y2) )
ax.add_patch(rect)

rs = RectangleSelector(ax, line_select_callback,
drawtype='box', useblit=False, button=[1],
minspanx=5, minspany=5, spancoords='pixels',
interactive=True)

plt.show()

How to draw a rectangle on a circular barplot using matplotlib

Here's one way:

import matplotlib.patches as patches

fig, ax = plt.subplots(subplot_kw={'projection': 'polar'})
axin = ax.inset_axes([0.0, 0.0, 1, 1])
axin.axis('off')
rect = patches.Rectangle((0.5, 0.25), 0.5, 0.25, linewidth=2, edgecolor='r', facecolor='none')
axin.add_patch(rect)

Sample Image



Related Topics



Leave a reply



Submit