Grant Grisham
Grant Grisham

Reputation: 45

How to take the standard deviation of an image

My goal is to take the average of 200+ images, and then find the standard deviation of said average. Ask the user for a threshold and then compare the threshold to the standard deviation. If the threshold is < the standard deviation for that particular pixel, change it to red [255,0,0]. My issue lies with taking the standard deviation of a pixel. Any help would be appreciated.

allimg = []
avg_img=[]
path = glob.glob('podatlamar/*.jpg')

for x in path:
    img = Image.open(x)
    img = np.float32(img)
    allimg.append(img)
avg_img = np.average(allimg, axis=0)

std = [0,0,0]
for img in allimg :
    std += (img-avg_img) ** 2
    std = np.sqrt(std / (len(allimg) - 1))

now:

 for i in range(0, len(std)): #i is row
   for j in range(0, len(std[i])): #j is column
        if(std[i][j] > float(threshold)).any : 
           avg_img[i][j] = [255.0, 0.0, 0.0]

 avg_img = np.clip(avg_img, 0, 255)

etc...

and the result is all pixels are made red

and here is the for loop debugging in action

[The avg_img being changed to 255,0,0

Snippet of what std looks like

[[0.19792126 0.05137325 0.03966657]
  [0.09997863 0.06348856 0.07472634]
  [0.0367469  0.18667144 0.21834356]
  ...
  [0.02421235 0.02454335 0.14083997]
  [0.02319027 0.02351524 0.13969136]
  [0.02285284 0.02317629 0.13930877]]

 [[0.03304812 0.06428259 0.04262938]
  [0.0978733  0.02841616 0.04049174]
  [0.09566899 0.02877731 0.0357872 ]
  ...
  [0.08500231 0.03502595 0.12032651]
  [0.08347222 0.03630779 0.1217759 ]
  [0.08385488 0.03598539 0.12141356]]

Upvotes: 1

Views: 4025

Answers (1)

Mark Setchell
Mark Setchell

Reputation: 207445

As you have not provided any input data, I used the individual frames of this animation as my 18 input frames to average across:

enter image description here

I extracted them using ImageMagick:

magick animation.gif -coalesce frame-%02d.jpg

The code I came up with looks like this:

#!/usr/bin/env python3

import glob
import numpy as np
from PIL import Image

# Generate list of image names
names = glob.glob('frame-*.jpg')

# Load all images into list
images = []
for filename in names:
    im = Image.open(filename)
    images.append(np.array(im))

# Generate average image, where each pixel is the average of that pixel position across all images
average = np.mean(images, axis=0)
Image.fromarray(average.astype(np.uint8)).save('tmp-average.png')    # DEBUG

# Generate stdev image, where each pixel is the stdev of that pixel position across all images
stdev = np.std(images, axis=0)
Image.fromarray(stdev.astype(np.uint8)).save('tmp-stdev.png')        # DEBUG

threshold = 80
average[np.any(stdev>threshold, axis=2)] = [255,0,0]
Image.fromarray(average.astype(np.uint8)).save(f'result.png')

And the result is this:

enter image description here

Just for fun, I made a little animation of the effect of setting the threshold differently:

enter image description here

Upvotes: 3

Related Questions