0

I have a binary array of size 64x64x64, where a volume of 40x40x40 is set to "1" and rest is "0". I have been trying to rotate this cube about its center around z-axis using skimage.transform.rotate and also Opencv as:

def rotateImage(image, angle):
    row, col = image.shape
    center = tuple(np.array([row, col]) / 2)
    rot_mat = cv2.getRotationMatrix2D(center, angle, 1.0)
    new_image = cv2.warpAffine(image, rot_mat, (col, row))
    return new_image

In the case of openCV, I tried, 2D rotation of each idividual slices in a cube (Cube[:,:,n=1,2,3...p]).

After rotating, total sum of the values in the array changes. This may be caused by interpolation during rotation. How can I rotate 3D array of this kind without adding anything to the array?

Alex Riley
  • 169,130
  • 45
  • 262
  • 238
pyculearn
  • 11
  • 1
  • 1
  • 5
  • You can use `numpy` functions that are for [Rearranging elements][ http://docs.scipy.org/doc/numpy/reference/routines.array-manipulation.html#rearranging-elements] – Mazdak Apr 07 '15 at 14:36
  • Thanks Kasra, but the intention is to be able to rotate, say 22,45,90, 135, and so on degrees. I can use the Rearrange elements only for 90,180,270... angles. Here is an example, but the problem with this is that the values are re-sampled with interpolation. http://stackoverflow.com/questions/4765341/how-can-i-rotate-a-3d-array – pyculearn Apr 07 '15 at 14:41
  • I updated my post, but could you tell me if the imagej rotation works for all angles to preserve the sum? I have to say imagej is quite mature and they have done an excellent job in image processing. – jfish003 Apr 07 '15 at 18:04

2 Answers2

0

Ok so I understand now what you are asking. The closest I can come up with is scipy.ndimage. But there is a way interface with imagej from python if which might be easier. But here is what I did with scipy.ndimage:

    from scipy.ndimage import interpolation
    angle = 25 #angle should be in degrees
    Rotatedim = interpolation.rotate(yourimage, angle, reshape = False,output = np.int32, order = 5,prefilter = False) 

This worked for some angles to preserve the some and not others, perhaps by playing around more with the parameters you might be able to get your desired outcome.

jfish003
  • 1,232
  • 8
  • 12
  • Thanks, I tried rotating a 3D stack image using imagej with no interpolation at all and ended up having same amount of pixels with value "1" as I had before the rotation. I was wondering, how to do that in python. So, basically, I want to rotate a 3D array of size 64x64x64 about z-axis with origin of rotation at the center of the array. Without any filtering or interpolation whatsoever. I am very new to python, so any help is very useful. thanks! – pyculearn Apr 07 '15 at 15:45
  • What angle did you rotate by in imagej just out of curiousity? – jfish003 Apr 07 '15 at 15:53
  • Sorry for the delay! As a test case, I tried, rotating a 100x100 square, with 60x60 pixel set to 1 or say 255 in case of ImageJ. This means, even if we rotate the whole array keeping the initial array size constant, we will not lose anything in the area with ones. because 60*sqrt(2) =~85, and the region with zeros are not relevant. Here are the test cases for all the angles. – pyculearn Apr 08 '15 at 11:24
  • So, I woul have expected 60*60 =3600 everywhere. Here, image is named "test*angle" and total Area are: test0 3600 test30 3600 test40 3600 test45 3612 test10 3596 test20 3596 test50 3600 test45 3612 test55 3596 test60 3600 test_65 3600 test70 3596 test75 3600 test80 3596 test85 3596 test90 3600 I tried scipy.ndimage before, I wanted to switch off the interpolation as much as possible. I switched the order to 1. How can I have rotation without any filtering or interpolation? – pyculearn Apr 08 '15 at 11:48
0

One option is to convert into sparse, and transform the coordinates using a matrix rotation. Then transform back into dense. In 2 dimensions, this looks like:

import numpy as np
import scipy.sparse
import math


N = 10
space = np.zeros((N, N), dtype=np.int8)
space[3:7, 3:7].fill(1)
print(space)
print(np.sum(space))

space_coo = scipy.sparse.coo_matrix(space)
Coords = np.array(space_coo.nonzero()) - 3

theta = 30 * 3.1416 / 180

R = np.array([[math.cos(theta), math.sin(theta)], [-math.sin(theta), math.cos(theta)]])
space2_coords = R.dot(Coords)
space2_coords = np.round(space2_coords)
space2_coords += 3
space2_sparse = scipy.sparse.coo_matrix(([1] * space2_coords.shape[1], (space2_coords[0], space2_coords[1])), shape=(N, N))
space2 = space2_sparse.todense()
print(space2)
print(np.sum(space2))

Output:

[[0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 1 1 1 1 0 0 0]
 [0 0 0 1 1 1 1 0 0 0]
 [0 0 0 1 1 1 1 0 0 0]
 [0 0 0 1 1 1 1 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]]
16
[[0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 1 0 0 0 0 0 0]
 [0 0 1 1 1 1 0 0 0 0]
 [0 0 1 1 1 1 1 0 0 0]
 [0 1 1 0 1 1 0 0 0 0]
 [0 0 0 1 1 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]
 [0 0 0 0 0 0 0 0 0 0]]
16

The advantage is that you'll get exactly as many 1 values before and after the transform. The downsides is that you might get 'holes', as above, and/or duplicate coordinates, giving values of '2' in the final dense matrix.

Hugh Perkins
  • 7,975
  • 7
  • 63
  • 71