Sampling a Greyscale image into 8 levels - image

What I am trying to do:-
Using MATLAB, I am trying to read a Greyscale image (having pixel values bw range 0-255) i.e. an 8bit image into like 3 bit image, hence it is like sampling the range into 8 different levels. For example if the pixel value is 25 then as it comes bw range 0-31, it will be assigned value 0, for bw 32-63 level will be 1 and so on until finally range 224-255 it will be on range 7.
After that I am counting the total no of pixels in different levels.
Code:-
img=imread('Cameraman.bmp');
r=size(img,1);
c=size(img,2);
pixel_count=zeros(9,1);
for i=1:r
for j=1:c
if fix(img(i,j)/31)==8
img(i,j)
end
img(i,j)=fix(img(i,j)/33);
pixel_count(img(i,j)+1)=pixel_count(img(i,j)+1)+1;
end
end
pixel_count
My Problem:-
Even if the range of each pixel is from 0-255, and I am dividing it into 8 levels, I am getting a total of 9 levels.
For debugging it I added the if statement in the code and my output is:--
ans = 248
ans = 250
ans = 249
ans = 249
ans = 235
ans = 249
ans = 249
ans = 235
...and more
pixel_count =
11314
3741
2061
5284
12629
25590
4439
437
41
As you can see for some values like 249,235 and more I am getting the extra 9th level.
What is the problem here. Please help.
Thank You.

You aren't dividing by the right value properly. You need to divide by 32, then take the floor / fix. Between 0-31, if you divide by 32 then take the floor / fix, you get the value 0, between 31-63, you get 1, up until 224-255 which gives you 7.
Also, your for loop is incorrect. You are mistakenly replacing the pixel of the input image with its bin location. I would also change the precision to double. It seems that with my experiments, using fix combined with a uint8 image gives me that random 9th bin index that you're talking about.
Take a look at some sample results from my REPL:
>> fix(240/32) + 1
ans =
8
>> fix(uint8(240)/32) + 1
ans =
9
>> fix(uint8(255)/32) + 1
ans =
9
>> fix(255/32) + 1
ans =
8
Therefore, it's a problem with the image type. For any values that are beyond 240, the value when being divided by 32 as it's uint8 gets rounded so that 240 / 32 = 7.5 but because it's uint8 and it's an integer, it gets rounded to 8, then adding 1 makes it go to 9. Therefore, anything beyond 240 will get rounded to 8 and ultimately giving you 9 when adding by 1.
So, simply change the division to be 32, not 33 or 31 and fix what I said above:
img=imread('Cameraman.bmp');
img = double(img); %// Change
r=size(img,1);
c=size(img,2);
pixel_count=zeros(8,1); %// Change
for i=1:r
for j=1:c
pix = fix(img(i,j)/32); %// Change here
pixel_count(pix+1)=pixel_count(pix+1) + 1; %// Change
end
end
pixel_count
As a minor note, to check to see if you're right, use histc:
pixel_count = histc(fix(double(img(:))/32) + 1, 1:8);
If you got your code right, your code and with what I wrote above should match. Using the cameraman.tif image that's built-in to the Image Processing Toolbox, let's compare the outputs:
>> pixel_count
pixel_count =
13532
2500
2104
8341
15333
22553
817
356
>> pixel_count2 = histc(fix(double(img(:))/32) + 1, 1:8)
pixel_count2 =
13532
2500
2104
8341
15333
22553
817
356
Looks good to me!

Related

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

Decomposing uint32s into uint8s in a large matrix

For a project I am working on, I am loading in large image files, which Matlab inputs as LxWx3 arrays of uint8s. I have a function which concatenates these component-wise into a LxWx1 array of uint32s, but I can't find a way to do the reverse without using nested for loops, which is far too slow for the matrices I am working with.
Could anyone recommend a way to accomplish this effuciently? Basically, given a LxW Matrix of uint32s, I want to return a LxWx3 matrix of uint8s, where the (x, y,1:3) components are the three most significant bytes in the corresponding uint32.
You can do that with typecast:
A = uint32([2^16 2^30; 256 513]);
B = permute(reshape(typecast(A(:), 'uint8'), [], size(A,1), size(A,2)), [2 3 1]);
B = flipdim(B, 3); %// flip 3rd dim to bring MSB first, if needed (depends on computer)
B = B(:,:,2:4);
Example: for A = uint32([2^16 2^30; 256 513]);
A =
65536 1073741824
256 513
the result is
B(:,:,1) =
1 0
0 0
B(:,:,2) =
0 0
1 2
B(:,:,3) =
0 0
0 1

What is the most efficient way to implement zig-zag ordering in MATLAB? [duplicate]

I have an NxM matrix in MATLAB that I would like to reorder in similar fashion to the way JPEG reorders its subblock pixels:
(image from Wikipedia)
I would like the algorithm to be generic such that I can pass in a 2D matrix with any dimensions. I am a C++ programmer by trade and am very tempted to write an old school loop to accomplish this, but I suspect there is a better way to do it in MATLAB.
I'd be rather want an algorithm that worked on an NxN matrix and go from there.
Example:
1 2 3
4 5 6 --> 1 2 4 7 5 3 6 8 9
7 8 9
Consider the code:
M = randi(100, [3 4]); %# input matrix
ind = reshape(1:numel(M), size(M)); %# indices of elements
ind = fliplr( spdiags( fliplr(ind) ) ); %# get the anti-diagonals
ind(:,1:2:end) = flipud( ind(:,1:2:end) ); %# reverse order of odd columns
ind(ind==0) = []; %# keep non-zero indices
M(ind) %# get elements in zigzag order
An example with a 4x4 matrix:
» M
M =
17 35 26 96
12 59 51 55
50 23 70 14
96 76 90 15
» M(ind)
ans =
17 35 12 50 59 26 96 51 23 96 76 70 55 14 90 15
and an example with a non-square matrix:
M =
69 9 16 100
75 23 83 8
46 92 54 45
ans =
69 9 75 46 23 16 100 83 92 54 8 45
This approach is pretty fast:
X = randn(500,2000); %// example input matrix
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).'; %'// output row vector
Benchmarking
The following code compares running time with that of Amro's excellent answer, using timeit. It tests different combinations of matrix size (number of entries) and matrix shape (number of rows to number of columns ratio).
%// Amro's approach
function y = zigzag_Amro(M)
ind = reshape(1:numel(M), size(M));
ind = fliplr( spdiags( fliplr(ind) ) );
ind(:,1:2:end) = flipud( ind(:,1:2:end) );
ind(ind==0) = [];
y = M(ind);
%// Luis' approach
function y = zigzag_Luis(X)
[r, c] = size(X);
M = bsxfun(#plus, (1:r).', 0:c-1);
M = M + bsxfun(#times, (1:r).'/(r+c), (-1).^M);
[~, ind] = sort(M(:));
y = X(ind).';
%// Benchmarking code:
S = [10 30 100 300 1000 3000]; %// reference to generate matrix size
f = [1 1]; %// number of cols is S*f(1); number of rows is S*f(2)
%// f = [0.5 2]; %// plotted with '--'
%// f = [2 0.5]; %// plotted with ':'
t_Amro = NaN(size(S));
t_Luis = NaN(size(S));
for n = 1:numel(S)
X = rand(f(1)*S(n), f(2)*S(n));
f_Amro = #() zigzag_Amro(X);
f_Luis = #() zigzag_Luis(X);
t_Amro(n) = timeit(f_Amro);
t_Luis(n) = timeit(f_Luis);
end
loglog(S.^2*prod(f), t_Amro, '.b-');
hold on
loglog(S.^2*prod(f), t_Luis, '.r-');
xlabel('number of matrix entries')
ylabel('time')
The figure below has been obtained with Matlab R2014b on Windows 7 64 bits. Results in R2010b are very similar. It is seen that the new approach reduces running time by a factor between 2.5 (for small matrices) and 1.4 (for large matrices). Results are seen to be almost insensitive to matrix shape, given a total number of entries.
Here's a non-loop solution zig_zag.m. It looks ugly but it works!:
function [M,index] = zig_zag(M)
[r,c] = size(M);
checker = rem(hankel(1:r,r-1+(1:c)),2);
[rEven,cEven] = find(checker);
[cOdd,rOdd] = find(~checker.'); %'#
rTotal = [rEven; rOdd];
cTotal = [cEven; cOdd];
[junk,sortIndex] = sort(rTotal+cTotal);
rSort = rTotal(sortIndex);
cSort = cTotal(sortIndex);
index = sub2ind([r c],rSort,cSort);
M = M(index);
end
And a test matrix:
>> M = [magic(4) zeros(4,1)];
M =
16 2 3 13 0
5 11 10 8 0
9 7 6 12 0
4 14 15 1 0
>> newM = zig_zag(M) %# Zig-zag sampled elements
newM =
16
2
5
9
11
3
13
10
7
4
14
6
8
0
0
12
15
1
0
0
Here's a way how to do this. Basically, your array is a hankel matrix plus vectors of 1:m, where m is the number of elements in each diagonal. Maybe someone else has a neat idea on how to create the diagonal arrays that have to be added to the flipped hankel array without a loop.
I think this should be generalizeable to a non-square array.
% for a 3x3 array
n=3;
numElementsPerDiagonal = [1:n,n-1:-1:1];
hadaRC = cumsum([0,numElementsPerDiagonal(1:end-1)]);
array2add = fliplr(hankel(hadaRC(1:n),hadaRC(end-n+1:n)));
% loop through the hankel array and add numbers counting either up or down
% if they are even or odd
for d = 1:(2*n-1)
if floor(d/2)==d/2
% even, count down
array2add = array2add + diag(1:numElementsPerDiagonal(d),d-n);
else
% odd, count up
array2add = array2add + diag(numElementsPerDiagonal(d):-1:1,d-n);
end
end
% now flip to get the result
indexMatrix = fliplr(array2add)
result =
1 2 6
3 5 7
4 8 9
Afterward, you just call reshape(image(indexMatrix),[],1) to get the vector of reordered elements.
EDIT
Ok, from your comment it looks like you need to use sort like Marc suggested.
indexMatrixT = indexMatrix'; % ' SO formatting
[dummy,sortedIdx] = sort(indexMatrixT(:));
sortedIdx =
1 2 4 7 5 3 6 8 9
Note that you'd need to transpose your input matrix first before you index, because Matlab counts first down, then right.
Assuming X to be the input 2D matrix and that is square or landscape-shaped, this seems to be pretty efficient -
[m,n] = size(X);
nlim = m*n;
n = n+mod(n-m,2);
mask = bsxfun(#le,[1:m]',[n:-1:1]);
start_vec = m:m-1:m*(m-1)+1;
a = bsxfun(#plus,start_vec',[0:n-1]*m);
offset_startcol = 2- mod(m+1,2);
[~,idx] = min(mask,[],1);
idx = idx - 1;
idx(idx==0) = m;
end_ind = a([0:n-1]*m + idx);
offsets = a(1,offset_startcol:2:end) + end_ind(offset_startcol:2:end);
a(:,offset_startcol:2:end) = bsxfun(#minus,offsets,a(:,offset_startcol:2:end));
out = a(mask);
out2 = m*n+1 - out(end:-1:1+m*(n-m+1));
result = X([out2 ; out(out<=nlim)]);
Quick runtime tests against Luis's approach -
Datasize: 500 x 2000
------------------------------------- With Proposed Approach
Elapsed time is 0.037145 seconds.
------------------------------------- With Luis Approach
Elapsed time is 0.045900 seconds.
Datasize: 5000 x 20000
------------------------------------- With Proposed Approach
Elapsed time is 3.947325 seconds.
------------------------------------- With Luis Approach
Elapsed time is 6.370463 seconds.
Let's assume for a moment that you have a 2-D matrix that's the same size as your image specifying the correct index. Call this array idx; then the matlab commands to reorder your image would be
[~,I] = sort (idx(:)); %sort the 1D indices of the image into ascending order according to idx
reorderedim = im(I);
I don't see an obvious solution to generate idx without using for loops or recursion, but I'll think some more.

How to define matching axis notches from existing "step list"

I need a way to align tick marks on two separate axis, while being able to control the "step" value (value between tick marks), where both axis start at mark 0 and end on a different maximum value.
Why this problem:
Flot, the JS charting package has an option to align tick marks, but when I do, I cannot control the step value. I can however control the step value directly, but then I lose the ability to align tick marks. I can however revert to defining my own max and step values, to get what I need (aligned tick marks while maintaining desired step value), but I need some help. yielding this question (read on for details).
Example
Let a be maximum value on axis A and b, be maximum value on axis B.
In this example, let a = 30, and b = 82.
Let's say I want 6 tick marks (not counting the extra tick mark at end of axis). In reality I guessed at 6 after trying out a few.
Once I have a desired number of tick marks, I can do something like this:
30 / 6 = 5 (I just go the needed step value for axis A)
Now need to figure out tick alignment for axis B
82 / 6 = 13.67 (not a good value, I prefer something more rounded)
move max value of B to 90 , where 90 / 6 = 15 (good - I just got the needed step value for axis B)
End Result
Input:
a_max = 30, b_max = 82
(in reality a_max could be 28.5, 29.42, b_max could be 84, 85.345, etc)
Output:
a_adjusted_max = 30, b_adjusted_max = 90,
a_step = 5, b_step = 15
number of ticks = 6 (+1 if count the end)
Visual:
|---------|---------|---------|---------|---------|---------> A
0 5 10 15 20 25 30
|---------|---------|---------|---------|---------|---------> B
0 15 30 45 60 75 90
Summary of "Demands"
Need step value for each axis to be one of 1, 2, 5, 10, 15, 20, 25, 50, 100 (in example was 5 for A, 15 for B)
Need adjusted max value for each axis (in example was 30 for A, 90 for B)
Need number of ticks to match for both axis
(optional) Number of ticks is flexible but should be anywhere between 4 and 12 as a sweet spot
adjusted max value is at or greater than original max value, and is located at a "rounded number" (i.e. 90 is prefered over 82 as in my above example)
Problems (Question)
I need to remove most of the guessing and automate tick mark generation.
i.e. at first, I Need better way to get number of tick marks because I guessed at number of tick marks I wanted above, because I wanted a good "step" value, which can be something like 1, 2, 5, 10, 15, 20, 25, 50, 100. Max values start at 4, and can go up to 100. In rarer cases go up to 500. In most cases the max values stay between 30-90.
How can I do so?
Here's a procedure I came up with. I'm assuming you only want integers.
choose a number of ticks from 4 to 12
calculate the number of steps needed for the A and B axes using this number of ticks
find how much we would have to extend axis A and axis B using these step values; add these numbers together and remember the result
repeat from the start for the next tick value
we choose the number of ticks that gives the minimal score; if there is a tie we choose the smaller number of ticks
Here are some example results:
a=30, b=82 gives 4 ticks
0 10 20 30
0 28 56 84
a=8, b=5 gives 6 ticks
0 2 4 6 8 10
0 1 2 3 4 5
Here's the pseudocode:
a = range of A axis
b = range of B axis
tickList[] = {4,5,6,7,8,9,10,11,12}
// calculate the scores for each number of ticks
for i from 0 to length(tickList)-1
ticks = tickList[i]
// find the number of steps we would use for this number of ticks
Astep = ceiling(a/(ticks-1))
Bstep = ceiling(b/(ticks-1))
// how much we would need to extend the A axis
if (a%Astep != 0)
Aextend[i] = Astep - a%Astep
else
Aextend[i] = 0
end
// how much we would need to extend the B axis
if (b%Bstep != 0)
Bextend[i] = Bstep - b%Bstep
else
Bextend[i] = 0
end
// the score is the total extending we would need to do
score[i] = Aextend[i] + Bextend[i]
end
// find the number of ticks that minimizes the score
bestIdx = 0
bestScore = 1000;
for i from 0 to length(tickList);
if (score[i] < bestScore)
bestIdx = i
bestScore = score[i]
end
end
bestTick = tickList[bestIdx]
bestAstep = ceiling(a/(bestTick-1))
bestBstep = ceiling(b/(bestTick-1))
A axis goes from 0 by bestAstep to bestAstep*bestTick
B axis goes from 0 by bestBstep to bestBstep*bestTick

fourier and zero padding

I'm filtering an image using a mask and the Discret Fourier Trasform, till now i have this
A=double(imread('C:\Users\samsung\Documents\Lab Imagenes\CHE.jpg','jpg'));
B=[1 4 6 4 1; 4 16 24 16 4; 6 24 36 24 6; 4 16 24 16 4; 1 4 6 4 1];
F=(1/256).*(B);
DFT_A=fftshift(fft2(A));
imshow(DFT_A);
DFT_A_F=DFT_A.*F;
figure
imshow(DFT_A_F)
but when i want to see partial results I got this error
??? Error using ==> times
Matrix dimensions must agree.
Error in ==> fourier1 at 10
DFT_A_F=DFT_A.*F;
I know that i need to do zero padding to the mask, but i don't know how to do it, please I need help
Thanks!
what you want is called 'padarray' , just after you define DFT_A:
padsize= [round(0.5*size(DFT_A,1)-0.5*size(F,1)) round(0.5*size(DFT_A,2)-0.5*size(F,2))];
F = padarray(F, padsize);
DFT_A_F=DFT_A.*F;
...
But why won't you just (given that A is a 2D matrix, so rgb2gray it if needed):
DFT_A_F = conv2(A,B,'same');
It is faster, because you don't need to multiply all these zeros, and should get you the same result.

Resources