OpenCV subplots images with titles and space around borders
I am looking to display some images in OpenCV Python with titles and borders around the each subplot. something like this (courtesy of the following stackoverflow post: OpenCV (Python) video subplots):
WHAT I WANT:
But I only manage to get this with that code adapted.
import cv2 im1 = cv2.imread('Lenna.png') final_frame = cv2.hconcat((im1, im1)) cv2.imshow('lena', final_frame)
WHAT I HAVE
Is it possible to obtain this using OpenCV? I know a workaround would be to put text on the images, but that's not what I want because it will cover important information that way.
UPDATE
My bad, I didn't specify initially: I have 4 subplots (so 4 different images) and not two like in the example. Also, I want the solution to be as fast as possible since I have video (time restrictions)
I have a pretty quick and dirty solution. You can refine it to suit your needs. I have the explanation alongside the code as well:
import cv2 import numpy as np img1 = cv2.imread('lena.jpg') #--- Here I am creating the border--- black = [0,0,0] #---Color of the border--- constant=cv2.copyMakeBorder(img1,10,10,10,10,cv2.BORDER_CONSTANT,value=black ) cv2.imshow('constant',constant)
You can find many other options for different borders ON THIS PAGE
#--- Here I created a violet background to include the text --- violet= np.zeros((100, constant.shape[1], 3), np.uint8) violet[:] = (255, 0, 180)
#--- I then concatenated it vertically to the image with the border --- vcat = cv2.vconcat((violet, constant)) cv2.imshow('vcat', vcat)
#--- Now I included some text --- font = cv2.FONT_HERSHEY_SIMPLEX cv2.putText(vcat,'FRAME',(30,50), font, 2,(0,0,0), 3, 0) cv2.imshow('Text', vcat)
#--- I finally concatenated both the above images horizontally--- final_img = cv2.hconcat((vcat, vcat)) cv2.imshow('Final', final_img) cv2.waitKey(0) cv2.destroyAllWindows()
Basic Operations on Images, If you want to create a border around an image, something like a photo frame, you can use cv. But it has more applications for convolution operation, zero padding etc. plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL'). OpenCV subframe images with titles and space around borders I am looking to display some images in OpenCV Python with titles and borders around the each subplot. something like this (courtesy of the following stackoverflow post: OpenCV (Python) video subplots): WHAT I WANT: But I only manage to get this with
The general idea would be to create a new image with width += width/10
and height += height/20
. Write some text as heading and place the input image along the center as:
import cv2 import numpy as np img = cv2.imread("/Users/anmoluppal/Downloads/Lenna.png") height, width, ch = img.shape new_width, new_height = width + width/20, height + height/8 # Crate a new canvas with new width and height. canvas = np.ones((new_height, new_width, ch), dtype=np.uint8) * 125 # New replace the center of canvas with original image padding_top, padding_left = 60, 10 if padding_top + height < new_height and padding_left + width < new_width: canvas[padding_top:padding_top + height, padding_left:padding_left + width] = img else: print "The Given padding exceeds the limits." text1 = "Sample Image 1" text2 = "Sample Image 2" img1 = cv2.putText(canvas.copy(), text1, (int(0.25*width), 30), cv2.FONT_HERSHEY_COMPLEX, 1, np.array([255, 0, 0])) img2 = cv2.putText(canvas.copy(), text2, (int(0.25*width), 30), cv2.FONT_HERSHEY_COMPLEX, 1, np.array([255, 0, 0])) final = cv2.hconcat((img1, img2)) cv2.imwrite("./debug.png", final)
Smoothing Images, Blur images with various low pass filters; Apply custom-made filters to images (2D convolution) plt.subplot(122),plt.imshow(dst),plt.title('Averaging') This Gaussian filter is a function of space alone, that is, nearby pixels are considered while Making Borders for Images (Padding) If you want to create a border around an image, something like a photo frame, you can use cv.copyMakeBorder(). But it has more applications for convolution operation, zero padding etc. This function takes following arguments: src - input image
I used the other answers to make a generalizable function which works for arbitrary row/columns:
def cvSubplot(imgs, # 2d np array of imgs (each img an np arrays of depth 1 or 3). pad=10, # number of pixels to use for padding between images. must be even titles=None, # (optional) np array of subplot titles win_name='CV Subplot' # name of cv2 window ): ''' Makes cv2 based subplots. Useful to plot image in actual pixel size ''' rows, cols = imgs.shape subplot_shapes = np.array([list(map(np.shape, x)) for x in imgs]) sp_height, sp_width, depth = np.max(np.max(subplot_shapes, axis=0), axis=0) title_pad = 30 if titles is not None: pad_top = pad + title_pad else: pad_top = pad frame = np.zeros((rows*(sp_height+pad_top), cols*(sp_width+pad), depth )) for r in range(rows): for c in range(cols): img = imgs[r, c] h, w, _ = img.shape y0 = r * (sp_height+pad_top) + pad_top//2 x0 = c * (sp_width+pad) + pad//2 frame[y0:y0+h, x0:x0+w, :] = img if titles is not None: frame = cv2.putText(frame, titles[r, c], (x0, y0-title_pad//4), cv2.FONT_HERSHEY_COMPLEX, .5, (255,255,255)) cv2.imshow(win_name, frame) cv2.waitKey(0)
Below is an example usage:
import cv2 import numpy as np a1 = np.random.random((40,400,1)) a2 = np.random.random((200,200,1)) a3 = np.random.random((100,100,1)) a4 = np.random.random((300,150,1)) a5 = np.random.random((100,150,1)) filler = np.zeros((0,0,1)) titles = np.array([['A', 'B', 'C'], ['D', 'E', 'Filler']]) imgs = np.array([[a1, a2, a3], [a4, a5, filler]]) cvSubplot(imgs, pad=20, titles=titles)
That script produces the following cv2 image:
Basic Operations on Images, For BGR image, it returns an array of Blue, Green, Red values. If you want to create a border around the image, something like a photo frame, you can use cv2. But it has more applications for convolution operation, zero padding etc. plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL') plt.subplot(232) We know that cv2.imshow() only shows 1 image at a time. Displaying images side by side helps greatly in analyzing the result. Unlike Matlab, there is no direct function for creating subplots. But since OpenCV reads images as arrays, we can concatenate arrays using the inbuilt cv2.hconcat() and cv2.vconcat() functions.
OpenCV Basic Operation On images, OpenCV Basic Operation On images with What is OpenCV, History, Installation, copyMakeBorder() function to create a border around the image, something like a photo frame. plt.subplot(232),plt.imshow(replicate,'gray'),plt.title('REPLICATE') The cvtColor is used to convert an image from one color space to another. The frame from opencv is with pixels, how can I make a subplot show the exact same image (in size), especially since matplotlib uses inches for dimensions? Getting frame: import cv2 import matplotlib.pyplot as plt cap = cv2.VideoCapture(0) ret, frame = cap.read()
OpenCV: Basic Operations on Images, Access pixel values and modify them; Access image properties; Setting Region of If you want to create a border around the image, something like a photo frame, you can use cv2. But it has more applications for convolution operation, zero padding etc. 15 plt.subplot(231),plt.imshow(img1,'gray'),plt.title('ORIGINAL'). Show image like its shows in full screen but in small size. from backend we select the image size and we have to show this image without any title bar and border. only image will appear. i try lot of methods, but didn't get success. is there any way to do this ? if any body knows, please help, i stuck in this from many days
Displaying images side by side helps greatly in analyzing the result. Unlike Matlab, there is no direct function for creating subplots. But since OpenCV reads images as arrays, we can concatenate arrays using the inbuilt cv2.hconcat() and cv2.vconcat() functions. After that, we display this concatenated image using cv2.imshow().
Comments
- Hope This answer helps. You can modify it to suit your requirement.
- one minor comment. why don't you increase the size of the border on top in, e.g.
constant=cv2.copyMakeBorder(img1,100,10,10,10,cv2.BORDER_CONSTANT,value=black )
. that way the violet rectangle is not necessary anymore. I am not looking for anything fancy, but for something fast& simple :) - As I said you can always modify it. You can also adjust the position of the text. :D
- I hope this helped you :)
- Hey this is a more optimal solution. Great work mate !!!! Mine was rather quick and dirty :D
- No Worries, Dude, Your solution is using inbuilt methods like
cv2.copyMakeBorder
instead of maths, which seems good to me , Thanks for appreciating :) - If you have images of the same size, you have to be wary of the
numpy array
flattens the array of arrays to a n-dimensional array. In this case a five dimensional array. I haven't found a good way to forcenumpy
to keep the array of arrays, but this workaround is working:a1 = np.random.random((300,300,1))
a2 = a1
imgs = np.empty([1,2], dtype=object)
imgs[0,0] = a1
imgs[0,1] = a2