This forum thread from an active Kaggle competition mentions something the author calls a "reflective center pad". Basically this is a transformation that takes the edges of the image and reflects them outwards, causing mirroring on the edges of the image, which the author shows slightly but noticeably improves model performance.
For reference, here is the image they post demonstrating this technique:
My question is twofold:
Does this transformation have a canonical name? "Reflective center pad" sounds unofficial.
What's a simple way of expressing this transformation in code, perhaps using numpy and something like skimage?
Does this transformation have a canonical name? "Reflective center
pad" sounds unofficial.
"Symmetric padding" is a commonly used expression to refer to this transformation.
What's a simple way of expressing this transformation in code
I think the simplest way to achieve that would be using Numpy's pad with mode='symmetric'.
Demo
import numpy as np
from skimage import data
import matplotlib.pyplot as plt
img = data.astronaut()
padded = np.pad(img, pad_width=((100, 200), (100, 500), (0, 0)), mode='symmetric')
fig, (ax1, ax2) = plt.subplots(2, 1)
ax1.imshow(img)
ax2.imshow(padded)
fig.show()
To answer your 2nd question:
import Image
import numpy as np
from scipy.misc import face
# example input
f = face()[200:500:2, 400:800:2]
# example output size
outy, outx = 480, 640
iny, inx, *_ = f.shape
iny -= 1; inx -= 1
yoffs, xoffs = (outy - iny) // 2, (outx - inx) // 2
Y, X = np.ogrid[:outy, :outx]
# transformation logic is essentially contained in line below
out = f[np.abs((Y - yoffs + iny) % (2*iny) - iny), np.abs((X - xoffs + inx) % (2*inx) - inx)]
Image.fromarray(out).save('m.png')
Result:
Related
I have a gray scale image that I want to rotate. However, I need to do optimization on it. Therefore, I cannot use pillow or opencv.
I want to reshape this image using python with numpy.reshape into an one dimensional vector (where I use the default settings C-style reshape).
And thereafter, I want to rotate this image around a point using matrix multiplication and addition, i.e. it should be something like
rotated_image_vector = A # vector + b # (or the equivalent in homogenious coordinates).
After this operation I want to reshape the outcome back to two dimensions and have the rotated image.
It would be best if it would as well use linear interpolation between the pixels that do not fit exactly to an other pixel.
The mathematical theory tells it is possible, and I believe there is a very elegant solution to this problem, but I do not see how to create this matrix. Did anyone already have this problem or sees an immediate solution?
Thanks a lot,
Eike
I like your approach but there is a slight misconception in it. What you want to transform are not the pixel values themselves but the coordinates. So you don't reshape your image but rather do a np.indices on it to obtain coordinates to each pixel. For those a rotation around a point looks like
rotation_matrix#(coordinates-fixed_point)+fixed_point
except that I have to transpose a bit to get the dimensions to align. The cove below is a slight adoption of my code in this answer.
As an example I am going to use the Wikipedia-logo-v2 by Nohat. It is licensed under the Creative Commons Attribution-Share Alike 3.0 Unported license.
First I read in the picture, swap x and y axis to not get mad and rotate the coordinates as described above.
import numpy as np
import matplotlib.pyplot as plt
import itertools
image = plt.imread('wikipedia.jpg')
image = np.swapaxes(image,0,1)/255
fixed_point = np.array(image.shape[:2], dtype='float')/2
points = np.moveaxis(np.indices(image.shape[:2]),0,-1).reshape(-1,2)
a = 2*np.pi/8
A = np.array([[np.cos(a),-np.sin(a)],[np.sin(a),np.cos(a)]])
rotated_coordinates = (A#(points-fixed_point.reshape(1,2)).T).T+fixed_point.reshape(1,2)
Now I set up a little class to interpolate between the pixels that do not fit exactly to an other pixel. And finally I swap the axis back and plot it.
class Image_knn():
def fit(self, image):
self.image = image.astype('float')
def predict(self, x, y):
image = self.image
weights_x = [(1-(x % 1)).reshape(*x.shape,1), (x % 1).reshape(*x.shape,1)]
weights_y = [(1-(y % 1)).reshape(*x.shape,1), (y % 1).reshape(*x.shape,1)]
start_x = np.floor(x)
start_y = np.floor(y)
return sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
image_model = Image_knn()
image_model.fit(image)
transformed_image = image_model.predict(*rotated_coordinates.T).reshape(*image.shape)
plt.imshow(np.swapaxes(transformed_image,0,1))
And I get a result like this
Possible Issue
The artifact in the bottom left that looks like one needs to clean the screen comes from the following problem: When we rotate it can happen that we don't have enough pixels to paint the lower left. What we do by default in image_knn is to clip the coordinates to an area where we have information. That means when we ask image knn for pixels coming from outside the image it gives us the pixels at the boundary of the image. This looks good if there is a background but if an object touches the edge of the picture it looks odd like here. Just something to keep in mind when using this.
Thank you for your answer!
But actually it is not a misconception that you could let this roation be represented by a matrix multiplication with the reshaped vector.
I used your code to generate such a matrix (its surely not the most efficient way but it works, most likely you see a more efficient implementation immediately XD. You see I really need it as a matix multiplication :-D).
What I basically did is to generate the representation matrix of the linear transformation, by computing how every of the 100*100 basis images (i.e. the image with zeros everywhere und a one) is mapped by your transformation.
import sys
import numpy as np
import matplotlib.pyplot as plt
import itertools
angle = 2*np.pi/6
image_expl = plt.imread('wikipedia.jpg')
image_expl = image_expl[:,:,0]
plt.imshow(image_expl)
plt.title("Image")
plt.show()
image_shape = image_expl.shape
pixel_number = image_shape[0]*image_shape[1]
rot_mat = np.zeros((pixel_number,pixel_number))
for i in range(pixel_number):
vector = np.zeros(pixel_number)
vector[i] = 1
image = vector.reshape(*image_shape)
fixed_point = np.array(image.shape, dtype='float')/2
points = np.moveaxis(np.indices(image.shape),0,-1).reshape(-1,2)
a = -angle
A = np.array([[np.cos(a),-np.sin(a)],[np.sin(a),np.cos(a)]])
rotated_coordinates = (A#(points-fixed_point.reshape(1,2)).T).T+fixed_point.reshape(1,2)
x,y = rotated_coordinates.T
image = image.astype('float')
weights_x = [(1-(x % 1)).reshape(*x.shape), (x % 1).reshape(*x.shape)]
weights_y = [(1-(y % 1)).reshape(*x.shape), (y % 1).reshape(*x.shape)]
start_x = np.floor(x)
start_y = np.floor(y)
transformed_image_returned = sum([image[np.clip(np.floor(start_x + x), 0, image.shape[0]-1).astype('int'),
np.clip(np.floor(start_y + y), 0, image.shape[1]-1).astype('int')] * weights_x[x]*weights_y[y]
for x,y in itertools.product(range(2),range(2))])
rot_mat[:,i] = transformed_image_returned
if i%100 == 0: print(int(100*i/pixel_number), "% finisched")
plt.imshow((rot_mat # image_expl.reshape(-1)).reshape(image_shape))
Thank you again :-)
I am training my model with several images.
When training my model I realized that I could increase my accuracy by replacing the zero elements in my image array with other values and so I replaced them with the median value of my image as shown with the following code.
import cv2
import imutils
import numpy as np
r_val_all = np.zeros((2000,112,112))
for r in range(len(r_val)):
#LOAD IMAGES
r_image_v = cv2.imread(r_val[r])
r_gray_v = cv2.cvtColor(r_image_v, cv2.COLOR_BGR2GRAY)
r_gray_v = imutils.resize(r_gray_v, width=112, height=112)
n = np.median(r_gray_v[r_gray_v > 0])
r_gray_v[r_gray_v == 0] = n
r_val_all[r,:,:] = r_gray_v
The accuracy did improve however it is not quite there yet.
What I actually require is something where the zero elements are replaced with a continuation of the pre-existent array values.
However I was not sure how to tackle such a problem are there any tools that perform the operation I require?
I used the second answer from the link, tell me if this is close to what you want, because it appeared to be what you wanted.
Creating one sample image and center it, so it's somewhat close to your first example image.
import numpy as np
import matplotlib.pyplot as plt
image = np.zeros((100, 100))
center_noise = np.random.normal(loc=10, size=(50, 50))
image[25:75, 25:75] = center_noise
plt.imshow(image, cmap='gray')
Inspired by rr_gray = np.where(rr_gray==0, np.nan, rr_gray) #convert zero elements to nan in your code, I'm replacing the zeros with NaN.
image_centered = np.where(image == 0, np.nan, image)
plt.imshow(image_centered, cmap='gray')
Now I used the function in the second answer of the link, fill.
test = fill(image_centered)
plt.imshow(test, cmap='gray')
This is the result
I'm sorry I can't help you more. I wish I could, I'm just not very well versed in image processing. I looked at your code and couldn't figure out why it's not working, sorry.
Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
I would like to rotate an image (such as the one below) in a way that one of its features (which resembles a line) becomes vertical. However, I can't seem to find a way to programmatically do it in Python.
Example_Image
The rotation itself can be done by the scipy.ndimage.interpolation.rotate operation.
The following first part is solving the issue for the example scenario in the original question (having one elongated data blob), see below for a more general (but slower) approach. Hope this helps!
First Approach: To find the axis and with that the angle of your line I suggest to use a PCA on the non-zero values:
from scipy.ndimage.interpolation import rotate
#from skimage.transform import rotate ## Alternatively
from sklearn.decomposition.pca import PCA ## Or use its numpy variant
import numpy as np
def verticalize_img(img):
"""
Method to rotate a greyscale image based on its principal axis.
:param img: Two dimensional array-like object, values > 0 being interpreted as containing to a line
:return rotated_img:
"""# Get the coordinates of the points of interest:
X = np.array(np.where(img > 0)).T
# Perform a PCA and compute the angle of the first principal axes
pca = PCA(n_components=2).fit(X)
angle = np.arctan2(*pca.components_[0])
# Rotate the image by the computed angle:
rotated_img = rotate(img,angle/pi*180-90)
return rotated_img
As usually this function could also be written as one-liner:
rotated_img = rotate(img,np.arctan2(*PCA(2).fit(np.array(np.where(img > 0)).T).components_[0])/pi*180-90)
And here is an example:
from matplotlib import pyplot as plt
# Example data:
img = np.array([[0,0,0,0,0,0,0],
[0,1,0,0,0,0,0],
[0,0,1,1,0,0,0],
[0,0,0,1,1,0,0],
[0,0,1,0,0,1,0],
[0,0,0,0,0,0,1]])
# Or alternatively a straight line:
img = np.diag(ones(15))
img = np.around(rotate(img,25))
# Or a distorted blob:
from sklearn import cluster, datasets
X, y = datasets.make_blobs(n_samples=100, centers = [[0,0]])
distortion = [[0.6, -0.6], [-0.4, 0.8]]
theta = np.radians(20)
rotation = np.array(((cos(theta),-sin(theta)), (sin(theta), cos(theta))))
X = np.dot(np.dot(X, distortion),rotation)
img = np.histogram2d(*X.T)[0] # > 0 ## uncomment for making the example binary
rotated_img = verticalize_img(img)
# Plot the results
plt.matshow(img)
plt.title('Original')
plt.matshow(rotated_img)
plt.title('Rotated'))
Note that for highly noisy data or images with no clear orientation this method will come up with arbitrary rotations.
And here is an example output:
Second Approach: Ok after clarification of the actual task in a more complicated setting (see comments) here a second approach based on template matching:
from matplotlib import pyplot as plt
import numpy as np
import pandas
from scipy.ndimage.interpolation import rotate
from scipy.signal import correlate2d#, fftconvolve
# Data from CSV file:
img = pandas.read_csv('/home/casibus/testdata.csv')
# Create a template:
template = np.zeros_like(img.values)
template[:,int(len(template[0])*1./2)] = 1
suggested_angles = np.arange(0,180,1) # Change to any resolution you like
overlaps = [np.amax(correlate2d(rotate(img,alpha,reshape=False),template,mode='same')) for alpha in suggested_angles]
# Determine the angle resulting in maximal overlap and rotate:
rotated_img = rotate(img.values,-suggested_angles[np.argmax(overlaps)])
plt.matshow(rotated_img)
plt.matshow(template)
How to do histogram equalization for multiple grayscaled images stored in a NumPy array easily?
I have the 96x96 pixel NumPy data in this 4D format:
(1800, 1, 96,96)
Moose's comment which points to this blog entry does the job quite nicely.
For completeness, I give an example here using nicer variable names and a looped execution on 1000 96x96 images which are in a 4D array as in the question. It is fast (1-2 seconds on my computer) and only needs NumPy.
import numpy as np
def image_histogram_equalization(image, number_bins=256):
# from http://www.janeriksolem.net/histogram-equalization-with-python-and.html
# get image histogram
image_histogram, bins = np.histogram(image.flatten(), number_bins, density=True)
cdf = image_histogram.cumsum() # cumulative distribution function
cdf = (number_bins-1) * cdf / cdf[-1] # normalize
# use linear interpolation of cdf to find new pixel values
image_equalized = np.interp(image.flatten(), bins[:-1], cdf)
return image_equalized.reshape(image.shape), cdf
if __name__ == '__main__':
# generate some test data with shape 1000, 1, 96, 96
data = np.random.rand(1000, 1, 96, 96)
# loop over them
data_equalized = np.zeros(data.shape)
for i in range(data.shape[0]):
image = data[i, 0, :, :]
data_equalized[i, 0, :, :] = image_histogram_equalization(image)[0]
Very fast and easy way is to use the cumulative distribution function provided by the skimage module. Basically what you do mathematically to proof it.
from skimage import exposure
import numpy as np
def histogram_equalize(img):
img = rgb2gray(img)
img_cdf, bin_centers = exposure.cumulative_distribution(img)
return np.interp(img, bin_centers, img_cdf)
As of today janeriksolem's url is broken.
I found however this gist that links the same page and claims to perform histogram equalization without computing the histogram.
The code is:
img_eq = np.sort(img.ravel()).searchsorted(img)
Here's an alternate implementation for a single channel image that is fast. See skimage.exposure.histogram for reference. Using timeit, 'image_histogram_equalization' in Trilarion's answer has a mean execution time was 0.3696 seconds, while this function has a mean execution time of 0.0534 seconds. However this implementation also relies on skimage.
import numpy as np
from skimage import exposure
def hist_eq(image):
hist, bins = exposure.histogram(image, nbins=256, normalize=False)
# append any remaining 0 values to the histogram
hist = np.hstack((hist, np.zeros((255 - bins[-1]))))
cdf = 255*(hist/hist.sum()).cumsum()
equalized = cdf[image].astype(np.uint8)
return equalized
In my application, the data data is sampled on a distorted grid, and I would like to resample it to a nondistorted grid. In order to test this, I wrote this program with examplary distortions and a simple function as data:
from __future__ import division
import numpy as np
import scipy.interpolate as intp
import pylab as plt
# Defining some variables:
quadratic = -3/128
linear = 1/16
pn = np.poly1d([quadratic, linear,0])
pixels_x = 50
pixels_y = 30
frame = np.zeros((pixels_x,pixels_y))
x_width= np.concatenate((np.linspace(8,7.8,57) , np.linspace(7.8,8,pixels_y-57)))
def data(x,y):
z = y*(np.exp(-(x-5)**2/3) + np.exp(-(x)**2/5) + np.exp(-(x+5)**2))
return(z)
# Generating grid coordinates
yt = np.arange(380,380+pixels_y*4,4)
xt = np.linspace(-7.8,7.8,pixels_x)
X, Y = np.meshgrid(xt,yt)
Y=Y.T
X=X.T
Y_m = np.zeros((pixels_x,pixels_y))
X_m = np.zeros((pixels_x,pixels_y))
# generating distorted grid coordinates:
for i in range(pixels_y):
Y_m[:,i] = Y[:,i] - pn(xt)
X_m[:,i] = np.linspace(-x_width[i],x_width[i],pixels_x)
# Sample data:
for i in range(pixels_y):
for j in range(pixels_x):
frame[j,i] = data(X_m[j,i],Y_m[j,i])
Y_m = Y_m.flatten()
X_m = X_m.flatten()
frame = frame.flatten()
##
Y = Y.flatten()
X = X.flatten()
ipf = intp.interp2d(X_m,Y_m,frame)
interpolated_frame = ipf(xt,yt)
At this point, I have to questions:
The code works, but I get the the following warning:
Warning: No more knots can be added because the number of B-spline coefficients
already exceeds the number of data points m. Probably causes: either
s or m too small. (fp>s)
kx,ky=1,1 nx,ny=54,31 m=1500 fp=0.000006 s=0.000000
Also, some interpolation artifacts appear, and I assume that they are related to the warning - Do you guys know what I am doing wrong?
For my actual applications, the frames need to be around 500*100, but when doing this, I get a MemoryError - Is there something I can do to help that, apart from splitting the frame into several parts?
Thanks!
This problem is most likely related to the usage of bisplrep and bisplev within interp2d. The docs mention that they use a smooting factor of s=0.0 and that bisplrep and bisplev should be used directly if more control over s is needed. The related docs mention that s should be found between (m-sqrt(2*m),m+sqrt(2*m)) where m is the number of points used to construct the splines. I had a similar problem and found it solved when using bisplrep and bisplev directly, where s is only optional.
For 2d interpolation,
griddata
is solid, local, fast.
Take a look at problem-with-2d-interpolation-in-scipy-non-rectangular-grid on SO.
You might want to look at the following interp method in basemap:
mpl_toolkits.basemap.interp
http://matplotlib.sourceforge.net/basemap/doc/html/api/basemap_api.html
unless you really need spline-based interpolation.