Bia
Bia

Reputation: 305

Resize a image while keeping its ratio using Python

I have a question regarding the pre-processing of some images using python. Most of the answers I found use Java and I need to use Python.

I have a training set of 2050 images and I want to resize them to 224 by 224 to later feed them to a convolutional neural network (CNN). The big problem is that I can't do it without losing the ratio of the images. Not all the images are the same size. For example, I have images 1000 by 700 pixels and 700 by 600 pixels.

My idea was to fill the smaller side of the image with pixels using the average of the surrounding ones. But I don't really know how to incorporate that with what I already have. I have heard of the cv2 library but I have never been able to make it work.

My code is the following:

training_set = pd.DataFrame({'Images': training_imgs,'Labels': training_labels})

 
train_dataGen = ImageDataGenerator(rescale=1./255)

train_generator = train_dataGen .flow_from_dataframe(
      dataframe = training_set, directory="",
      x_col="Images", y_col="Labels", 
      class_mode="categorical", 
      target_size=(224,224), batch_size=32)

## Steps to plot the images
imgs, labels = next(train_generator)

for i in range(batch_size):   
    image = imgs[i]
    plt.imshow(image)
    plt.show()

Upvotes: 2

Views: 1503

Answers (2)

Threnloe
Threnloe

Reputation: 41

Here is a resizing program that I wrote based on your description:

from PIL import Image
import math

def ResizeImage(Img, SourceWidth, SourceHeight, DestinationWidth, DestinationHeight, Average=True):
    tmp = [None] * DestinationWidth * DestinationHeight
    x_ratio = SourceWidth / float(DestinationWidth); y_ratio = SourceHeight / float(DestinationHeight)
    for i in range(0, DestinationWidth):
         for j in range(0, DestinationHeight):
             if Average:
                 tmp[i*DestinationWidth+j] = AverageOfSurrounding(Img, math.floor(j*x_ratio), math.floor(i*y_ratio))
             else:
                 tmp[i*DestinationWidth+j] = Img.getpixel((math.floor(j*x_ratio),math.floor(i*y_ratio)))
    im2 = Image.new(Img.mode, (DestinationWidth,DestinationHeight))
    im2.putdata(tmp)
    return im2

def AverageOfSurrounding(Img, x, y):
    if (x == 0):  # Left
        if (y == 0): # Bottom left
            p1 = Img.getpixel((x,y)); p2 = Img.getpixel((x+1,y)); p3 = Img.getpixel((x+1,y+1)); p4 = Img.getpixel((x,y+1))
            return (int((p1[0]+p2[0]+p3[0]+p4[0])/4), int((p1[1]+p2[1]+p3[1]+p4[1])/4), int((p1[2]+p2[2]+p3[2]+p4[2])/4), int((p1[3]+p2[3]+p3[3]+p4[3])/4))
        elif (y == Img.height): # Top left
            p1 = Img.getpixel((x,y)); p2 = Img.getpixel((x+1,y)); p3 = Img.getpixel((x+1,y-1)); p4 = Img.getpixel((x,y-1))
            return (int((p1[0]+p2[0]+p3[0]+p4[0])/4), int((p1[1]+p2[1]+p3[1]+p4[1])/4), int((p1[2]+p2[2]+p3[2]+p4[2])/4), int((p1[3]+p2[3]+p3[3]+p4[3])/4))
        else:
            p1 = Img.getpixel((x,y)); p2 = Img.getpixel((x+1,y)); p3 = Img.getpixel((x+1,y+1)); p4 = Img.getpixel((x,y+1)); p5 = Img.getpixel((x,y-1)); p6 = Img.getpixel((x+1,y-1))
            return (int((p1[0]+p2[0]+p3[0]+p4[0]+p5[0]+p6[0])/6), int((p1[1]+p2[1]+p3[1]+p4[1]+p5[1]+p6[1])/6), int((p1[2]+p2[2]+p3[2]+p4[2]+p5[2]+p6[2])/6), int((p1[3]+p2[3]+p3[3]+p4[3]+p5[3]+p6[3])/6))
    elif (x == Img.width):  # Right
        if (y == 0): # Bottom Right
            p1 = Img.getpixel((x,y)); p2 = Img.getpixel((x-1,y)); p3 = Img.getpixel((x-1,y+1)); p4 = Img.getpixel((x,y+1))
            return (int((p1[0]+p2[0]+p3[0]+p4[0])/4), int((p1[1]+p2[1]+p3[1]+p4[1])/4), int((p1[2]+p2[2]+p3[2]+p4[2])/4), int((p1[3]+p2[3]+p3[3]+p4[3])/4))
        elif (y == Img.height): # Top Right
            p1 = Img.getpixel((x,y)); p2 = Img.getpixel((x-1,y)); p3 = Img.getpixel((x-1,y-1)); p4 = Img.getpixel((x,y-1))
            return (int((p1[0]+p2[0]+p3[0]+p4[0])/4), int((p1[1]+p2[1]+p3[1]+p4[1])/4), int((p1[2]+p2[2]+p3[2]+p4[2])/4), int((p1[3]+p2[3]+p3[3]+p4[3])/4))
        else:
            p1 = Img.getpixel((x,y)); p2 = Img.getpixel((x-1,y)); p3 = Img.getpixel((x-1,y+1)); p4 = Img.getpixel((x,y+1)); p5 = Img.getpixel((x,y-1)); p6 = Img.getpixel((x-1,y-1))
            return (int((p1[0]+p2[0]+p3[0]+p4[0]+p5[0]+p6[0])/6), int((p1[1]+p2[1]+p3[1]+p4[1]+p5[1]+p6[1])/6), int((p1[2]+p2[2]+p3[2]+p4[2]+p5[2]+p6[2])/6), int((p1[3]+p2[3]+p3[3]+p4[3]+p5[3]+p6[3])/6))
    else:
        p1 = Img.getpixel((x-1,y+1)); p2 = Img.getpixel((x,y+1)); p3 = Img.getpixel((x+1,y+1))
        p4 = Img.getpixel((x-1,y)); p5 = Img.getpixel((x,y)); p6 = Img.getpixel((x+1,y))
        p7 = Img.getpixel((x-1,y-1)); p8 = Img.getpixel((x,y-1)); p9 = Img.getpixel((x+1,y-1))
        A = (p1[0]+p2[0]+p3[0]+p4[0]+p5[0]+p6[0]+p7[0]+p8[0]+p9[0])/9
        R = (p1[1]+p2[1]+p3[1]+p4[1]+p5[1]+p6[1]+p7[1]+p8[1]+p9[1])/9
        G = (p1[2]+p2[2]+p3[2]+p4[2]+p5[2]+p6[2]+p7[2]+p8[2]+p9[2])/9
        B = (p1[3]+p2[3]+p3[3]+p4[3]+p5[3]+p6[3]+p7[3]+p8[3]+p9[3])/9
        return (int(A), int(R), int(G), int(B))

im = Image.open("MyImage.png")
im = ResizeImage(im, im.width, im.height, 224, 224, False) 
im.show()

I used Pillow library, because it is quite efficient for small tasks like this and cv2 felt like shooting a mouse with bazooka.

EDIT: If you want to lock the image ratio for x and y (ratio = SourceWidth/SourceHeight), you can easily edit the code for that and maybe use zero pixel padding for the extra pixels.

Upvotes: 1

Prefect
Prefect

Reputation: 1777

You are almost done. You do not need opencv here if you use keras.

image = imgs[i,:,:,:] # batch_size, height, width, channels
print(np.shape(image) # should be (224,224) in case your image is grayscale
plt.imshow(image)
plt.show()

This will display your resized image and print the shape of it. If you want to change the interpolation method for your resizing method, you can check the documentation. It is bilinear as default, and I assume this is what you are looking for.

EDIT 0: If you want to create your custom function for any kind of preprocessesing on your images (resizing etc.), you might define preprocessing_function in your ImageDataGenerator method. This function is applied to every image in the batch.

import cv2

def preprocess(image):
   # feel free to define your own function here
   # be sure this function returns an image

   processed_image = cv2.resize(image, dsize=(224,224)) 

   return processed_image

train_dataGen = ImageDataGenerator(preprocessing_function=preprocess,
                                   rescale=1./255)

Upvotes: 1

Related Questions