How to generate a gaussian distribution intensity for an ROI? - python

I have an array A size of 64x64. An ROI region has pixels intensities is 100 if the pixels are inside ROI. Outside of ROI is zero
import numpy as np
A= np.zeros((64,64))
A[16:48,26:48]=100
I want to change the intensity value of inside ROI to a Gaussian distribution with the mean is 100 and variance is 1. How can I do it? I tried the below code but it is not correct because np.random.normal for whole array, instead of the ROI
noise_value = np.random.normal(0, 1, size=A.shape)
A = A*noise_value + A

try this:
import numpy as np
import matplotlib.pyplot as plt
def gaus(x, a, m, s):
return np.sqrt(a)*np.exp(-(x-m)**2/(2*s**2))
# if you want it normalized:
#return 1/(np.sqrt(2*np.pi*s**2))*np.exp(-(x-m)**2/(2*s**2))
xx, yy = np.meshgrid(np.arange(100), np.arange(100))
gaus2d = gaus(xx, 100, 50, 10)*gaus(yy, 100, 50, 10)
plt.figure()
plt.imshow(gaus2d)
plt.colorbar()

The region you're multiplying noise_value by needs to be the same shape as noise_value. Try:
A[16:48,26:48] = A[16:48,26:48] * noise_value + A[16:48,26:48]
also, your formula A = A * noise_value + A, doesn't seem to match the intensity distribution you describe. If that's the case, you might just:
A = np.zeros((64,64))
A[16:48,26:48] = np.random.normal(100, 1, size=[32,22])

Related

How to take Red pixel at image and transform Red pixel to probability transform and perform KL divergence at python

I am trying to extract red pixel at image and transform to probability density function to perform KL divergence at python
For the next i would like to perform with 2 image red pixel and calculate KL divergence
This is what i already do, sorry if noob and mess code
from PIL import Image
import matplotlib.pyplot as plt
import numpy as np
from scipy.stats.kde import gaussian_kde
from numpy import linspace
from scipy.interpolate import UnivariateSpline
from scipy.stats import norm
import tensorflow as tf
import seaborn as sns
sns.set()
from scipy.stats import entropy
def getRed(redVal):
return '#%02x%02x%02x' % (redVal, 0, 0)
def getGreen(greenVal):
return '#%02x%02x%02x' % (0, greenVal, 0)
def getBlue(blueVal):
return '#%02x%02x%02x' % (0, 0, blueVal)
# Create an Image with specific RGB value
image = Image.open("C:/Users/ahsan/Downloads/bunga/1.png")
# Modify the color of two pixels
image.putpixel((0,1), (1,1,5))
image.putpixel((0,2), (2,1,5))
# Display the image
plt.imshow(image)
plt.show()
# Get the color histogram of the image
histogram = image.histogram()
# Take only the Red counts
l1 = histogram[0:256]
# Take only the Blue counts
l2 = histogram[256:512]
# Take only the Green counts
l3 = histogram[512:768]
plt.figure(0)
# R histogram
for i in range(0, 256):
plt.bar(i, l1[i], color = getRed(i), edgecolor=getRed(i), alpha=0.3)
plt.figure(3)
data = l1
kde = gaussian_kde( data )
dist_space = linspace( min(data), max(data), 100 )
# plot the results
plt.plot( dist_space, kde(dist_space) )
plt.show()
def kl(p, q):
"""Kullback-Leibler divergence D(P || Q) for discrete distributions
Parameters
----------
p, q : array-like, dtype=float, shape=n
Discrete probability distributions.
"""
p = np.asarray(p, dtype=np.float)
q = np.asarray(q, dtype=np.float)
return np.sum(np.where(p != 0, p * np.log(p / q), 0))
plt.plot(x, q, c='red')
p=l1 #Just example i will try with 2 image
q=l2 # Just ecample would like to try with 2 image
assert entropy(p, q) == kl(p, q)
But i am still can't calculate the KL divergence

Gaussian filtering image with a cut-off value in python

I have some data basically surrounded by 0 value and I would like to apply a Gaussian filter just to the no-zero values masking the zero ones.
This is a MWE:
import numpy as np
import scipy.ndimage as ndimage
import matplotlib.pyplot as plt
import random
data = np.zeros((100, 100))
for i in range(25, 76, 1):
for j in range(25, 76, 1):
data[i, j] = random.random()
data2 = ndimage.gaussian_filter(data, sigma=5.0)
fig1 = plt.figure("data")
ax = fig1.add_subplot(111)
cf1 = ax.contourf(data)
fig1.colorbar(cf1)
fig1 = plt.figure("data2")
ax = fig1.add_subplot(111)
cf1 = ax.contourf(data2)
fig1.colorbar(cf1)
Sorry I don't know why but I couldn't upload the results of the MWE.
The MWE produces a 100x100 array with no-zero values in a sub-square 50x50 located in the centre with position [25:75, 25:75].
When I apply the filter the number of no-zero values increase with position [5:96, 5:96].
What I want it's to mask in some way the zeros values and not apply the filter there, the filter have to be applied just in the 50x50 sub-square.
I tried to use numpy masked array but it didn't work.
Does anyone know how to do it?
Gaussian filtering is a local weighted averaging. If you want to adapt the weights such that certain pixels (those outside the selected area) are not used to compute the average for other pixels, you need to use normalized convolution:
( (f . m) * g ) / ( m * g )
where f is the image, m is the mask with 0 value for pixels to not be used, g is the Gaussian kernel, and * is the convolution.
Using your code it would be implemented as:
data = np.zeros((100, 100))
mask = np.zeros((100, 100))
for i in range(25, 76, 1):
for j in range(25, 76, 1):
data[i, j] = random.random()
mask[i, j] = 1
data2 = ndimage.gaussian_filter(data * mask, sigma=5.0)
data2 /= ndimage.gaussian_filter(mask, sigma=5.0)
This will make is so that each output pixel contains a weighted average of only pixels that have a 1 in the mask image. However, zero pixels will still get a value. You now need to mask those:
data2[np.logical_not(mask)] = 0

fastest way to select 7*7 neighbor pixels for every pixel in an image in Python

need to read an image as an array and for each pixel select 7*7 neighbor pixels then reshape it and put as a first row of training set:
import numpy as np
from scipy import misc
face1=misc.imread('face1.jpg')
face1 dimensions are (288, 352, 3) , need to find 7*7 neighbor pixels for every pixel , so 49*3 color then reshape it as a (1,147) array and stack it into an array for all pixels , i took the following approach:
X_training=np.zeros([1,147] ,dtype=np.uint8)
for i in range(3, face1.shape[0]-3):
for j in range(3, face1.shape[1]-3):
block=face1[i-3:i+4,j-3:j+4]
pxl=np.reshape(block,(1,147))
X_training=np.vstack((pxl,X_training))
resulting X_training shape is (97572, 147)
and as last row contains all zeros then:
a = len(X_training)-1
X_training = X_training[:a]
above code works well for one picture but with Wall time: 5min 19s i have 2000 images, so it will take ages to do it for all the images. I am looking for a faster way to iterate over every pixel and do the above task.
Edit:
this is what i mean by neighbor pixels , for every pixel face1[i-3 : i+4 ,j-3:j+4]
An efficient way is to use stride_tricks to create a 2d rolling window over the image, then flatten it out:
import numpy as np
face1 = np.arange(288*352*3).reshape(288, 352, 3) # toy data
n = 7 # neighborhood size
h, w, d = face1.shape
s = face1.strides
tmp = np.lib.stride_tricks.as_strided(face1, strides=s[:2] + s,
shape=(h - n + 1, w - n + 1, n, n, d))
X_training = tmp.reshape(-1, n**2 * d)
X_training = X_training[::-1] # to get the rows into same order as in the question
tmp is a 5D view into the image, where tmp[x, y, :, :, c] is equivalent to the neigborhood face1[x:x+n, y:y+n, c] in color channel c.
The following is < 1s on my laptop:
import scipy as sp
im = sp.rand(300, 300, 3)
size = 3
ij = sp.meshgrid(range(size, im.shape[0]-size), range(size, im.shape[1]-size))
i = ij[0].T.flatten()
j = ij[1].T.flatten()
N = len(i)
L = (2*size + 1)**2
X_training = sp.empty(shape=[N, 3*L])
for pixel in range(N):
si = (slice(i[pixel]-size, i[pixel]+size+1))
sj = (slice(j[pixel]-size, j[pixel]+size+1))
X_training[pixel, :] = im[si, sj, :].flatten()
X_training = X_training[-1::-1, :]
I'm always a bit sad when I can't think of one-line vectorized version, but at least it's faster for you.
Using scikit-image:
import numpy as np
from skimage import util
image = np.random.random((288, 352, 3))
windows = util.view_as_windows(image, (7, 7, 3))
out = windows.reshape(-1, 7 * 7 * 3)

scipy.ndimage.interpolation.zoom uses nearest-neighbor-like algorithm for scaling-down

While testing scipy's zoom function, I found that the results of scailng-down an array are similar to the nearest-neighbour algorithm, rather than averaging. This increases noise drastically, and is generally suboptimal for many application.
Is there an alternative that does not use nearest-neighbor-like algorithm and will properly average the array when downsizing? While coarsegraining works for integer scaling factors, I would need non-integer scaling factors as well.
Test case: create a random 100*M x 100*M array, for M = 2..20
Downscale the array by the factor of M three ways:
1) by taking the mean in MxM blocks
2) by using scipy's zoom with a scaling factor 1/M
3) by taking a first point within a
Resulting arrays have the same mean, the same shape, but scipy's array has the variance as high as the nearest-neighbor. Taking a different order for scipy.zoom does not really help.
import scipy.ndimage.interpolation
import numpy as np
import matplotlib.pyplot as plt
mean1, mean2, var1, var2, var3 = [],[],[],[],[]
values = range(1,20) # down-scaling factors
for M in values:
N = 100 # size of an array
a = np.random.random((N*M,N*M)) # large array
b = np.reshape(a, (N, M, N, M))
b = np.mean(np.mean(b, axis=3), axis=1)
assert b.shape == (N,N) #coarsegrained array
c = scipy.ndimage.interpolation.zoom(a, 1./M, order=3, prefilter = True)
assert c.shape == b.shape
d = a[::M, ::M] # picking one random point within MxM block
assert b.shape == d.shape
mean1.append(b.mean())
mean2.append(c.mean())
var1.append(b.var())
var2.append(c.var())
var3.append(d.var())
plt.plot(values, mean1, label = "Mean coarsegraining")
plt.plot(values, mean2, label = "mean scipy.zoom")
plt.plot(values, var1, label = "Variance coarsegraining")
plt.plot(values, var2, label = "Variance zoom")
plt.plot(values, var3, label = "Variance Neareset neighbor")
plt.xscale("log")
plt.yscale("log")
plt.legend(loc=0)
plt.show()
EDIT: Performance of scipy.ndimage.zoom on a real noisy image is also very poor
The original image is here http://wiz.mit.edu/lena_noisy.png
The code that produced it:
from PIL import Image
import numpy as np
import matplotlib.pyplot as plt
from scipy.ndimage.interpolation import zoom
im = Image.open("/home/magus/Downloads/lena_noisy.png")
im = np.array(im)
plt.subplot(131)
plt.title("Original")
plt.imshow(im, cmap="Greys_r")
plt.subplot(132)
im2 = zoom(im, 1 / 8.)
plt.title("Scipy zoom 8x")
plt.imshow(im2, cmap="Greys_r", interpolation="none")
im.shape = (64, 8, 64, 8)
im3 = np.mean(im, axis=3)
im3 = np.mean(im3, axis=1)
plt.subplot(133)
plt.imshow(im3, cmap="Greys_r", interpolation="none")
plt.title("averaging over 8x8 blocks")
plt.show()
Nobody posted a working answer, so I will post a solution I currently use. Not the most elegant, but works.
import numpy as np
import scipy.ndimage
def zoomArray(inArray, finalShape, sameSum=False,
zoomFunction=scipy.ndimage.zoom, **zoomKwargs):
"""
Normally, one can use scipy.ndimage.zoom to do array/image rescaling.
However, scipy.ndimage.zoom does not coarsegrain images well. It basically
takes nearest neighbor, rather than averaging all the pixels, when
coarsegraining arrays. This increases noise. Photoshop doesn't do that, and
performs some smart interpolation-averaging instead.
If you were to coarsegrain an array by an integer factor, e.g. 100x100 ->
25x25, you just need to do block-averaging, that's easy, and it reduces
noise. But what if you want to coarsegrain 100x100 -> 30x30?
Then my friend you are in trouble. But this function will help you. This
function will blow up your 100x100 array to a 120x120 array using
scipy.ndimage zoom Then it will coarsegrain a 120x120 array by
block-averaging in 4x4 chunks.
It will do it independently for each dimension, so if you want a 100x100
array to become a 60x120 array, it will blow up the first and the second
dimension to 120, and then block-average only the first dimension.
Parameters
----------
inArray: n-dimensional numpy array (1D also works)
finalShape: resulting shape of an array
sameSum: bool, preserve a sum of the array, rather than values.
by default, values are preserved
zoomFunction: by default, scipy.ndimage.zoom. You can plug your own.
zoomKwargs: a dict of options to pass to zoomFunction.
"""
inArray = np.asarray(inArray, dtype=np.double)
inShape = inArray.shape
assert len(inShape) == len(finalShape)
mults = [] # multipliers for the final coarsegraining
for i in range(len(inShape)):
if finalShape[i] < inShape[i]:
mults.append(int(np.ceil(inShape[i] / finalShape[i])))
else:
mults.append(1)
# shape to which to blow up
tempShape = tuple([i * j for i, j in zip(finalShape, mults)])
# stupid zoom doesn't accept the final shape. Carefully crafting the
# multipliers to make sure that it will work.
zoomMultipliers = np.array(tempShape) / np.array(inShape) + 0.0000001
assert zoomMultipliers.min() >= 1
# applying scipy.ndimage.zoom
rescaled = zoomFunction(inArray, zoomMultipliers, **zoomKwargs)
for ind, mult in enumerate(mults):
if mult != 1:
sh = list(rescaled.shape)
assert sh[ind] % mult == 0
newshape = sh[:ind] + [sh[ind] // mult, mult] + sh[ind + 1:]
rescaled.shape = newshape
rescaled = np.mean(rescaled, axis=ind + 1)
assert rescaled.shape == finalShape
if sameSum:
extraSize = np.prod(finalShape) / np.prod(inShape)
rescaled /= extraSize
return rescaled
myar = np.arange(16).reshape((4,4))
rescaled = zoomArray(myar, finalShape=(3, 5))
print(myar)
print(rescaled)
FWIW i found that order=1 at least preserves the mean a lot better than the default or order=3 (as expected really)

2D histogram of 1D function of random number

I have a 1D function of N values. I want to make a 2D histogram so that I get an image of nx*ny pixels and then cut the image summing along 1 dimension. The function before and after should be the same. I tried with a gaussian but I am missing a factor sqrt. Please see the code. Am I missing something in my fucntion?
I draw r from a random number
import numpy as np
import random
import matplotlib.pyplot as plt
sigma=0.5
N=100000
r=np.random.normal(0, sigma, N)
ind = np.where( r>=0 )
r =r[ind]
N=len(r)
phi=2*np.pi*np.random.rand(N)
x=r*np.cos(phi)
y=r*np.sin(phi)
Ir=np.zeros(N)
Ir[:]=1
Now I want the see the distribution Ir=f(x, y), as a 2D image.
def getImage2D(x, y, fun, nx=100, ny=100, xmin=-1, xmax=1, ymin=-1, ymax=1):
dx=(xmax-xmin)*(1.0/nx);
dy=(ymax-ymin)*(1.0/ny);
image = np.ndarray(shape=(nx, ny), dtype=float); image.fill(0.0)
for i in range(len(fun)):
mr = (x[i]-xmin)/dx;
nr = (y[i]-ymin)/dy;
m, n=int(mr), int(nr)
image[m, n]=image[m, n]+fun[i];
return image
P=getImage2D(x, y, Ir, nx=101, ny=101, xmin=-3, xmax=3, ymin=-3, ymax=3)
#P=getImage2D(x, y, r**0.5*Ir, nx=101, ny=101, xmin=-3, xmax=3, ymin=-3, ymax=3)
Px=np.sum(P, axis=1)
Px=Px/np.max(Px)
plt.figure()
plt.imshow(P)
plt.show(block=False)
If I plot the cut Px (after summing along y), I am not getting the same gaussian of width sigma!!
v=np.linspace(np.min(r), np.max(r), len(r))
v=v/np.max(v)
plt.figure()
plt.plot(np.linspace(-3, 3, 101), Px)
plt.plot(np.sort(r)[::-1], v, 'g')
plt.show(block=False)
Why the width is not same in both cases? If I put a weight r**0.5 with Ir then the width is same(sigma=0.5). Is there any mistake that I am doing in getImage2D function?

Categories

Resources