I have a 3D matrix where the x-y plane(s) represent an image and the z-plane represents image layers.
The issue is when I try to extract the first (or other layers) using idz, I do not get the expected results. It looks like the array, once in CUDA, has different indexes for x, y or z than what I expect (as in pycuda). I see this by the result array below.
The following is a step by step process for this mini example (I used generic int numbers to represent my images to save uploading images and the entire code)!
Here I import libraries and define image size and layers...
import pycuda.driver as cuda
import pycuda.autoinit
from pycuda.compiler import SourceModule
import numpy
from pycuda.gpuarray import to_gpu
row = 10
column = 10
depth = 5
Then I define my input 3D array and my output 2D array...
#--==== Input 3D Array ====---
arrayA = numpy.full((row, column, depth), 0)
#populate each layer with fixed values
for i in range(depth):
arrayA[:,:,i] = i + 1
arrayA = arrayA.astype(numpy.uint16)
arrayA_gpu = cuda.mem_alloc(arrayA.nbytes)
cuda.memcpy_htod(arrayA_gpu, arrayA)
arrayA_Answer = numpy.empty_like(arrayA)
#--==== Output 2D array container ====---
arrayB = numpy.zeros([row, column], dtype = numpy.uint16)
arrayB_gpu = cuda.mem_alloc(arrayB.nbytes)
cuda.memcpy_htod(arrayB_gpu, arrayB)
arrayB_Answer = numpy.empty_like(arrayB)
Next I define the CUDA kernal and function in pycuda
mod = SourceModule("""
__global__ void getLayer(int *arrayA, int *arrayB)
{
int idx = threadIdx.x + (blockIdx.x * blockDim.x); // x coordinate (numpy axis 2)
int idy = threadIdx.y + (blockIdx.y * blockDim.y); // y coordinate (numpy axis 1)
int idz = 0; //The first layer, this can set in range from 0-4
int x_width = (blockDim.x * gridDim.x);
int y_width = (blockDim.y * gridDim.y);
arrayB[idx + (x_width * idy)] = arrayA[idx + (x_width * idy) + (x_width * y_width) * idz];
}
""")
func = mod.get_function("getLayer")
func(arrayA_gpu, arrayB_gpu, block=(row, column, 1), grid=(1,1))
Using standard pycuda commands, I extract the results (not what I expected)
arrayA[:,:,0] = 10x10 matrix populated with 1's (good)
print(arrayA_Answer[:,:,0])
[[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]
[1 1 1 1 1 1 1 1 1 1]]
arrayB[:,:] = 10x10 matrix populated with the following (bad), expected to be equal to arrayA[:,:,0]...
print(arrayB_Answer)
[[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]
[1 2 3 4 5 1 2 3 4 5]]
As discussed here, the numpy 3D storage order pattern is that the "z" (i.e. "3rd") index is the rapidly varying index, as you progress linearly through memory. Your code assumes that the first index ("x") is the rapidly varying one.
Since your kernel is already organized for efficient ("coalesced") load/store behavior, you could address this by reordering the storage of your images/layers/slices in numpy. Here is a worked example:
$ cat t10.py
from __future__ import print_function
import pycuda.driver as cuda
import pycuda.autoinit
from pycuda.compiler import SourceModule
import numpy
from pycuda.gpuarray import to_gpu
row = 5
column = 10
depth = 10
#--==== Input 3D Array ====---
arrayA = numpy.full((row, column, depth), 0)
my_slice=numpy.int32(3) # choose the layer
#populate each layer with fixed values
for i in range(row):
arrayA[i,:,:] = i + 1
arrayA = arrayA.astype(numpy.int32)
arrayA_gpu = cuda.mem_alloc(arrayA.nbytes)
cuda.memcpy_htod(arrayA_gpu, arrayA)
arrayA_Answer = numpy.empty_like(arrayA)
#--==== Output 2D array container ====---
arrayB = numpy.zeros([column, depth], dtype = numpy.int32)
arrayB_gpu = cuda.mem_alloc(arrayB.nbytes)
cuda.memcpy_htod(arrayB_gpu, arrayB)
arrayB_Answer = numpy.empty_like(arrayB)
mod = SourceModule("""
__global__ void getLayer(int *arrayA, int *arrayB, int slice)
{
int idx = threadIdx.x + (blockIdx.x * blockDim.x); // x coordinate (numpy axis 2)
int idy = threadIdx.y + (blockIdx.y * blockDim.y); // y coordinate (numpy axis 1)
int idz = slice; //The "layer"
int x_width = (blockDim.x * gridDim.x);
int y_width = (blockDim.y * gridDim.y);
arrayB[idx + (x_width * idy)] = arrayA[idx + (x_width * idy) + (x_width * y_width) * idz];
}
""")
func = mod.get_function("getLayer")
func(arrayA_gpu, arrayB_gpu, my_slice, block=(depth, column, 1), grid=(1,1))
cuda.memcpy_dtoh(arrayB_Answer,arrayB_gpu)
print(arrayA[my_slice,:,:])
print(arrayB_Answer[:,:])
$ python t10.py
[[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]]
[[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]
[4 4 4 4 4 4 4 4 4 4]]
$
Note that I have also changed your use of uint16 to int32, to match the kernel type int.
Related
Who can help.
To transform a vector into a one-dimensional matrix just run in Julia:
a = copy(permutedims([1,2,3]))
To transform the matrix "a" into a vector just use:
b = copy(vec(a))
If you have a matrix "[1 2 3; 4 5 6]" to transform it into a vector, just:
c = vec([1 2 3; 4 5 6])
Now how to make the vector have the form of the 2x3 matrix like:
2×3 Matrix{Int64}:
1 2 3
4 5 6
You can use reshape
julia> c = vec([1 2 3; 4 5 6])
6-element Vector{Int64}:
1
4
2
5
3
6
julia> M=reshape(c,2,3)
2×3 Matrix{Int64}:
1 2 3
4 5 6
Note that this operation does not reallocate memory, c and M share the same memory. By example:
julia> c[1]=10
10
julia> M
2×3 Matrix{Int64}:
10 2 3
4 5 6
Say I have matrix A:
A = [1 1 1 2 2 3 3 3;
1 1 1 2 2 3 3 3;
1 1 1 2 2 4 4 5;
2 2 2 2 2 5 5 5]
and matrix B with the same labels, just in different positions and not always with the same elements in each cluster:
B = [3 3 3 3 5 1 1 1:
3 3 3 3 5 1 1 1;
3 3 3 3 5 2 2 4:
5 5 5 5 5 4 4 4]
and I want matrix C to look like this
C = [1 1 1 1 2 3 3 3;
1 1 1 1 2 3 3 3;
1 1 1 1 2 4 4 5;
2 2 2 2 2 5 5 5]
Basically, I want the clusters in B that have a similar position to A to also have the same label as A, even if the clusters in B don't have the same exact amount of elements as the clusters in A. This is just a basic example because what I'm really working on are two images that have different labellings.
example of the image I'm working on
i would like to sum all the values from my 2nd column which have the same value in the first column.
So my matrix looks maybe like this:
column: [1 1 1 2 2 3 3 3 3 4 5 5]
column: [3 5 8 2 6 4 0 6 1 0 2 6]
now i would like to have for the value 1 in the 1st column a sum of 3, 5 and 8 in the 2nd column, the same goes for 2, 3 and so from the 1st column.
Like this for example:
[1 2 3 4 5],
[16 8 11 0 8]
i'm thankful for any suggestions!
Sum all values when values are equal :
Just to init :
a = [1 1 1 2 2 3 3 3 3 4 5 5 ; 3 5 8 2 6 4 0 6 1 0 2 6];
a = a.';
Let's go :
n=0
for i=1:size(a,1)
if a(i,1) == a(i,2)
n = n + a(i,1)
end
end
n
For the second question :
mat=0
for j = 1:max(a(:,1))
n=0
for i=1:size(a,1)
if j == a(i,1)
n = n + a(i,2)
end
end
mat(j,1) = j
mat(j,2) = n
end
mat
Result :
mat =
1 16
2 8
3 11
4 0
5 8
I am trying to recieve all values from a variable (b) when using a criterium based on another variable (a) (it's like the =IF function in excel). like this:
Example:
(a): 1 2 2 2 3 3 3 3
(b): 3 6 3 5 6 4 5 4
my criteria is
(a) = 2
my reply has to be:
(b) = 6 3 5
I tried to find a solution using arrayfun, like this:
arrayfun(#(x) b(find(a == x, 1, 'first')), 2)
obviously, it only answers the 6, the first number that matches the criterium. Can I somehow formulate arrayfun correctly? Or do I need a whole other function?
Thanks!
Don't you just want:
a = [ 1 2 2 2 3 3 3 3]
b = [3 6 3 5 6 4 5 4]
b(a == 2)
ans =
6 3 5
If a was a matrix then:
a = [ 1 2 2 2 3 3 3 3; ...
1 1 1 2 2 3 4 4; ]
b = [3 6 3 5 6 4 5 4]
b(a(1,:)==2)
ans =
6 3 5
I am interested in how can I add rows and columns of zeros in a matrix so that it looks like this:
1 0 2 0 3
1 2 3 0 0 0 0 0
2 3 4 => 2 0 3 0 4
5 4 3 0 0 0 0 0
5 0 4 0 3
Actually I am interested in how can I do this efficiently, because walking the matrix and adding zeros takes a lot of time if you work with a big matrix.
Update:
Thank you very much.
Now I'm trying to replace the zeroes with the sum of their neighbors:
1 0 2 0 3 1 3 2 5 3
1 2 3 0 0 0 0 0 3 8 5 12... and so on
2 3 4 => 2 0 3 0 4 =>
5 4 3 0 0 0 0 0
5 0 4 0 3
as you can see i'm considering all the 8 neighbors of an element, but again using for and walking the matrix slows me down quite a bit, is there a faster way ?
Let your little matrix be called m1. Then:
m2 = zeros(5)
m2(1:2:end,1:2:end) = m1(:,:)
Obviously this is hard-wired to your example, I'll leave it to you to generalise.
Here are two ways to do part 2 of the question. The first does the shifts explicitly, and the second uses conv2. The second way should be faster.
M=[1 2 3; 2 3 4 ; 5 4 3];
% this matrix (M expanded) has zeros inserted, but also an extra row and column of zeros
Mex = kron(M,[1 0 ; 0 0 ]);
% The sum matrix is built from shifts of the original matrix
Msum = Mex + circshift(Mex,[1 0]) + ...
circshift(Mex,[-1 0]) +...
circshift(Mex,[0 -1]) + ...
circshift(Mex,[0 1]) + ...
circshift(Mex,[1 1]) + ...
circshift(Mex,[-1 1]) + ...
circshift(Mex,[1 -1]) + ...
circshift(Mex,[-1 -1]);
% trim the extra line
Msum = Msum(1:end-1,1:end-1)
% another version, a bit more fancy:
MexTrimmed = Mex(1:end-1,1:end-1);
MsumV2 = conv2(MexTrimmed,ones(3),'same')
Output:
Msum =
1 3 2 5 3
3 8 5 12 7
2 5 3 7 4
7 14 7 14 7
5 9 4 7 3
MsumV2 =
1 3 2 5 3
3 8 5 12 7
2 5 3 7 4
7 14 7 14 7
5 9 4 7 3