Happy
Happy

Reputation: 469

Efficient way to find median value of a number of RGB images

I'm playing around with a script in Python where I want to find the median of a number of images of same dimensions. That is, I wan't to take all (red, green and blue) pixels in position [x,y], and construct a new image with their median values.

My current method uses Python PIL (the imaging library), but it is quite slow! I would very much like to use the OpenCV (cv2) interface, since it loads every image directly as a numpy array. However, I keep getting indices wrong when stacking x images of dimension (2560,1920,3). Any help?

My current, inefficient code with PIL, is the following:

from PIL import Image, ImageChops,ImageDraw,ImageFilter,cv
import sys,glob,sys,math,shutil,time,os, errno,numpy,string
from os import *

inputs = ()
path = str(os.getcwd())
BGdummyy=0
os.chdir(path)
for files in glob.glob("*.png"):
    inputs = inputs + (str(str(files)),)
BGdummy=0
for file in inputs:
    BGdummy=BGdummy+1
    im = cv.LoadImage(file)
    cv.CvtColor( im, im, cv.CV_BGR2RGB )
    img = Image.fromstring("RGB", cv.GetSize(im), im.tostring())
    vars()["file"+str(BGdummy)] = img.load()
imgnew = Image.new("RGB", (2560,1920))
pixnew = imgnew.load()
for x in range(2560):
    for y in range(1920):
        R=[];G=[];B=[];
        for z in range(len(inputs)):
            R.append(vars()["file"+str(z+1)][x,y][0])
            G.append(vars()["file"+str(z+1)][x,y][1])
            B.append(vars()["file"+str(z+1)][x,y][2])
        R = sorted(R)
        G = sorted(G)
        B = sorted(B)
        mid = int(len(inputs)/2.)
        Rnew = R[mid]
        Gnew = G[mid]
        Bnew = B[mid]
        pixnew[x,y] = (Rnew,Gnew,Bnew)
    BGdummyy = BGdummyy+1
imgnew.save("NewBG.png")

Upvotes: 1

Views: 5018

Answers (1)

Abid Rahman K
Abid Rahman K

Reputation: 52646

I will demonstrate on how to do it with 5 small arrays of size (3,3,3).

First I will create 5 arrays, then keep them in a list X. In your case you will have keep your 30 images in this list. ( I am doing it in a single line )

X = [a,b,c,d,e] = [np.random.randint(0,255,(3,3,3)) for i in xrange(5)]

Next you flatten each image to a long single row. So earlier your image would be like

[R1G1B1 R2G2B2 R3G3B3,
 R4G4B4 R5G5B5 R6G6B6,
 R7G7B7 R8G8B8 R9G9B9]

This will change into [R1 G1 B1 R2 G2 B2 R3 G3 B3......... R9 G9 B9] . Then you stack all these flattened images to form a big 2D array. In that array, you see, all first red pixels comes in first column and so on. Then you can simply apply np.median for that.

Y = np.vstack((x.ravel() for x in X))

I lattened each image and stacked. In my case, Y is an array of size 5x27 (row - number of images, column - number of pixels in an image)

Now I find median of this Y and reshape it to our original image shape :

Z = np.median(Y,axis = 0)
Z = np.uint8(Z.reshape(a.shape))

Done.

Just to make sure it is working fine, let's check the value of arbitrary pixel, say Z[0,1,2] :

In [50]: G1 = [x[0,1,2] for x in X]

In [51]: G1
Out[51]: [225, 65, 26, 182, 51]

In [52]: Z[0,1,2]
Out[52]: 65.0

Yes, the data is correct.

Upvotes: 4

Related Questions