0

I have read in some images with the code below. These images are of different sizes. In order to get them to equal sizes, I would like to add a black frame around the images. I found some code to do this for a single image but not for a list as in my case.

import cv2
import numpy
import glob
import matplotlib.pyplot as plt
from PIL import Image, ImageOps

folders = glob.glob(r'path\to\images\*')
imagenames_list = []
for folder in folders:
    for f in glob.glob(folder+'/*.png'):
        imagenames_list.append(f)



read_images = []        

for image in imagenames_list:
    read_images.append(cv2.imread(image, cv2.IMREAD_GRAYSCALE))

To add a black frame for a single picture I used this code:

from PIL import Image 
import numpy as np 


old_im = Image.open('path/to/single/picture/*.png')
old_size = old_im.size
print(old_size)


new_size = (500, 500)
print(new_size)
new_im = Image.new("RGB", new_size)
x = int((new_size[0]-old_size[0])/2)
y = int((new_size[1]-old_size[1])/2)
new_im.paste(old_im, (x,y))
Tobitor
  • 1,388
  • 1
  • 23
  • 58
  • what about using a for loop? – Miki Apr 24 '20 at 12:19
  • Yes, I already tried this, but it did not work out... Can you maybe show how to do it? – Tobitor Apr 24 '20 at 12:25
  • please add the relevant code to the question – Miki Apr 24 '20 at 12:27
  • I added the code to the question :) – Tobitor Apr 24 '20 at 12:33
  • 1
    Make a function that takes an image and the new size, and call it for every image in the list. BTW, since you're using opencv, you can use `copyMakeBorder`. See also [here](https://stackoverflow.com/q/43391205/5008845) – Miki Apr 24 '20 at 12:40
  • I tried to use `copyMakeBorder` and this worked out but I still have the issue that images are of different sizes because we add a frame to the image and do not embed the image into a frame. – Tobitor Apr 24 '20 at 13:06
  • Why are you mixing OpenCV and PIL - you will confuse yourself! – Mark Setchell Apr 24 '20 at 14:25
  • Do you really need to load all your images in a list? It just hogs a ton of memory and makes your machine and applications slow. Why not generate a list of filenames and open and resize them one-at-a-time? – Mark Setchell Apr 24 '20 at 14:27
  • I mixed it up because I did not find a similar function in OpenCV. The copyMakeBorder function does only add a frame... Your suggestion just to use a list of filenames seems plausible as well... But how do I do this? I run into errors again and again... – Tobitor Apr 24 '20 at 15:59

1 Answers1

2

Image read by OpenCV are just numpy arrays. You can just use numpy slicing to copy:

def makeborder(cv2img, new_width, new_height):
    '''
    cv2img: an image returned by cv2.imread()
    '''
    # gray scale or BGR/BGRA
    if len(cv2img.shape) == 2:
        new_shape = (new_height, new_width)
    else:
        new_shape = (new_height, new_width, cv2img.shape[-1])

    new_img = np.zeros(new_shape, dtype=cv2img.dtype)

    # compute the offsets, similar to your x & y
    offset_height = (new_height - cv2img.shape[0])//2
    offset_weight = (new_width - cv2img.shape[1])//2

    # should check offset_height >= 0 and offset_weight >= 0
    # but we skip here
    # ...

    # now we just use numpy slicing to copy
    new_img[offset_height:offset_height + cv2img.shape[0],
            offset_width: offset_width + cv2img.shape[1]] \
        = cv2img

    return new_img
Quang Hoang
  • 146,074
  • 10
  • 56
  • 74
  • How do I call this function? I tried the following code but got a Syntax Error. `images_with_black_frame = list(map(makeborder, new_width = 500, new_height = 500, read_images))`. SyntaxError: positional argument follows keyword argument – Tobitor Apr 28 '20 at 11:44
  • 1
    you can just do `img_with_black_frame = [makeborder(img, new_width=500, new_height=600) for img in read_images]`. – Quang Hoang Apr 28 '20 at 12:44