How can I efficiently place an RGB numpy array with an image environment?

I have RGB images that have already been rescaled so that the longer edge becomes 256 pixels, now I want to place a border with the average RGB values โ€‹โ€‹of this image so that the resulting image is always 256x256 pixels.

This code is already working, but I'm sure there might be an easier and more elegant way to do it:

img = loadAndFitImage(filePath, maxSideLength=256, upscale=True)
shp = img.shape
#the shp in this case is typically (256,123,3) or (99,256,3)

leftPad = (256 - shp[0]) / 2
rightPad = 256 - shp[0] - leftPad
topPad = (256 - shp[1]) / 2
bottomPad = 256 - shp[1] - topPad

# this part looks like there might be a way to do it with one median call instead of 3:
median = (np.median(img[:, :, 0]),np.median(img[:, :, 1]),np.median(img[:, :, 2]))

img = np.lib.pad(img, ((leftPad,rightPad),(topPad,bottomPad),(0,0)),
 'constant',constant_values=0)

if leftPad > 0:
    img[:leftPad,:,0].fill(median[0])
    img[:leftPad,:,1].fill(median[1])
    img[:leftPad,:,2].fill(median[2])
if rightPad > 0:
    img[-rightPad:,:,0].fill(median[0])
    img[-rightPad:,:,1].fill(median[1])
    img[-rightPad:,:,2].fill(median[2])
if topPad > 0:
    img[:,:topPad,0].fill(median[0])
    img[:,:topPad,1].fill(median[1])
    img[:,:topPad,2].fill(median[2])
if bottomPad > 0:
    img[:,-bottomPad:,0].fill(median[0])
    img[:,-bottomPad:,1].fill(median[1])
    img[:,-bottomPad:,2].fill(median[2])

      

Edit (More information):

  • This is what the final result should look like:

  • Desired:

  • This is how it shouldn't look (median for the column):

  • Undesirable:

+3


source to share


3 answers


You can do it easily:

import numpy as np

a = np.asarray([[1,2,3,4,5,6],
     [8,4,5,6,7,7],
     [1,2,3,4,5,6],
     [1,2,3,4,5,6],
     [1,2,3,4,5,6],
     [1,2,3,4,5,6]])

b = a * 3
c = a * 4
d = (a,b,c)

im = np.asarray([np.pad(x, (2,), 'constant', constant_values=(np.median(x) ,)) for x in d])
print im

      

Output:

[[[ 4  4  4  4  4  4  4  4  4  4]
  [ 4  4  4  4  4  4  4  4  4  4]
  [ 4  4  1  2  3  4  5  6  4  4]
  [ 4  4  8  4  5  6  7  7  4  4]
  [ 4  4  1  2  3  4  5  6  4  4]
  [ 4  4  1  2  3  4  5  6  4  4]
  [ 4  4  1  2  3  4  5  6  4  4]
  [ 4  4  1  2  3  4  5  6  4  4]
  [ 4  4  4  4  4  4  4  4  4  4]
  [ 4  4  4  4  4  4  4  4  4  4]]

 [[12 12 12 12 12 12 12 12 12 12]
  [12 12 12 12 12 12 12 12 12 12]
  [12 12  3  6  9 12 15 18 12 12]
  [12 12 24 12 15 18 21 21 12 12]
  [12 12  3  6  9 12 15 18 12 12]
  [12 12  3  6  9 12 15 18 12 12]
  [12 12  3  6  9 12 15 18 12 12]
  [12 12  3  6  9 12 15 18 12 12]
  [12 12 12 12 12 12 12 12 12 12]
  [12 12 12 12 12 12 12 12 12 12]]

 [[16 16 16 16 16 16 16 16 16 16]
  [16 16 16 16 16 16 16 16 16 16]
  [16 16  4  8 12 16 20 24 16 16]
  [16 16 32 16 20 24 28 28 16 16]
  [16 16  4  8 12 16 20 24 16 16]
  [16 16  4  8 12 16 20 24 16 16]
  [16 16  4  8 12 16 20 24 16 16]
  [16 16  4  8 12 16 20 24 16 16]
  [16 16 16 16 16 16 16 16 16 16]
  [16 16 16 16 16 16 16 16 16 16]]]

      

Or in your specific case:



Original image

import numpy as np
from PIL import Image

filePath = '/home/george/Desktop/img.jpg'

Img = Image.open(filePath)
img = np.asarray(Img, np.int)
shp = np.shape(img)
img = img.transpose(2,0,1).reshape(3,215,215)

leftPad = round(float((255 - shp[0])) / 2)
rightPad = round(float(255 - shp[0]) - leftPad)
topPad = round(float((255 - shp[1])) / 2)
bottomPad = round(float(255 - shp[1]) - topPad)
pads = ((leftPad,rightPad),(topPad,bottomPad))

img_arr = np.ndarray((3,255,255),np.int)
for i,x in enumerate(img):
    cons = np.int(np.median(x))
    x_p = np.pad(x,pads,
                'constant', 
                 constant_values=cons)
    img_arr[i,:,:] = x_p

im_shp = np.shape(img_arr)
ii = np.uint8(img_arr).transpose(1,2,0)

im = Image.fromarray(np.array( (ii) ))
im.show()
im.save((filePath), "JPEG")

      

Output:

Median Padded Image

+5


source


Calculating the median can be done with median = np.median(img.reshape(-1, 3), axis=0)

or something similar, see this answer .



Subscription can be done with one line per side, something like img[:leftPad,:,:] = median

. Check out the broadcasting rules .

+2


source


I also struggled with this and figured out the elegant answer:

color = np.median(img, axis=(0,1)) img = np.stack([np.pad(img[:,:,c], pad, mode='constant', constant_values=color[c]) for c in range(3)], axis=2)

+2


source







All Articles