I'm trying to denoise an image by averaging all pixels that are within a certain euclidian distance using the following loop. Currently this loop takes 65 seconds and likely needs performed thousands of times. Is there a way to accomplish this in python without a prohibitive run time? Any help would be greatly appreciated.
for row in range(0, width):
for column in range(0, height):
if euclid_dist(point, pix[row,column]) <= threshold:
to_average[(row,column)] = pix[row, column]
euclid_dist is defined as the following:
def euclid_dist(tuple1, tuple2):
tot_sq = 0
for num1, num2 in zip(tuple1, tuple2):
tot_sq += (num1 + num2)**2
return math.sqrt(tot_sq)
If you want to just average everything in a circle (equally, rather than with a gaussian), you can make a hard circle kernel, then convolve your image with it as below.
# make a noisy circle thing
img = np.random.randint(100, 200, (200, 200), dtype=np.uint8)
xx, yy = np.meshgrid(np.arange(-100, 100), np.arange(-100, 100))
img = img + 10 * (xx**2 + yy**2 < 50**2)
plt.imshow(img)
# make a non-standard kernel
radius = 10
kernel_size = 2 * int(radius) + 1 # odd
xy = np.arange(-kernel_size//2 + 1, kernel_size//2 + 1)
xx, yy = np.meshgrid(xy, xy)
kernel = np.zeros((kernel_size,) * 2)
kernel[xx**2 + yy**2 <= radius**2] = 1
plt.imshow(kernel)
# convolve the two, depending on the mode, it will change the dimensions of the output
plt.imshow(sig.convolve2d(img, kernel, mode='valid'))
If you want to de-noise, you could also use a gaussian kernel, which is a bit more common. It's more simply called "Gaussian blurring" then.
Related
I try to rotate an image clockwise 45 degree and translate the image -50,-50.
Rotation process works fine:(I refer to this page:How do I rotate an image manually without using cv2.getRotationMatrix2D)
import numpy as np
import math
from scipy import ndimage
from PIL import Image
# inputs
img = ndimage.imread("A.png")
rotation_amount_degree = 45
# convert rotation amount to radian
rotation_amount_rad = rotation_amount_degree * np.pi / 180.0
# get dimension info
height, width, num_channels = img.shape
# create output image, for worst case size (45 degree)
max_len = int(math.sqrt(height*height + width*width))
rotated_image = np.zeros((max_len, max_len, num_channels))
#rotated_image = np.zeros((img.shape))
rotated_height, rotated_width, _ = rotated_image.shape
mid_row = int( (rotated_height+1)/2 )
mid_col = int( (rotated_width+1)/2 )
# for each pixel in output image, find which pixel
#it corresponds to in the input image
for r in range(rotated_height):
for c in range(rotated_width):
# apply rotation matrix, the other way
y = (r-mid_col)*math.cos(rotation_amount_rad) + (c-mid_row)*math.sin(rotation_amount_rad)
x = -(r-mid_col)*math.sin(rotation_amount_rad) + (c-mid_row)*math.cos(rotation_amount_rad)
# add offset
y += mid_col
x += mid_row
# get nearest index
#a better way is linear interpolation
x = round(x)
y = round(y)
#print(r, " ", c, " corresponds to-> " , y, " ", x)
# check if x/y corresponds to a valid pixel in input image
if (x >= 0 and y >= 0 and x < width and y < height):
rotated_image[r][c][:] = img[y][x][:]
# save output image
output_image = Image.fromarray(rotated_image.astype("uint8"))
output_image.save("rotated_image.png")
However, when I try to translate the image. I edited the above code to this:
if (x >= 0 and y >= 0 and x < width and y < height):
rotated_image[r-50][c-50][:] = img[y][x][:]
But I got something like this:
It seems the right and the bottom did not show the right pixel. How could I solve it?
Any suggestions would be highly appreciated.
The translation needs to be handled as a wholly separate step. Trying to translate the value from the source image doesn't account for newly created 0,0,0 (if RGB) valued pixels by the rotation.
Further, simply subtracting 50 from the rotated array index values, without validating them at that stage for positivity, is allowing for a negative valued index, which is fully supported by Python. That is why you are getting a "wrap" effect instead of a translation
You said your script rotated the image as intended, so while perhaps not the most efficient, the most intuitive is to simply shift the values of the image assembled after you rotate. You could test that the values for the new image remain positive after subtracting 50 and only saving the ones >= 0 or being cognizant of the fact that you are shifting the values downward by 50, any number less than 50 will be discarded and you get:
<what you in the block you said was functional then:>
translated_image = np.zeros((max_len, max_len, num_channels))
for i in range(0, rotated_height-50): # range(start, stop[, step])
for j in range(0, rotated_width-50):
translated_image[i+50][j+50][:] = rotated[i][j][:]
# save output image
output_image = Image.fromarray(translated_image.astype("uint8"))
output_image.save("rotated_translated_image.png")
I have the following Python program that endeavours to use the normalized iteration count algorithm to colour the Mandelbrot set:
from PIL import Image
import numpy as np
from matplotlib.colors import hsv_to_rgb
steps = 256 # maximum iterations
bailout_radius = 64 # bailout radius
def normalized_iteration(n, abs_z):
return n + 1 - np.log2(np.log(abs_z))/np.log(2)
def make_set(real_start, real_end, imag_start, imag_end, height):
width = \
int(abs(height * (real_end - real_start) / (imag_end - imag_start)))
real_axis = \
np.linspace(real_start, real_end, num = width)
imag_axis = \
np.linspace(imag_start, imag_end, num = height)
complex_plane = \
np.zeros((height, width), dtype = np.complex_)
real, imag = np.meshgrid(real_axis, imag_axis)
complex_plane.real = real
complex_plane.imag = imag
pixels = \
np.zeros((height, width, 3), dtype = np.float_)
new = np.zeros_like(complex_plane)
is_not_done = np.ones((height, width), dtype = bool)
# cosine_interpolation = lambda x: (np.cos(x * np.pi + np.pi) + 1) / 2
for i in range(steps):
new[is_not_done] = \
new[is_not_done] ** 2 + complex_plane[is_not_done]
mask = np.logical_and(np.absolute(new) > bailout_radius, is_not_done)
pixels[mask, :] = (i, 0.6, 1)
is_not_done = np.logical_and(is_not_done, np.logical_not(mask))
new_after_mask = np.zeros_like(complex_plane)
new_after_mask[np.logical_not(is_not_done)] = \
new[np.logical_not(is_not_done)]
new_after_mask[is_not_done] = bailout_radius
pixels[:, :, 0] = \
normalized_iteration(pixels[:, :, 0], np.absolute(new_after_mask)) / steps
image = Image.fromarray((hsv_to_rgb(np.flipud(pixels)) * 255).astype(np.uint8))
image.show()
make_set(-2, 1, -1, 1, 2000)
It produces a fairly nice image. However, when I compare it to other sets employing this algorithm, the colours in my set barely change. If I reduce steps, I get a more varied gradient, but that reduces the quality of the fractal. The important parts of this code are my normalized_iteration definition, which varies slightly from this Wikipedia article's version,
def normalized_iteration(n, abs_z):
return n + 1 - np.log2(np.log(abs_z))/np.log(2)
where I use that definition (mapping the function to the array of pixels),
pixels[:, :, 0] = \
normalized_iteration(pixels[:, :, 0], np.absolute(new_after_mask)) / steps
and the final array, where I convert the HSV format to RGB and turn the pixel values on [0, 1) to values on [0, 255)
image = Image.fromarray((hsv_to_rgb(np.flipud(pixels)) * 255).astype(np.uint8))
I have been fighting with this problem for a while now, and I am not sure of what is going wrong. Thanks for helping me determine how to make the gradient more varied in colour and for bearing with my perhaps hard-to-read code. Also, I realize that there is room for optimization in there.
I have a specific python issue, that desperately needs to be sped up by avoiding the use of a loop, yet, I am at a loss as to how to do this. I need to read in a fits image, convert this to a numpy array (roughly, 2000 x 2000 elements in size), then for each element compute the statistics of a ring of elements around it.
As I have my code now, the statistics of the ring around the element is computed with a function using masks. This is fast but, of course, I call this function 2000x2000 times (the slow part).
I am relatively new to python. I think that using the mask function is clever, but I cannot find a way around individually addressing each element. Best of thanks for any help you can provide.
# First, the function computing the statistics within a ring
around the central pixel:<br/>
# flux = image intensity at pixel (i,j)<br/>
# rad1, rad2 = inner and outer radii<br/>
# array = image array<br/>_
def snr(flux, i, j, rad1, rad2, array):
a, b = i, j
nx, ny = array.shape
y, x = np.ogrid[-a:nx-a, -b:ny-b]
mask = (x*x + y*y >= rad1*rad1) & (x*x + y*y <= rad2*rad2)
Nmask = np.count_nonzero(mask)
noise = 0.6052697 * abs(Nmask * flux - sum(array[mask]))
return noise
# Now, the call to snr for each pixel in the array data1:<br/>_
frame1 = fits.open(in_frame, mode='readonly') # read in fits file
data1 = frame1[ext].data # convert to np array
ny, nx = data1.shape # array dimensions
noise1 = zeros((ny, nx), float) # empty array
r1 = 5 # inner radius (pixels)
r2 = 7 # outer radius (pixels)
# The function is fast, but calling it 2k x 2k times is not:
for j in range(ny):
for i in range(nx):
noise1[i,j] = der_snr(data1[i,j], i, j, r1, r2, data1)
The operation that you are trying to do can be expressed as an image convolution. Try something like this:
import numpy as np
import scipy.ndimage
from astropy.io import fits
def make_kernel(inner_radius, outer_radius):
if inner_radius > outer_radius:
raise ValueError
x, y = np.ogrid[-outer_radius:outer_radius + 1, -outer_radius:outer_radius + 1]
r2 = x * x + y * y
kernel = (r2 >= inner_radius * inner_radius) & (r2 <= outer_radius * outer_radius)
return kernel
in_frame = '<file path>'
ext = '...'
frame1 = fits.open(in_frame, mode='readonly')
data1 = frame1[ext].data
inner_radius = 5
outer_radius = 7
kernel = make_kernel(inner_radius, outer_radius)
n_kernel = np.count_nonzero(kernel)
conv = scipy.ndimage.convolve(data1, kernel, mode='constant')
noise1 = 0.6052697 * np.abs(n_kernel * data1 - conv)
I am currently completing a program in Pyhton (3.6) as per internal requirement. As part of it, I am having to loop through a colour image (3 bytes per pixel, R, G & B) and distort the image pixel by pixel.
I have the same code in other languages (C++, C#), and non-optimized code executes in about two seconds, while optimized code executes in less than a second. By non-optimized code I mean that the matrix multiplication is performed by a 10 line function I implemented. The optimized version just uses external libraries for multiplication.
In Python, this code takes close to 300 seconds. I canĀ“t think of a way to vectorize this logic or speed it up, as there are a couple of "if"s inside the nested loop. Any help would be greatly appreciated.
import numpy as np
#for test purposes:
#roi = rect.rect(0, 0, 1200, 1200)
#input = DCImage.DCImage(1200, 1200, 3)
#correctionImage = DCImage.DCImage(1200,1200,3)
#siteToImage= np.zeros((3,3), np.float32)
#worldToSite= np.zeros ((4, 4))
#r11 = r12 = r13 = r21 = r22 = r23 = r31 = r32 = r33 = 0.0
#xMean = yMean = zMean = 0
#tx = ty = tz = 0
#epsilon = np.finfo(float).eps
#fx = fy = cx = cy = k1 = k2 = p1 = p2 = 0
for i in range (roi.x, roi.x + roi.width):
for j in range (roi.y , roi.y + roi.height):
if ( (input.pixels [i] [j] == [255, 0, 0]).all()):
#Coordinates conversion
siteMat = np.matmul(siteToImage, [i, j, 1])
world =np.matmul(worldToSite, [siteMat[0], siteMat[1], 0.0, 1.0])
xLocal = world[0] - xMean
yLocal = world[1] - yMean
zLocal = z_ortho - zMean
#From World to camera
xCam = r11*xLocal + r12*yLocal + r13*zLocal + tx
yCam = r21*xLocal + r22*yLocal + r23*zLocal + ty
zCam = r31*xLocal + r32*yLocal + r33*zLocal + tz
if (zCam > epsilon or zCam < -epsilon):
xCam = xCam / zCam
yCam = yCam / zCam
#// DISTORTIONS
r2 = xCam*xCam + yCam*yCam
a1 = 2*xCam*yCam
a2 = r2 + 2*xCam*xCam
a3 = r2 + 2*yCam*yCam
cdist = 1 + k1*r2 + k2*r2*r2
u = int((xCam * cdist + p1 * a1 + p2 * a2) * fx + cx + 0.5)
v = int((yCam * cdist + p1 * a3 + p2 * a1) * fy + cy + 0.5)
if (u>=0 and u<correctionImage.width and v>=0 and v < correctionImage.height):
input.pixels [i] [j] = correctionImage.pixels [u][v]
You normally vectorize this kind of thing by making a displacement map.
Make a complex image where each pixel has the value of its own coordinate, apply the usual math operations to compute whatever transform you want, then apply the map to your source image.
For example, in pyvips you might write:
import sys
import pyvips
image = pyvips.Image.new_from_file(sys.argv[1])
# this makes an image where pixel (0, 0) (at the top-left) has value [0, 0],
# and pixel (image.width, image.height) at the bottom-right has value
# [image.width, image.height]
index = pyvips.Image.xyz(image.width, image.height)
# make a version with (0, 0) at the centre, negative values up and left,
# positive down and right
centre = index - [image.width / 2, image.height / 2]
# to polar space, so each pixel is now distance and angle in degrees
polar = centre.polar()
# scale sin(distance) by 1/distance to make a wavey pattern
d = 10000 * (polar[0] * 3).sin() / (1 + polar[0])
# and back to rectangular coordinates again to make a set of vectors we can
# apply to the original index image
distort = index + d.bandjoin(polar[1]).rect()
# distort the image
distorted = image.mapim(distort)
# pick pixels from either the distorted image or the original, depending on some
# condition
result = (d.abs() > 10 or image[2] > 100).ifthenelse(distorted, image)
result.write_to_file(sys.argv[2])
That's just a silly wobble pattern, but you can swap it for any distortion you want. Then run as:
$ /usr/bin/time -f %M:%e ./wobble.py ~/pics/horse1920x1080.jpg x.jpg
54572:0.31
300ms and 55MB of memory on this two-core, 2015 laptop to make:
After much testing, the only way to speed the function without writing it in C++ was dissassembling it and vectorizing it. The way to do it in this particular instance is to create an array with the valid indexes at the beginning of the funcion and use them as tuples to index the final solution.
subArray[roi.y:roi.y+roi.height,roi.x:roi.x+roi.width,] = input.pixels[roi.y:roi.y+roi.height,roi.x:roi.x+roi.width,]
#Calculate valid XY indexes
y_index, x_index = np.where(np.all(subArray== np.array([255,0,0]), axis=-1))
#....
#do stuff
#....
#Join result values with XY indexes
ij_xy = np.column_stack((i, j, y_index, x_index))
#Only keep valid ij values
valids_ij_xy = ij_xy [(ij_xy [:,0] >= 0) & (ij_xy [:,0] < correctionImage.height) & (ij_xy [:,1] >= 0) & (ij_xy [:,1] < correctionImage.width)]
#Assign values
input.pixels [tuple(np.array(valids_ij_xy [:,2:]).T)] = correctionImage.pixels[tuple(np.array(valids_ij_xy [:,:2]).T)]
I am trying to return only the center crop with the oversample of Caffe using Python code the caffe.io.oversample function (not the classifier.py). II have tried to modify the code to return only the center crop however it still returns 10 instead of 1 crops. I have rebuilt the caffe and pycaffe however the situation is still the same. How can I get the python code to return only one crop?? I am confused. I have used matcaffe before but I don't know python, I am just figuring out as I go. Thanks!
Modified part:
def oversample(images, crop_dims, flow=False):
"""
Crop images into the four corners, center, and their mirrored versions.
Take
image: iterable of (H x W x K) ndarrays
crop_dims: (height, width) tuple for the crops.
Give
crops: (10*N x H x W x K) ndarray of crops for number of inputs N.
"""
# Dimensions and center.
im_shape = np.array(images[0].shape)
crop_dims = np.array(crop_dims)
center = im_shape[:2] / 2.0
# Make crop coordinates
# Take center crop.
crop = np.tile(center, (1, 2))[0] + np.concatenate([
-self.crop_dims / 2.0,
self.crop_dims / 2.0
])
crops = images[:, crop[0]:crop[2], crop[1]:crop[3], :]
return crops
Its original:
def oversample(images, crop_dims, flow=False):
"""
Crop images into the four corners, center, and their mirrored versions.
Take
image: iterable of (H x W x K) ndarrays
crop_dims: (height, width) tuple for the crops.
Give
crops: (10*N x H x W x K) ndarray of crops for number of inputs N.
"""
# Dimensions and center.
im_shape = np.array(images[0].shape)
crop_dims = np.array(crop_dims)
im_center = im_shape[:2] / 2.0
# Make crop coordinates
h_indices = (0, im_shape[0] - crop_dims[0])
w_indices = (0, im_shape[1] - crop_dims[1])
crops_ix = np.empty((5, 4), dtype=int)
curr = 0
for i in h_indices:
for j in w_indices:
crops_ix[curr] = (i, j, i + crop_dims[0], j + crop_dims[1])
curr += 1
crops_ix[4] = np.tile(im_center, (1, 2)) + np.concatenate([
-crop_dims / 2.0,
crop_dims / 2.0
])
crops_ix = np.tile(crops_ix, (2, 1))
# Extract crops
crops = np.empty((10 * len(images), crop_dims[0], crop_dims[1],
im_shape[-1]), dtype=np.float32)
ix = 0
for im in images:
for crop in crops_ix:
crops[ix] = im[crop[0]:crop[2], crop[1]:crop[3], :]
ix += 1
crops[ix-5:ix] = crops[ix-5:ix, :, ::-1, :] # flip for mirrors
if flow: #if using a flow input, should flip first channel which corresponds to x-flow
crops[ix-5:ix,:,:,0] = 1-crops[ix-5:ix,:,:,0]
return crops
I was trying to do the same. In the end, I had to use array slicing to crop the image:
def crop_center(img, cropx, cropy):
_,x,y = img.shape
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
return img[:, starty:starty + cropy, startx:startx + cropx]
The input to the function is an (C, W, H) array.