Get X and Y positions of a Pixel given the HEIGHT , WIDTH and index of a pixel in FLATTENED array representing the image - image

Imagine you have this image
[[1 2 3] [4 5 6] [7 8 90]]
You flatten it into this format -
[1 2 3 4 5 6 7 8 90]
Now you are given the index of Pixel 90 to be 8.
How can you find that pixel 90 is in Row 3 and column 3?

OpenCL, similarly to other programming languages like C, C++, Java and so on, uses zero based indexing. So in this terms you are looking for Row 2 and Column 2.
Now to calculate which row that is we need to divide index position 8 by number of columns:
8 / 3 = 2
So in zero based indexing that is a second row.
Now to calculate which column that is we use modulo operator:
8 % 3 = 2

In the 2D case, a point (x,y) in a rectangle with the dimensions (sx,sy) can be represented in 1D space by a linear index n as follows:
n = x+y*sx
Converting the 1D index n back to (x,y) works as follows:
x = n%sx
y = n/sx
For the 3D case, a point (x,y,z) in the a box with dimensions (sx,sy,sz) can be represented in 1D as
n = x+(y+z*sy)*sx
and converted back to (x,y,z) like this:
z = n/(sx*sy);
temp = n%(sx*sy);
y = temp/sx;
x = temp%sx;
Note that "/" here means integer division (always rounds down the result) and "%" is the modulo operator.

Related

Neighbors in the matrix - algorithm

I have a problem with coming up with an algorithm for the "graph" :(
Maybe one of you would be so kind and direct me somehow <3
The task is as follows:
We have a board of at least 3x3 (it doesn't have to be a square, it can be 4x5 for example). The user specifies a sequence of moves (as in Android lock pattern). The task is to check how many points he has given are adjacent to each other horizontally or vertically.
Here is an example:
Matrix:
1 2 3 4
5 6 7 8
9 10 11 12
The user entered the code: 10,6,7,3
The algorithm should return the number 3 because:
10 is a neighbor of 6
6 is a neighbor of 7
7 is a neighbor of 3
Eventually return 3
Second example:
Matrix:
1 2 3
4 5 6
7 8 9
The user entered the code: 7,8,6,3
The algorithm should return 2 because:
7 is a neighbor of 8
8 is not a neighbor of 6
6 is a neighbor of 3
Eventually return 2
Ofc number of operations equal length of array - 1
Sorry for "ile" and "tutaj", i'm polish
If all the codes are unique, use them as keys to a dictionary (with (row/col) pairs as values). Loop thru the 2nd item in user input to the end, check if math.Abs(cur.row-prev.row)+math.Abs(cur.col-prev.col)==1. This is not space efficient but deal with user input in linear complexity.
The idea is you have 4 conditions, one for each direction. Given any matrix of the shape n,m which is made of a sequence of integers AND given any element:
The element left or right will always be + or - 1 to the given element.
The element up or down will always be + or - m to the given element.
So, if abs(x-y) is 1 or m, then x and y are neighbors.
I demonstrate this in python.
def get_neighbors(seq,matrix):
#Conditions
check = lambda x,y,m: np.abs(x-y)==1 or np.abs(x-y)==m
#Pairs of sequences appended with m
params = zip(seq, seq[1:], [matrix.shape[1]]*(len(seq)-1))
neighbours = [check(*i) for i in params]
count = sum(neighbours)
return neighbours, count
seq = [7,8,6,3]
matrix = np.arange(1,10).reshape((3,3))
neighbours, count = get_neighbors(seq, matrix)
print('Matrix:')
print(matrix)
print('')
print('Sequence:', seq)
print('')
print('Count of neighbors:',count)
Matrix:
[[ 1 2 3 4]
[ 5 6 7 8]
[ 9 10 11 12]]
Sequence: [10, 6, 7, 3]
Count of neighbors: 3
Another example -
seq = [7,8,6,3]
matrix = np.arange(1,10).reshape((3,3))
neighbours, count = get_neighbors(seq, matrix)
Matrix:
[[1 2 3]
[4 5 6]
[7 8 9]]
Sequence: [7, 8, 6, 3]
Count of neighbors: 2
So your input is the width of a table, the height of a table, and a list of numbers.
W = 4, H = 3, list = [10,6,7,3]
There are two steps:
Convert the list of numbers into a list of row/column coordinates (1 to [1,1], 5 to [2,1], 12 to [3,4]).
In the new list of coordinates, find consequent pairs, which have one coordinate identical, and the other one has a difference of 1.
Both steps are quite simple ("for" loops). Do you have problems with 1 or 2?

Matlab - Algorithm for calculating 1d consecutive line segment edges from midpoints?

So I have a rectilinear grid that can be described with 2 vectors. 1 for the x-coordinates of the cell centres and one for the y-coordinates. These are just points with spacing like x spacing is 50 scaled to 10 scaled to 20 (55..45..30..10,10,10..10,12..20,20,20) and y spacing is 60 scaled to 40 scaled to 60 (60,60,60,55..42,40,40,40..40,42..60,60) and the grid is made like this
e.g. x = 1 2 3, gridx = 1 2 3, y = 10 11 12, gridy = 10 10 10
1 2 3 11 11 11
1 2 3 12 12 12
so then cell centre 1 is 1,10 cc2 is 2,10 etc.
Now Im trying to formulate an algorithm to calculate the positions of the cell edges in the x and y direction. So like my first idea was to first get the first edge using x(1)-[x(2)-x(1)]/2, in the real case x(2)-x(1) is equal to 60 and x(1) = 16348.95 so celledge1 = x(1)-30 = 16318.95. Then after calculating the first one I go through a loop and calculate the rest like this:
for aa = 2:length(x)+1
celledge1(aa) = x(aa-1) + [x(aa-1)-celledge(aa-1)]
end
And I did the same for y. This however does not work and my y vector in the area where the edge spacing should be should be 40 is 35,45,35,45... approx.
Anyone have any idea why this doesnt work and can point me in the right direction. Cheers
Edit: Tried to find a solution using geometric alebra:
We are trying to find the points A,B,C,....H. From basic geometry we know:
c1 (centre 1) = [A+B]/2 and c2 = [B+C]/2 etc. etc.
So we have 7 equations and 8 variables. We also know the the first few distances between centres are equal (60,60,60,60) therefore the first segment is 60 too.
B - A = 60
So now we have 8 equations and 8 variables so I made this algorithm in Matlab:
edgex = zeros(length(DATA2.x)+1,1);
edgey = zeros(length(DATA2.y)+1,1);
edgex(1) = (DATA2.x(1)*2-diffx(1))/2;
edgey(1) = (DATA2.y(1)*2-diffy(1))/2;
for aa = 2:length(DATA2.x)+1
edgex(aa) = DATA2.x(aa-1)*2-edgex(aa-1);
end
for aa = 2:length(DATA2.y)+1
edgey(aa) = DATA2.y(aa-1)*2-edgey(aa-1);
end
And I still got the same answer as before with the y spacing going 35,45,35,45 where it should be 40,40,40... Could it be an accuracy error??
Edit: here are the numbers if ur interested and I did the same computation as above only in excel: http://www.filedropper.com/workoutedges
It seems you're just trying to interpolate your data. You can do this with the built-in interp1
x = [30 24 19 16 8 7 16 22 29 31];
xi = interp1(2:2:numel(x)*2, x, 1:(numel(x)*2+1), 'linear', 'extrap');
This just sets up the original data as the even-indexed elements and interpolates the odd indices, including extrapolation for the two end points.
Results:
xi =
Columns 1 through 11:
33.0000 30.0000 27.0000 24.0000 21.5000 19.0000 17.5000 16.0000 12.0000 8.0000 7.5000
Columns 12 through 21:
7.0000 11.5000 16.0000 19.0000 22.0000 25.5000 29.0000 30.0000 31.0000 32.0000

MATLAB: Fast creation of random symmetric Matrix with fixed degree (sum of rows)

I am searching for a method to create, in a fast way a random matrix A with the follwing properties:
A = transpose(A)
A(i,i) = 0 for all i
A(i,j) >= 0 for all i, j
sum(A) =~ degree; the sum of rows are randomly distributed by a distribution I want to specify (here =~ means approximate equality).
The distribution degree comes from a matrix orig, specifically degree=sum(orig), thus I know that matrices with this distribution exist.
For example: orig=[0 12 7 5; 12 0 1 9; 7 1 0 3; 5 9 3 0]
orig =
0 12 7 5
12 0 1 9
7 1 0 3
5 9 3 0
sum(orig)=[24 22 11 17];
Now one possible matrix A=[0 11 5 8, 11 0 4 7, 5 4 0 2, 8 7 2 0] is
A =
0 11 5 8
11 0 4 7
5 4 0 2
8 7 2 0
with sum(A)=[24 22 11 17].
I am trying this for quite some time, but unfortunatly my two ideas didn't work:
version 1:
I switch Nswitch times two random elements: A(k1,k3)--; A(k1,k4)++; A(k2,k3)++; A(k2,k4)--; (the transposed elements aswell).
Unfortunatly, Nswitch = log(E)*E (with E=sum(sum(nn))) in order that the Matrices are very uncorrelated. As my E > 5.000.000, this is not feasible (in particular, as I need at least 10 of such matrices).
version 2:
I create the matrix according to the distribution from scratch. The idea is, to fill every row i with degree(i) numbers, based on the distribution of degree:
nn=orig;
nnR=zeros(size(nn));
for i=1:length(nn)
degree=sum(nn);
howmany=degree(i);
degree(i)=0;
full=rld_cumsum(degree,1:length(degree));
rr=randi(length(full),[1,howmany]);
ff=full(rr);
xx=i*ones([1,length(ff)]);
nnR = nnR + accumarray([xx(:),ff(:)],1,size(nnR));
end
A=nnR;
However, while sum(A')=degree, sum(A) systematically deviates from degree, and I am not able to find the reason for that.
Small deviations from degree are fine of course, but there seem to be systmatical deviations in particulat of the matrices contain in some places large numbers.
I would be very happy if somebody could either show me a fast method for version1, or a reason for the systematic deviation of the distribution in version 2, or a method to create such matrices in a different way. Thank you!
Edit:
This is the problem in matsmath's proposed solution:
Imagine you have the matrix:
orig =
0 12 3 1
12 0 1 9
3 1 0 3
1 9 3 0
with r(i)=[16 22 7 13].
Step 1: r(1)=16, my random integer partition is p(i)=[0 7 3 6].
Step 2: Check that all p(i)<=r(i), which is the case.
Step 3:
My random matrix starts looks like
A =
0 7 3 6
7 0 . .
3 . 0 .
6 . . 0
with the new row sum vector rnew=[r(2)-p(2),...,r(n)-p(n)]=[15 4 7]
Second iteration (here the problem occures):
Step 1: rnew(1)=15, my random integer partition is p(i)=[0 A B]: rnew(1)=15=A+B.
Step 2: Check that all p(i)<=rnew(i), which gives A<=4, B<=7. So A+B<=11, but A+B has to be 15. contradiction :-/
Edit2:
This is the code representing (to the best of my knowledge) the solution posted by David Eisenstat:
orig=[0 12 3 1; 12 0 1 9; 3 1 0 3; 1 9 3 0];
w=[2.2406 4.6334 0.8174 1.6902];
xfull=zeros(4);
for ii=1:1000
rndmat=[poissrnd(w(1),1,4); poissrnd(w(2),1,4); poissrnd(w(3),1,4); poissrnd(w(4),1,4)];
kkk=rndmat.*(ones(4)-eye(4)); % remove diagonal
hhh=sum(sum(orig))/sum(sum(kkk))*kkk; % normalisation
xfull=xfull+hhh;
end
xf=xfull/ii;
disp(sum(orig)); % gives [16 22 7 13]
disp(sum(xf)); % gives [14.8337 9.6171 18.0627 15.4865] (obvious systematic problem)
disp(sum(xf')) % gives [13.5230 28.8452 4.9635 10.6683] (which is also systematically different from [16, 22, 7, 13]
Since it's enough to approximately preserve the degree sequence, let me propose a random distribution where each entry above the diagonal is chosen according to a Poisson distribution. My intuition is that we want to find weights w_i such that the i,j entry for i != j has mean w_i*w_j (all of the diagonal entries are zero). This gives us a nonlinear system of equations:
for all i, (sum_{j != i} w_i*w_j) = d_i,
where d_i is the degree of i. Equivalently,
for all i, w_i * (sum_j w_j) - w_i^2 = d_i.
The latter can be solved by applying Newton's method as described below from a starting solution of w_i = d_i / sqrt(sum_j d_j).
Once we have the w_is, we can sample repeatedly using poissrnd to generate samples of multiple Poisson distributions at once.
(If I have time, I'll try implementing this in numpy.)
The Jacobian matrix of the equation system for a 4 by 4 problem is
(w_2 + w_3 + w_4) w_1 w_1 w_1
w_2 (w_1 + w_3 + w_4) w_2 w_2
w_3 w_3 (w_1 + w_2 + w_4) w_3
w_4 w_4 w_4 (w_1 + w_2 + w_3).
In general, let A be a diagonal matrix where A_{i,i} = sum_j w_j - 2*w_i. Let u = [w_1, ..., w_n]' and v = [1, ..., 1]'. The Jacobian can be written J = A + u*v'. The inverse is given by the Sherman--Morrison formula
A^-1*u*v'*A^-1
J^-1 = (A + u*v')^-1 = A^-1 - -------------- .
1 + v'*A^-1*u
For the Newton step, we need to compute J^-1*y for some given y. This can be done straightforwardly in time O(n) using the above equation. I'll add more detail when I get the chance.
First approach (based on version2)
Let your row sum vector given by the matrix orig [r(1),r(2),...,r(n)].
Step 1. Take a random integer partition of the integer r(1) into exactly n-1 parts, say p(2), p(3), ..., p(n)
Step 2. Check if p(i)<=r(i) for all i=2...n. If not, go to Step 1.
Step 3. Fill out your random matrix first row and colum by the entries 0, p(2), ... , p(n), and consider the new row sum vector [r(2)-p(2),...,r(n)-p(n)].
Repeat these steps with a matrix of order n-1.
The point is, that you randomize one row at a time, and reduce the problem to searching for a matrix of size one less.
As pointed out by OP in the comment, this naive algorithm fails. The reason is that the matrices in question have a further necessary condition on their entries as follows:
FACT:
If A is an orig matrix with row sums [r(1), r(2), ..., r(n)] then necessarily for every i=1..n it holds that r(i)<=-r(i)+sum(r(j),j=1..n).
That is, any row sum, say the ith, r(i), is necessarily at most as big as the sum of the other row sums (not including r(i)).
In light of this, a revised algorithm is possible. Note that in Step 2b. we check if the new row sum vector has the property discussed above.
Step 1. Take a random integer partition of the integer r(1) into exactly n-1 parts, say p(2), p(3), ..., p(n)
Step 2a. Check if p(i)<=r(i) for all i=2...n. If not, go to Step 1.
Step 2b. Check if r(i)-p(i)<=-r(i)+p(i)+sum(r(j)-p(j),j=2..n) for all i=2..n. If not, go to Step 1.
Step 3. Fill out your random matrix first row and colum by the entries 0, p(2), ... , p(n), and consider the new row sum vector [r(2)-p(2),...,r(n)-p(n)].
Second approach (based on version1)
I am not sure if this approach gives you random matrices, but it certainly gives you different matrices.
The idea here is to change some parts of your orig matrix locally, in a way which maintains all of its properties.
You should look for a random 2x2 submatrix below the main diagonal which contains strictly positive entries, like [[a,b],[c,d]] and perturbe its contents by a random value r to [[a+r,b-r],[c-r,d+r]]. You make the same change above the main diagonal too, to keep your new matrix symmetric. Here the point is that the changes within the entries "cancel" each other out.
Of course, r should be chosen in a way such that b-r>=0 and c-r>=0.
You can pursue this idea to modify larger submatrices too. For example, you might choose 3 random row coordinates r1, r2, r2 and 3 random column coordinates c1, c2, and c3 and then make changes in your orig matrix at the 9 positions (ri,cj) as follows: you change your 3x3 submatrix [[a b c],[d e f], [g h i]] to [[a-r b+r c] [d+r e f-r], [g h-r i+r]]. You do the same at the transposed places. Again, the random value r must be chosen in a way so that a-r>=0 and f-r>=0 and h-r>=0. Moreover, c1 and r1, and c3 and r3 must be distinct as you can't change the 0 entries in the main diagonal of the matrix orig.
You can repeat such things over and over again, say 100 times, until you find something which looks random. Note that this idea uses the fact that you have existing knowledge of a solution, this is the matrix orig, while the first approach does not use such knowledge at all.

Make an n x n-1 matrix from 1 x n vector where the i-th row is the vector without the i-th element, without a for loop

I need this for Lagrange polynomials. I'm curious how one would do this without a for loop. The code currently looks like this:
tj = 1:n;
ti = zeros(n,n-1);
for i = 1:n
ti(i,:) = tj([1:i-1, i+1:end]);
end
My tj is not really just a 1:n vector but that's not important. While this for loop gets the job done, I'd rather use some matrix operation. I tried looking for some appropriate matrices to multiply it with, but no luck so far.
Here's a way:
v = [10 20 30 40]; %// example vector
n = numel(v);
M = repmat(v(:), 1, n);
M = M(~eye(n));
M = reshape(M,n-1,n).';
gives
M =
20 30 40
10 30 40
10 20 40
10 20 30
This should generalize to any n
ti = flipud(reshape(repmat(1:n, [n-1 1]), [n n-1]));
Taking a closer look at what's going on. If you look at the resulting matrix closely, you'll see that it's n-1 1's, n-1 2's, etc. from the bottom up.
For the case where n is 3.
ti =
2 3
1 3
1 2
So we can flip this vertically and get
f = flipud(ti);
1 2
1 3
2 3
Really this is [1, 2, 3; 1, 2, 3] reshaped to be 3 x 2 rather than 2 x 3.
In that line of thinking
a = repmat(1:3, [2 1])
1 2 3
1 2 3
b = reshape(a, [3 2]);
1 2
1 3
2 3
c = flipud(b);
2 3
1 3
1 2
We are now back to where you started when we bring it all together and replace 3's with n and 2's with n-1.
Here's another way. First create a matrix where each row is the vector tj but are stacked on top of each other. Next, extract the lower and upper triangular parts of the matrix without the diagonal, then add the results together ensuring that you remove the last column of the lower triangular matrix and the first column of the upper triangular matrix.
n = numel(tj);
V = repmat(tj, n, 1);
L = tril(V,-1);
U = triu(V,1);
ti = L(:,1:end-1) + U(:,2:end);
numel finds the total number of values in tj which we store in n. repmat facilitates the stacking of the vector tj to create a matrix that is n x n large. After, we use tril and triu so that we extract the lower and upper triangular parts of the matrices without the diagonal. In addition, the rest of the matrix is all zero except for the relevant triangular parts. The -1 and 1 flags for tril and triu respectively extract this out successfully while ensuring that the diagonal is all zero. This creates a column of extra zeroes appearing at the last column when calling tril and the first column when calling triu. The last part is to simply add these two matrices together ignoring the last column of the tril result and the first column of the triu result.
Given that tj = [10 20 30 40]; (borrowed from Luis Mendo's example), we get:
ti =
20 30 40
10 30 40
10 20 40
10 20 30

Replace multiple pixels value in an image with a certain value Matlab

I have an image 640x480 img, and I want to replace pixels having values not in this list or array x=[1, 2, 3, 4, 5] with a certain value 10, so that any pixel in img which doesn't have the any of the values in x will be replaced with 10. I already know how to replace only one value using img(img~=1)=10 or multiple values using this img(img~=1 & img~=2 & img~=3 & img~=4 & img~=5)=10 but I when I tried this img(img~=x)=10 it gave an error saying Matrix dimensions must agree. So if anyone could please advise.
You can achieve this very easily with a combination of permute and bsxfun. We can create a 3D column vector that consists of the elements of [1,2,3,4,5], then use bsxfun with the not equals method (#ne) on your image (assuming grayscale) so that we thus create a 3D matrix of 5 slices. Each slice would tell you whether the locations in the image do not match an element in x. The first slice would give you the locations that don't match x = 1, the second slice would give you the locations that don't match x = 2, and so on.
Once you finish this, we can use an all call operating on the third dimension to consolidate the pixel locations that are not equal to all of 1, 2, 3, 4 or 5. The last step would be to take this logical map, which that tells you the locations that are none of 1, 2, 3, 4, or 5 and we'd set those locations to 10.
One thing we need to consider is that the image type and the vector x must be the same type. We can ensure this by casting the vector to be the same class as img.
As such, do something like this:
x = permute([1 2 3 4 5], [3 1 2]);
vals = bsxfun(#ne, img, cast(x, class(img)));
ind = all(vals, 3);
img(ind) = 10;
The advantage of the above method is that the list you want to use to check for the elements can be whatever you want. It prevents having messy logical indexing syntax, like img(img ~= 1 & img ~= 2 & ....). All you have to do is change the input list at the beginning line of the code, and bsxfun, permute and any should do the work for you.
Here's an example 5 x 5 image:
>> rng(123123);
>> img = randi(7, 5, 5)
img =
3 4 3 6 5
7 2 6 5 1
3 1 6 1 7
6 4 4 3 3
6 2 4 1 3
By using the code above, the output we get is:
img =
3 4 3 10 5
10 2 10 5 1
3 1 10 1 10
10 4 4 3 3
10 2 4 1 3
You can most certainly see that those elements that are neither 1, 2, 3, 4 or 5 get set to 10.
Aside
If you don't like the permute and bsxfun approach, one way would be to have a for loop and with an initially all true array, keep logical ANDing the final result with a logical map that consists of those locations which are not equal to each value in x. In the end, we will have a logical map where true are those locations that are neither equal to 1, 2, 3, 4 or 5.
Therefore, do something like this:
ind = true(size(img));
for idx = 1 : 5
ind = ind & img ~= idx;
end
img(ind) = 10;
If you do this instead, you'll see that we get the same answer.
Approach #1
You can use ismember,
which according to its official documentation for a case of ismember(A,B) would output a logical array of the same size as A and with 1's where
any element from B is present in A, 0's otherwise. Since, you are looking to detect "not in the list or array", you need to invert it afterwards, i.e. ~ismember().
In your case, you have img as A and x as B, so ~ismember(img,x) would give you those places where img~=any element in x
You can then map into img to set all those in it to 10 with this final solution -
img(~ismember(img,x)) = 10
Approach #2
Similar to rayryeng's solution, you can use bsxfun, but keep it in 2D which could be more efficient as it would also avoid permute. The implementation would look something like this -
img(reshape(all(bsxfun(#ne,img(:),x(:).'),2),size(img))) = 10

Resources