def align_rotations(images):
    """Align the rotations of a set of images by trying rotations up to 10 degrees clockwise and counterclockwise."""
    
    # Rotate the images to be all aligned.
    # We take the first image as the base, and then search in the -10 to +10 degrees for the rotation
    # 
    # We take the rotation angle for which the inner square of the images is best aligned
    
    image_shape = images[0].size
    
    aligned_images = []
    aligned_images.append(images[0])
    rotation_base_img = images[0]
    rotation_crop_x1 = int(image_shape[0] * (3/8.0))
    rotation_crop_x2 = image_shape[0] - rotation_crop_x1
    rotation_crop_y1 = 0 #  image_shape[1] / (3/8.0)
    rotation_crop_y2 = image_shape[1] - rotation_crop_y1
    rotation_crop_box = (rotation_crop_x1, rotation_crop_y1, rotation_crop_x2, rotation_crop_y2)
    rotation_test_angles = np.hstack([np.arange(0,3,0.25), np.arange(3.5,5,0.5),np.arange(5,10,1)])
    rotation_test_angles = np.hstack([0 - rotation_test_angles, rotation_test_angles])

    rotation_base = as_ary(rotation_base_img.crop(rotation_crop_box))
    for i, img in enumerate(images[1:]):
        min_diff = np.Inf
        min_diff_angle = np.nan
        best_rotated_image = None
        # find the angle a for which the difference between the original and the rotated image is minimized
        for a in rotation_test_angles:
            rotated_img = img.rotate(a, expand=0) # expand=0 to keep the size the same
            rotated_img_ary = as_ary(rotated_img.crop(rotation_crop_box)) # only compare cropped versions of the images. Restricting to a vertical bar
            diff = np.square(rotation_base - rotated_img_ary) 
            summed_diff = np.mean(diff)
            # print "Trying angle %f with diff %f" % (a, summed_diff)
            if summed_diff < min_diff:
                min_diff = summed_diff
                min_diff_angle = a
                best_rotated_image = rotated_img
        if best_rotated_image != None: # should always hold
            aligned_images.append(best_rotated_image)
            
    return aligned_images
            # pixels = np.arange(image_ary.shape[0] * image_ary.shape[1]) * 3
            # image = image_ary.flatten()
            # img = np.amax(np.vstack([image[pixels], image[pixels + 1], image[pixels + 2]]),0)
            # img = to_img(img, image_ary.shape[0:2])
            
            images.append(img)
            image_names.append(filename)
        
print "Aligning rotations"
aligned_images = align_rotations(images)

print "Cropping"
cropped_images = [crop_to_wafer(img) for img in aligned_images]

# Convert it all to one large array:
image_arr = np.vstack([as_ary(img).flatten() for img in cropped_images])

image_shape = as_ary(cropped_images[0]).shape

print "Computing mean and variance"
# Compute mean and variance of the batch
mean = np.mean(image_arr, axis=0)
std  = np.std(image_arr,axis=0)
threshold = 2


# rescale to [0.0, 255]
def rescale(dat):
    mn = np.amin(dat)
    mx = np.amax(dat)
    return ((dat - mn) / (mx - mn)) * 512