Reputation: 251
Within a global matplotlib figure, I want to have a few subplots of different sizes. For example, one subplot should be the exact size of the frame captured from opencv2 (webcam), and the other should be a smaller image obtained from that frame.
I'm having two different issues, both regarding to sizing:
Getting frame:
import cv2
import matplotlib.pyplot as plt
cap = cv2.VideoCapture(0)
ret, frame = cap.read()
Building figure and subplots:
fig = plt.figure()
img = fig.add_subplot(121)
img2 = fig.add_subplot(122)
Then putting frame into subplot
img.imshow(frame) #this should be the original size of captured frame
#take out a square of the frame, and plot with box dimensions
#img2.imshow(box)
Cheers!
------ EDIT ------
Although I'll be using a webcam for the images, the core of my problem is the following: 1. Open image with opencv 2. Plot the image into a subplot, having same dimensions as the opencv read image
The code:
import cv2
import matplotlib.pyplot as plt
img = cv2.imread('flower.jpg')
cv2.imshow('img',img)
fig = plt.figure()
video_plot = plt.subplot2grid((10, 10), (0, 0)) #Here I need to specify the same size as original
video = video_plot.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
cv2.waitKey(0)
cv2.destroyAllWindows()
plt.show()
Original 256x256 picture, read opened with opencv
Smaller image, if I leave out colspan and rowspan (=1) plt.subplot2grid((10, 10), (0, 0))
Bigger image, if I max out the colspan and rowspan: plt.subplot2grid((10, 10), (0, 0), colspan=10, rowspan=10)
So to sum up, how can I plot the same image size?
Upvotes: 3
Views: 8429
Reputation: 15586
The solution of @ImportanceOfBeingErnes worked for me but i had a hard time understanding it. The issue is with the difference in the width/height extraction from the array for images of opencv and matplotlib. I prefer to use readable variable names to hide this complexity.
So this helper function is the core of getting an apropriate axis:
def addImage(fig,image,x,y):
dpi=fig.dpi
fw,fh=fig.get_size_inches()[:2]
ih,iw=image.shape[:2]
ax = fig.add_axes([x/dpi/fw,y/dpi/fh,iw/dpi/fw,ih/dpi/fh])
return ax
Code: see also https://github.com/WolfgangFahl/play-chess-with-a-webcam/blob/master/examples/matplotlib/image_show.py
#!/usr/bin/python
# -*- encoding: utf-8 -*-
# part of https://github.com/WolfgangFahl/play-chess-with-a-webcam
# see https://stackoverflow.com/questions/43372792/matplotlib-opencv-image-subplot
import matplotlib.pyplot as plt
import sys
import cv2
def main(argv):
# get the square chessbord file as an example
default_file = '../../testMedia/chessBoard011.jpg'
filename = argv[0] if len(argv) > 0 else default_file
image = cv2.imread(filename)
cv2.cvtColor(ima,cv2.COLOR_BGR2RGB)
# resize it to fit at the default 100 dpi
w=320
h=320
x=50
y=50
image = cv2.resize(image,(w,h))
# 6 x 4 inch figure
figsize=(6, 4)
fig = plt.figure(figsize=figsize)
# add the image at position x=50, y=50 (in image coordinates)
ax=addImage(fig,image,x,y)
im = ax.imshow(image)
subboard2x2=image[w//4:w//2,h//4:h//2,:]
yLegendOffset=100
ax2=addImage(fig,subboard2x2,w+yLegendOffset,h//2)
im2=ax2.imshow(subboard2x2)
ax.axis("off")
plt.show()
def addImage(fig,image,x,y):
dpi=fig.dpi
fw,fh=fig.get_size_inches()[:2]
ih,iw=image.shape[:2]
ax = fig.add_axes([x/dpi/fw,y/dpi/fh,iw/dpi/fw,ih/dpi/fh])
return ax
if __name__ == "__main__":
main(sys.argv[1:])
Upvotes: 0
Reputation: 339560
The figure size is indeed specified in inches. The dots per inch (dpi) are specified as well in matplotlib.
Knowing the figure size in inches and the dpi allows you to position an axes in figure coordinates (from 0 to 1), by dividing the pixels by the dpi and the figure size. E.g. in order to place an axes 50 pixels away from the lower left corner you'd place the axes at
fig.add_axes([50./dpi/figsize[0], 50./dpi/figsize[1], ...])
The width and height are in analogy determined by the number of pixels in each direction divided by dpi and figure size in inches.
Complete example:
import matplotlib.pyplot as plt
import numpy as np; np.random.seed(0)
ar = np.random.poisson(lam=10, size=(250,300))
cut = ar[50:100, 200:250]; print cut.shape
dpi=100. # dots per inch
figsize=(5, 3.5)
fig = plt.figure(figsize=figsize)
ax = fig.add_axes([50./dpi/figsize[0],50./dpi/figsize[1],
ar.shape[1]/dpi/figsize[0],ar.shape[0]/dpi/figsize[1]])
im = ax.imshow(ar)
ax2 = fig.add_axes([100./dpi/figsize[0]+ar.shape[1]/dpi/figsize[0],50./dpi/figsize[1],
cut.shape[1]/dpi/figsize[0],cut.shape[0]/dpi/figsize[1]])
im2 = ax2.imshow(cut)
ax.axis("off")
plt.show()
Upvotes: 1