How to apply random rotation to multiple images with the same rotation angle - tensorflow

I am trying to do some preprocessing to my input. I have three input images (X1, X2, X3). I want to augment the data and apply tf.keras.layers.experimental.preprocessing.RandomRotation() on every element (X1, X2, X3). I am hoping all the images in the same element are rotated with the same angle. How can I do that?
A related post: How to apply random image geometric transformation simultaneously to multiple images?
Any other solutions besides calling tf.stack() and tf.split()?

try this
import scipy.misc
import numpy as np
from scipy import ndimage
import matplotlib.pyplot as plt
img1_path=r'c:\temp\people\test\savory\001.jpg' # path to first image
img2_path=r'c:\temp\people\test\savory\002.jpg' # path to second image
img3_path=r'c:\temp\people\test\savory\003.jpg' # path to third image
path_list=[img1_path, img2_path, img3_path] # list of image paths
degree=np.random.random() * 360 # get random angle between 0 to 360 degrees
print (' rotation angle in counter clockwise direction = ', degree)
plt.figure(figsize=(12,12))
rotated_img_list=[] # create empty list to store rotated images
for i,path in enumerate(path_list):
img= plt.imread(path) # read in the image
rotated_img = ndimage.rotate(img, degree) # rotate the image counter clockwise by degree
rotated_img_list.append(rotated_img) # appended rotated image to the list
plt.subplot(1, 3, i + 1)
plt.imshow(rotated_img) # show the ith rotated image
name=str(i) #label the image
plt.title(name, color='white', fontsize=16)
plt.axis('off')
plt.show()
In your case you probably have the images in a list so don't both with reading them in. I showed an example for reading in and rotating 3 different images by the same random angle and displayed the results.

Related

Tensorflow how to sample large number of textures from small dataset of large images

I have 100 large-ish (1000x1000) images which I want to use as a training data set for a texture analysis system. I want to randomly generate texture swatches of about 200x200. What is the best way to do this? I would prefer to not preprocess all of the swatches so that each epoch is trained with slightly different swatches.
My initial (naive?) implementation included preprocessing layers in the model that do random crops on the image and just do a ton of epochs to accommodate the small number of large pictures, however after about ~400 epochs TF would crash without exception (it would just exit).
I now find myself coding a data generator (tf.keras.utils.Sequence) that will return a batch of swatches on request, but I feel like I'm reinventing the wheel and it is getting clunky - making me think this can't be the best way.
What is the best way to handle such a situation where you have a somewhat small dataset that you dynamically create more samples from?
I have written a function that will segment an image. Code is below
import cv2
def image_segment( image_path, img_resize, crop_size):
image_list=[]
img=cv2.imread(image_path)
img=cv2.resize(img, img_resize)
shape=img.shape
xsteps =int( shape[0]/crop_size[0])
ysteps = int( shape[1]/crop_size[1])
print (xsteps, ysteps)
for i in range (xsteps):
for j in range (ysteps):
x= i * crop_size[0]
xend=x + crop_size[0]
y= j * crop_size[1]
yend = y + crop_size[1]
cropped_image = cropped_image=img[x: xend, y: yend]
image_list.append(cropped_image)
return image_list
below is an example of use
# This code provides input to the image_segment function
image_path=r'c:\temp\landscape.jpg' # location of image
width=1000 # width to resize input image
height= 800 # height to resize input image
image_resize=( width, height) # specify original image (width, height)
crop_width=200 # width of desired cropped images
crop_height=400 # height of desired cropped images
# Note to get full set of cropped images width/cropped_width and height/cropped_height should be integer values
crop_size=(crop_height, crop_width)
images=image_segment(image_path, image_resize, crop_size) # call the function
The code below will display the resized input image and the resultant cropped images
# this code will display the resized input image and the resultant cropped images
import matplotlib.pyplot as plt
from matplotlib.pyplot import imshow
img=cv2.imread(image_path) # read in the image
input_resized_image=cv2.resize(img, image_resize) # resize the image
imshow(input_resized_image) # show the resized input image
r=len(images)
plt.figure(figsize=(20, 20))
for i in range(r):
plt.subplot(5, 5, i + 1)
image=images # scale images between 0 and 1 becaue pre-processor set them between -1 and +1
plt.imshow(image[i])
class_name=str(i)
plt.title(class_name, color='green', fontsize=16)
plt.axis('off')
plt.show()

Pixel manipulation by python matplotlib

I found the python matplotlib function very useful. I would be much better for image processing if each individual pixel is fixed.
I would like to do pixel by pixel processing. The problem is, after using the "handles.append" function, the image margin changes.
from skimage.io import imread
import matplotlib.pyplot as plt
img = imread('uk_figure.png')
my_dpi = 96
plt.figure(figsize=(800/my_dpi, 800/my_dpi), dpi = my_dpi, frameon=False)
fig1 = plt.figure(1)
fig1.set_figheight(40) #image size is set here, pixel = 8 *100 = 800
fig1.set_figwidth(40) #image size is set here, pixel = 8 *100 = 800
imgplot = plt.imshow(img)
#to add code below
plt.axis("off")
plt.subplots_adjust(left = 0, right =1, top =1, bottom =0)
plt.savefig("uk_figure_addcolor4.png", pad_inches=0)
The figure saved is EXACTLY the same as original image. Thus, I think I could process the two image by pixel by pixel processing.
After added this code, matplotlib automatically added a margin for my image. Thus, pixel by pixel processing fails. Is there a way to instruct matplotlib to save image with fixed pixel?
x = [457,458,459,460]
y = [288,289,290,291]
handles = []
handles.append(plt.scatter(x,y, color='blue', marker='+')) #margin changes after adding this line
The margin is added due to a change in the axis-limits. If your image is 1000 by 1000 pixels big say, you can solve your problem by adding plt.gca().set_xlim(0, 1000) and plt.gca().set_ylim(0, 1000). plt.gca() will get you the current axis, and then to that axis set the x- and y-limits.
Note: You might need to invert the y-limits to plt.gca().set_ylim(1000, 0), since matplotlib usually chooses the upper left corner as the origin for images. I used random data so its hard to tell if the image is flipped or not, but if you have a motive in your image you can very easy tell if the image is flipped or not. If it is flipped, invert the ylim as above.

Figures with lots of data points in matplotlib

I generated the attached image using matplotlib (png format). I would like to use eps or pdf, but I find that with all the data points, the figure is really slow to render on the screen. Other than just plotting less of the data, is there anyway to optimize it so that it loads faster?
I think you have three options:
As you mentioned yourself, you can plot fewer points. For the plot you showed in your question I think it would be fine to only plot every other point.
As #tcaswell stated in his comment, you can use a line instead of points which will be rendered more efficiently.
You could rasterize the blue dots. Matplotlib allows you to selectively rasterize single artists, so if you pass rasterized=True to the plotting command you will get a bitmapped version of the points in the output file. This will be way faster to load at the price of limited zooming due to the resolution of the bitmap. (Note that the axes and all the other elements of the plot will remain as vector graphics and font elements).
First, if you want to show a "trend" in your plot , and considering the x,y arrays you are plotting are "huge" you could apply a random sub-sampling to your x,y arrays, as a fraction of your data:
import numpy as np
import matplotlib.pyplot as plt
fraction = 0.50
x_resampled = []
y_resampled = []
for k in range(0,len(x)):
if np.random.rand() < fraction:
x_resampled.append(x[k])
y_resampled.append(y[k])
plt.scatter(x_resampled,y_resampled , s=6)
plt.show()
Second, have you considered using log-scale in the x-axis to increase visibility?
In this example, only the plotting area is rasterized, the axis are still in vector format:
import numpy as np
import matplotlib.pyplot as plt
x = np.random.uniform(size=400000)
y = np.random.uniform(size=400000)
plt.scatter(x, y, marker='x', rasterized=False)
plt.savefig("norm.pdf", format='pdf')

draw grid lines over an image in matplotlib

How can I draw regular grid lines over a tiff image?
I want to draw regular square grids for each interval (say 100 by 100 pixels) over the image and save that with the drawings. I also need to overlay each grid id as '1','2',...at the middle of each grid box.
You will need the python imaging library (PIL) installed. (See here https://pypi.python.org/pypi/PIL). See these answers for examples of ways to install PIL: answer 1, answer 2
Right, with that installed, the following code should do what you ask for:
import matplotlib.pyplot as plt
import matplotlib.ticker as plticker
try:
from PIL import Image
except ImportError:
import Image
# Open image file
image = Image.open('myImage.tiff')
my_dpi=300.
# Set up figure
fig=plt.figure(figsize=(float(image.size[0])/my_dpi,float(image.size[1])/my_dpi),dpi=my_dpi)
ax=fig.add_subplot(111)
# Remove whitespace from around the image
fig.subplots_adjust(left=0,right=1,bottom=0,top=1)
# Set the gridding interval: here we use the major tick interval
myInterval=100.
loc = plticker.MultipleLocator(base=myInterval)
ax.xaxis.set_major_locator(loc)
ax.yaxis.set_major_locator(loc)
# Add the grid
ax.grid(which='major', axis='both', linestyle='-')
# Add the image
ax.imshow(image)
# Find number of gridsquares in x and y direction
nx=abs(int(float(ax.get_xlim()[1]-ax.get_xlim()[0])/float(myInterval)))
ny=abs(int(float(ax.get_ylim()[1]-ax.get_ylim()[0])/float(myInterval)))
# Add some labels to the gridsquares
for j in range(ny):
y=myInterval/2+j*myInterval
for i in range(nx):
x=myInterval/2.+float(i)*myInterval
ax.text(x,y,'{:d}'.format(i+j*nx),color='w',ha='center',va='center')
# Save the figure
fig.savefig('myImageGrid.tiff',dpi=my_dpi)
Which, if used on the grace_hopper.png example file, produces the following output:
This can be done effectively in two lines by looping over the image data at your grid intervals. Using the canonical image from the SIPI database as an example
import pylab as plt
# Load the image
img = plt.imread("lena512color.tiff")
# Grid lines at these intervals (in pixels)
# dx and dy can be different
dx, dy = 100,100
# Custom (rgb) grid color
grid_color = [0,0,0]
# Modify the image to include the grid
img[:,::dy,:] = grid_color
img[::dx,:,:] = grid_color
# Show the result
plt.imshow(img)
plt.show()
The answer by #tom may be more robust as it works with the matplotlib library. I'll leave this example up for its simplicity.
Let me just leave it here
def draw_grid(image, line_space=20):
H, W = image.shape
image[0:H:line_space] = 1
image[:, 0:W:line_space] = 1

numpy: 1d histogram based on 2d-pixel euclidean distance from center

I am using python, with scipy, numpy, etc.
I want to compute the histogram of intensity values of a grayscale image, based on the distance of the pixels to the center of mass of the image. The following solution works, but is very slow:
import matplotlib.pyplot as plt
from scipy import ndimage
import numpy as np
import math
# img is a 2-dimensionsl numpy array
img = np.random.rand(300, 300)
# center of mass of the pixels is easy to get
centerOfMass = np.array(list(ndimage.measurements.center_of_mass(img)))
# declare histogram buckets
histogram = np.zeros(100)
# declare histogram range, which is half the diagonal length of the image, enough in this case.
maxDist = len(img)/math.sqrt(2.0)
# size of the bucket might be less than the width of a pixel, which is fine.
bucketSize = maxDist/len(histogram)
# fill the histogram buckets
for i in range(len(img)):
for j in range(len(img[i])):
dist = np.linalg.norm(centerOfMass - np.array([i,j]))
if(dist/bucketSize < len(histogram)):
histogram[int(dist/bucketSize)] += img[i, j]
# plot the img array
plt.subplot(121)
imgplot = plt.imshow(img)
imgplot.set_cmap('hot')
plt.colorbar()
plt.draw()
# plot the histogram
plt.subplot(122)
plt.plot(histogram)
plt.draw()
plt.show()
As I said before, this works, but is very slow because you are not supposed to double-loop arrays in this manner in numpy. Is there a more efficient way of doing the same thing? I assume I need to apply some function on all the array elements, but I need the index coordinates as well. How can I do that? Currently it takes several seconds for a 1kx1k image, which is ridiculously slow.
All numpy binning functions (bincount, histogram, histogram2d... have a weights keyword argument you can use to do really weird things, such as yours. This is how I would do it:
rows, cols = 300, 300
img = np.random.rand(rows, cols)
# calculate center of mass position
row_com = np.sum(np.arange(rows)[:, None] * img) / np.sum(img)
col_com = np.sum(np.arange(cols) * img) / np.sum(img)
# create array of distances to center of mass
dist = np.sqrt(((np.arange(rows) - row_com)**2)[:, None] +
(np.arange(cols) - col_com)**2)
# build histogram, with intensities as weights
bins = 100
hist, edges = np.histogram(dist, bins=bins, weights=img)
# to reproduce your exact results, you must specify the bin edges
bins = np.linspace(0, len(img)/math.sqrt(2.0), 101)
hist2, edges2 = np.histogram(dist, bins=bins, weights=img)
Haven't timed both approaches, but judging from the delay when running both from the terminal, this is noticeably faster.